cleanup of obsolote leaflet components
This commit is contained in:
@@ -1,250 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
import { Settings } from "$lib/models/settings";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import {
|
||||
createMarkerFromWaypoint,
|
||||
endIcon,
|
||||
startIcon,
|
||||
} from "$lib/util/leaflet_util";
|
||||
import "$lib/vendor/leaflet-elevation/src/index.css";
|
||||
import type AutoGraticule from "$lib/vendor/leaflet-graticule/leaflet-auto-graticule";
|
||||
import type { Map, Marker } from "leaflet";
|
||||
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Dropdown from "../base/dropdown.svelte";
|
||||
|
||||
export let trail: Trail | null;
|
||||
export let markers: Marker[] = [];
|
||||
export let map: Map | null = null;
|
||||
export let options: any = {};
|
||||
export let graticule: AutoGraticule | null = null;
|
||||
export let crosshair: boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let L: any;
|
||||
let controlElevation: any;
|
||||
|
||||
let selectedMetric: "altitude" | "slope" | "speed" | false = "altitude";
|
||||
|
||||
$: gpxData = trail?.expand.gpx_data;
|
||||
$: if (gpxData && controlElevation) {
|
||||
controlElevation.updateOptions({
|
||||
autofitBounds: options.autofitBounds ?? true,
|
||||
});
|
||||
controlElevation.clear();
|
||||
controlElevation.load(gpxData);
|
||||
}
|
||||
|
||||
$: if (options) {
|
||||
controlElevation?.updateOptions(options);
|
||||
}
|
||||
|
||||
$: hotlineSwitcherItems = [
|
||||
{
|
||||
text: $_("altitude"),
|
||||
value: "altitude",
|
||||
icon: selectedMetric == "altitude" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("slope"),
|
||||
value: "slope",
|
||||
icon: selectedMetric == "slope" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("speed"),
|
||||
value: "speed",
|
||||
icon: selectedMetric == "speed" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("off"),
|
||||
value: false,
|
||||
icon: selectedMetric == false ? "circle-dot" : "circle",
|
||||
},
|
||||
];
|
||||
|
||||
onMount(async () => {
|
||||
L = (await import("leaflet")).default;
|
||||
await import("leaflet-gpx");
|
||||
await import("leaflet.awesome-markers");
|
||||
//@ts-ignore
|
||||
await import("$lib/vendor/leaflet-elevation/src/index.js");
|
||||
const AutoGraticule = (
|
||||
await import("$lib/vendor/leaflet-graticule/leaflet-auto-graticule")
|
||||
).default;
|
||||
|
||||
map = L.map("map", { preferCanvas: true }).setView(
|
||||
[trail?.lat ?? 0, trail?.lon ?? 0],
|
||||
3,
|
||||
);
|
||||
map!.attributionControl.setPrefix(false);
|
||||
|
||||
map!.on("zoomend", function () {
|
||||
dispatch("zoomend", map);
|
||||
});
|
||||
|
||||
map!.on("click", function (e) {
|
||||
dispatch("click", e);
|
||||
});
|
||||
|
||||
const baseLayer = L.tileLayer(
|
||||
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const topoLayer = L.tileLayer(
|
||||
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const baseMaps: Record<string, L.TileLayer> = {
|
||||
OpenStreetMaps: baseLayer,
|
||||
OpenTopoMaps: topoLayer,
|
||||
...($page.data.settings as Settings)?.tilesets?.reduce<
|
||||
Record<string, string>
|
||||
>((t, current) => {
|
||||
t[current.name] = L.tileLayer(current.url);
|
||||
return t;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
L.control.layers(baseMaps).addTo(map);
|
||||
|
||||
const layerPreference = localStorage.getItem("layer");
|
||||
|
||||
if (
|
||||
layerPreference &&
|
||||
Object.keys(baseMaps).includes(layerPreference)
|
||||
) {
|
||||
baseMaps[layerPreference].addTo(map!);
|
||||
} else {
|
||||
baseLayer.addTo(map);
|
||||
}
|
||||
|
||||
map!.on("baselayerchange", function (e) {
|
||||
localStorage.setItem("layer", e.name);
|
||||
});
|
||||
|
||||
const localMetric =
|
||||
(localStorage.getItem("gradient") as any) ?? "altitude";
|
||||
selectedMetric = localMetric === "false" ? false : localMetric;
|
||||
|
||||
const default_elevation_options = {
|
||||
height: 200,
|
||||
|
||||
theme: "lightblue-theme",
|
||||
detached: true,
|
||||
elevationDiv: "#elevation",
|
||||
closeBtn: false,
|
||||
followMarker: true,
|
||||
autofitBounds: true,
|
||||
imperial: ($page.data.settings?.unit ?? "metric") == "imperial",
|
||||
reverseCoords: false,
|
||||
acceleration: false,
|
||||
slope: true,
|
||||
speed: true,
|
||||
altitude: true,
|
||||
time: true,
|
||||
distance: true,
|
||||
// Summary track info style: "inline" || "multiline" || false
|
||||
summary: false,
|
||||
downloadLink: false,
|
||||
ruler: false,
|
||||
legend: true,
|
||||
// Toggle "leaflet-almostover" integration
|
||||
almostOver: false,
|
||||
// Toggle "leaflet-distance-markers" integration
|
||||
distanceMarkers: {
|
||||
lazy: false,
|
||||
distance: false,
|
||||
direction: true,
|
||||
offset: 500,
|
||||
},
|
||||
// Toggle "leaflet-edgescale" integration
|
||||
edgeScale: false,
|
||||
// Toggle "leaflet-hotline" integration
|
||||
hotline: selectedMetric,
|
||||
// Display track datetimes: true || false
|
||||
timestamps: false,
|
||||
waypoints: false,
|
||||
wptIcons: false,
|
||||
wptLabels: false,
|
||||
preferCanvas: true,
|
||||
trkStart: {
|
||||
interactive: false,
|
||||
className: "hihi",
|
||||
icon: startIcon(),
|
||||
},
|
||||
trkEnd: {
|
||||
icon: endIcon(),
|
||||
},
|
||||
graticule: false,
|
||||
drawing: false,
|
||||
};
|
||||
|
||||
const elevation_options = Object.assign(
|
||||
default_elevation_options,
|
||||
options,
|
||||
);
|
||||
|
||||
controlElevation = L.control.elevation(elevation_options).addTo(map);
|
||||
|
||||
for (const waypoint of trail?.expand.waypoints ?? []) {
|
||||
const marker = createMarkerFromWaypoint(L, waypoint);
|
||||
marker.addTo(map!);
|
||||
markers.push(marker);
|
||||
}
|
||||
|
||||
if (elevation_options.graticule) {
|
||||
graticule = new AutoGraticule();
|
||||
graticule.addTo(map!);
|
||||
}
|
||||
});
|
||||
|
||||
function switchHotline(metric: "altitude" | "slope" | "speed" | false) {
|
||||
controlElevation.updateOptions({
|
||||
hotline: metric,
|
||||
autofitBounds: false,
|
||||
});
|
||||
selectedMetric = metric;
|
||||
localStorage.setItem("gradient", metric.toString());
|
||||
controlElevation.clear();
|
||||
controlElevation.load(gpxData);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="map-container" class="flex flex-col h-full relative">
|
||||
<div
|
||||
id="map"
|
||||
class="rounded-xl z-0 basis-full min-h-96 md:min-h-0"
|
||||
style={crosshair
|
||||
? "position: relative; outline-style: none;cursor: crosshair !important"
|
||||
: "position: relative; outline-style: none;"}
|
||||
>
|
||||
<div class="absolute top-20 right-3 text-sm" style="z-index: 500">
|
||||
<Dropdown
|
||||
items={hotlineSwitcherItems}
|
||||
on:change={(e) => switchHotline(e.detail.value)}
|
||||
let:toggleMenu={openDropdown}
|
||||
>
|
||||
<button
|
||||
class="rounded-md border-2 border-black border-opacity-30 bg-white text-black hover:bg-gray-200 focus:ring-4 ring-gray-100/50 transition-colors h-12 w-12"
|
||||
on:click={openDropdown}
|
||||
>
|
||||
<i class="fa fa-route text-lg"></i>
|
||||
</button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<slot />
|
||||
<div class="basis-[300px] flex-grow flex-shrink-0" id="elevation"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,6 +16,7 @@
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
||||
import MaplibreGraticule from "$lib/vendor/maplibre-graticule/maplibre-graticule";
|
||||
import { FullscreenControl } from "$lib/vendor/maplibre-fullscreen/fullscreen-control";
|
||||
|
||||
export let trails: Trail[] = [];
|
||||
export let markers: M.Marker[] = [];
|
||||
@@ -24,6 +25,8 @@
|
||||
export let showElevation: boolean = true;
|
||||
export let showInfoPopup: boolean = false;
|
||||
export let showGrid: boolean = false;
|
||||
export let showStyleSwitcher: boolean = true;
|
||||
export let showFullscreen: boolean = false;
|
||||
export let fitBounds: "animate" | "instant" | "off" = "instant";
|
||||
|
||||
export let elevationProfileContainer: string | HTMLDivElement | undefined =
|
||||
@@ -520,7 +523,18 @@
|
||||
});
|
||||
map.addControl(epc);
|
||||
}
|
||||
map.addControl(switcherControl);
|
||||
if (showStyleSwitcher) {
|
||||
map.addControl(switcherControl);
|
||||
}
|
||||
|
||||
if (showFullscreen) {
|
||||
map.addControl(
|
||||
new FullscreenControl(() => {
|
||||
dispatch("fullscreen");
|
||||
}),
|
||||
"bottom-right",
|
||||
);
|
||||
}
|
||||
|
||||
map.on("styledata", () => {
|
||||
trails.forEach((t, i) => {
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
import { Settings } from "$lib/models/settings";
|
||||
import type { Trail } from "$lib/models/trail";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import {
|
||||
formatDistance,
|
||||
formatElevation,
|
||||
formatTimeHHMM,
|
||||
} from "$lib/util/format_util";
|
||||
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
|
||||
import "$lib/vendor/leaflet-elevation/src/index.css";
|
||||
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import Dropdown from "../base/dropdown.svelte";
|
||||
|
||||
export let trails: Trail[];
|
||||
export let map: any | null = null;
|
||||
export let options: any = {};
|
||||
export let activeTrailIndex: number | null = 0;
|
||||
export let markers: any[] = [];
|
||||
export let bindRoutePopup: boolean = true;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let L: any;
|
||||
let gpxGroup: any;
|
||||
|
||||
let selectedMetric: "altitude" | "slope" | "speed" | false = "altitude";
|
||||
|
||||
$: gpxData = trails.map((t) => {
|
||||
return { id: t.id, gpx: t.expand.gpx_data };
|
||||
});
|
||||
$: if (
|
||||
gpxData &&
|
||||
gpxGroup &&
|
||||
map &&
|
||||
(gpxData.length != gpxGroup._tracks.length ||
|
||||
gpxGroup?._tracks[0] !== gpxData[0])
|
||||
) {
|
||||
// if (gpxData.length == 0) {
|
||||
// map?.setView([0, 0], 4);
|
||||
// }
|
||||
gpxGroup._elevation.updateOptions({
|
||||
autofitBounds: options.autofitBounds ?? true,
|
||||
});
|
||||
gpxGroup.clear();
|
||||
gpxGroup._tracks = gpxData;
|
||||
gpxGroup.addTracks();
|
||||
}
|
||||
|
||||
$: if (options && gpxGroup) {
|
||||
gpxGroup._elevation.updateOptions(options);
|
||||
}
|
||||
|
||||
$: hotlineSwitcherItems = [
|
||||
{
|
||||
text: $_("altitude"),
|
||||
value: "altitude",
|
||||
icon: selectedMetric == "altitude" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("slope"),
|
||||
value: "slope",
|
||||
icon: selectedMetric == "slope" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("speed"),
|
||||
value: "speed",
|
||||
icon: selectedMetric == "speed" ? "circle-dot" : "circle",
|
||||
},
|
||||
{
|
||||
text: $_("off"),
|
||||
value: false,
|
||||
icon: selectedMetric == false ? "circle-dot" : "circle",
|
||||
},
|
||||
];
|
||||
|
||||
onMount(async () => {
|
||||
L = (await import("leaflet")).default;
|
||||
await import("leaflet-gpx");
|
||||
await import("leaflet.awesome-markers");
|
||||
await import("$lib/vendor/leaflet-elevation/src/index.js");
|
||||
await import("$lib/vendor/leaflet-elevation/libs/leaflet-gpxgroup");
|
||||
|
||||
map = L.map("map", {
|
||||
preferCanvas: true,
|
||||
plugins: ["/vendor/leaflet-elevation/libs/leaflet-gpxgroup.js"],
|
||||
}).setView(
|
||||
[
|
||||
trails.at(activeTrailIndex ?? 0)?.lat ?? 0,
|
||||
trails.at(activeTrailIndex ?? 0)?.lon ?? 0,
|
||||
],
|
||||
3,
|
||||
);
|
||||
|
||||
dispatch("init", map);
|
||||
map!.attributionControl.setPrefix(false);
|
||||
|
||||
map!.on("zoomend", function () {
|
||||
dispatch("zoomend", map);
|
||||
});
|
||||
|
||||
map!.on("click", function (e: any) {
|
||||
dispatch("click", e);
|
||||
});
|
||||
|
||||
map!.on("moveend", function (e: any) {
|
||||
dispatch("moveend", e);
|
||||
});
|
||||
|
||||
const baseLayer = L.tileLayer(
|
||||
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const topoLayer = L.tileLayer(
|
||||
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const baseMaps: Record<string, L.TileLayer> = {
|
||||
OpenStreetMaps: baseLayer,
|
||||
OpenTopoMaps: topoLayer,
|
||||
...($page.data?.settings as Settings)?.tilesets?.reduce<
|
||||
Record<string, string>
|
||||
>((t, current) => {
|
||||
t[current.name] = L.tileLayer(current.url);
|
||||
return t;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
L.control.layers(baseMaps).addTo(map);
|
||||
|
||||
const layerPreference = localStorage.getItem("layer");
|
||||
|
||||
if (
|
||||
layerPreference &&
|
||||
Object.keys(baseMaps).includes(layerPreference)
|
||||
) {
|
||||
baseMaps[layerPreference].addTo(map!);
|
||||
} else {
|
||||
baseLayer.addTo(map);
|
||||
}
|
||||
|
||||
map!.on("baselayerchange", function (e: any) {
|
||||
localStorage.setItem("layer", e.name);
|
||||
});
|
||||
|
||||
const localMetric =
|
||||
(localStorage.getItem("gradient") as any) ?? "altitude";
|
||||
selectedMetric = localMetric === "false" ? false : localMetric;
|
||||
|
||||
const default_elevation_options = {
|
||||
height: 200,
|
||||
|
||||
theme: "lightblue-theme",
|
||||
detached: true,
|
||||
elevationDiv: "#elevation",
|
||||
closeBtn: false,
|
||||
followMarker: true,
|
||||
autofitBounds: true,
|
||||
imperial: $page.data.settings?.unit == "imperial",
|
||||
reverseCoords: false,
|
||||
acceleration: false,
|
||||
slope: true,
|
||||
speed: true,
|
||||
altitude: true,
|
||||
time: true,
|
||||
distance: true,
|
||||
// Summary track info style: "inline" || "multiline" || false
|
||||
summary: false,
|
||||
downloadLink: false,
|
||||
ruler: false,
|
||||
legend: true,
|
||||
// Toggle "leaflet-almostover" integration
|
||||
almostOver: false,
|
||||
// Toggle "leaflet-distance-markers" integration
|
||||
distanceMarkers: {
|
||||
lazy: false,
|
||||
distance: false,
|
||||
direction: true,
|
||||
offset: 1000,
|
||||
},
|
||||
// Toggle "leaflet-edgescale" integration
|
||||
edgeScale: false,
|
||||
// Toggle "leaflet-hotline" integration
|
||||
hotline: selectedMetric,
|
||||
// Display track datetimes: true || false
|
||||
timestamps: false,
|
||||
waypoints: false,
|
||||
wptIcons: false,
|
||||
wptLabels: false,
|
||||
preferCanvas: true,
|
||||
graticule: false,
|
||||
drawing: false,
|
||||
};
|
||||
|
||||
const elevation_options = Object.assign(
|
||||
default_elevation_options,
|
||||
options,
|
||||
);
|
||||
|
||||
gpxGroup = L.gpxGroup(gpxData, {
|
||||
points: [],
|
||||
elevation: true,
|
||||
elevation_options: elevation_options,
|
||||
flyToBounds: options.flyToBounds,
|
||||
distanceMarkers: true,
|
||||
itinerary: options.itinerary,
|
||||
});
|
||||
|
||||
const markerLayerGroup = L.layerGroup().addTo(map);
|
||||
|
||||
gpxGroup.on("selection_changed", ({ polyline }: { polyline: any }) => {
|
||||
markerLayerGroup.clearLayers();
|
||||
activeTrailIndex = null;
|
||||
markers = [];
|
||||
|
||||
if (polyline && polyline._selected) {
|
||||
activeTrailIndex = trails.findIndex(
|
||||
(t) => t.id == polyline.options.id,
|
||||
);
|
||||
|
||||
for (const waypoint of trails.at(activeTrailIndex!)?.expand
|
||||
.waypoints ?? []) {
|
||||
const marker = createMarkerFromWaypoint(L, waypoint);
|
||||
marker.addTo(markerLayerGroup!);
|
||||
markers.push(marker);
|
||||
}
|
||||
|
||||
dispatch(
|
||||
"select",
|
||||
trails.find((t) => t.id == polyline.options.id),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
gpxGroup.on("clear", () => {
|
||||
markerLayerGroup.clearLayers();
|
||||
activeTrailIndex = null;
|
||||
});
|
||||
|
||||
gpxGroup.on("route_loaded", ({ route }: { route: any }) => {
|
||||
const trail = trails.find((t) => t.id == route.options.id);
|
||||
|
||||
if (!trail) {
|
||||
return;
|
||||
}
|
||||
const thumbnail = trail.photos.length
|
||||
? getFileURL(trail, trail.photos[trail.thumbnail])
|
||||
: "/imgs/default_thumbnail.webp";
|
||||
if (bindRoutePopup) {
|
||||
route.bindPopup(
|
||||
`<a href="/trail/view/${trail.id}" data-sveltekit-preload-data="off">
|
||||
<li class="flex items-center gap-4 cursor-pointer text-black max-w-80">
|
||||
<div class="shrink-0"><img class="h-14 w-14 object-cover rounded-xl" src="${thumbnail}" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold text-lg">${trail.name}</h4>
|
||||
<div class="flex gap-x-4">
|
||||
${trail.location ? `<h5><i class="fa fa-location-dot mr-2"></i>${trail.location}</h5>` : ""}
|
||||
<h5><i class="fa fa-gauge mr-2"></i>${$_(trail.difficulty as string)}</h5>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 mt-2 gap-x-4 gap-y-2 text-sm text-gray-500 flex-wrap"><span class="shrink-0"><i
|
||||
class="fa fa-left-right mr-2"></i>${formatDistance(
|
||||
trail.distance,
|
||||
)}</span><span class="shrink-0"><i class="fa fa-clock mr-2"></i>${formatTimeHHMM(
|
||||
trail.duration,
|
||||
)}</span><span class="shrink-0"><i class="fa fa-arrow-trend-up mr-2"></i>${formatElevation(
|
||||
trail.elevation_gain,
|
||||
)}</span></span> <span class="shrink-0"><i class="fa fa-arrow-trend-down mr-2"></i>${formatElevation(
|
||||
trail.elevation_loss,
|
||||
)}</span></div>
|
||||
</div>
|
||||
</li>
|
||||
</a>`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
gpxGroup.addTo(map);
|
||||
});
|
||||
|
||||
export function highlightTrail(id: string) {
|
||||
gpxGroup?.highlightTrack(id);
|
||||
}
|
||||
|
||||
export function unHighlightTrail(id: string) {
|
||||
gpxGroup?.unHighlightTrack(id);
|
||||
}
|
||||
|
||||
export function selectTrail(id: string) {
|
||||
gpxGroup?.select(id);
|
||||
}
|
||||
|
||||
export function resetSelection() {
|
||||
gpxGroup?.resetSelection();
|
||||
}
|
||||
|
||||
export function openPopup(id: string) {
|
||||
gpxGroup?.openPopup(id);
|
||||
}
|
||||
|
||||
export function closePopup(id: string) {
|
||||
gpxGroup?.closePopup(id);
|
||||
}
|
||||
|
||||
function switchHotline(metric: "altitude" | "slope" | "speed" | false) {
|
||||
if (metric === selectedMetric) {
|
||||
return;
|
||||
}
|
||||
gpxGroup._elevation.updateOptions({
|
||||
hotline: metric,
|
||||
});
|
||||
gpxGroup.options.flyToBounds = false;
|
||||
selectedMetric = metric;
|
||||
localStorage.setItem("gradient", metric.toString());
|
||||
gpxGroup.clear();
|
||||
gpxGroup._tracks = gpxData;
|
||||
gpxGroup.addTracks();
|
||||
|
||||
gpxGroup.options.flyToBounds = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="map-container" class="h-full relative">
|
||||
<div
|
||||
id="map"
|
||||
class="rounded-xl z-0 h-full min-h-96 md:min-h-0"
|
||||
style="position: relative; outline-style: none;"
|
||||
>
|
||||
<div class="absolute top-20 right-3 text-sm" style="z-index: 500">
|
||||
<Dropdown
|
||||
items={hotlineSwitcherItems}
|
||||
on:change={(e) => switchHotline(e.detail.value)}
|
||||
let:toggleMenu={openDropdown}
|
||||
>
|
||||
<button
|
||||
class="rounded-md border-2 border-black border-opacity-30 bg-white text-black hover:bg-gray-200 focus:ring-4 ring-gray-100/50 transition-colors h-12 w-12"
|
||||
on:click={openDropdown}
|
||||
>
|
||||
<i class="fa fa-route text-lg"></i>
|
||||
</button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute bottom-0 bg-background/80 w-full" id="elevation"></div>
|
||||
</div>
|
||||
@@ -20,15 +20,7 @@
|
||||
formatElevation,
|
||||
formatTimeHHMM,
|
||||
} from "$lib/util/format_util";
|
||||
import {
|
||||
createMarkerFromWaypoint,
|
||||
endIcon,
|
||||
startIcon,
|
||||
} from "$lib/util/leaflet_util";
|
||||
import "$lib/vendor/leaflet-elevation/src/index.css";
|
||||
import type { Map, Marker } from "leaflet";
|
||||
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
|
||||
import "photoswipe/style.css";
|
||||
import { onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
@@ -40,10 +32,11 @@
|
||||
import SummitLogTable from "../summit_log/summit_log_table.svelte";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import * as M from "maplibre-gl";
|
||||
import MapWithElevationMaplibre from "./map_with_elevation_maplibre.svelte";
|
||||
|
||||
export let trail: Trail;
|
||||
export let mode: "overview" | "map" | "list" = "map";
|
||||
export let markers: (Marker | M.Marker)[] = [];
|
||||
export let markers: M.Marker[] = [];
|
||||
|
||||
const tabs = [
|
||||
$_("description"),
|
||||
@@ -56,8 +49,6 @@
|
||||
const trailIsShared =
|
||||
(trail.expand?.trail_share_via_trail?.length ?? 0) > 0;
|
||||
|
||||
let map: Map;
|
||||
|
||||
let activeTab = 0;
|
||||
|
||||
let thumbnail = trail.photos.length
|
||||
@@ -74,64 +65,14 @@
|
||||
fetchComments();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (mode == "overview") {
|
||||
const L = (await import("leaflet")).default;
|
||||
await import("leaflet-gpx");
|
||||
await import("leaflet.awesome-markers");
|
||||
|
||||
map = L.map("map").setView([0, 0], 2);
|
||||
map.attributionControl.setPrefix(false);
|
||||
|
||||
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
}).addTo(map);
|
||||
|
||||
const gpxLayer = new L.GPX(trail.expand.gpx_data!, {
|
||||
async: true,
|
||||
polyline_options: {
|
||||
className: "lightblue-theme elevation-polyline",
|
||||
opacity: 0.75,
|
||||
weight: 5,
|
||||
},
|
||||
gpx_options: {
|
||||
parseElements: ["track", "route"] as any,
|
||||
},
|
||||
marker_options: {
|
||||
startIcon: startIcon() as any,
|
||||
startIconUrl: "",
|
||||
endIcon: endIcon() as any,
|
||||
endIconUrl: "",
|
||||
shadowUrl: "",
|
||||
},
|
||||
})
|
||||
.on("loaded", function (e) {
|
||||
map.fitBounds(e.target.getBounds());
|
||||
})
|
||||
.addTo(map);
|
||||
|
||||
for (const waypoint of trail.expand.waypoints) {
|
||||
const marker = createMarkerFromWaypoint(L, waypoint);
|
||||
marker.addTo(map);
|
||||
markers.push(marker);
|
||||
}
|
||||
}
|
||||
});
|
||||
onMount(async () => {});
|
||||
|
||||
function openMarkerPopup(i: number) {
|
||||
if (typeof (markers[i] as Marker<any>).openPopup === "function") {
|
||||
(markers[i] as Marker<any>).openPopup();
|
||||
} else {
|
||||
(markers[i] as M.Marker).togglePopup();
|
||||
}
|
||||
(markers[i] as M.Marker).togglePopup();
|
||||
}
|
||||
|
||||
function closeMarkerPopup(i: number) {
|
||||
if (typeof (markers[i] as Marker<any>).closePopup === "function") {
|
||||
(markers[i] as Marker<any>).closePopup();
|
||||
} else {
|
||||
(markers[i] as M.Marker).togglePopup();
|
||||
}
|
||||
(markers[i] as M.Marker).togglePopup();
|
||||
}
|
||||
|
||||
async function toggleMapFullScreen() {
|
||||
@@ -425,16 +366,16 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if mode == "overview"}
|
||||
<div class="relative">
|
||||
<div class="rounded-xl h-72" id="map">
|
||||
<div class="leaflet-top leaflet-right">
|
||||
<button
|
||||
class="leaflet-control fa fa-maximize rounded-full text-lg bg-white text-black px-[14px] py-2 hover:bg-gray-100"
|
||||
style="cursor: pointer !important"
|
||||
on:click={() => toggleMapFullScreen()}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative h-72 rounded-xl">
|
||||
<MapWithElevationMaplibre
|
||||
trails={[trail]}
|
||||
showElevation={false}
|
||||
showStyleSwitcher={false}
|
||||
showFullscreen={true}
|
||||
mapOptions={{ attributionControl: false }}
|
||||
on:fullscreen={toggleMapFullScreen}
|
||||
bind:markers
|
||||
></MapWithElevationMaplibre>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user