diff --git a/lib/Controller/ApiBase.php b/lib/Controller/ApiBase.php index 4a73669c..9ec93740 100644 --- a/lib/Controller/ApiBase.php +++ b/lib/Controller/ApiBase.php @@ -297,7 +297,7 @@ class ApiBase extends Controller /** * Given a list of file ids, return the first preview image possible. */ - protected function getPreviewFromImageList(array &$list, int $quality=512) + protected function getPreviewFromImageList(array &$list, int $quality = 512) { // Get preview manager $previewManager = \OC::$server->get(\OCP\IPreview::class); diff --git a/lib/Controller/MapController.php b/lib/Controller/MapController.php index 612a2420..d7e4f145 100644 --- a/lib/Controller/MapController.php +++ b/lib/Controller/MapController.php @@ -58,6 +58,7 @@ class MapController extends ApiBase try { $clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds, $root); + return new JSONResponse($clusters); } catch (\Exception $e) { return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); diff --git a/lib/Db/TimelineQueryMap.php b/lib/Db/TimelineQueryMap.php index 28c51a6c..9efa94e4 100644 --- a/lib/Db/TimelineQueryMap.php +++ b/lib/Db/TimelineQueryMap.php @@ -44,7 +44,7 @@ trait TimelineQueryMap $count = $query->createFunction('COUNT(m.fileid) AS count'); $query->select($lat, $lon, $count) - ->from('memories_map_clusters', 'c') + ->from('memories_mapclusters', 'c') ; if ($gridLen > 0.02) { @@ -58,7 +58,7 @@ trait TimelineQueryMap } // JOIN with memories for files from the current user - $query->innerJoin('c', 'memories', 'm', $query->expr()->eq('c.id', 'm.map_cluster_id')); + $query->innerJoin('c', 'memories', 'm', $query->expr()->eq('c.id', 'm.mapcluster')); // JOIN with filecache for existing files $query = $this->joinFilecache($query, $root, true, false); @@ -96,7 +96,7 @@ trait TimelineQueryMap // SELECT all photos with this tag $query->select('f.fileid', 'f.etag')->from('memories', 'm')->where( - $query->expr()->eq('m.map_cluster_id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)) + $query->expr()->eq('m.mapcluster', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT)) ); // WHERE these photos are in the user's requested folder recursively diff --git a/lib/Db/TimelineWrite.php b/lib/Db/TimelineWrite.php index 89edb592..d5c511a8 100644 --- a/lib/Db/TimelineWrite.php +++ b/lib/Db/TimelineWrite.php @@ -15,6 +15,7 @@ use Psr\Log\LoggerInterface; require_once __DIR__.'/../ExifFields.php'; const DELETE_TABLES = ['memories', 'memories_livephoto', 'memories_places']; +const TRUNCATE_TABLES = ['memories_mapclusters']; class TimelineWrite { @@ -82,7 +83,7 @@ class TimelineWrite // Check if need to update $query = $this->connection->getQueryBuilder(); - $query->select('fileid', 'mtime', 'map_cluster_id') + $query->select('fileid', 'mtime', 'mapcluster') ->from('memories') ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) ; @@ -163,16 +164,24 @@ class TimelineWrite // Store location data $lat = null; $lon = null; - $mapCluster = $prevRow ? (int) $prevRow['map_cluster_id'] : -1; + $mapCluster = $prevRow ? (int) $prevRow['mapcluster'] : -1; if (\array_key_exists('GPSLatitude', $exif) && \array_key_exists('GPSLongitude', $exif)) { + $lat = (float) $exif['GPSLatitude']; + $lon = (float) $exif['GPSLongitude']; + + try { + $mapCluster = $this->getMapCluster($mapCluster, $lat, $lon); + $mapCluster = $mapCluster <= 0 ? null : $mapCluster; + } catch (\Error $e) { + $logger = \OC::$server->get(LoggerInterface::class); + $logger->log(3, 'Error updating map cluster data: '.$e->getMessage(), ['app' => 'memories']); + } + try { - $lat = (float) $exif['GPSLatitude']; - $lon = (float) $exif['GPSLongitude']; - $mapCluster = $this->getMapCluster($fileId, $mapCluster, $lat, $lon); $this->updatePlacesData($fileId, $lat, $lon); } catch (\Error $e) { $logger = \OC::$server->get(LoggerInterface::class); - $logger->log(3, 'Error updating geo data: '.$e->getMessage(), ['app' => 'memories']); + $logger->log(3, 'Error updating places data: '.$e->getMessage(), ['app' => 'memories']); } } @@ -191,7 +200,7 @@ class TimelineWrite 'liveid' => $query->createNamedParameter($liveid, IQueryBuilder::PARAM_STR), 'lat' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR), 'lon' => $query->createNamedParameter($lon, IQueryBuilder::PARAM_STR), - 'map_cluster_id' => $query->createNamedParameter($mapCluster, IQueryBuilder::PARAM_INT), + 'mapcluster' => $query->createNamedParameter($mapCluster, IQueryBuilder::PARAM_INT), ]; if ($prevRow) { @@ -241,7 +250,7 @@ class TimelineWrite public function clear() { $p = $this->connection->getDatabasePlatform(); - foreach (DELETE_TABLES as $table) { + foreach (array_merge(DELETE_TABLES, TRUNCATE_TABLES) as $table) { $this->connection->executeStatement($p->getTruncateTableSQL('*PREFIX*'.$table, false)); } } diff --git a/lib/Db/TimelineWriteMap.php b/lib/Db/TimelineWriteMap.php index 93f04338..02999d28 100644 --- a/lib/Db/TimelineWriteMap.php +++ b/lib/Db/TimelineWriteMap.php @@ -7,27 +7,27 @@ namespace OCA\Memories\Db; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +const CLUSTER_DEG = 0.0003; + trait TimelineWriteMap { protected IDBConnection $connection; - protected function getMapCluster(int $fileId, int $prevCluster, float $lat, float $lon): int + protected function getMapCluster(int $prevCluster, float $lat, float $lon): int { - // get all clusters within 30 metres + // Get all possible clusters within CLUSTER_DEG radius $query = $this->connection->getQueryBuilder(); - $query->select('id') - ->from('memories_map_clusters') - ->andWhere($query->expr()->gte('lat', $query->createNamedParameter($lat - 0.0003, IQueryBuilder::PARAM_STR))) - ->andWhere($query->expr()->lte('lat', $query->createNamedParameter($lat + 0.0003, IQueryBuilder::PARAM_STR))) - ->andWhere($query->expr()->gte('lon', $query->createNamedParameter($lon - 0.0003, IQueryBuilder::PARAM_STR))) - ->andWhere($query->expr()->lte('lon', $query->createNamedParameter($lon + 0.0003, IQueryBuilder::PARAM_STR))) + $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))) ; - - $result = $query->executeQuery(); - $rows = $result->fetchAll(); + $rows = $query->executeQuery()->fetchAll(); // Find cluster closest to the point - $minDist = 999999999; + $minDist = PHP_INT_MAX; $minId = -1; foreach ($rows as $r) { $clusterLat = (float) $r['lat']; @@ -59,10 +59,28 @@ trait TimelineWriteMap return $minId; } - protected function createMapCluster(float $lat, float $lon): int + 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}")) + ->set('lat', $query->createFunction('lat_sum / point_count')) + ->set('lon', $query->createFunction('lon_sum / point_count')) + ->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT))) + ; + $query->executeStatement(); + } + + private function createMapCluster(float $lat, float $lon): int { $query = $this->connection->getQueryBuilder(); - $query->insert('memories_map_clusters') + $query->insert('memories_mapclusters') ->values([ 'point_count' => $query->createNamedParameter(1, IQueryBuilder::PARAM_INT), 'lat_sum' => $query->createNamedParameter($lat, IQueryBuilder::PARAM_STR), @@ -76,35 +94,17 @@ trait TimelineWriteMap return (int) $query->getLastInsertId(); } - protected function removeFromCluster(int $clusterId, float $lat, float $lon): void + private function removeFromCluster(int $clusterId, float $lat, float $lon): void { if ($clusterId <= 0) { return; } $query = $this->connection->getQueryBuilder(); - $query->update('memories_map_clusters') + $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)) - ->set('lat', $query->createFunction('lat_sum / point_count')) - ->set('lon', $query->createFunction('lon_sum / point_count')) - ->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT))) - ; - $query->executeStatement(); - } - - protected function addToCluster(int $clusterId, float $lat, float $lon): void - { - if ($clusterId <= 0) { - return; - } - - $query = $this->connection->getQueryBuilder(); - $query->update('memories_map_clusters') - ->set('point_count', $query->createFunction('point_count + 1')) - ->set('lat_sum', $query->createFunction('lat_sum + '.$lat)) - ->set('lon_sum', $query->createFunction('lon_sum + '.$lon)) + ->set('lat_sum', $query->createFunction("lat_sum - {$lat}")) + ->set('lon_sum', $query->createFunction("lon_sum - {$lon}")) ->set('lat', $query->createFunction('lat_sum / point_count')) ->set('lon', $query->createFunction('lon_sum / point_count')) ->where($query->expr()->eq('id', $query->createNamedParameter($clusterId, IQueryBuilder::PARAM_INT))) diff --git a/lib/Migration/Version401100Date20230208181533.php b/lib/Migration/Version401100Date20230208181533.php index 98486b49..bcbd6058 100644 --- a/lib/Migration/Version401100Date20230208181533.php +++ b/lib/Migration/Version401100Date20230208181533.php @@ -48,8 +48,8 @@ class Version401100Date20230208181533 extends SimpleMigrationStep /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); + // Add lat lon to memories $table = $schema->getTable('memories'); - if (!$table->hasColumn('lat')) { $table->addColumn('lat', Types::DECIMAL, [ 'notnull' => false, @@ -63,8 +63,44 @@ class Version401100Date20230208181533 extends SimpleMigrationStep 'precision' => 9, 'scale' => 6, ]); - $table->addIndex(['lat', 'lon'], 'memories_lat_lon_index'); + + $table->addColumn('mapcluster', Types::INTEGER, [ + 'notnull' => false, + 'default' => null, + ]); + $table->addIndex(['mapcluster'], 'memories_mapcluster_index'); + } + + // Add clusters table + if (!$schema->hasTable('memories_mapclusters')) { + $table = $schema->createTable('memories_mapclusters'); + $table->addColumn('id', Types::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('point_count', Types::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('lat_sum', Types::FLOAT, [ + 'notnull' => false, + 'default' => null, + ]); + $table->addColumn('lon_sum', Types::FLOAT, [ + 'notnull' => false, + 'default' => null, + ]); + $table->addColumn('lat', Types::FLOAT, [ + 'notnull' => false, + 'default' => null, + ]); + $table->addColumn('lon', Types::FLOAT, [ + 'notnull' => false, + 'default' => null, + ]); + + $table->setPrimaryKey(['id']); + $table->addIndex(['lat', 'lon'], 'memories_clst_ll_idx'); } return $schema;