2023-02-09 05:55:12 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace OCA\Memories\Db;
|
|
|
|
|
|
|
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
|
|
|
use OCP\IDBConnection;
|
|
|
|
|
2023-02-09 08:35:35 +00:00
|
|
|
const CLUSTER_DEG = 0.0003;
|
|
|
|
|
2023-02-09 05:55:12 +00:00
|
|
|
trait TimelineWriteMap
|
|
|
|
{
|
|
|
|
protected IDBConnection $connection;
|
|
|
|
|
2023-02-09 08:35:35 +00:00
|
|
|
protected function getMapCluster(int $prevCluster, float $lat, float $lon): int
|
2023-02-09 05:55:12 +00:00
|
|
|
{
|
2023-02-09 08:35:35 +00:00
|
|
|
// Get all possible clusters within CLUSTER_DEG radius
|
2023-02-09 05:55:12 +00:00
|
|
|
$query = $this->connection->getQueryBuilder();
|
2023-02-09 08:35:35 +00:00
|
|
|
$query->select('id', 'lat', 'lon')
|
|
|
|
->from('memories_mapclusters')
|
|
|
|
->andWhere($query->expr()->gte('lat', $query->createNamedParameter($lat - CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
|
|
|
|
->andWhere($query->expr()->lte('lat', $query->createNamedParameter($lat + CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
|
|
|
|
->andWhere($query->expr()->gte('lon', $query->createNamedParameter($lon - CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
|
|
|
|
->andWhere($query->expr()->lte('lon', $query->createNamedParameter($lon + CLUSTER_DEG, IQueryBuilder::PARAM_STR)))
|
2023-02-09 05:55:12 +00:00
|
|
|
;
|
2023-02-09 08:35:35 +00:00
|
|
|
$rows = $query->executeQuery()->fetchAll();
|
2023-02-09 05:55:12 +00:00
|
|
|
|
|
|
|
// Find cluster closest to the point
|
2023-02-09 08:35:35 +00:00
|
|
|
$minDist = PHP_INT_MAX;
|
2023-02-09 05:55:12 +00:00
|
|
|
$minId = -1;
|
2023-02-10 04:13:30 +00:00
|
|
|
foreach ($rows as &$r) {
|
2023-02-09 05:55:12 +00:00
|
|
|
$clusterLat = (float) $r['lat'];
|
|
|
|
$clusterLon = (float) $r['lon'];
|
|
|
|
$dist = ($lat - $clusterLat) ** 2 + ($lon - $clusterLon) ** 2;
|
|
|
|
if ($dist < $minDist) {
|
|
|
|
$minDist = $dist;
|
2023-02-10 04:13:30 +00:00
|
|
|
$minId = (int) $r['id'];
|
2023-02-09 05:55:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no cluster found, create a new one
|
|
|
|
if ($minId <= 0) {
|
|
|
|
$this->removeFromCluster($prevCluster, $lat, $lon);
|
|
|
|
|
|
|
|
return $this->createMapCluster($lat, $lon);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the file was previously in the same cluster, return that
|
|
|
|
if ($prevCluster === $minId) {
|
|
|
|
return $minId;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the file was previously in a different cluster,
|
|
|
|
// remove it from the first cluster and add it to the second
|
|
|
|
$this->removeFromCluster($prevCluster, $lat, $lon);
|
|
|
|
$this->addToCluster($minId, $lat, $lon);
|
|
|
|
|
|
|
|
return $minId;
|
|
|
|
}
|
|
|
|
|
2023-02-09 08:35:35 +00:00
|
|
|
protected function addToCluster(int $clusterId, float $lat, float $lon): void
|
|
|
|
{
|
|
|
|
if ($clusterId <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = $this->connection->getQueryBuilder();
|
|
|
|
$query->update('memories_mapclusters')
|
|
|
|
->set('point_count', $query->createFunction('point_count + 1'))
|
|
|
|
->set('lat_sum', $query->createFunction("lat_sum + {$lat}"))
|
|
|
|
->set('lon_sum', $query->createFunction("lon_sum + {$lon}"))
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))
|
|
|
|
;
|
|
|
|
$query->executeStatement();
|
2023-02-10 17:26:49 +00:00
|
|
|
|
|
|
|
$this->updateMapAggregates($clusterId);
|
2023-02-09 08:35:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function createMapCluster(float $lat, float $lon): int
|
2023-02-09 05:55:12 +00:00
|
|
|
{
|
|
|
|
$query = $this->connection->getQueryBuilder();
|
2023-02-09 08:35:35 +00:00
|
|
|
$query->insert('memories_mapclusters')
|
2023-02-09 05:55:12 +00:00
|
|
|
->values([
|
|
|
|
'point_count' => $query->createNamedParameter(1, IQueryBuilder::PARAM_INT),
|
|
|
|
'lat_sum' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR),
|
|
|
|
'lon_sum' => $query->createNamedParameter($lon, IQueryBuilder::PARAM_STR),
|
|
|
|
])
|
|
|
|
;
|
|
|
|
$query->executeStatement();
|
|
|
|
|
2023-02-10 17:26:49 +00:00
|
|
|
$clusterId = (int) $query->getLastInsertId();
|
|
|
|
$this->updateMapAggregates($clusterId);
|
|
|
|
|
|
|
|
return $clusterId;
|
2023-02-09 05:55:12 +00:00
|
|
|
}
|
|
|
|
|
2023-02-09 08:35:35 +00:00
|
|
|
private function removeFromCluster(int $clusterId, float $lat, float $lon): void
|
2023-02-09 05:55:12 +00:00
|
|
|
{
|
|
|
|
if ($clusterId <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = $this->connection->getQueryBuilder();
|
2023-02-09 08:35:35 +00:00
|
|
|
$query->update('memories_mapclusters')
|
2023-02-09 05:55:12 +00:00
|
|
|
->set('point_count', $query->createFunction('point_count - 1'))
|
2023-02-09 08:35:35 +00:00
|
|
|
->set('lat_sum', $query->createFunction("lat_sum - {$lat}"))
|
|
|
|
->set('lon_sum', $query->createFunction("lon_sum - {$lon}"))
|
2023-02-10 17:26:49 +00:00
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))
|
|
|
|
;
|
|
|
|
$query->executeStatement();
|
|
|
|
|
|
|
|
$this->updateMapAggregates($clusterId);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function updateMapAggregates(int $clusterId): void
|
|
|
|
{
|
|
|
|
if ($clusterId <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = $this->connection->getQueryBuilder();
|
|
|
|
$query->update('memories_mapclusters')
|
2023-02-09 05:55:12 +00:00
|
|
|
->set('lat', $query->createFunction('lat_sum / point_count'))
|
|
|
|
->set('lon', $query->createFunction('lon_sum / point_count'))
|
2023-02-09 16:25:37 +00:00
|
|
|
->set('last_update', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
|
2023-02-09 05:55:12 +00:00
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)))
|
|
|
|
;
|
|
|
|
$query->executeStatement();
|
|
|
|
}
|
|
|
|
}
|