edit-meta: allow removing gps data (#418)

pull/465/head
Varun Patil 2023-03-08 11:32:57 -08:00
parent fcd18604ae
commit 3073d92a89
5 changed files with 103 additions and 51 deletions

View File

@ -86,7 +86,7 @@ class TimelineWrite
// Check if need to update // Check if need to update
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->select('fileid', 'mtime', 'mapcluster', 'orphan') $query->select('fileid', 'mtime', 'mapcluster', 'orphan', 'lat', 'lon')
->from('memories') ->from('memories')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
; ;
@ -176,15 +176,15 @@ class TimelineWrite
} }
// Store location data // Store location data
$lat = null; $lat = \array_key_exists('GPSLatitude', $exif) ? (float) $exif['GPSLatitude'] : null;
$lon = null; $lon = \array_key_exists('GPSLongitude', $exif) ? (float) $exif['GPSLongitude'] : null;
$oldLat = $prevRow ? (float) $prevRow['lat'] : null;
$oldLon = $prevRow ? (float) $prevRow['lon'] : null;
$mapCluster = $prevRow ? (int) $prevRow['mapcluster'] : -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'];
if ($lat || $lon || $oldLat || $oldLon) {
try { try {
$mapCluster = $this->mapGetCluster($mapCluster, $lat, $lon); $mapCluster = $this->mapGetCluster($mapCluster, $lat, $lon, $oldLat, $oldLon);
} catch (\Error $e) { } catch (\Error $e) {
$logger = \OC::$server->get(LoggerInterface::class); $logger = \OC::$server->get(LoggerInterface::class);
$logger->log(3, 'Error updating map cluster data: '.$e->getMessage(), ['app' => 'memories']); $logger->log(3, 'Error updating map cluster data: '.$e->getMessage(), ['app' => 'memories']);

View File

@ -17,14 +17,23 @@ trait TimelineWriteMap
* Get the cluster ID for a given point. * Get the cluster ID for a given point.
* If the cluster ID changes, update the old cluster and the new cluster. * If the cluster ID changes, update the old cluster and the new cluster.
* *
* @param int $prevCluster The current cluster ID of the point * @param int $prevCluster The current cluster ID of the point
* @param float $lat The latitude of the point * @param null|float $lat The latitude of the point
* @param float $lon The longitude of the point * @param null|float $lon The longitude of the point
* @param null|float $oldLat The old latitude of the point
* @param null|float $oldLon The old longitude of the point
* *
* @return int The new cluster ID * @return int The new cluster ID
*/ */
protected function mapGetCluster(int $prevCluster, float $lat, float $lon): int protected function mapGetCluster(int $prevCluster, $lat, $lon, $oldLat, $oldLon): int
{ {
// Just remove from old cluster if the point is no longer valid
if (null === $lat || null === $lon) {
$this->mapRemoveFromCluster($prevCluster, $oldLat, $oldLon);
return -1;
}
// Get all possible clusters within CLUSTER_DEG radius // Get all possible clusters within CLUSTER_DEG radius
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();
$query->select('id', 'lat', 'lon') $query->select('id', 'lat', 'lon')
@ -51,7 +60,7 @@ trait TimelineWriteMap
// If no cluster found, create a new one // If no cluster found, create a new one
if ($minId <= 0) { if ($minId <= 0) {
$this->mapRemoveFromCluster($prevCluster, $lat, $lon); $this->mapRemoveFromCluster($prevCluster, $oldLat, $oldLon);
return $this->mapCreateCluster($lat, $lon); return $this->mapCreateCluster($lat, $lon);
} }
@ -63,7 +72,7 @@ trait TimelineWriteMap
// If the file was previously in a different cluster, // If the file was previously in a different cluster,
// remove it from the first cluster and add it to the second // remove it from the first cluster and add it to the second
$this->mapRemoveFromCluster($prevCluster, $lat, $lon); $this->mapRemoveFromCluster($prevCluster, $oldLat, $oldLon);
$this->mapAddToCluster($minId, $lat, $lon); $this->mapAddToCluster($minId, $lat, $lon);
return $minId; return $minId;
@ -135,9 +144,9 @@ trait TimelineWriteMap
* @param float $lat The latitude of the point * @param float $lat The latitude of the point
* @param float $lon The longitude of the point * @param float $lon The longitude of the point
*/ */
private function mapRemoveFromCluster(int $clusterId, float $lat, float $lon): void private function mapRemoveFromCluster(int $clusterId, $lat, $lon): void
{ {
if ($clusterId <= 0) { if ($clusterId <= 0 || null === $lat || null === $lon) {
return; return;
} }

View File

@ -13,12 +13,31 @@ trait TimelineWritePlaces
/** /**
* Add places data for a file. * Add places data for a file.
*
* @param int $fileId The file ID
* @param null|float $lat The latitude of the file
* @param null|float $lon The longitude of the file
*/ */
protected function updatePlacesData(int $fileId, float $lat, float $lon): void protected function updatePlacesData(int $fileId, $lat, $lon): void
{ {
// Get GIS type // Get GIS type
$gisType = \OCA\Memories\Util::placesGISType(); $gisType = \OCA\Memories\Util::placesGISType();
// Check if valid
if ($gisType <= 0) return;
// Delete previous records
$query = $this->connection->getQueryBuilder();
$query->delete('memories_places')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
;
$query->executeStatement();
// Just remove from if the point is no longer valid
if (null === $lat || null === $lon) {
return;
}
// Construct WHERE clause depending on GIS type // Construct WHERE clause depending on GIS type
$where = null; $where = null;
if (1 === $gisType) { if (1 === $gisType) {
@ -47,13 +66,6 @@ trait TimelineWritePlaces
$result = $this->connection->executeQuery($sql); $result = $this->connection->executeQuery($sql);
$rows = $result->fetchAll(); $rows = $result->fetchAll();
// Delete previous records
$query = $this->connection->getQueryBuilder();
$query->delete('memories_places')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
;
$query->executeStatement();
// Insert records // Insert records
foreach ($rows as $row) { foreach ($rows as $row) {
$query = $this->connection->getQueryBuilder(); $query = $this->connection->getQueryBuilder();

View File

@ -158,7 +158,7 @@ export default defineComponent({
} }
list.push({ list.push({
title: this.address || this.t("memories", "Unknown coordinates"), title: this.address || this.t("memories", "No coordinates"),
subtitle: this.address subtitle: this.address
? [] ? []
: [this.t("memories", "Click edit to set location")], : [this.t("memories", "Click edit to set location")],

View File

@ -2,6 +2,13 @@
<div class="outer"> <div class="outer">
<div class="lat-lon"> <div class="lat-lon">
<span>{{ loc }}</span> {{ dirty ? "*" : "" }} <span>{{ loc }}</span> {{ dirty ? "*" : "" }}
<div class="action">
<UndoIcon :size="20" v-if="dirty" @click="reset" />
</div>
<div class="action">
<CloseIcon :size="20" v-if="lat && lon" @click="clear" />
</div>
</div> </div>
<NcTextField <NcTextField
@ -12,7 +19,7 @@
@trailing-button-click="search" @trailing-button-click="search"
@keypress.enter="search" @keypress.enter="search"
> >
<Magnify :size="16" /> <MagnifyIcon :size="16" />
</NcTextField> </NcTextField>
<div class="osm-attribution"> <div class="osm-attribution">
@ -54,7 +61,9 @@ const NcTextField = () => import("@nextcloud/vue/dist/Components/NcTextField");
const NcListItem = () => import("@nextcloud/vue/dist/Components/NcListItem"); const NcListItem = () => import("@nextcloud/vue/dist/Components/NcListItem");
import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon"; import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon";
import Magnify from "vue-material-design-icons/Magnify.vue"; import MagnifyIcon from "vue-material-design-icons/Magnify.vue";
import CloseIcon from "vue-material-design-icons/Close.vue";
import UndoIcon from "vue-material-design-icons/UndoVariant.vue";
type NLocation = { type NLocation = {
osm_id: number; osm_id: number;
@ -70,7 +79,9 @@ export default defineComponent({
NcTextField, NcTextField,
NcListItem, NcListItem,
NcLoadingIcon, NcLoadingIcon,
Magnify, MagnifyIcon,
CloseIcon,
UndoIcon,
}, },
props: { props: {
@ -95,36 +106,41 @@ export default defineComponent({
if (this.lat && this.lon) { if (this.lat && this.lon) {
return `${this.lat.toFixed(6)}, ${this.lon.toFixed(6)}`; return `${this.lat.toFixed(6)}, ${this.lon.toFixed(6)}`;
} }
return this.t("memories", "Unknown coordinates"); return this.t("memories", "No coordinates");
}, },
}, },
mounted() { mounted() {
const photos = this.photos as IPhoto[]; this.reset();
let lat = 0,
lon = 0,
count = 0;
for (const photo of photos) {
const exif = photo.imageInfo?.exif;
if (!exif) {
continue;
}
if (exif.GPSLatitude && exif.GPSLongitude) {
lat += Number(exif.GPSLatitude);
lon += Number(exif.GPSLongitude);
count++;
}
}
if (count > 0) {
this.lat = lat / count;
this.lon = lon / count;
}
}, },
methods: { methods: {
reset() {
this.dirty = false;
const photos = this.photos as IPhoto[];
let lat = 0,
lon = 0,
count = 0;
for (const photo of photos) {
const exif = photo.imageInfo?.exif;
if (!exif) {
continue;
}
if (exif.GPSLatitude && exif.GPSLongitude) {
lat += Number(exif.GPSLatitude);
lon += Number(exif.GPSLongitude);
count++;
}
}
if (count > 0) {
this.lat = lat / count;
this.lon = lon / count;
}
},
search() { search() {
if (this.loading || this.searchBar.length === 0) { if (this.loading || this.searchBar.length === 0) {
return; return;
@ -151,6 +167,12 @@ export default defineComponent({
}); });
}, },
clear() {
this.dirty = true;
this.lat = 0;
this.lon = 0;
},
select(option: NLocation) { select(option: NLocation) {
this.dirty = true; this.dirty = true;
this.lat = Number(option.lat); this.lat = Number(option.lat);
@ -160,7 +182,7 @@ export default defineComponent({
}, },
result() { result() {
if (!this.dirty || !this.lat || !this.lon) { if (!this.dirty) {
return null; return null;
} }
@ -180,9 +202,18 @@ export default defineComponent({
.lat-lon { .lat-lon {
padding: 4px; padding: 4px;
> span { > span {
user-select: all; user-select: all;
} }
> .action {
float: right;
margin-left: 2px;
> * {
cursor: pointer;
}
}
} }
.osm-attribution { .osm-attribution {