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
$query = $this->connection->getQueryBuilder();
$query->select('fileid', 'mtime', 'mapcluster', 'orphan')
$query->select('fileid', 'mtime', 'mapcluster', 'orphan', 'lat', 'lon')
->from('memories')
->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
;
@ -176,15 +176,15 @@ class TimelineWrite
}
// Store location data
$lat = null;
$lon = null;
$lat = \array_key_exists('GPSLatitude', $exif) ? (float) $exif['GPSLatitude'] : 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;
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 {
$mapCluster = $this->mapGetCluster($mapCluster, $lat, $lon);
$mapCluster = $this->mapGetCluster($mapCluster, $lat, $lon, $oldLat, $oldLon);
} catch (\Error $e) {
$logger = \OC::$server->get(LoggerInterface::class);
$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.
* If the cluster ID changes, update the old cluster and the new cluster.
*
* @param int $prevCluster The current cluster ID of the point
* @param float $lat The latitude of the point
* @param float $lon The longitude of the point
* @param int $prevCluster The current cluster ID of the point
* @param null|float $lat The latitude 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
*/
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
$query = $this->connection->getQueryBuilder();
$query->select('id', 'lat', 'lon')
@ -51,7 +60,7 @@ trait TimelineWriteMap
// If no cluster found, create a new one
if ($minId <= 0) {
$this->mapRemoveFromCluster($prevCluster, $lat, $lon);
$this->mapRemoveFromCluster($prevCluster, $oldLat, $oldLon);
return $this->mapCreateCluster($lat, $lon);
}
@ -63,7 +72,7 @@ trait TimelineWriteMap
// If the file was previously in a different cluster,
// 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);
return $minId;
@ -135,9 +144,9 @@ trait TimelineWriteMap
* @param float $lat The latitude 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;
}

View File

@ -13,12 +13,31 @@ trait TimelineWritePlaces
/**
* 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
$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
$where = null;
if (1 === $gisType) {
@ -47,13 +66,6 @@ trait TimelineWritePlaces
$result = $this->connection->executeQuery($sql);
$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
foreach ($rows as $row) {
$query = $this->connection->getQueryBuilder();

View File

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

View File

@ -2,6 +2,13 @@
<div class="outer">
<div class="lat-lon">
<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>
<NcTextField
@ -12,7 +19,7 @@
@trailing-button-click="search"
@keypress.enter="search"
>
<Magnify :size="16" />
<MagnifyIcon :size="16" />
</NcTextField>
<div class="osm-attribution">
@ -54,7 +61,9 @@ const NcTextField = () => import("@nextcloud/vue/dist/Components/NcTextField");
const NcListItem = () => import("@nextcloud/vue/dist/Components/NcListItem");
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 = {
osm_id: number;
@ -70,7 +79,9 @@ export default defineComponent({
NcTextField,
NcListItem,
NcLoadingIcon,
Magnify,
MagnifyIcon,
CloseIcon,
UndoIcon,
},
props: {
@ -95,36 +106,41 @@ export default defineComponent({
if (this.lat && this.lon) {
return `${this.lat.toFixed(6)}, ${this.lon.toFixed(6)}`;
}
return this.t("memories", "Unknown coordinates");
return this.t("memories", "No coordinates");
},
},
mounted() {
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;
}
this.reset();
},
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() {
if (this.loading || this.searchBar.length === 0) {
return;
@ -151,6 +167,12 @@ export default defineComponent({
});
},
clear() {
this.dirty = true;
this.lat = 0;
this.lon = 0;
},
select(option: NLocation) {
this.dirty = true;
this.lat = Number(option.lat);
@ -160,7 +182,7 @@ export default defineComponent({
},
result() {
if (!this.dirty || !this.lat || !this.lon) {
if (!this.dirty) {
return null;
}
@ -180,9 +202,18 @@ export default defineComponent({
.lat-lon {
padding: 4px;
> span {
user-select: all;
}
> .action {
float: right;
margin-left: 2px;
> * {
cursor: pointer;
}
}
}
.osm-attribution {