mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-04-28 06:15:54 +00:00
Compare commits
24 Commits
72578b00bd
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17559913fd | ||
|
|
dd9aba3adb | ||
|
|
55590d68a6 | ||
|
|
bd40fbae74 | ||
|
|
690cbc49cc | ||
|
|
8e9f16c460 | ||
|
|
36b16ddeef | ||
|
|
c60b64f24f | ||
|
|
16b8988fa7 | ||
|
|
fdb6fe4e52 | ||
|
|
40f97b7c35 | ||
|
|
54b3113480 | ||
|
|
0bf168e67e | ||
|
|
d762a45eb9 | ||
|
|
548ab9a459 | ||
|
|
315c1f6a61 | ||
|
|
98257bee12 | ||
|
|
5561b7d9fe | ||
|
|
320887206c | ||
|
|
04a1bf6a55 | ||
|
|
0989371874 | ||
|
|
3dcd6e52d3 | ||
|
|
0f06d0d461 | ||
|
|
db33310a10 |
@@ -142,6 +142,7 @@ export class MapLayerEventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleMouseMove(e: maplibregl.MapMouseEvent) {
|
private _handleMouseMove(e: maplibregl.MapMouseEvent) {
|
||||||
|
if (e.originalEvent.buttons > 0) return;
|
||||||
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
|
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
|
||||||
Object.keys(this._listeners).forEach((layerId) => {
|
Object.keys(this._listeners).forEach((layerId) => {
|
||||||
const features = featuresByLayer[layerId] || [];
|
const features = featuresByLayer[layerId] || [];
|
||||||
|
|||||||
@@ -81,8 +81,13 @@ export class StyleManager {
|
|||||||
|
|
||||||
let basemap = get(currentBasemap);
|
let basemap = get(currentBasemap);
|
||||||
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
|
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
|
||||||
const basemapStyle = await this.get(basemapInfo);
|
|
||||||
|
|
||||||
|
let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification;
|
||||||
|
try {
|
||||||
|
basemapStyle = await this.get(basemapInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message);
|
||||||
|
}
|
||||||
this.merge(style, basemapStyle);
|
this.merge(style, basemapStyle);
|
||||||
|
|
||||||
if (this._maptilerKey !== '') {
|
if (this._maptilerKey !== '') {
|
||||||
@@ -109,45 +114,52 @@ export class StyleManager {
|
|||||||
if (!layers[overlay]) {
|
if (!layers[overlay]) {
|
||||||
if (this._pastOverlays.has(overlay)) {
|
if (this._pastOverlays.has(overlay)) {
|
||||||
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
|
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
|
||||||
const overlayStyle = await this.get(overlayInfo);
|
try {
|
||||||
for (let layer of overlayStyle.layers ?? []) {
|
const overlayStyle = await this.get(overlayInfo);
|
||||||
if (map_.getLayer(layer.id)) {
|
for (let layer of overlayStyle.layers ?? []) {
|
||||||
map_.removeLayer(layer.id);
|
if (map_.getLayer(layer.id)) {
|
||||||
|
map_.removeLayer(layer.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Should not happen
|
||||||
}
|
}
|
||||||
this._pastOverlays.delete(overlay);
|
this._pastOverlays.delete(overlay);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
|
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
|
||||||
const overlayStyle = await this.get(overlayInfo);
|
try {
|
||||||
const opacity = overlayOpacities[overlay];
|
const overlayStyle = await this.get(overlayInfo);
|
||||||
|
const opacity = overlayOpacities[overlay];
|
||||||
|
|
||||||
for (let sourceId in overlayStyle.sources) {
|
for (let sourceId in overlayStyle.sources) {
|
||||||
if (!map_.getSource(sourceId)) {
|
if (!map_.getSource(sourceId)) {
|
||||||
map_.addSource(sourceId, overlayStyle.sources[sourceId]);
|
map_.addSource(sourceId, overlayStyle.sources[sourceId]);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let layer of overlayStyle.layers ?? []) {
|
|
||||||
if (!map_.getLayer(layer.id)) {
|
|
||||||
if (opacity !== undefined) {
|
|
||||||
if (layer.type === 'raster') {
|
|
||||||
if (!layer.paint) {
|
|
||||||
layer.paint = {};
|
|
||||||
}
|
|
||||||
layer.paint['raster-opacity'] = opacity;
|
|
||||||
} else if (layer.type === 'hillshade') {
|
|
||||||
if (!layer.paint) {
|
|
||||||
layer.paint = {};
|
|
||||||
}
|
|
||||||
layer.paint['hillshade-exaggeration'] = opacity / 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this._pastOverlays.add(overlay);
|
for (let layer of overlayStyle.layers ?? []) {
|
||||||
|
if (!map_.getLayer(layer.id)) {
|
||||||
|
if (opacity !== undefined) {
|
||||||
|
if (layer.type === 'raster') {
|
||||||
|
if (!layer.paint) {
|
||||||
|
layer.paint = {};
|
||||||
|
}
|
||||||
|
layer.paint['raster-opacity'] = opacity;
|
||||||
|
} else if (layer.type === 'hillshade') {
|
||||||
|
if (!layer.paint) {
|
||||||
|
layer.paint = {};
|
||||||
|
}
|
||||||
|
layer.paint['hillshade-exaggeration'] = opacity / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._pastOverlays.add(overlay);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
@@ -181,6 +193,9 @@ export class StyleManager {
|
|||||||
styleUrl = styleUrl.replace(maptilerKeyPlaceHolder, this._maptilerKey);
|
styleUrl = styleUrl.replace(maptilerKeyPlaceHolder, this._maptilerKey);
|
||||||
}
|
}
|
||||||
const response = await fetch(styleUrl, { cache: 'force-cache' });
|
const response = await fetch(styleUrl, { cache: 'force-cache' });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error fetching style "${styleInfo}": ${response.status}`);
|
||||||
|
}
|
||||||
const style = await response.json();
|
const style = await response.json();
|
||||||
return style;
|
return style;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -57,8 +57,10 @@ export class RoutingControls {
|
|||||||
|
|
||||||
updateControlsBinded: () => void = this.updateControls.bind(this);
|
updateControlsBinded: () => void = this.updateControls.bind(this);
|
||||||
appendAnchorBinded: (e: MapMouseEvent) => void = this.appendAnchor.bind(this);
|
appendAnchorBinded: (e: MapMouseEvent) => void = this.appendAnchor.bind(this);
|
||||||
|
addIntermediateAnchorBinded: (e: MapMouseEvent) => void = this.addIntermediateAnchor.bind(this);
|
||||||
|
|
||||||
draggedAnchorIndex: number | null = null;
|
draggedAnchorIndex: number | null = null;
|
||||||
|
lastDraggedAnchorEventTime: number = 0;
|
||||||
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
|
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
|
||||||
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
|
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
|
||||||
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
|
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
|
||||||
@@ -133,6 +135,7 @@ export class RoutingControls {
|
|||||||
map_.on('style.load', this.updateControlsBinded);
|
map_.on('style.load', this.updateControlsBinded);
|
||||||
map_.on('click', this.appendAnchorBinded);
|
map_.on('click', this.appendAnchorBinded);
|
||||||
layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
||||||
|
layerEventManager.on('click', this.fileId, this.addIntermediateAnchorBinded);
|
||||||
|
|
||||||
this.fileUnsubscribe = this.file.subscribe(this.updateControlsBinded);
|
this.fileUnsubscribe = this.file.subscribe(this.updateControlsBinded);
|
||||||
}
|
}
|
||||||
@@ -237,6 +240,7 @@ export class RoutingControls {
|
|||||||
map_?.off('style.load', this.updateControlsBinded);
|
map_?.off('style.load', this.updateControlsBinded);
|
||||||
map_?.off('click', this.appendAnchorBinded);
|
map_?.off('click', this.appendAnchorBinded);
|
||||||
layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
|
||||||
|
layerEventManager?.off('click', this.fileId, this.addIntermediateAnchorBinded);
|
||||||
map_?.off('mousemove', this.updateTemporaryAnchorBinded);
|
map_?.off('mousemove', this.updateTemporaryAnchorBinded);
|
||||||
|
|
||||||
this.layers.forEach((layer) => {
|
this.layers.forEach((layer) => {
|
||||||
@@ -521,12 +525,19 @@ export class RoutingControls {
|
|||||||
if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
|
if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
this.draggedAnchorIndex !== null ||
|
||||||
|
Date.now() - this.lastDraggedAnchorEventTime < 100
|
||||||
|
) {
|
||||||
|
// Exit if anchor is being dragged
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
e.target.queryRenderedFeatures(e.point, {
|
e.target.queryRenderedFeatures(e.point, {
|
||||||
layers: [...this.layers.values()].map((layer) => layer.id),
|
layers: [this.fileId, ...[...this.layers.values()].map((layer) => layer.id)],
|
||||||
}).length
|
}).length
|
||||||
) {
|
) {
|
||||||
// Clicked on routing control, ignoring
|
// Clicked on routing control or layer, ignoring
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.appendAnchorWithCoordinates({
|
this.appendAnchorWithCoordinates({
|
||||||
@@ -598,6 +609,15 @@ export class RoutingControls {
|
|||||||
await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]);
|
await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addIntermediateAnchor(e: maplibregl.MapMouseEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (this.temporaryAnchor !== null) {
|
||||||
|
this.turnIntoPermanentAnchor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getNeighbouringAnchors(anchor: Anchor): [Anchor | null, Anchor | null] {
|
getNeighbouringAnchors(anchor: Anchor): [Anchor | null, Anchor | null] {
|
||||||
let previousAnchor: Anchor | null = null;
|
let previousAnchor: Anchor | null = null;
|
||||||
let nextAnchor: Anchor | null = null;
|
let nextAnchor: Anchor | null = null;
|
||||||
@@ -818,8 +838,11 @@ export class RoutingControls {
|
|||||||
onClick(e: MapLayerMouseEvent) {
|
onClick(e: MapLayerMouseEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (this.temporaryAnchor !== null) {
|
if (
|
||||||
this.turnIntoPermanentAnchor();
|
this.draggedAnchorIndex !== null ||
|
||||||
|
Date.now() - this.lastDraggedAnchorEventTime < 100
|
||||||
|
) {
|
||||||
|
// Exit if anchor is being dragged
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,6 +931,8 @@ export class RoutingControls {
|
|||||||
lat: e.lngLat.lat,
|
lat: e.lngLat.lat,
|
||||||
lon: e.lngLat.lng,
|
lon: e.lngLat.lng,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.lastDraggedAnchorEventTime = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) {
|
onMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) {
|
||||||
@@ -946,6 +971,7 @@ export class RoutingControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.draggedAnchorIndex = null;
|
this.draggedAnchorIndex = null;
|
||||||
|
this.lastDraggedAnchorEventTime = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
showTemporaryAnchor(e: MapLayerMouseEvent) {
|
showTemporaryAnchor(e: MapLayerMouseEvent) {
|
||||||
|
|||||||
@@ -162,38 +162,33 @@ function getLayerValidator(allowed: Record<string, any>, fallback: string) {
|
|||||||
|
|
||||||
function filterLayerTree(t: LayerTreeType, allowed: LayerTreeType | undefined): LayerTreeType {
|
function filterLayerTree(t: LayerTreeType, allowed: LayerTreeType | undefined): LayerTreeType {
|
||||||
const filtered: LayerTreeType = {};
|
const filtered: LayerTreeType = {};
|
||||||
const values = Object.values(t);
|
if (allowed) {
|
||||||
if (values.length == 0) return filtered;
|
Object.entries(allowed).forEach(([key, value]) => {
|
||||||
if (typeof values[0] === 'boolean') {
|
if (Object.hasOwn(t, key)) {
|
||||||
if (allowed) {
|
if (typeof value === 'boolean') {
|
||||||
Object.keys(allowed).forEach((key) => {
|
|
||||||
if (Object.hasOwn(t, key)) {
|
|
||||||
filtered[key] = t[key];
|
filtered[key] = t[key];
|
||||||
} else {
|
} else if (typeof value === 'object') {
|
||||||
filtered[key] = allowed[key];
|
filtered[key] = filterLayerTree(
|
||||||
|
typeof t[key] === 'object' ? t[key] : {},
|
||||||
|
value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
}
|
|
||||||
Object.entries(t).forEach(([key, value]) => {
|
|
||||||
if (
|
|
||||||
!Object.hasOwn(filtered, key) &&
|
|
||||||
(key.startsWith('custom-') || key.startsWith('extension-'))
|
|
||||||
) {
|
|
||||||
filtered[key] = value;
|
filtered[key] = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
Object.entries(t).forEach(([key, value]) => {
|
|
||||||
if (typeof value === 'object') {
|
|
||||||
filtered[key] = filterLayerTree(
|
|
||||||
value,
|
|
||||||
typeof allowed === 'object' && typeof allowed[key] === 'object'
|
|
||||||
? allowed[key]
|
|
||||||
: undefined
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Object.entries(t).forEach(([key, value]) => {
|
||||||
|
if (!Object.hasOwn(filtered, key)) {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (key.startsWith('custom-') || key.startsWith('extension-')) {
|
||||||
|
filtered[key] = value;
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
filtered[key] = filterLayerTree(value, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user