150 lines
3.9 KiB
Svelte
150 lines
3.9 KiB
Svelte
<script lang="ts">
|
|
import * as M from "maplibre-gl";
|
|
|
|
import { fromFile, toGeoJson } from "$lib/util/gpx_util";
|
|
import { createEventDispatcher, onMount } from "svelte";
|
|
export let trailFile: File | null;
|
|
export let trailData: string | undefined;
|
|
export let label: string = "";
|
|
|
|
let map: M.Map;
|
|
let layer: M.LineLayerSpecification | undefined;
|
|
let source: M.GeoJSONSource | undefined;
|
|
|
|
const dispatch = createEventDispatcher();
|
|
|
|
$: if (trailData !== undefined) {
|
|
showTrailOnMap();
|
|
} else {
|
|
removeTrailFromMap();
|
|
}
|
|
|
|
onMount(async () => {
|
|
if (!map) {
|
|
await initMap();
|
|
}
|
|
});
|
|
|
|
async function initMap() {
|
|
map = new M.Map({
|
|
container: "trail-picker-map",
|
|
attributionControl: false,
|
|
dragPan: false,
|
|
scrollZoom: false,
|
|
preserveDrawingBuffer: true
|
|
});
|
|
}
|
|
|
|
function openTrailBrowser() {
|
|
if (trailData) {
|
|
trailFile = null;
|
|
trailData = undefined;
|
|
|
|
dispatch("change", null);
|
|
} else {
|
|
document.getElementById("trail-input")!.click();
|
|
}
|
|
}
|
|
|
|
async function handleTrailSelection(files?: FileList | null) {
|
|
if (!files) {
|
|
files = (document.getElementById("trail-input") as HTMLInputElement)
|
|
.files;
|
|
}
|
|
|
|
if (!files) {
|
|
return;
|
|
}
|
|
|
|
trailFile = files.item(0);
|
|
if (!trailFile) {
|
|
return;
|
|
}
|
|
const parseResult = await fromFile(trailFile);
|
|
|
|
trailData = parseResult.gpxData;
|
|
|
|
dispatch("change", trailData);
|
|
}
|
|
|
|
async function showTrailOnMap() {
|
|
if (!trailData || !map) {
|
|
return;
|
|
}
|
|
const sourceId = "trail-picker-geojson-source";
|
|
const layerId = "trail-picker-geojson-layer";
|
|
|
|
const geojson = toGeoJson(trailData);
|
|
|
|
if (layer) {
|
|
map.removeLayer(layerId);
|
|
map.removeSource(sourceId);
|
|
}
|
|
|
|
map.addSource(sourceId, {
|
|
type: "geojson",
|
|
data: geojson,
|
|
});
|
|
map.addLayer({
|
|
id: layerId,
|
|
type: "line",
|
|
source: sourceId,
|
|
paint: {
|
|
"line-color": "#3388ff",
|
|
"line-width": 3,
|
|
},
|
|
});
|
|
layer = map.getLayer(layerId) as M.LineLayerSpecification;
|
|
source = map.getSource(sourceId) as M.GeoJSONSource;
|
|
map.resize()
|
|
map.fitBounds(geojson.bbox as M.LngLatBoundsLike, {animate: false, padding: 8});
|
|
}
|
|
|
|
function removeTrailFromMap() {
|
|
if (layer) {
|
|
map?.removeLayer(layer.id);
|
|
layer = undefined;
|
|
}
|
|
if (source) {
|
|
map?.removeSource(source.id);
|
|
source = undefined;
|
|
}
|
|
|
|
trailData = undefined;
|
|
trailFile = null;
|
|
}
|
|
</script>
|
|
|
|
<div>
|
|
{#if label.length}
|
|
<p class="text-sm font-medium pb-1">
|
|
{label}
|
|
</p>
|
|
{/if}
|
|
<div class="flex gap-x-4" role="dialog">
|
|
<input
|
|
type="file"
|
|
id="trail-input"
|
|
accept=".gpx,.GPX,.tcx,.TCX,.kml,.KML,.fit,.FIT"
|
|
multiple={false}
|
|
style="display: none;"
|
|
on:change={() => handleTrailSelection()}
|
|
/>
|
|
<button
|
|
type="button"
|
|
on:click={openTrailBrowser}
|
|
class="h-28 aspect-square rounded-xl !bg-background border border-input-border-focus hover:!bg-secondary-hover group"
|
|
id="trail-picker-map"
|
|
>
|
|
{#if !trailFile && !trailData}
|
|
<i class="fa fa-plus text-lg"></i>
|
|
{:else}
|
|
<i
|
|
class="fa fa-trash text-red-500 text-lg hidden group-hover:block relative"
|
|
style="z-index: 1000"
|
|
></i>
|
|
{/if}
|
|
</button>
|
|
</div>
|
|
</div>
|