edit-meta: allow removing gps data (#418)
parent
fcd18604ae
commit
3073d92a89
|
@ -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']);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")],
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue