diff --git a/web/src/lib/components/base/combobox.svelte b/web/src/lib/components/base/combobox.svelte index 5ef1a812..7c791908 100644 --- a/web/src/lib/components/base/combobox.svelte +++ b/web/src/lib/components/base/combobox.svelte @@ -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( diff --git a/web/src/lib/components/base/toast.svelte b/web/src/lib/components/base/toast.svelte index ce6301e1..216e525c 100644 --- a/web/src/lib/components/base/toast.svelte +++ b/web/src/lib/components/base/toast.svelte @@ -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"} >

{t.text} @@ -38,4 +39,8 @@ color: #ef4444; background-color: #fecaca; } + .warning-toast { + color: #ffb900; + background-color: #fef3c6; + } diff --git a/web/src/lib/components/waypoint/waypoint_modal.svelte b/web/src/lib/components/waypoint/waypoint_modal.svelte index 4d7b0497..a34a94ed 100644 --- a/web/src/lib/components/waypoint/waypoint_modal.svelte +++ b/web/src/lib/components/waypoint/waypoint_modal.svelte @@ -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", }); diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json index 5f2149c2..768510be 100644 --- a/web/src/lib/i18n/locales/de.json +++ b/web/src/lib/i18n/locales/de.json @@ -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", diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json index cbe1fdd2..e3610173 100644 --- a/web/src/lib/i18n/locales/en.json +++ b/web/src/lib/i18n/locales/en.json @@ -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", diff --git a/web/src/lib/i18n/locales/es.json b/web/src/lib/i18n/locales/es.json index 0feae6e2..4f4c476f 100644 --- a/web/src/lib/i18n/locales/es.json +++ b/web/src/lib/i18n/locales/es.json @@ -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í", diff --git a/web/src/lib/i18n/locales/fr.json b/web/src/lib/i18n/locales/fr.json index 3d0552b8..503f00ad 100644 --- a/web/src/lib/i18n/locales/fr.json +++ b/web/src/lib/i18n/locales/fr.json @@ -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", diff --git a/web/src/lib/i18n/locales/hu.json b/web/src/lib/i18n/locales/hu.json index 3f51e790..f4c7ecdc 100644 --- a/web/src/lib/i18n/locales/hu.json +++ b/web/src/lib/i18n/locales/hu.json @@ -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", diff --git a/web/src/lib/i18n/locales/it.json b/web/src/lib/i18n/locales/it.json index 01735b29..81b59429 100644 --- a/web/src/lib/i18n/locales/it.json +++ b/web/src/lib/i18n/locales/it.json @@ -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", diff --git a/web/src/lib/i18n/locales/nl.json b/web/src/lib/i18n/locales/nl.json index afce5af9..2c2b96cb 100644 --- a/web/src/lib/i18n/locales/nl.json +++ b/web/src/lib/i18n/locales/nl.json @@ -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", diff --git a/web/src/lib/i18n/locales/pl.json b/web/src/lib/i18n/locales/pl.json index b7959abd..1240acd5 100644 --- a/web/src/lib/i18n/locales/pl.json +++ b/web/src/lib/i18n/locales/pl.json @@ -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ęć", diff --git a/web/src/lib/i18n/locales/pt.json b/web/src/lib/i18n/locales/pt.json index dc500f39..975b9bdb 100644 --- a/web/src/lib/i18n/locales/pt.json +++ b/web/src/lib/i18n/locales/pt.json @@ -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", diff --git a/web/src/lib/i18n/locales/zh.json b/web/src/lib/i18n/locales/zh.json index 659c0e0c..f4adf8cb 100644 --- a/web/src/lib/i18n/locales/zh.json +++ b/web/src/lib/i18n/locales/zh.json @@ -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", diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte index 180c278a..a7807eef 100644 --- a/web/src/routes/trail/edit/[id]/+page.svelte +++ b/web/src/routes/trail/edit/[id]/+page.svelte @@ -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((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); + } + } + } @@ -1113,6 +1162,20 @@ onclick={() => beforeWaypointModalOpen()} >{$_("add-waypoint")} + + handleWaypointPhotoSelection()} + />


{$_("photos")}