adds waypoints from photos
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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í",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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ęć",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user