adds trail export
This commit is contained in:
80
web/package-lock.json
generated
80
web/package-lock.json
generated
@@ -18,6 +18,7 @@
|
|||||||
"crypto-random-string": "^5.0.0",
|
"crypto-random-string": "^5.0.0",
|
||||||
"isomorphic-xml2js": "^0.1.3",
|
"isomorphic-xml2js": "^0.1.3",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-gpx": "^1.7.0",
|
"leaflet-gpx": "^1.7.0",
|
||||||
"leaflet.awesome-markers": "^2.0.5",
|
"leaflet.awesome-markers": "^2.0.5",
|
||||||
@@ -1884,6 +1885,11 @@
|
|||||||
"url": "https://opencollective.com/core-js"
|
"url": "https://opencollective.com/core-js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||||
|
},
|
||||||
"node_modules/cross-fetch": {
|
"node_modules/cross-fetch": {
|
||||||
"version": "3.1.8",
|
"version": "3.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
|
||||||
@@ -2879,6 +2885,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -3442,6 +3453,22 @@
|
|||||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jszip/node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
|
},
|
||||||
"node_modules/kleur": {
|
"node_modules/kleur": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||||
@@ -3465,6 +3492,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/leaflet.awesome-markers/-/leaflet.awesome-markers-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/leaflet.awesome-markers/-/leaflet.awesome-markers-2.0.5.tgz",
|
||||||
"integrity": "sha512-Ne/xDjkGyaujwNVVkv2tyXQUV0ZW7gZ0Mo0FuQY4jp2qWrvXi0hwDBvmZyF/8YOvybyMabTMM/mFWCTd1jZIQA=="
|
"integrity": "sha512-Ne/xDjkGyaujwNVVkv2tyXQUV0ZW7gZ0Mo0FuQY4jp2qWrvXi0hwDBvmZyF/8YOvybyMabTMM/mFWCTd1jZIQA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
@@ -4394,6 +4429,11 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||||
|
},
|
||||||
"node_modules/property-expr": {
|
"node_modules/property-expr": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
||||||
@@ -4486,6 +4526,25 @@
|
|||||||
"pify": "^2.3.0"
|
"pify": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream/node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
@@ -4682,6 +4741,11 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
"node_modules/safer-buffer": {
|
"node_modules/safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
@@ -4761,6 +4825,11 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@@ -4884,6 +4953,14 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
@@ -5611,8 +5688,7 @@
|
|||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/utrie": {
|
"node_modules/utrie": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"crypto-random-string": "^5.0.0",
|
"crypto-random-string": "^5.0.0",
|
||||||
"isomorphic-xml2js": "^0.1.3",
|
"isomorphic-xml2js": "^0.1.3",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-gpx": "^1.7.0",
|
"leaflet-gpx": "^1.7.0",
|
||||||
"leaflet.awesome-markers": "^2.0.5",
|
"leaflet.awesome-markers": "^2.0.5",
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import type { User } from '$lib/models/user'
|
import type { User } from '$lib/models/user';
|
||||||
import { pb } from '$lib/pocketbase'
|
import { pb } from '$lib/pocketbase';
|
||||||
import { settings_show } from '$lib/stores/settings_store';
|
import { currentUser } from '$lib/stores/user_store';
|
||||||
import { currentUser } from '$lib/stores/user_store'
|
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
pb.authStore.loadFromCookie(document.cookie)
|
pb.authStore.loadFromCookie(document.cookie)
|
||||||
pb.authStore.onChange(() => {
|
pb.authStore.onChange(() => {
|
||||||
|
|||||||
@@ -9,17 +9,22 @@
|
|||||||
} from "$lib/stores/list_store";
|
} from "$lib/stores/list_store";
|
||||||
import { show_toast } from "$lib/stores/toast_store";
|
import { show_toast } from "$lib/stores/toast_store";
|
||||||
import { trails_delete } from "$lib/stores/trail_store";
|
import { trails_delete } from "$lib/stores/trail_store";
|
||||||
import { getFileURL } from "$lib/util/file_util";
|
import { trail2gpx } from "$lib/util/gpx_util";
|
||||||
|
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
|
||||||
import ConfirmModal from "../confirm_modal.svelte";
|
import ConfirmModal from "../confirm_modal.svelte";
|
||||||
import ListSelectModal from "../list/list_select_modal.svelte";
|
import ListSelectModal from "../list/list_select_modal.svelte";
|
||||||
import { _ } from "svelte-i18n";
|
import TrailExportModal from "./trail_export_modal.svelte";
|
||||||
|
import JSZip from "jszip";
|
||||||
|
import { getFileURL, saveAs } from "$lib/util/file_util";
|
||||||
|
|
||||||
export let trail: Trail;
|
export let trail: Trail;
|
||||||
export let mode: "overview" | "map";
|
export let mode: "overview" | "map";
|
||||||
|
|
||||||
let openConfirmModal: () => void;
|
let openConfirmModal: () => void;
|
||||||
let openListSelectModal: () => void;
|
let openListSelectModal: () => void;
|
||||||
|
let openExportModal: () => void;
|
||||||
|
|
||||||
const dropdownItems: DropdownItem[] = [
|
const dropdownItems: DropdownItem[] = [
|
||||||
mode == "overview"
|
mode == "overview"
|
||||||
@@ -34,7 +39,7 @@
|
|||||||
...(trail.gpx
|
...(trail.gpx
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
text: $_("download-gpx"),
|
text: $_("export"),
|
||||||
value: "download",
|
value: "download",
|
||||||
icon: "download",
|
icon: "download",
|
||||||
},
|
},
|
||||||
@@ -65,7 +70,7 @@
|
|||||||
} else if (item.value == "print") {
|
} else if (item.value == "print") {
|
||||||
goto(`/map/trail/${trail.id}/print`);
|
goto(`/map/trail/${trail.id}/print`);
|
||||||
} else if (item.value == "download") {
|
} else if (item.value == "download") {
|
||||||
downloadURI(getFileURL(trail, trail.gpx), trail.gpx!);
|
openExportModal();
|
||||||
} else if (item.value == "edit") {
|
} else if (item.value == "edit") {
|
||||||
goto(`/trail/edit/${trail.id}`);
|
goto(`/trail/edit/${trail.id}`);
|
||||||
} else if (item.value == "delete") {
|
} else if (item.value == "delete") {
|
||||||
@@ -73,13 +78,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadURI(uri: string, name: string) {
|
async function exportTrail(exportSettings: {
|
||||||
var link = document.createElement("a");
|
fileFormat: "gpx" | "json";
|
||||||
link.setAttribute("download", name);
|
photos: boolean;
|
||||||
link.href = uri;
|
summitLog: boolean;
|
||||||
document.body.appendChild(link);
|
}) {
|
||||||
link.click();
|
try {
|
||||||
link.remove();
|
let fileData: string = await trail2gpx(trail);
|
||||||
|
if (exportSettings.fileFormat == "json") {
|
||||||
|
fileData = JSON.stringify(gpx(new DOMParser().parseFromString(fileData, "text/xml")));
|
||||||
|
}
|
||||||
|
const zip = new JSZip();
|
||||||
|
zip.file(`${trail.name}.${exportSettings.fileFormat}`, fileData);
|
||||||
|
if (exportSettings.photos) {
|
||||||
|
const photoFolder = zip.folder($_("photos"));
|
||||||
|
for (const photo of trail.photos) {
|
||||||
|
const photoURL = getFileURL(trail, photo);
|
||||||
|
const photoBlob = await fetch(photoURL).then((response) =>
|
||||||
|
response.blob(),
|
||||||
|
);
|
||||||
|
const photoData = new File([photoBlob], photo);
|
||||||
|
photoFolder?.file(photo, photoData, { base64: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(exportSettings.summitLog) {
|
||||||
|
let summitLogString = ""
|
||||||
|
for (const summitLog of trail.expand.summit_logs) {
|
||||||
|
summitLogString += `${summitLog.date},${summitLog.text}\n`
|
||||||
|
}
|
||||||
|
zip.file(`${trail.name} - ${$_("summit-book")}.csv`, summitLogString)
|
||||||
|
}
|
||||||
|
const blob = await zip.generateAsync({ type: "blob" });
|
||||||
|
saveAs(blob, `${trail.name}.zip`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
show_toast({
|
||||||
|
type: "error",
|
||||||
|
icon: "close",
|
||||||
|
text: $_("error-exporting-trail"),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteTrail() {
|
async function deleteTrail() {
|
||||||
@@ -137,3 +175,7 @@
|
|||||||
bind:openModal={openListSelectModal}
|
bind:openModal={openListSelectModal}
|
||||||
on:change={(e) => handleListSelection(e.detail)}
|
on:change={(e) => handleListSelection(e.detail)}
|
||||||
></ListSelectModal>
|
></ListSelectModal>
|
||||||
|
<TrailExportModal
|
||||||
|
bind:openModal={openExportModal}
|
||||||
|
on:export={(e) => exportTrail(e.detail)}
|
||||||
|
></TrailExportModal>
|
||||||
|
|||||||
80
web/src/lib/components/trail/trail_export_modal.svelte
Normal file
80
web/src/lib/components/trail/trail_export_modal.svelte
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Modal from "$lib/components/base/modal.svelte";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import Select from "../base/select.svelte";
|
||||||
|
|
||||||
|
export let openModal: (() => void) | undefined = undefined;
|
||||||
|
export let closeModal: (() => void) | undefined = undefined;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
|
||||||
|
const fileFormats = [
|
||||||
|
{ text: "GPX", value: "gpx" },
|
||||||
|
{ text: "GeoJSON", value: "json" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const exportSettings = {
|
||||||
|
fileFormat: "gpx",
|
||||||
|
photos: false,
|
||||||
|
summitLog: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function exportTrail() {
|
||||||
|
dispatch("export", exportSettings);
|
||||||
|
closeModal!();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
id="export-modal"
|
||||||
|
title="Export trail"
|
||||||
|
size="max-w-sm"
|
||||||
|
bind:openModal
|
||||||
|
bind:closeModal
|
||||||
|
>
|
||||||
|
<div slot="content">
|
||||||
|
<div class="mb-3">
|
||||||
|
<Select
|
||||||
|
bind:value={exportSettings.fileFormat}
|
||||||
|
items={fileFormats}
|
||||||
|
label={$_("file-format")}
|
||||||
|
></Select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input
|
||||||
|
id="include-photos-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={exportSettings.photos}
|
||||||
|
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
|
||||||
|
/>
|
||||||
|
<label for="include-photos-checkbox" class="ms-2 text-sm"
|
||||||
|
>{$_("photos")}</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input
|
||||||
|
id="include-summit-log-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={exportSettings.summitLog}
|
||||||
|
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
|
||||||
|
/>
|
||||||
|
<label for="include-summit-log-checkbox" class="ms-2 text-sm"
|
||||||
|
>{$_("summit-book")}</label
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="flex items-center gap-4">
|
||||||
|
<button class="btn-secondary" on:click={closeModal}
|
||||||
|
>{$_("cancel")}</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-primary"
|
||||||
|
type="button"
|
||||||
|
on:click={exportTrail}
|
||||||
|
name="save">{$_("export")}</button
|
||||||
|
>
|
||||||
|
</div></Modal
|
||||||
|
>
|
||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Eintrag",
|
"entry": "Eintrag",
|
||||||
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
||||||
"error-during-login": "Fehler beim Login",
|
"error-during-login": "Fehler beim Login",
|
||||||
|
"error-exporting-trail": "Fehler beim Exportieren des Trails",
|
||||||
"error-printing-map": "Fehler beim Drucken der Karte",
|
"error-printing-map": "Fehler beim Drucken der Karte",
|
||||||
"error-reading-file": "Fehler beim Lesen der Datei",
|
"error-reading-file": "Fehler beim Lesen der Datei",
|
||||||
"est-duration": "Gesch. Dauer",
|
"est-duration": "Gesch. Dauer",
|
||||||
"explore": "Erkunden",
|
"explore": "Erkunden",
|
||||||
"explore-some-trails": "Erkunden Sie einige Routen",
|
"explore-some-trails": "Erkunden Sie einige Routen",
|
||||||
|
"export": "Exportieren",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
|
"file-format": "Dateiformat",
|
||||||
"french": "Französisch",
|
"french": "Französisch",
|
||||||
"german": "Deutsch",
|
"german": "Deutsch",
|
||||||
"grid": "Gitter",
|
"grid": "Gitter",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Entry",
|
"entry": "Entry",
|
||||||
"error-creating-user": "Error creating user",
|
"error-creating-user": "Error creating user",
|
||||||
"error-during-login": "Error during login",
|
"error-during-login": "Error during login",
|
||||||
|
"error-exporting-trail": "Error exporting trail",
|
||||||
"error-printing-map": "Error printing map",
|
"error-printing-map": "Error printing map",
|
||||||
"error-reading-file": "Error reading file",
|
"error-reading-file": "Error reading file",
|
||||||
"est-duration": "Est. duration",
|
"est-duration": "Est. duration",
|
||||||
"explore": "Explore",
|
"explore": "Explore",
|
||||||
"explore-some-trails": "Explore some trails",
|
"explore-some-trails": "Explore some trails",
|
||||||
|
"export": "Export",
|
||||||
"features": "Features",
|
"features": "Features",
|
||||||
|
"file-format": "File format",
|
||||||
"french": "French",
|
"french": "French",
|
||||||
"german": "German",
|
"german": "German",
|
||||||
"grid": "Grid",
|
"grid": "Grid",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Entrée",
|
"entry": "Entrée",
|
||||||
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
||||||
"error-during-login": "Erreur durant la connexion",
|
"error-during-login": "Erreur durant la connexion",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "Erreur d'impression de la carte",
|
"error-printing-map": "Erreur d'impression de la carte",
|
||||||
"error-reading-file": "Erreur de lecture du fichier",
|
"error-reading-file": "Erreur de lecture du fichier",
|
||||||
"est-duration": "Temps estimé",
|
"est-duration": "Temps estimé",
|
||||||
"explore": "Explorer",
|
"explore": "Explorer",
|
||||||
"explore-some-trails": "Explorer certains itinéraires",
|
"explore-some-trails": "Explorer certains itinéraires",
|
||||||
|
"export": "",
|
||||||
"features": "Fonctionnalités",
|
"features": "Fonctionnalités",
|
||||||
|
"file-format": "",
|
||||||
"french": "Français",
|
"french": "Français",
|
||||||
"german": "Allemand",
|
"german": "Allemand",
|
||||||
"grid": "Grille",
|
"grid": "Grille",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Bejegyzés",
|
"entry": "Bejegyzés",
|
||||||
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
||||||
"error-during-login": "Hiba bejelentkezés közben",
|
"error-during-login": "Hiba bejelentkezés közben",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "",
|
"error-printing-map": "",
|
||||||
"error-reading-file": "Hiba a fájl olvasása közben",
|
"error-reading-file": "Hiba a fájl olvasása közben",
|
||||||
"est-duration": "Becsült időtartam",
|
"est-duration": "Becsült időtartam",
|
||||||
"explore": "Felfedezés",
|
"explore": "Felfedezés",
|
||||||
"explore-some-trails": "Fedezzen fel néhány ösvényt",
|
"explore-some-trails": "Fedezzen fel néhány ösvényt",
|
||||||
|
"export": "",
|
||||||
"features": "Jellemzők",
|
"features": "Jellemzők",
|
||||||
|
"file-format": "",
|
||||||
"french": "Francia",
|
"french": "Francia",
|
||||||
"german": "Német",
|
"german": "Német",
|
||||||
"grid": "",
|
"grid": "",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Item",
|
"entry": "Item",
|
||||||
"error-creating-user": "Het account kan niet worden aangemaakt",
|
"error-creating-user": "Het account kan niet worden aangemaakt",
|
||||||
"error-during-login": "Het inloggen is mislukt",
|
"error-during-login": "Het inloggen is mislukt",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "Fout bij afdrukken van kaart",
|
"error-printing-map": "Fout bij afdrukken van kaart",
|
||||||
"error-reading-file": "Het bestand kan niet worden ingelezen",
|
"error-reading-file": "Het bestand kan niet worden ingelezen",
|
||||||
"est-duration": "Geschatte duur",
|
"est-duration": "Geschatte duur",
|
||||||
"explore": "Verkennen",
|
"explore": "Verkennen",
|
||||||
"explore-some-trails": "Verken enkele wandelroutes",
|
"explore-some-trails": "Verken enkele wandelroutes",
|
||||||
|
"export": "",
|
||||||
"features": "Kenmerken",
|
"features": "Kenmerken",
|
||||||
|
"file-format": "",
|
||||||
"french": "Frans",
|
"french": "Frans",
|
||||||
"german": "Duits",
|
"german": "Duits",
|
||||||
"grid": "Rooster",
|
"grid": "Rooster",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Pozycja",
|
"entry": "Pozycja",
|
||||||
"error-creating-user": "Błąd tworzenia użytkownika",
|
"error-creating-user": "Błąd tworzenia użytkownika",
|
||||||
"error-during-login": "Błąd podczas logowania",
|
"error-during-login": "Błąd podczas logowania",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "",
|
"error-printing-map": "",
|
||||||
"error-reading-file": "Błąd wczytywania pliku",
|
"error-reading-file": "Błąd wczytywania pliku",
|
||||||
"est-duration": "Szacowany czas",
|
"est-duration": "Szacowany czas",
|
||||||
"explore": "Eksploruj",
|
"explore": "Eksploruj",
|
||||||
"explore-some-trails": "Eksploruj różne ścieżki",
|
"explore-some-trails": "Eksploruj różne ścieżki",
|
||||||
|
"export": "",
|
||||||
"features": "Funkcje",
|
"features": "Funkcje",
|
||||||
|
"file-format": "",
|
||||||
"french": "Francuski",
|
"french": "Francuski",
|
||||||
"german": "Niemiecki",
|
"german": "Niemiecki",
|
||||||
"grid": "",
|
"grid": "",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "Entrada",
|
"entry": "Entrada",
|
||||||
"error-creating-user": "Erro ao criar usuário",
|
"error-creating-user": "Erro ao criar usuário",
|
||||||
"error-during-login": "Erro durante o login",
|
"error-during-login": "Erro durante o login",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "",
|
"error-printing-map": "",
|
||||||
"error-reading-file": "Erro ao ler o arquivo",
|
"error-reading-file": "Erro ao ler o arquivo",
|
||||||
"est-duration": "Duração prevista",
|
"est-duration": "Duração prevista",
|
||||||
"explore": "Explorar",
|
"explore": "Explorar",
|
||||||
"explore-some-trails": "Explore algumas trilhas",
|
"explore-some-trails": "Explore algumas trilhas",
|
||||||
|
"export": "",
|
||||||
"features": "Características",
|
"features": "Características",
|
||||||
|
"file-format": "",
|
||||||
"french": "Francês",
|
"french": "Francês",
|
||||||
"german": "Alemão",
|
"german": "Alemão",
|
||||||
"grid": "",
|
"grid": "",
|
||||||
|
|||||||
@@ -55,12 +55,15 @@
|
|||||||
"entry": "日程",
|
"entry": "日程",
|
||||||
"error-creating-user": "创建用户错误",
|
"error-creating-user": "创建用户错误",
|
||||||
"error-during-login": "登录错误",
|
"error-during-login": "登录错误",
|
||||||
|
"error-exporting-trail": "",
|
||||||
"error-printing-map": "",
|
"error-printing-map": "",
|
||||||
"error-reading-file": "读取文件错误",
|
"error-reading-file": "读取文件错误",
|
||||||
"est-duration": "预计时长",
|
"est-duration": "预计时长",
|
||||||
"explore": "探索",
|
"explore": "探索",
|
||||||
"explore-some-trails": "探索行程",
|
"explore-some-trails": "探索行程",
|
||||||
|
"export": "",
|
||||||
"features": "特性",
|
"features": "特性",
|
||||||
|
"file-format": "",
|
||||||
"french": "法语",
|
"french": "法语",
|
||||||
"german": "德语",
|
"german": "德语",
|
||||||
"grid": "",
|
"grid": "",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default class Metadata {
|
|||||||
desc: string;
|
desc: string;
|
||||||
time: Date;
|
time: Date;
|
||||||
keywords: string;
|
keywords: string;
|
||||||
extensions: string;
|
extensions?: string;
|
||||||
author?: Person;
|
author?: Person;
|
||||||
link?: Link[];
|
link?: Link[];
|
||||||
bounds?: Bounds;
|
bounds?: Bounds;
|
||||||
@@ -18,7 +18,7 @@ export default class Metadata {
|
|||||||
desc: string,
|
desc: string,
|
||||||
time: string,
|
time: string,
|
||||||
keywords: string,
|
keywords: string,
|
||||||
extensions: string,
|
extensions?: string,
|
||||||
author?: Person,
|
author?: Person,
|
||||||
link?: Link | Link[],
|
link?: Link | Link[],
|
||||||
bounds?: Bounds,
|
bounds?: Bounds,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Trail {
|
|||||||
this.id = params?.id;
|
this.id = params?.id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.location = params?.location;
|
this.location = params?.location;
|
||||||
this.date = params?.date;
|
this.date = params?.date ?? new Date().toISOString().split('T')[0];
|
||||||
this.public = params?.public ?? false
|
this.public = params?.public ?? false
|
||||||
this.distance = params?.distance;
|
this.distance = params?.distance;
|
||||||
this.elevation_gain = params?.elevation_gain;
|
this.elevation_gain = params?.elevation_gain;
|
||||||
|
|||||||
@@ -17,3 +17,14 @@ export function readAsDataURLAsync(file: File) {
|
|||||||
fr.readAsDataURL(file);
|
fr.readAsDataURL(file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function saveAs(data: Blob, fileName: string) {
|
||||||
|
var a = document.createElement("a") as HTMLAnchorElement;
|
||||||
|
a.setAttribute("style", "display: none");
|
||||||
|
document.body.appendChild(a);
|
||||||
|
const url = window.URL.createObjectURL(data);
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import GPX from "$lib/models/gpx/gpx";
|
import GPX from "$lib/models/gpx/gpx";
|
||||||
import { Trail } from "$lib/models/trail";
|
import { Trail } from "$lib/models/trail";
|
||||||
import { Waypoint } from "$lib/models/waypoint";
|
import { Waypoint } from "$lib/models/waypoint";
|
||||||
|
import { currentUser } from "$lib/stores/user_store";
|
||||||
import GeoJsonToGpx from "$lib/vendor/geoJSONToGPX";
|
import GeoJsonToGpx from "$lib/vendor/geoJSONToGPX";
|
||||||
import { kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON";
|
import { kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON";
|
||||||
import cryptoRandomString from "crypto-random-string";
|
import cryptoRandomString from "crypto-random-string";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
export async function gpx2trail(gpxString: string) {
|
export async function gpx2trail(gpxString: string) {
|
||||||
const gpx = await GPX.parse(gpxString);
|
const gpx = await GPX.parse(gpxString);
|
||||||
@@ -18,7 +20,7 @@ export async function gpx2trail(gpxString: string) {
|
|||||||
|
|
||||||
trail.description = gpx.metadata?.desc;
|
trail.description = gpx.metadata?.desc;
|
||||||
|
|
||||||
for (const wpt of gpx.wpt ?? []) {
|
for (const wpt of gpx.wpt ?? []) {
|
||||||
const wp = new Waypoint(wpt.$.lat ?? 0, wpt.$.lon ?? 0);
|
const wp = new Waypoint(wpt.$.lat ?? 0, wpt.$.lon ?? 0);
|
||||||
wp.id = cryptoRandomString({ length: 15 });
|
wp.id = cryptoRandomString({ length: 15 });
|
||||||
wp.name = wpt.name
|
wp.name = wpt.name
|
||||||
@@ -29,13 +31,13 @@ export async function gpx2trail(gpxString: string) {
|
|||||||
const totals = gpx.getTotals()
|
const totals = gpx.getTotals()
|
||||||
|
|
||||||
const trackPoints = gpx.trk?.at(0)?.trkseg?.at(0)?.trkpt
|
const trackPoints = gpx.trk?.at(0)?.trkseg?.at(0)?.trkpt
|
||||||
const routePoints = gpx.rte?.at(0)?.rtept;
|
const routePoints = gpx.rte?.at(0)?.rtept;
|
||||||
|
|
||||||
const startPoint = trackPoints?.at(0) ?? routePoints?.at(0);
|
const startPoint = trackPoints?.at(0) ?? routePoints?.at(0);
|
||||||
if (startPoint) {
|
if (startPoint) {
|
||||||
trail.lat = startPoint.$.lat
|
trail.lat = startPoint.$.lat
|
||||||
trail.lon = startPoint.$.lon
|
trail.lon = startPoint.$.lon
|
||||||
}
|
}
|
||||||
|
|
||||||
const startTime = trackPoints?.at(0)?.time;
|
const startTime = trackPoints?.at(0)?.time;
|
||||||
const endTime = trackPoints?.at((trackPoints?.length ?? 1) - 1)?.time
|
const endTime = trackPoints?.at((trackPoints?.length ?? 1) - 1)?.time
|
||||||
@@ -49,7 +51,44 @@ export async function gpx2trail(gpxString: string) {
|
|||||||
trail.elevation_gain = totals.elevationGain;
|
trail.elevation_gain = totals.elevationGain;
|
||||||
trail.distance = totals.distance
|
trail.distance = totals.distance
|
||||||
|
|
||||||
return {gpx: gpx, trail: trail}
|
return { gpx: gpx, trail: trail }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function trail2gpx(trail: Trail) {
|
||||||
|
if (!trail.expand.gpx_data) {
|
||||||
|
throw Error("Trail has no GPX data")
|
||||||
|
}
|
||||||
|
const gpx = await GPX.parse(trail.expand.gpx_data) as GPX;
|
||||||
|
|
||||||
|
if (gpx instanceof Error) {
|
||||||
|
throw gpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpx.metadata = {
|
||||||
|
name: trail.name,
|
||||||
|
desc: trail.description ?? "",
|
||||||
|
time: trail.date ? new Date(trail.date) : new Date(),
|
||||||
|
keywords: `${trail.category ?? ""}, ${trail.location ?? ""}`,
|
||||||
|
author: { name: trail.author ?? "", email: get(currentUser)?.email ?? "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!gpx.wpt) {
|
||||||
|
gpx.wpt = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const wp of trail.expand.waypoints) {
|
||||||
|
const gpxWpt = gpx.wpt.find((w) => w.$.lat == wp.lat && w.$.lon == wp.lon)
|
||||||
|
if(!gpxWpt) {
|
||||||
|
gpx.wpt.push({
|
||||||
|
$: {
|
||||||
|
lat: wp.lat,
|
||||||
|
lon: wp.lon
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpx.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromKML(kmlData: string) {
|
export function fromKML(kmlData: string) {
|
||||||
|
|||||||
@@ -101,9 +101,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const modesOfTransport = [
|
const modesOfTransport = [
|
||||||
{ text: $_('hiking'), value: "pedestrian" },
|
{ text: $_("hiking"), value: "pedestrian" },
|
||||||
{ text: $_('cycling'), value: "bicycle" },
|
{ text: $_("cycling"), value: "bicycle" },
|
||||||
{ text: $_('driving'), value: "auto" },
|
{ text: $_("driving"), value: "auto" },
|
||||||
];
|
];
|
||||||
let selectedModeOfTransport = modesOfTransport[0].value;
|
let selectedModeOfTransport = modesOfTransport[0].value;
|
||||||
|
|
||||||
@@ -385,6 +385,11 @@
|
|||||||
savedWaypoint.marker = marker;
|
savedWaypoint.marker = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function beforSummitLogModalOpen() {
|
||||||
|
summitLog.set(new SummitLog(new Date().toISOString().split('T')[0]));
|
||||||
|
openSummitLogModal();
|
||||||
|
}
|
||||||
|
|
||||||
function saveSummitLog(e: CustomEvent<SummitLog>) {
|
function saveSummitLog(e: CustomEvent<SummitLog>) {
|
||||||
const savedSummitLog = e.detail;
|
const savedSummitLog = e.detail;
|
||||||
let editedSummitLogIndex = $form.expand.summit_logs.findIndex(
|
let editedSummitLogIndex = $form.expand.summit_logs.findIndex(
|
||||||
@@ -791,7 +796,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
type="button"
|
type="button"
|
||||||
on:click={openSummitLogModal}
|
on:click={beforSummitLogModalOpen}
|
||||||
><i class="fa fa-plus mr-2"></i>{$_("add-entry")}</button
|
><i class="fa fa-plus mr-2"></i>{$_("add-entry")}</button
|
||||||
>
|
>
|
||||||
{#if $lists.length}
|
{#if $lists.length}
|
||||||
@@ -846,7 +851,10 @@
|
|||||||
>
|
>
|
||||||
<Toggle bind:value={autoRouting} label="Enable auto-routing"
|
<Toggle bind:value={autoRouting} label="Enable auto-routing"
|
||||||
></Toggle>
|
></Toggle>
|
||||||
<Select items={modesOfTransport} bind:value={selectedModeOfTransport} disabled={!autoRouting}
|
<Select
|
||||||
|
items={modesOfTransport}
|
||||||
|
bind:value={selectedModeOfTransport}
|
||||||
|
disabled={!autoRouting}
|
||||||
></Select>
|
></Select>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user