mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-27 02:27:18 +00:00
Merge branch 'dev' into tauri
This commit is contained in:
commit
873e2e1654
53 files changed
+570
-509
No files matched your search
@@ -2,6 +2,7 @@
|
||||
import CustomControl from './custom-control';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import type { Map } from 'maplibre-gl';
|
||||
|
||||
let {
|
||||
position = 'top-right',
|
||||
@@ -17,7 +18,7 @@
|
||||
let control: CustomControl | null = null;
|
||||
|
||||
onMount(() => {
|
||||
map.onLoad((map: maplibregl.Map) => {
|
||||
map.onLoad((map: Map) => {
|
||||
if (position.includes('right')) container.classList.add('float-right');
|
||||
else container.classList.add('float-left');
|
||||
container.classList.remove('hidden');
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { MapPopup } from '$lib/components/map/map-popup';
|
||||
import type { Map } from 'maplibre-gl';
|
||||
|
||||
export let waypointPopup: MapPopup | null = null;
|
||||
export let trackpointPopup: MapPopup | null = null;
|
||||
|
||||
export function createPopups(map: maplibregl.Map) {
|
||||
export function createPopups(map: Map) {
|
||||
removePopups();
|
||||
waypointPopup = new MapPopup(map, {
|
||||
closeButton: false,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { get, type Readable } from 'svelte/store';
|
||||
import maplibregl, {
|
||||
import {
|
||||
type GeoJSONSource,
|
||||
type FilterSpecification,
|
||||
type MapLayerMouseEvent,
|
||||
type MapLayerTouchEvent,
|
||||
Point,
|
||||
} from 'maplibre-gl';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
|
||||
@@ -61,7 +62,7 @@ export class GPXLayer {
|
||||
selected: boolean = false;
|
||||
currentWaypointData: GeoJSON.FeatureCollection | null = null;
|
||||
draggedWaypointIndex: number | null = null;
|
||||
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
|
||||
draggingStartingPosition: Point = new Point(0, 0);
|
||||
unsubscribe: Function[] = [];
|
||||
|
||||
updateBinded: () => void = this.update.bind(this);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { Layers } from '@lucide/svelte';
|
||||
import { settings } from '$lib/state/settings';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import type { Map } from 'maplibre-gl';
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let overpassLayer: OverpassLayer;
|
||||
@@ -21,7 +22,7 @@
|
||||
selectedOverpassTree,
|
||||
} = settings;
|
||||
|
||||
map.onLoad((_map: maplibregl.Map) => {
|
||||
map.onLoad((_map: Map) => {
|
||||
if (overpassLayer) {
|
||||
overpassLayer.remove();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
poi: PopupItem<any>;
|
||||
} = $props();
|
||||
|
||||
let tags: Record<string, string> = $derived(poi ? JSON.parse(poi.item.tags) : {});
|
||||
let tags: Record<string, string> = $derived(
|
||||
poi ? (typeof poi.item.tags == 'object' ? poi.item.tags : JSON.parse(poi.item.tags)) : {}
|
||||
);
|
||||
let name = $derived.by(() => {
|
||||
if (poi) {
|
||||
if (tags.name !== undefined && tags.name !== '') {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { get, writable } from 'svelte/store';
|
||||
import { overpassQueryData } from '$lib/data/layers';
|
||||
import { MapPopup } from '$lib/components/map/map-popup';
|
||||
import { settings } from '$lib/state/settings';
|
||||
import type { GeoJSONSource } from 'maplibre-gl';
|
||||
import type { GeoJSONSource, Map as MapLibreMap } from 'maplibre-gl';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
|
||||
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
|
||||
import { loadSVGIcon } from '$lib/utils';
|
||||
@@ -23,7 +23,7 @@ export class OverpassLayer {
|
||||
overpassUrl = 'https://overpass.gpx.studio/api/interpreter';
|
||||
minZoom = 12;
|
||||
queryZoom = 12;
|
||||
map: maplibregl.Map;
|
||||
map: MapLibreMap;
|
||||
layerEventManager: MapLayerEventManager;
|
||||
popup: MapPopup;
|
||||
|
||||
@@ -39,7 +39,7 @@ export class OverpassLayer {
|
||||
updateBinded = this.update.bind(this);
|
||||
onHoverBinded = this.onHover.bind(this);
|
||||
|
||||
constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) {
|
||||
constructor(map: MapLibreMap, layerEventManager: MapLayerEventManager) {
|
||||
this.map = map;
|
||||
this.layerEventManager = layerEventManager;
|
||||
this.popup = new MapPopup(map, {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { engine } from '$lib/engine';
|
||||
import { get } from 'svelte/store';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import {
|
||||
LngLatBounds,
|
||||
type Map,
|
||||
type MapGeoJSONFeature,
|
||||
type MapLayerMouseEvent,
|
||||
type MapLayerTouchEvent,
|
||||
MapMouseEvent,
|
||||
MapTouchEvent,
|
||||
type Point,
|
||||
} from 'maplibre-gl';
|
||||
|
||||
type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void;
|
||||
type MapLayerTouchEventListener = (e: maplibregl.MapLayerTouchEvent) => void;
|
||||
type MapLayerMouseEventListener = (e: MapLayerMouseEvent) => void;
|
||||
type MapLayerTouchEventListener = (e: MapLayerTouchEvent) => void;
|
||||
type MapLayerListener = {
|
||||
features: maplibregl.MapGeoJSONFeature[];
|
||||
features: MapGeoJSONFeature[];
|
||||
mousemoves: MapLayerMouseEventListener[];
|
||||
mouseenters: MapLayerMouseEventListener[];
|
||||
mouseleaves: MapLayerMouseEventListener[];
|
||||
@@ -16,10 +25,10 @@ type MapLayerListener = {
|
||||
};
|
||||
|
||||
export class MapLayerEventManager {
|
||||
private _map: maplibregl.Map;
|
||||
private _map: Map;
|
||||
private _listeners: Record<string, MapLayerListener> = {};
|
||||
|
||||
constructor(map: maplibregl.Map) {
|
||||
constructor(map: Map) {
|
||||
this._map = map;
|
||||
this._map.on('mousemove', this._handleMouseMove.bind(this));
|
||||
this._map.on('click', this._handleMouseClick.bind(this, 'click'));
|
||||
@@ -142,7 +151,7 @@ export class MapLayerEventManager {
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMouseMove(e: maplibregl.MapMouseEvent) {
|
||||
private _handleMouseMove(e: MapMouseEvent) {
|
||||
if (e.originalEvent.buttons > 0) return;
|
||||
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
|
||||
Object.keys(this._listeners).forEach((layerId) => {
|
||||
@@ -151,29 +160,20 @@ export class MapLayerEventManager {
|
||||
if ((features.length == 0) != (listener.features.length == 0)) {
|
||||
if (features.length > 0) {
|
||||
if (listener.mouseenters.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent(
|
||||
'mouseenter',
|
||||
e.target,
|
||||
e.originalEvent,
|
||||
{
|
||||
features: featuresByLayer[layerId]!,
|
||||
}
|
||||
);
|
||||
const event = new MapMouseEvent('mouseenter', e.target, e.originalEvent, {
|
||||
features: featuresByLayer[layerId]!,
|
||||
});
|
||||
listener.mouseenters.forEach((l) => l(event));
|
||||
}
|
||||
} else {
|
||||
if (listener.mouseleaves.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent(
|
||||
'mouseleave',
|
||||
e.target,
|
||||
e.originalEvent
|
||||
);
|
||||
const event = new MapMouseEvent('mouseleave', e.target, e.originalEvent);
|
||||
listener.mouseleaves.forEach((l) => l(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (features.length > 0 && listener.mousemoves.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent('mousemove', e.target, e.originalEvent, {
|
||||
const event = new MapMouseEvent('mousemove', e.target, e.originalEvent, {
|
||||
features: featuresByLayer[layerId]!,
|
||||
});
|
||||
listener.mousemoves.forEach((l) => l(event));
|
||||
@@ -182,49 +182,39 @@ export class MapLayerEventManager {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleMouseClick(type: string, e: maplibregl.MapMouseEvent) {
|
||||
private _handleMouseClick(type: string, e: MapMouseEvent) {
|
||||
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
|
||||
Object.keys(this._listeners).forEach((layerId) => {
|
||||
const features = featuresByLayer[layerId] || [];
|
||||
const listener = this._listeners[layerId];
|
||||
if (features.length > 0) {
|
||||
if (type === 'click' && listener.clicks.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent('click', e.target, e.originalEvent, {
|
||||
const event = new MapMouseEvent('click', e.target, e.originalEvent, {
|
||||
features: features,
|
||||
});
|
||||
listener.clicks.forEach((l) => l(event));
|
||||
} else if (type === 'contextmenu' && listener.contextmenus.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent(
|
||||
'contextmenu',
|
||||
e.target,
|
||||
e.originalEvent,
|
||||
{
|
||||
features: features,
|
||||
}
|
||||
);
|
||||
const event = new MapMouseEvent('contextmenu', e.target, e.originalEvent, {
|
||||
features: features,
|
||||
});
|
||||
listener.contextmenus.forEach((l) => l(event));
|
||||
} else if (type === 'mousedown' && listener.mousedowns.length > 0) {
|
||||
const event = new maplibregl.MapMouseEvent(
|
||||
'mousedown',
|
||||
e.target,
|
||||
e.originalEvent,
|
||||
{
|
||||
features: features,
|
||||
}
|
||||
);
|
||||
const event = new MapMouseEvent('mousedown', e.target, e.originalEvent, {
|
||||
features: features,
|
||||
});
|
||||
listener.mousedowns.forEach((l) => l(event));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleTouchStart(e: maplibregl.MapTouchEvent) {
|
||||
private _handleTouchStart(e: MapTouchEvent) {
|
||||
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
|
||||
Object.keys(this._listeners).forEach((layerId) => {
|
||||
const features = featuresByLayer[layerId] || [];
|
||||
const listener = this._listeners[layerId];
|
||||
if (features.length > 0) {
|
||||
const event: maplibregl.MapLayerTouchEvent = new maplibregl.MapTouchEvent(
|
||||
const event: MapLayerTouchEvent = new MapTouchEvent(
|
||||
'touchstart',
|
||||
e.target,
|
||||
e.originalEvent
|
||||
@@ -235,18 +225,15 @@ export class MapLayerEventManager {
|
||||
});
|
||||
}
|
||||
|
||||
private _getBounds(point: maplibregl.Point) {
|
||||
private _getBounds(point: Point) {
|
||||
const delta = 30;
|
||||
return new maplibregl.LngLatBounds(
|
||||
return new LngLatBounds(
|
||||
this._map.unproject([point.x - delta, point.y + delta]),
|
||||
this._map.unproject([point.x + delta, point.y - delta])
|
||||
);
|
||||
}
|
||||
|
||||
private _filterLayersIntersectingBounds(
|
||||
layerIds: string[],
|
||||
bounds: maplibregl.LngLatBounds
|
||||
): string[] {
|
||||
private _filterLayersIntersectingBounds(layerIds: string[], bounds: LngLatBounds): string[] {
|
||||
const files = get(engine.files);
|
||||
let result = layerIds.filter((layerId) => {
|
||||
if (!this._map.getLayer(layerId)) return false;
|
||||
@@ -268,7 +255,7 @@ export class MapLayerEventManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getRenderedFeaturesByLayer(e: maplibregl.MapMouseEvent | maplibregl.MapTouchEvent) {
|
||||
private _getRenderedFeaturesByLayer(e: MapMouseEvent | MapTouchEvent) {
|
||||
const layerIds = this._filterLayersIntersectingBounds(
|
||||
Object.keys(this._listeners),
|
||||
this._getBounds(e.point)
|
||||
@@ -277,7 +264,7 @@ export class MapLayerEventManager {
|
||||
layerIds.length > 0
|
||||
? this._map.queryRenderedFeatures(e.point, { layers: layerIds })
|
||||
: [];
|
||||
const featuresByLayer: Record<string, maplibregl.MapGeoJSONFeature[]> = {};
|
||||
const featuresByLayer: Record<string, MapGeoJSONFeature[]> = {};
|
||||
features.forEach((f) => {
|
||||
if (!featuresByLayer[f.layer.id]) {
|
||||
featuresByLayer[f.layer.id] = [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { LngLat, type Map, type MapMouseEvent, Popup, type PopupOptions } from 'maplibre-gl';
|
||||
import { mount, tick, unmount } from 'svelte';
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
import MapPopupComponent from '$lib/components/map/MapPopup.svelte';
|
||||
@@ -13,15 +13,15 @@ export type PopupItem<T = any> = {
|
||||
};
|
||||
|
||||
export class MapPopup {
|
||||
map: maplibregl.Map;
|
||||
popup: maplibregl.Popup;
|
||||
map: Map;
|
||||
popup: Popup;
|
||||
item: Writable<PopupItem | null> = writable(null);
|
||||
component: ReturnType<typeof mount>;
|
||||
maybeHideBinded = this.maybeHide.bind(this);
|
||||
|
||||
constructor(map: maplibregl.Map, options?: maplibregl.PopupOptions) {
|
||||
constructor(map: Map, options?: PopupOptions) {
|
||||
this.map = map;
|
||||
this.popup = new maplibregl.Popup(options);
|
||||
this.popup = new Popup(options);
|
||||
this.component = mount(MapPopupComponent, {
|
||||
target: document.body,
|
||||
props: {
|
||||
@@ -53,7 +53,7 @@ export class MapPopup {
|
||||
this.map.on('mousemove', this.maybeHideBinded);
|
||||
}
|
||||
|
||||
maybeHide(e: maplibregl.MapMouseEvent) {
|
||||
maybeHide(e: MapMouseEvent) {
|
||||
const item = get(this.item);
|
||||
if (item === null) {
|
||||
this.hide();
|
||||
@@ -77,8 +77,8 @@ export class MapPopup {
|
||||
getCoordinates() {
|
||||
const item = get(this.item);
|
||||
if (item === null) {
|
||||
return new maplibregl.LngLat(0, 0);
|
||||
return new LngLat(0, 0);
|
||||
}
|
||||
return new maplibregl.LngLat(item.item.lng, item.item.lat);
|
||||
return new LngLat(item.item.lng, item.item.lat);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import * as maplibregl from 'maplibre-gl';
|
||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
|
||||
import MaplibreGeocoder, {
|
||||
type MaplibreGeocoderFeatureResults,
|
||||
} from '@maplibre/maplibre-gl-geocoder';
|
||||
@@ -39,6 +40,7 @@ export class MapLibreGLMap {
|
||||
geocoder: boolean,
|
||||
geolocate: boolean
|
||||
) {
|
||||
maplibregl.setWorkerUrl(workerUrl);
|
||||
this._maptilerKey = maptilerKey;
|
||||
this._styleManager = new StyleManager(this._mapStore, this._maptilerKey);
|
||||
// MapLibre gives Safari more workers, but only recognizes it by its user agent: not
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { i18n } from '$lib/i18n/i18n.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
|
||||
import type { Map } from 'maplibre-gl';
|
||||
|
||||
const { streetViewSource } = settings;
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
let container: HTMLElement;
|
||||
|
||||
onMount(() => {
|
||||
map.onLoad((map_: maplibregl.Map) => {
|
||||
map.onLoad((map_: Map) => {
|
||||
googleRedirect = new GoogleRedirect(map_);
|
||||
mapillaryLayer = new MapillaryLayer(
|
||||
map_,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Map, MapMouseEvent } from 'maplibre-gl';
|
||||
import { mapCursor, MapCursorState } from '$lib/state/map-cursor';
|
||||
|
||||
export class GoogleRedirect {
|
||||
map: maplibregl.Map;
|
||||
map: Map;
|
||||
enabled = false;
|
||||
|
||||
constructor(map: maplibregl.Map) {
|
||||
constructor(map: Map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@@ -24,7 +25,7 @@ export class GoogleRedirect {
|
||||
this.map.off('click', this.openStreetView);
|
||||
}
|
||||
|
||||
openStreetView(e: maplibregl.MapMouseEvent) {
|
||||
openStreetView(e: MapMouseEvent) {
|
||||
window.open(
|
||||
`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${e.lngLat.lat},${e.lngLat.lng}`
|
||||
);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import maplibregl, { type LayerSpecification, type VectorSourceSpecification } from 'maplibre-gl';
|
||||
import {
|
||||
Map,
|
||||
Marker,
|
||||
type LayerSpecification,
|
||||
type MapLayerMouseEvent,
|
||||
type VectorSourceSpecification,
|
||||
} from 'maplibre-gl';
|
||||
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
|
||||
import 'mapillary-js/dist/mapillary.css';
|
||||
import { mapCursor, MapCursorState } from '$lib/state/map-cursor';
|
||||
@@ -43,9 +49,9 @@ const mapillaryImageLayer: LayerSpecification = {
|
||||
};
|
||||
|
||||
export class MapillaryLayer {
|
||||
map: maplibregl.Map;
|
||||
map: Map;
|
||||
layerEventManager: MapLayerEventManager;
|
||||
marker: maplibregl.Marker;
|
||||
marker: Marker;
|
||||
viewer: Viewer;
|
||||
|
||||
active = false;
|
||||
@@ -56,7 +62,7 @@ export class MapillaryLayer {
|
||||
onMouseLeaveBinded = this.onMouseLeave.bind(this);
|
||||
|
||||
constructor(
|
||||
map: maplibregl.Map,
|
||||
map: Map,
|
||||
layerEventManager: MapLayerEventManager,
|
||||
container: HTMLElement,
|
||||
popupOpen: { value: boolean }
|
||||
@@ -75,7 +81,7 @@ export class MapillaryLayer {
|
||||
dot.className = 'maplibregl-user-location-dot';
|
||||
element.appendChild(dot);
|
||||
|
||||
this.marker = new maplibregl.Marker({
|
||||
this.marker = new Marker({
|
||||
rotationAlignment: 'map',
|
||||
element,
|
||||
});
|
||||
@@ -140,7 +146,7 @@ export class MapillaryLayer {
|
||||
this.popupOpen.value = false;
|
||||
}
|
||||
|
||||
onMouseEnter(e: maplibregl.MapLayerMouseEvent) {
|
||||
onMouseEnter(e: MapLayerMouseEvent) {
|
||||
if (
|
||||
e.features &&
|
||||
e.features.length > 0 &&
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import type {
|
||||
GeoJSONSourceSpecification,
|
||||
LayerSpecification,
|
||||
Map,
|
||||
SkySpecification,
|
||||
StyleSpecification,
|
||||
} from 'maplibre-gl';
|
||||
import { settings } from '$lib/state/settings';
|
||||
import { get, type Writable } from 'svelte/store';
|
||||
import {
|
||||
@@ -13,13 +20,23 @@ import { i18n } from '$lib/i18n/i18n.svelte';
|
||||
const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource, distanceUnits } =
|
||||
settings;
|
||||
|
||||
const emptySource: maplibregl.GeoJSONSourceSpecification = {
|
||||
const emptySource: GeoJSONSourceSpecification = {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [],
|
||||
},
|
||||
};
|
||||
/**
|
||||
* The sky above the horizon when the map is tilted, without the atmosphere around the globe.
|
||||
*/
|
||||
const sky: SkySpecification = {
|
||||
'sky-color': '#88C6FC',
|
||||
'horizon-color': '#ffffff',
|
||||
'sky-horizon-blend': 0.8,
|
||||
'atmosphere-blend': 0,
|
||||
};
|
||||
|
||||
export const ANCHOR_LAYER_KEY = {
|
||||
overlays: 'overlays-end',
|
||||
mapillary: 'mapillary-end',
|
||||
@@ -32,18 +49,18 @@ export const ANCHOR_LAYER_KEY = {
|
||||
waypoints: 'waypoints-end',
|
||||
routingControls: 'routing-controls-end',
|
||||
};
|
||||
const anchorLayers: maplibregl.LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({
|
||||
const anchorLayers: LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({
|
||||
id: id,
|
||||
type: 'symbol',
|
||||
source: 'empty-source',
|
||||
}));
|
||||
|
||||
export class StyleManager {
|
||||
private _map: Writable<maplibregl.Map | null>;
|
||||
private _map: Writable<Map | null>;
|
||||
private _maptilerKey: string;
|
||||
private _pastOverlays: Set<string> = new Set();
|
||||
|
||||
constructor(map: Writable<maplibregl.Map | null>, maptilerKey: string) {
|
||||
constructor(map: Writable<Map | null>, maptilerKey: string) {
|
||||
this._map = map;
|
||||
this._maptilerKey = maptilerKey;
|
||||
this._map.subscribe((map_) => {
|
||||
@@ -58,6 +75,7 @@ export class StyleManager {
|
||||
opacities.subscribe(() => this.updateOverlays());
|
||||
terrainSource.subscribe(() => this.updateTerrain());
|
||||
customLayers.subscribe(() => this.updateBasemap());
|
||||
i18n.subscribe(() => this.updateBasemap());
|
||||
distanceUnits.subscribe(() => {
|
||||
const map = get(this._map);
|
||||
if (map && (map.getLayer('contours_m') || map.getLayer('contours_ft'))) {
|
||||
@@ -75,10 +93,10 @@ export class StyleManager {
|
||||
});
|
||||
}
|
||||
|
||||
async buildStyle(basemap: string): Promise<maplibregl.StyleSpecification> {
|
||||
async buildStyle(basemap: string): Promise<StyleSpecification> {
|
||||
const custom = get(customLayers);
|
||||
|
||||
const style: maplibregl.StyleSpecification = {
|
||||
const style: StyleSpecification = {
|
||||
version: 8,
|
||||
projection: {
|
||||
type: 'globe',
|
||||
@@ -91,7 +109,7 @@ export class StyleManager {
|
||||
|
||||
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
|
||||
|
||||
let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification;
|
||||
let basemapStyle = basemaps.openStreetMap as StyleSpecification;
|
||||
try {
|
||||
basemapStyle = await this.get(basemapInfo);
|
||||
for (const source in basemapStyle.sources) {
|
||||
@@ -110,6 +128,7 @@ export class StyleManager {
|
||||
console.error(e.message);
|
||||
}
|
||||
this.merge(style, basemapStyle);
|
||||
style.sky ??= sky;
|
||||
|
||||
const terrain = this.getCurrentTerrain();
|
||||
style.sources[terrain.source] = terrainSources[terrain.source];
|
||||
@@ -202,9 +221,7 @@ export class StyleManager {
|
||||
}
|
||||
}
|
||||
|
||||
async get(
|
||||
styleInfo: maplibregl.StyleSpecification | string
|
||||
): Promise<maplibregl.StyleSpecification> {
|
||||
async get(styleInfo: StyleSpecification | string): Promise<StyleSpecification> {
|
||||
if (typeof styleInfo === 'string') {
|
||||
let styleUrl = styleInfo as string;
|
||||
const response = await fetch(styleUrl, { cache: 'force-cache' });
|
||||
@@ -218,7 +235,7 @@ export class StyleManager {
|
||||
}
|
||||
}
|
||||
|
||||
merge(style: maplibregl.StyleSpecification, other: maplibregl.StyleSpecification) {
|
||||
merge(style: StyleSpecification, other: StyleSpecification) {
|
||||
style.sources = { ...style.sources, ...other.sources };
|
||||
const units = get(distanceUnits);
|
||||
for (let layer of other.layers ?? []) {
|
||||
@@ -270,6 +287,9 @@ export class StyleManager {
|
||||
if (other.glyphs && !style.glyphs) {
|
||||
style.glyphs = other.glyphs;
|
||||
}
|
||||
if (other.sky && !style.sky) {
|
||||
style.sky = other.sky;
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentTerrain() {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import Clean from '$lib/components/toolbar/tools/Clean.svelte';
|
||||
import Reduce from '$lib/components/toolbar/tools/reduce/Reduce.svelte';
|
||||
import RoutingControlPopup from '$lib/components/toolbar/tools/routing/RoutingControlPopup.svelte';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { Popup } from 'maplibre-gl';
|
||||
import { settings } from '$lib/state/settings';
|
||||
|
||||
let {
|
||||
@@ -23,11 +23,11 @@
|
||||
const { minimizeRoutingMenu } = settings;
|
||||
|
||||
let popupElement: HTMLDivElement | undefined = $state(undefined);
|
||||
let popup: maplibregl.Popup | undefined = $derived.by(() => {
|
||||
let popup: Popup | undefined = $derived.by(() => {
|
||||
if (!popupElement) {
|
||||
return undefined;
|
||||
}
|
||||
let popup = new maplibregl.Popup({
|
||||
let popup = new Popup({
|
||||
closeButton: false,
|
||||
maxWidth: undefined,
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { getURLForLanguage } from '$lib/utils';
|
||||
import { Trash2 } from '@lucide/svelte';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import type { GeoJSONSource } from 'maplibre-gl';
|
||||
import type { GeoJSONSource, LngLat } from 'maplibre-gl';
|
||||
import { engine } from '$lib/engine';
|
||||
import { mapCursor, MapCursorState } from '$lib/state/map-cursor';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
|
||||
@@ -28,7 +28,7 @@
|
||||
let cleanType = $state(CleanType.INSIDE);
|
||||
let deleteTrackpoints = $state(true);
|
||||
let deleteWaypoints = $state(true);
|
||||
let rectangleCoordinates: maplibregl.LngLat[] = $state([]);
|
||||
let rectangleCoordinates: LngLat[] = $state([]);
|
||||
|
||||
$effect(() => {
|
||||
if ($map) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Popup } from 'maplibre-gl';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
@@ -43,7 +44,7 @@
|
||||
}: {
|
||||
minimized?: boolean;
|
||||
minimizable?: boolean;
|
||||
popup?: maplibregl.Popup;
|
||||
popup?: Popup;
|
||||
popupElement?: HTMLDivElement;
|
||||
class?: string;
|
||||
} = $props();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { distance, type Coordinates } from '$lib/geo/coordinates';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import maplibregl, {
|
||||
type MapMouseEvent,
|
||||
import {
|
||||
type GeoJSONSource,
|
||||
type MapLayerMouseEvent,
|
||||
type MapLayerTouchEvent,
|
||||
type MapMouseEvent,
|
||||
Point,
|
||||
type Popup,
|
||||
} from 'maplibre-gl';
|
||||
import { route } from './routing';
|
||||
import { toast } from 'svelte-sonner';
|
||||
@@ -69,7 +71,7 @@ export class RoutingControls {
|
||||
selection: Selection = { type: 'empty' };
|
||||
/** Ids of the files whose line layer is listened to. */
|
||||
listenedFileIds: string[] = [];
|
||||
popup: maplibregl.Popup;
|
||||
popup: Popup;
|
||||
popupElement: HTMLElement;
|
||||
unsubscribes: Function[] = [];
|
||||
|
||||
@@ -80,7 +82,7 @@ export class RoutingControls {
|
||||
/** Id of the anchor that is dragged: `anchors.length` for the temporary anchor. */
|
||||
draggedAnchorId: number | null = null;
|
||||
lastDraggedAnchorEventTime = 0;
|
||||
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
|
||||
draggingStartingPosition: Point = new Point(0, 0);
|
||||
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
|
||||
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
|
||||
onClickBinded: (e: MapLayerMouseEvent) => void = this.onClick.bind(this);
|
||||
@@ -100,7 +102,7 @@ export class RoutingControls {
|
||||
this.showTemporaryAnchor.bind(this);
|
||||
updateTemporaryAnchorBinded: (e: MapMouseEvent) => void = this.updateTemporaryAnchor.bind(this);
|
||||
|
||||
constructor(popup: maplibregl.Popup, popupElement: HTMLElement) {
|
||||
constructor(popup: Popup, popupElement: HTMLElement) {
|
||||
this.popup = popup;
|
||||
this.popupElement = popupElement;
|
||||
|
||||
@@ -233,7 +235,7 @@ export class RoutingControls {
|
||||
|
||||
// a single source for all the anchors, filtered by zoom: one update for the map to process
|
||||
try {
|
||||
const source = map_.getSource(LAYER_ID) as maplibregl.GeoJSONSource | undefined;
|
||||
const source = map_.getSource(LAYER_ID) as GeoJSONSource | undefined;
|
||||
const data: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features };
|
||||
if (source) {
|
||||
source.setData(data);
|
||||
@@ -439,7 +441,7 @@ export class RoutingControls {
|
||||
return distance(first, last) <= 1000;
|
||||
}
|
||||
|
||||
async appendAnchor(e: maplibregl.MapMouseEvent) {
|
||||
async appendAnchor(e: MapMouseEvent) {
|
||||
// Add a new anchor to the end of the last segment
|
||||
if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
|
||||
return;
|
||||
@@ -496,7 +498,7 @@ export class RoutingControls {
|
||||
);
|
||||
}
|
||||
|
||||
addIntermediateAnchor(e: maplibregl.MapMouseEvent) {
|
||||
addIntermediateAnchor(e: MapMouseEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.temporaryAnchor !== null) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors';
|
||||
import { Scissors } from 'lucide-static';
|
||||
import { get } from 'svelte/store';
|
||||
import { mapCursor, MapCursorState } from '$lib/state/map-cursor';
|
||||
import type { GeoJSONSource } from 'maplibre-gl';
|
||||
import type { GeoJSONSource, Map, MapLayerMouseEvent } from 'maplibre-gl';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
|
||||
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
|
||||
import { loadSVGIcon } from '$lib/utils';
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
* selection.
|
||||
*/
|
||||
export class SplitControls {
|
||||
map: maplibregl.Map;
|
||||
map: Map;
|
||||
layerEventManager: MapLayerEventManager;
|
||||
unsubscribes: Function[] = [];
|
||||
/** What the tool needs from the engine, while it shows the anchors. */
|
||||
@@ -32,7 +32,7 @@ export class SplitControls {
|
||||
layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this);
|
||||
layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this);
|
||||
|
||||
constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) {
|
||||
constructor(map: Map, layerEventManager: MapLayerEventManager) {
|
||||
this.map = map;
|
||||
this.layerEventManager = layerEventManager;
|
||||
loadSVGIcon(
|
||||
@@ -184,7 +184,7 @@ export class SplitControls {
|
||||
mapCursor.notify(MapCursorState.SPLIT_CONTROL, false);
|
||||
}
|
||||
|
||||
layerOnClick(e: maplibregl.MapLayerMouseEvent) {
|
||||
layerOnClick(e: MapLayerMouseEvent) {
|
||||
const revision = this.statistics?.anchors?.revision;
|
||||
if (revision !== undefined) {
|
||||
engine.split(revision, e.features![0].properties!.index, get(splitAs));
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { selectedWaypoint } from './waypoint';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { mapCursor, MapCursorState } from '$lib/state/map-cursor';
|
||||
import maplibregl from 'maplibre-gl';
|
||||
import { Marker } from 'maplibre-gl';
|
||||
import { getSvgForSymbol } from '$lib/components/map/gpx-layer/gpx-layer';
|
||||
|
||||
let props: {
|
||||
@@ -43,7 +43,7 @@
|
||||
})
|
||||
);
|
||||
|
||||
let marker: maplibregl.Marker | null = null;
|
||||
let marker: Marker | null = null;
|
||||
|
||||
function reset() {
|
||||
if ($selectedWaypoint) {
|
||||
@@ -136,7 +136,7 @@
|
||||
let element = document.createElement('div');
|
||||
element.classList.add('w-8', 'h-8');
|
||||
element.innerHTML = getSvgForSymbol(symbolKey);
|
||||
marker = new maplibregl.Marker({
|
||||
marker = new Marker({
|
||||
element,
|
||||
anchor: 'bottom',
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user