diff --git a/website/src/lib/components/map/MapPopup.svelte b/website/src/lib/components/map/MapPopup.svelte
index 007e9d43..ae7caaa8 100644
--- a/website/src/lib/components/map/MapPopup.svelte
+++ b/website/src/lib/components/map/MapPopup.svelte
@@ -3,20 +3,32 @@
import WaypointPopup from '$lib/components/map/gpx-layer/WaypointPopup.svelte';
import TrackpointPopup from '$lib/components/map/gpx-layer/TrackpointPopup.svelte';
import OverpassPopup from '$lib/components/map/layer-control/OverpassPopup.svelte';
- import type { PopupItem } from '$lib/components/map/map.svelte';
+ import type { PopupItem } from '$lib/components/map/map-popup';
+ import type { Writable } from 'svelte/store';
- let { item, container = null }: { item: PopupItem | null; container: HTMLDivElement | null } =
+ let {
+ item,
+ onContainerReady,
+ }: { item: Writable
; onContainerReady: (div: HTMLDivElement) => void } =
$props();
+
+ let container: HTMLDivElement | null = $state(null);
+
+ $effect(() => {
+ if (container) {
+ onContainerReady(container);
+ }
+ });
- {#if item}
- {#if item.item instanceof Waypoint}
-
- {:else if item.item instanceof TrackPoint}
-
+ {#if $item}
+ {#if $item.item instanceof Waypoint}
+
+ {:else if $item.item instanceof TrackPoint}
+
{:else}
-
+
{/if}
{/if}
diff --git a/website/src/lib/components/map/custom-control/CustomControl.svelte b/website/src/lib/components/map/custom-control/CustomControl.svelte
index be97f03a..7e4f827b 100644
--- a/website/src/lib/components/map/custom-control/CustomControl.svelte
+++ b/website/src/lib/components/map/custom-control/CustomControl.svelte
@@ -1,6 +1,6 @@
diff --git a/website/src/lib/components/map/gpx-layer/DistanceMarkers.ts b/website/src/lib/components/map/gpx-layer/DistanceMarkers.ts
index 34949603..40219c59 100644
--- a/website/src/lib/components/map/gpx-layer/DistanceMarkers.ts
+++ b/website/src/lib/components/map/gpx-layer/DistanceMarkers.ts
@@ -1,9 +1,8 @@
-import { settings } from '$lib/db';
-import { gpxStatistics } from '$lib/stores';
+import { settings } from '$lib/logic/settings';
import type { GeoJSONSource } from 'mapbox-gl';
import { get } from 'svelte/store';
-// const { distanceMarkers, distanceUnits } = settings;
+const { distanceMarkers, distanceUnits } = settings;
const stops = [
[100, 0],
diff --git a/website/src/lib/components/map/gpx-layer/GPXLayers.svelte b/website/src/lib/components/map/gpx-layer/GPXLayers.svelte
index 9e86d2fa..3ae77206 100644
--- a/website/src/lib/components/map/gpx-layer/GPXLayers.svelte
+++ b/website/src/lib/components/map/gpx-layer/GPXLayers.svelte
@@ -1,56 +1,41 @@
diff --git a/website/src/lib/components/map/gpx-layer/TrackpointPopup.svelte b/website/src/lib/components/map/gpx-layer/TrackpointPopup.svelte
index 235a4143..9256b98b 100644
--- a/website/src/lib/components/map/gpx-layer/TrackpointPopup.svelte
+++ b/website/src/lib/components/map/gpx-layer/TrackpointPopup.svelte
@@ -1,11 +1,11 @@
diff --git a/website/src/lib/components/map/gpx-layer/WaypointPopup.svelte b/website/src/lib/components/map/gpx-layer/WaypointPopup.svelte
index 330c6755..9a05bbda 100644
--- a/website/src/lib/components/map/gpx-layer/WaypointPopup.svelte
+++ b/website/src/lib/components/map/gpx-layer/WaypointPopup.svelte
@@ -3,16 +3,16 @@
import { Button } from '$lib/components/ui/button';
import Shortcut from '$lib/components/Shortcut.svelte';
import CopyCoordinates from '$lib/components/map/gpx-layer/CopyCoordinates.svelte';
- import { deleteWaypoint } from './GPXLayerPopup';
import WithUnits from '$lib/components/WithUnits.svelte';
import { Dot, ExternalLink, Trash2 } from '@lucide/svelte';
- import { tool, Tool } from '$lib/components/toolbar/utils.svelte';
+ import { currentTool, Tool } from '$lib/components/toolbar/tools';
import { getSymbolKey, symbols } from '$lib/assets/symbols';
import { i18n } from '$lib/i18n.svelte';
import sanitizeHtml from 'sanitize-html';
import type { Waypoint } from 'gpx';
- import type { PopupItem } from '$lib/components/map/map.svelte';
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
+ import type { PopupItem } from '$lib/components/map/map';
+ import { fileActions } from '$lib/logic/file-actions';
export let waypoint: PopupItem;
@@ -80,11 +80,15 @@
- {#if tool.current === Tool.WAYPOINT}
+ {#if $currentTool === Tool.WAYPOINT}
-
+
{i18n._('toolbar.waypoint.add')}
diff --git a/website/src/lib/components/map/layer-control/utils.svelte.ts b/website/src/lib/components/map/layer-control/utils.ts
similarity index 94%
rename from website/src/lib/components/map/layer-control/utils.svelte.ts
rename to website/src/lib/components/map/layer-control/utils.ts
index cd53896c..3cd8cab7 100644
--- a/website/src/lib/components/map/layer-control/utils.svelte.ts
+++ b/website/src/lib/components/map/layer-control/utils.ts
@@ -1,4 +1,5 @@
import type { LayerTreeType } from '$lib/assets/layers';
+import { writable } from 'svelte/store';
export function anySelectedLayer(node: LayerTreeType) {
return (
@@ -54,6 +55,4 @@ export function toggle(node: LayerTreeType, id: string) {
return node;
}
-export const customBasemapUpdate = $state({
- value: 0,
-});
+export const customBasemapUpdate = writable(0);
diff --git a/website/src/lib/components/map/map-popup.ts b/website/src/lib/components/map/map-popup.ts
new file mode 100644
index 00000000..ac156c8d
--- /dev/null
+++ b/website/src/lib/components/map/map-popup.ts
@@ -0,0 +1,84 @@
+import { TrackPoint, Waypoint } from 'gpx';
+import mapboxgl from 'mapbox-gl';
+import { mount, tick } from 'svelte';
+import { get, writable, type Writable } from 'svelte/store';
+import MapPopupComponent from '$lib/components/map/MapPopup.svelte';
+
+export type PopupItem = {
+ item: T;
+ fileId?: string;
+ hide?: () => void;
+};
+
+export class MapPopup {
+ map: mapboxgl.Map;
+ popup: mapboxgl.Popup;
+ item: Writable = writable(null);
+ component: ReturnType;
+ maybeHideBinded = this.maybeHide.bind(this);
+
+ constructor(map: mapboxgl.Map, options?: mapboxgl.PopupOptions) {
+ this.map = map;
+ this.popup = new mapboxgl.Popup(options);
+ this.component = mount(MapPopupComponent, {
+ target: document.body,
+ props: {
+ item: this.item,
+ onContainerReady: (container: HTMLDivElement) => {
+ this.popup.setDOMContent(container);
+ },
+ },
+ });
+ }
+
+ setItem(item: PopupItem | null) {
+ if (item) item.hide = () => this.hide();
+ this.item.set(item);
+ if (item === null) {
+ this.hide();
+ } else {
+ tick().then(() => this.show());
+ }
+ }
+
+ show() {
+ const item = get(this.item);
+ if (item === null) {
+ this.hide();
+ return;
+ }
+ this.popup.setLngLat(this.getCoordinates()).addTo(this.map);
+ this.map.on('mousemove', this.maybeHideBinded);
+ }
+
+ maybeHide(e: mapboxgl.MapMouseEvent) {
+ const item = get(this.item);
+ if (item === null) {
+ this.hide();
+ return;
+ }
+ if (this.map.project(this.getCoordinates()).dist(this.map.project(e.lngLat)) > 60) {
+ this.hide();
+ }
+ }
+
+ hide() {
+ this.popup.remove();
+ this.map.off('mousemove', this.maybeHideBinded);
+ }
+
+ remove() {
+ this.popup.remove();
+ this.component.$destroy();
+ }
+
+ getCoordinates() {
+ const item = get(this.item);
+ if (item === null) {
+ return new mapboxgl.LngLat(0, 0);
+ }
+ return item.item instanceof Waypoint || item.item instanceof TrackPoint
+ ? item.item.getCoordinates()
+ : new mapboxgl.LngLat(item.item.lon, item.item.lat);
+ }
+}
diff --git a/website/src/lib/components/map/utils.svelte.ts b/website/src/lib/components/map/map.ts
similarity index 67%
rename from website/src/lib/components/map/utils.svelte.ts
rename to website/src/lib/components/map/map.ts
index 768aa5f6..10cd6d1f 100644
--- a/website/src/lib/components/map/utils.svelte.ts
+++ b/website/src/lib/components/map/map.ts
@@ -1,10 +1,9 @@
-import { TrackPoint, Waypoint, type Coordinates } from 'gpx';
import mapboxgl from 'mapbox-gl';
-import { tick, mount } from 'svelte';
-// import MapPopupComponent from '$lib/components/map/MapPopup.svelte';
-import { get } from 'svelte/store';
-// import { fileObservers } from '$lib/db';
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
+import { get, writable, type Writable } from 'svelte/store';
+import { settings } from '$lib/logic/settings';
+
+const { treeFileView, elevationProfile, bottomPanelSize, rightPanelSize, distanceUnits } = settings;
let fitBoundsOptions: mapboxgl.MapOptions['fitBoundsOptions'] = {
maxZoom: 15,
@@ -13,13 +12,17 @@ let fitBoundsOptions: mapboxgl.MapOptions['fitBoundsOptions'] = {
};
export class MapboxGLMap {
- private _map: mapboxgl.Map | null = $state(null);
+ private _map: Writable = writable(null);
private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = [];
+ private _unsubscribes: (() => void)[] = [];
+
+ subscribe(run: (value: mapboxgl.Map | null) => void, invalidate?: () => void) {
+ return this._map.subscribe(run, invalidate);
+ }
init(
accessToken: string,
language: string,
- distanceUnits: 'metric' | 'imperial' | 'nautical',
hash: boolean,
geocoder: boolean,
geolocate: boolean
@@ -126,7 +129,7 @@ export class MapboxGLMap {
);
}
const scaleControl = new mapboxgl.ScaleControl({
- unit: distanceUnits,
+ unit: get(distanceUnits),
});
map.addControl(scaleControl);
map.on('style.load', () => {
@@ -160,46 +163,58 @@ export class MapboxGLMap {
});
});
map.on('load', () => {
- this._map = map; // only set the store after the map has loaded
+ this._map.set(map); // only set the store after the map has loaded
window._map = map; // entry point for extensions
- scaleControl.setUnit(distanceUnits);
+ scaleControl.setUnit(get(distanceUnits));
this._onLoadCallbacks.forEach((callback) => callback(map));
this._onLoadCallbacks = [];
});
+
+ this._unsubscribes.push(treeFileView.subscribe(() => this.resize()));
+ this._unsubscribes.push(elevationProfile.subscribe(() => this.resize()));
+ this._unsubscribes.push(bottomPanelSize.subscribe(() => this.resize()));
+ this._unsubscribes.push(rightPanelSize.subscribe(() => this.resize()));
+ this._unsubscribes.push(
+ distanceUnits.subscribe((units) => {
+ scaleControl.setUnit(units);
+ })
+ );
}
onLoad(callback: (map: mapboxgl.Map) => void) {
- if (this._map) {
- callback(this._map);
+ const map = get(this._map);
+ if (map) {
+ callback(map);
} else {
this._onLoadCallbacks.push(callback);
}
}
destroy() {
- if (this._map) {
- this._map.remove();
- this._map = null;
+ const map = get(this._map);
+ if (map) {
+ map.remove();
+ this._map.set(null);
}
- }
-
- get value(): mapboxgl.Map | null {
- return this._map;
+ this._unsubscribes.forEach((unsubscribe) => unsubscribe());
+ this._unsubscribes = [];
}
resize() {
- if (this._map) {
- this._map.resize();
+ const map = get(this._map);
+ if (map) {
+ map.resize();
}
}
toggle3D() {
- if (this._map) {
- if (this._map.getPitch() === 0) {
- this._map.easeTo({ pitch: 70 });
+ const map = get(this._map);
+ if (map) {
+ if (map.getPitch() === 0) {
+ map.easeTo({ pitch: 70 });
} else {
- this._map.easeTo({ pitch: 0 });
+ map.easeTo({ pitch: 0 });
}
}
}
@@ -207,15 +222,15 @@ export class MapboxGLMap {
export const map = new MapboxGLMap();
-const targetMapBounds: {
- bounds: mapboxgl.LngLatBounds;
- ids: string[];
- total: number;
-} = $state({
- bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
- ids: [],
- total: 0,
-});
+// const targetMapBounds: {
+// bounds: mapboxgl.LngLatBounds;
+// ids: string[];
+// total: number;
+// } = $state({
+// bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
+// ids: [],
+// total: 0,
+// });
// $effect(() => {
// if (
@@ -251,32 +266,32 @@ const targetMapBounds: {
// map.current.fitBounds(targetMapBounds.bounds, { padding: 80, linear: true, easing: () => 1 });
// });
-export function initTargetMapBounds(ids: string[]) {
- targetMapBounds.bounds = new mapboxgl.LngLatBounds([180, 90, -180, -90]);
- targetMapBounds.ids = ids;
- targetMapBounds.total = ids.length;
-}
+// export function initTargetMapBounds(ids: string[]) {
+// targetMapBounds.bounds = new mapboxgl.LngLatBounds([180, 90, -180, -90]);
+// targetMapBounds.ids = ids;
+// targetMapBounds.total = ids.length;
+// }
-export function updateTargetMapBounds(
- id: string,
- bounds: { southWest: Coordinates; northEast: Coordinates }
-) {
- if (targetMapBounds.ids.indexOf(id) === -1) {
- return;
- }
+// export function updateTargetMapBounds(
+// id: string,
+// bounds: { southWest: Coordinates; northEast: Coordinates }
+// ) {
+// if (targetMapBounds.ids.indexOf(id) === -1) {
+// return;
+// }
- if (
- bounds.southWest.lat !== 90 ||
- bounds.southWest.lon !== 180 ||
- bounds.northEast.lat !== -90 ||
- bounds.northEast.lon !== -180
- ) {
- // Avoid update for empty (new) files
- targetMapBounds.ids = targetMapBounds.ids.filter((x) => x !== id);
- targetMapBounds.bounds.extend(bounds.southWest);
- targetMapBounds.bounds.extend(bounds.northEast);
- }
-}
+// if (
+// bounds.southWest.lat !== 90 ||
+// bounds.southWest.lon !== 180 ||
+// bounds.northEast.lat !== -90 ||
+// bounds.northEast.lon !== -180
+// ) {
+// // Avoid update for empty (new) files
+// targetMapBounds.ids = targetMapBounds.ids.filter((x) => x !== id);
+// targetMapBounds.bounds.extend(bounds.southWest);
+// targetMapBounds.bounds.extend(bounds.northEast);
+// }
+// }
// export function centerMapOnSelection() {
// let selected = get(selection).getSelected();
@@ -308,77 +323,3 @@ export function updateTargetMapBounds(
// maxZoom: 15,
// });
// }
-
-export type PopupItem = {
- item: T;
- fileId?: string;
- hide?: () => void;
-};
-
-// export class MapPopup {
-// map: mapboxgl.Map;
-// popup: mapboxgl.Popup;
-// item: PopupItem | null = $state(null);
-// maybeHideBinded = this.maybeHide.bind(this);
-
-// constructor(map: mapboxgl.Map, options?: mapboxgl.PopupOptions) {
-// this.map = map;
-// this.popup = new mapboxgl.Popup(options);
-
-// let component = mount(MapPopupComponent, {
-// target: document.body,
-// props: {
-// item: this.item,
-// },
-// });
-
-// tick().then(() => this.popup.setDOMContent(component.container));
-// }
-
-// setItem(item: PopupItem | null) {
-// if (item) item.hide = () => this.hide();
-// this.item = item;
-// if (item === null) {
-// this.hide();
-// } else {
-// tick().then(() => this.show());
-// }
-// }
-
-// show() {
-// if (this.item === null) {
-// this.hide();
-// return;
-// }
-// this.popup.setLngLat(this.getCoordinates()).addTo(this.map);
-// this.map.on('mousemove', this.maybeHideBinded);
-// }
-
-// maybeHide(e: mapboxgl.MapMouseEvent) {
-// if (this.item === null) {
-// this.hide();
-// return;
-// }
-// if (this.map.project(this.getCoordinates()).dist(this.map.project(e.lngLat)) > 60) {
-// this.hide();
-// }
-// }
-
-// hide() {
-// this.popup.remove();
-// this.map.off('mousemove', this.maybeHideBinded);
-// }
-
-// remove() {
-// this.popup.remove();
-// }
-
-// getCoordinates() {
-// if (this.item === null) {
-// return new mapboxgl.LngLat(0, 0);
-// }
-// return this.item.item instanceof Waypoint || this.item.item instanceof TrackPoint
-// ? this.item.item.getCoordinates()
-// : new mapboxgl.LngLat(this.item.item.lon, this.item.item.lat);
-// }
-// }
diff --git a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte
index 84863398..e62fd43a 100644
--- a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte
+++ b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte
@@ -1,13 +1,13 @@