adds waypoints from photos

This commit is contained in:
Christian Beutel
2025-04-15 15:49:21 +02:00
parent 5db80db06f
commit 8fc10baaf1
14 changed files with 93 additions and 2 deletions

View File

@@ -63,6 +63,9 @@
for (let i = 0; i < dropdownMenu.children.length; i++) {
const li = dropdownMenu.children[i];
const textNode = li.getElementsByTagName("p")[0];
if(!textNode) {
return
}
textNode.innerHTML = items[i].text;
const text = textNode.innerText.replace(

View File

@@ -14,6 +14,7 @@
class="fa fa-{t.icon} p-3 mr-2 rounded-lg"
class:success-toast={t.type == "success"}
class:error-toast={t.type == "error"}
class:warning-toast={t.type == "warning"}
></i>
<p class="mr-4">
{t.text}
@@ -38,4 +39,8 @@
color: #ef4444;
background-color: #fecaca;
}
.warning-toast {
color: #ffb900;
background-color: #fef3c6;
}
</style>

View File

@@ -88,7 +88,7 @@
setFields("lon", convertDMSToDD(lon, lonDir));
} else {
show_toast({
text: "No GPS data in image",
text: $_('no-gps-data-in-image'),
icon: "close",
type: "error",
});

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Passwort vergessen?",
"french": "Französisch",
"from-file": "Aus einer Datei",
"from-photos": "",
"from-url": "Aus einer URL",
"german": "Deutsch",
"get-position-from-exif": "Koordinaten aus EXIF Daten",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Bisher keine Kommentare",
"no-data": "Keine Daten",
"no-description-for-now": "Noch keine Beschreibung",
"no-gps-data-in-image": "No GPS data in image",
"no-grid": "Kein Gitter",
"no-notifications": "Keine Benachrichtigungen",
"no-photos-here": "Hier sind noch keine Fotos",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "French",
"from-file": "From file",
"from-photos": "From Photos",
"from-url": "From URL",
"german": "German",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
"no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos or videos here",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "¿Contraseña olvidada?",
"french": "Francés",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "Alemán",
"get-position-from-exif": "Obtener las coordenadas de los datos EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Ningún comentario todavía",
"no-data": "No datos",
"no-description-for-now": "Ninguna descripción de momento",
"no-gps-data-in-image": "",
"no-grid": "Ninguna cuadrícula",
"no-notifications": "No notificaciones",
"no-photos-here": "No fotos aquí",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Mot de passe oublié ?",
"french": "Français",
"from-file": "Depuis un fichier",
"from-photos": "",
"from-url": "Depuis une URL",
"german": "Allemand",
"get-position-from-exif": "Obtenir les coordonnées à partir des données EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Aucun commentaire pour l'instant",
"no-data": "Pas de données",
"no-description-for-now": "Pas de description pour le moment",
"no-gps-data-in-image": "",
"no-grid": "Aucune grille",
"no-notifications": "Pas de notifications",
"no-photos-here": "Aucune photo ici",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Francia",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "Német",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
"no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Password dimenticata?",
"french": "Francese",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "Tedesco",
"get-position-from-exif": "Ottieni posizione da dati EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Nessun commento per il momento",
"no-data": "Nessun dato",
"no-description-for-now": "Nessuna descrizione per il momento",
"no-gps-data-in-image": "",
"no-grid": "Nessuna griglia",
"no-notifications": "Nessuna notifica",
"no-photos-here": "Nessuna foto qui",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Frans",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "Duits",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
"no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Zapomniałeś hasła?",
"french": "Francuski",
"from-file": "Z pliku",
"from-photos": "",
"from-url": "Z URL",
"german": "Niemiecki",
"get-position-from-exif": "Odczytaj współrzędne z danych EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Nie ma jeszcze komentarzy",
"no-data": "Brak danych",
"no-description-for-now": "Nie ma jeszcze opisu",
"no-gps-data-in-image": "",
"no-grid": "Brak Siatki",
"no-notifications": "Brak powiadomień",
"no-photos-here": "Nie ma tu zdjęć",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Francês",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "Alemão",
"get-position-from-exif": "Obter coordenadas dos dados EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "Sem dados",
"no-description-for-now": "No description for now",
"no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",

View File

@@ -139,6 +139,7 @@
"forgot-your-password": "忘记密码?",
"french": "法语",
"from-file": "From file",
"from-photos": "",
"from-url": "From URL",
"german": "德语",
"get-position-from-exif": "从EXIF数据获取坐标",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "无数据",
"no-description-for-now": "No description for now",
"no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",

View File

@@ -45,7 +45,7 @@
setRoute,
} from "$lib/stores/valhalla_store";
import { waypoint } from "$lib/stores/waypoint_store";
import { getFileURL } from "$lib/util/file_util";
import { getFileURL, readAsDataURLAsync } from "$lib/util/file_util";
import {
formatDistance,
formatElevation,
@@ -65,6 +65,7 @@
} from "$lib/components/base/search.svelte";
import RoutingOptionsPopup from "$lib/components/trail/routing_options_popup.svelte";
import { TagCreateSchema } from "$lib/models/api/tag_schema.js";
import { convertDMSToDD } from "$lib/models/gpx/utils.js";
import { Tag } from "$lib/models/tag.js";
import {
searchLocationReverse,
@@ -77,6 +78,7 @@
createAnchorMarker,
createEditTrailMapPopup,
} from "$lib/util/maplibre_util";
import EXIF from "$lib/vendor/exif-js/exif.js";
import { validator } from "@felte/validator-zod";
import cryptoRandomString from "crypto-random-string";
import { createForm } from "felte";
@@ -899,6 +901,53 @@
const result = await tags_index(q);
tagItems = result.items.map((t) => ({ text: t.name, value: t }));
}
function openPhotoBrowser() {
document.getElementById("waypoint-photo-input")!.click();
}
async function handleWaypointPhotoSelection() {
const files = (
document.getElementById("waypoint-photo-input") as HTMLInputElement
).files;
if (!files) {
return;
}
for (const file of files) {
const coords = await new Promise<number[]>((resolve) => {
EXIF.getData(file, function (p) {
const lat = EXIF.getTag(p, "GPSLatitude");
const latDir = EXIF.getTag(p, "GPSLatitudeRef");
const lon = EXIF.getTag(p, "GPSLongitude");
const lonDir = EXIF.getTag(p, "GPSLongitudeRef");
if (lat && lon) {
resolve([
convertDMSToDD(lat, latDir),
convertDMSToDD(lon, lonDir),
]);
} else {
resolve([]);
}
});
});
if (coords.length) {
const wp: Waypoint = new Waypoint(coords[0], coords[1], {
icon: "image",
});
wp._photos = [file];
saveWaypoint(wp);
} else {
show_toast({
type: "warning",
icon: "warning",
text: `${file.name}: ${$_("no-gps-data-in-image")}`,
}, 10000);
}
}
}
</script>
<svelte:head>
@@ -1113,6 +1162,20 @@
onclick={() => beforeWaypointModalOpen()}
><i class="fa fa-plus mr-2"></i>{$_("add-waypoint")}</button
>
<button
class="btn-secondary"
type="button"
onclick={() => openPhotoBrowser()}
><i class="fa fa-image mr-2"></i>{$_("from-photos")}</button
>
<input
type="file"
id="waypoint-photo-input"
accept="image/*"
multiple={true}
style="display: none;"
onchange={() => handleWaypointPhotoSelection()}
/>
<hr class="border-separator" />
<h3 class="text-xl font-semibold">{$_("photos")}</h3>
<PhotoPicker