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