mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-12-28 05:59:59 +00:00
progress
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
class: className = '',
|
||||
}: {
|
||||
coordinates: Coordinates;
|
||||
onCopy: () => void;
|
||||
onCopy?: () => void;
|
||||
class?: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -1,56 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { map, gpxLayers } from '$lib/stores';
|
||||
import { GPXLayer } from './GPXLayer';
|
||||
import { fileObservers } from '$lib/db';
|
||||
import { DistanceMarkers } from './DistanceMarkers';
|
||||
import { StartEndMarkers } from './StartEndMarkers';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { createPopups, removePopups } from './GPXLayerPopup';
|
||||
import { gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers';
|
||||
import { onMount } from 'svelte';
|
||||
// import { map, gpxLayers } from '$lib/stores';
|
||||
// import { GPXLayer } from './gpx-layer';
|
||||
// import { DistanceMarkers } from './DistanceMarkers';
|
||||
// import { StartEndMarkers } from './StartEndMarkers';
|
||||
// import { onDestroy } from 'svelte';
|
||||
// import { createPopups, removePopups } from './GPXLayerPopup';
|
||||
|
||||
let distanceMarkers: DistanceMarkers | undefined = undefined;
|
||||
let startEndMarkers: StartEndMarkers | undefined = undefined;
|
||||
// let distanceMarkers = $derived(map.current ? new DistanceMarkers(map.current) : undefined);
|
||||
// let startEndMarkers = $derived(map.current ? new StartEndMarkers(map.current) : undefined);
|
||||
|
||||
$: if ($map && $fileObservers) {
|
||||
// remove layers for deleted files
|
||||
gpxLayers.forEach((layer, fileId) => {
|
||||
if (!$fileObservers.has(fileId)) {
|
||||
layer.remove();
|
||||
gpxLayers.delete(fileId);
|
||||
} else if ($map !== layer.map) {
|
||||
layer.updateMap($map);
|
||||
}
|
||||
});
|
||||
// add layers for new files
|
||||
$fileObservers.forEach((file, fileId) => {
|
||||
if (!gpxLayers.has(fileId)) {
|
||||
gpxLayers.set(fileId, new GPXLayer($map, fileId, file));
|
||||
}
|
||||
});
|
||||
}
|
||||
// $: if ($map) {
|
||||
// if (distanceMarkers) {
|
||||
// distanceMarkers.remove();
|
||||
// }
|
||||
// if (startEndMarkers) {
|
||||
// startEndMarkers.remove();
|
||||
// }
|
||||
// createPopups($map);
|
||||
// distanceMarkers = new DistanceMarkers($map);
|
||||
// startEndMarkers = new StartEndMarkers($map);
|
||||
// }
|
||||
|
||||
$: if ($map) {
|
||||
if (distanceMarkers) {
|
||||
distanceMarkers.remove();
|
||||
}
|
||||
if (startEndMarkers) {
|
||||
startEndMarkers.remove();
|
||||
}
|
||||
createPopups($map);
|
||||
distanceMarkers = new DistanceMarkers($map);
|
||||
startEndMarkers = new StartEndMarkers($map);
|
||||
}
|
||||
// onDestroy(() => {
|
||||
// removePopups();
|
||||
// if (distanceMarkers) {
|
||||
// distanceMarkers.remove();
|
||||
// distanceMarkers = undefined;
|
||||
// }
|
||||
// if (startEndMarkers) {
|
||||
// startEndMarkers.remove();
|
||||
// startEndMarkers = undefined;
|
||||
// }
|
||||
// });
|
||||
|
||||
onDestroy(() => {
|
||||
gpxLayers.forEach((layer) => layer.remove());
|
||||
gpxLayers.clear();
|
||||
removePopups();
|
||||
if (distanceMarkers) {
|
||||
distanceMarkers.remove();
|
||||
distanceMarkers = undefined;
|
||||
}
|
||||
if (startEndMarkers) {
|
||||
startEndMarkers.remove();
|
||||
startEndMarkers = undefined;
|
||||
}
|
||||
onMount(() => {
|
||||
gpxLayers.init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { TrackPoint } from 'gpx';
|
||||
import type { PopupItem } from '$lib/components/map/map.svelte';
|
||||
import CopyCoordinates from '$lib/components/map/gpx-layer/CopyCoordinates.svelte';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import WithUnits from '$lib/components/WithUnits.svelte';
|
||||
import { Compass, Mountain, Timer } from '@lucide/svelte';
|
||||
import { i18n } from '$lib/i18n.svelte';
|
||||
import type { PopupItem } from '$lib/components/map/map';
|
||||
|
||||
let { trackpoint }: { trackpoint: PopupItem<TrackPoint> } = $props();
|
||||
</script>
|
||||
|
||||
@@ -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<Waypoint>;
|
||||
|
||||
@@ -80,11 +80,15 @@
|
||||
</ScrollArea>
|
||||
<div class="mt-2 flex flex-col gap-1">
|
||||
<CopyCoordinates coordinates={waypoint.item.attributes} />
|
||||
{#if tool.current === Tool.WAYPOINT}
|
||||
{#if $currentTool === Tool.WAYPOINT}
|
||||
<Button
|
||||
class="w-full px-2 py-1 h-8 justify-start"
|
||||
variant="outline"
|
||||
onclick={() => deleteWaypoint(waypoint.fileId, waypoint.item._data.index)}
|
||||
onclick={() => {
|
||||
if (waypoint.fileId) {
|
||||
fileActions.deleteWaypoint(waypoint.fileId, waypoint.item._data.index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 size="16" class="mr-1" />
|
||||
{i18n._('menu.delete')}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { dbUtils } from '$lib/db';
|
||||
import { MapPopup } from '$lib/components/map/map.svelte';
|
||||
import { MapPopup } from '$lib/components/map/map-popup';
|
||||
|
||||
export let waypointPopup: MapPopup | null = null;
|
||||
export let trackpointPopup: MapPopup | null = null;
|
||||
@@ -38,7 +37,3 @@ export function removePopups() {
|
||||
trackpointPopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteWaypoint(fileId: string, waypointIndex: number) {
|
||||
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, []));
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { currentTool, map, splitAs, Tool } from '$lib/stores';
|
||||
import { settings, type GPXFileWithStatistics, dbUtils } from '$lib/db';
|
||||
import { get, type Readable } from 'svelte/store';
|
||||
import mapboxgl from 'mapbox-gl';
|
||||
import { waypointPopup, deleteWaypoint, trackpointPopup } from './GPXLayerPopup';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
|
||||
import {
|
||||
ListTrackSegmentItem,
|
||||
ListWaypointItem,
|
||||
@@ -19,9 +18,16 @@ import {
|
||||
setPointerCursor,
|
||||
setScissorsCursor,
|
||||
} from '$lib/utils';
|
||||
import { selectedWaypoint } from '$lib/components/toolbar/tools/waypoint/utils.svelte';
|
||||
import { selectedWaypoint } from '$lib/components/toolbar/tools/waypoint/waypoint';
|
||||
import { MapPin, Square } from 'lucide-static';
|
||||
import { getSymbolKey, symbols } from '$lib/assets/symbols';
|
||||
import type { GPXFileWithStatistics } from '$lib/logic/statistics';
|
||||
import { selection } from '$lib/logic/selection';
|
||||
import { settings } from '$lib/logic/settings';
|
||||
import { currentTool, Tool } from '$lib/components/toolbar/tools';
|
||||
import { fileActionManager } from '$lib/logic/file-action-manager';
|
||||
import { fileActions } from '$lib/logic/file-actions';
|
||||
import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors';
|
||||
|
||||
const colors = [
|
||||
'#ff0000',
|
||||
@@ -81,10 +87,9 @@ function getMarkerForSymbol(symbol: string | undefined, layerColor: string) {
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
// const { directionMarkers, treeFileView, defaultOpacity, defaultWidth } = settings;
|
||||
const { directionMarkers, treeFileView, defaultOpacity, defaultWidth } = settings;
|
||||
|
||||
export class GPXLayer {
|
||||
map: mapboxgl.Map;
|
||||
fileId: string;
|
||||
file: Readable<GPXFileWithStatistics | undefined>;
|
||||
layerColor: string;
|
||||
@@ -100,15 +105,18 @@ export class GPXLayer {
|
||||
layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this);
|
||||
layerOnContextMenuBinded: (e: any) => void = this.layerOnContextMenu.bind(this);
|
||||
|
||||
constructor(
|
||||
map: mapboxgl.Map,
|
||||
fileId: string,
|
||||
file: Readable<GPXFileWithStatistics | undefined>
|
||||
) {
|
||||
this.map = map;
|
||||
constructor(fileId: string, file: Readable<GPXFileWithStatistics | undefined>) {
|
||||
this.fileId = fileId;
|
||||
this.file = file;
|
||||
this.layerColor = getColor();
|
||||
this.unsubscribe.push(
|
||||
map.subscribe(($map) => {
|
||||
if ($map) {
|
||||
$map.on('style.import.load', this.updateBinded);
|
||||
this.update();
|
||||
}
|
||||
})
|
||||
);
|
||||
this.unsubscribe.push(file.subscribe(this.updateBinded));
|
||||
this.unsubscribe.push(
|
||||
selection.subscribe(($selection) => {
|
||||
@@ -135,13 +143,12 @@ export class GPXLayer {
|
||||
})
|
||||
);
|
||||
this.draggable = get(currentTool) === Tool.WAYPOINT;
|
||||
|
||||
this.map.on('style.import.load', this.updateBinded);
|
||||
}
|
||||
|
||||
update() {
|
||||
const _map = get(map);
|
||||
let file = get(this.file)?.file;
|
||||
if (!file) {
|
||||
if (!_map || !file) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,18 +162,18 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
try {
|
||||
let source = this.map.getSource(this.fileId);
|
||||
let source = _map.getSource(this.fileId);
|
||||
if (source) {
|
||||
source.setData(this.getGeoJSON());
|
||||
} else {
|
||||
this.map.addSource(this.fileId, {
|
||||
_map.addSource(this.fileId, {
|
||||
type: 'geojson',
|
||||
data: this.getGeoJSON(),
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.map.getLayer(this.fileId)) {
|
||||
this.map.addLayer({
|
||||
if (!_map.getLayer(this.fileId)) {
|
||||
_map.addLayer({
|
||||
id: this.fileId,
|
||||
type: 'line',
|
||||
source: this.fileId,
|
||||
@@ -181,16 +188,16 @@ export class GPXLayer {
|
||||
},
|
||||
});
|
||||
|
||||
this.map.on('click', this.fileId, this.layerOnClickBinded);
|
||||
this.map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
|
||||
this.map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
|
||||
this.map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
|
||||
this.map.on('mousemove', this.fileId, this.layerOnMouseMoveBinded);
|
||||
_map.on('click', this.fileId, this.layerOnClickBinded);
|
||||
_map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
|
||||
_map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
|
||||
_map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
|
||||
_map.on('mousemove', this.fileId, this.layerOnMouseMoveBinded);
|
||||
}
|
||||
|
||||
if (get(directionMarkers)) {
|
||||
if (!this.map.getLayer(this.fileId + '-direction')) {
|
||||
this.map.addLayer(
|
||||
if (!_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.addLayer(
|
||||
{
|
||||
id: this.fileId + '-direction',
|
||||
type: 'symbol',
|
||||
@@ -212,12 +219,12 @@ export class GPXLayer {
|
||||
'text-halo-color': 'white',
|
||||
},
|
||||
},
|
||||
this.map.getLayer('distance-markers') ? 'distance-markers' : undefined
|
||||
_map.getLayer('distance-markers') ? 'distance-markers' : undefined
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this.map.getLayer(this.fileId + '-direction')) {
|
||||
this.map.removeLayer(this.fileId + '-direction');
|
||||
if (_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.removeLayer(this.fileId + '-direction');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +235,7 @@ export class GPXLayer {
|
||||
}
|
||||
});
|
||||
|
||||
this.map.setFilter(
|
||||
_map.setFilter(
|
||||
this.fileId,
|
||||
[
|
||||
'any',
|
||||
@@ -240,8 +247,8 @@ export class GPXLayer {
|
||||
],
|
||||
{ validate: false }
|
||||
);
|
||||
if (this.map.getLayer(this.fileId + '-direction')) {
|
||||
this.map.setFilter(
|
||||
if (_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.setFilter(
|
||||
this.fileId + '-direction',
|
||||
[
|
||||
'any',
|
||||
@@ -299,7 +306,7 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
if (get(currentTool) === Tool.WAYPOINT && e.shiftKey) {
|
||||
deleteWaypoint(this.fileId, marker._waypoint._data.index);
|
||||
fileActions.deleteWaypoint(this.fileId, marker._waypoint._data.index);
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
@@ -312,11 +319,11 @@ export class GPXLayer {
|
||||
false
|
||||
)
|
||||
) {
|
||||
addSelectItem(
|
||||
selection.addSelectItem(
|
||||
new ListWaypointItem(this.fileId, marker._waypoint._data.index)
|
||||
);
|
||||
} else {
|
||||
selectItem(
|
||||
selection.selectItem(
|
||||
new ListWaypointItem(this.fileId, marker._waypoint._data.index)
|
||||
);
|
||||
}
|
||||
@@ -336,7 +343,7 @@ export class GPXLayer {
|
||||
resetCursor();
|
||||
marker.getElement().style.cursor = '';
|
||||
getElevation([marker._waypoint]).then((ele) => {
|
||||
dbUtils.applyToFile(this.fileId, (file) => {
|
||||
fileActionManager.applyToFile(this.fileId, (file) => {
|
||||
let latLng = marker.getLngLat();
|
||||
let wpt = file.wpt[marker._waypoint._data.index];
|
||||
wpt.setCoordinates({
|
||||
@@ -361,36 +368,31 @@ export class GPXLayer {
|
||||
|
||||
this.markers.forEach((marker) => {
|
||||
if (!marker._waypoint._data.hidden) {
|
||||
marker.addTo(this.map);
|
||||
marker.addTo(_map);
|
||||
} else {
|
||||
marker.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateMap(map: mapboxgl.Map) {
|
||||
this.map = map;
|
||||
this.map.on('style.import.load', this.updateBinded);
|
||||
this.update();
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (get(map)) {
|
||||
this.map.off('click', this.fileId, this.layerOnClickBinded);
|
||||
this.map.off('contextmenu', this.fileId, this.layerOnContextMenuBinded);
|
||||
this.map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
|
||||
this.map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
|
||||
this.map.off('mousemove', this.fileId, this.layerOnMouseMoveBinded);
|
||||
this.map.off('style.import.load', this.updateBinded);
|
||||
const _map = get(map);
|
||||
if (_map) {
|
||||
_map.off('click', this.fileId, this.layerOnClickBinded);
|
||||
_map.off('contextmenu', this.fileId, this.layerOnContextMenuBinded);
|
||||
_map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
|
||||
_map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
|
||||
_map.off('mousemove', this.fileId, this.layerOnMouseMoveBinded);
|
||||
_map.off('style.import.load', this.updateBinded);
|
||||
|
||||
if (this.map.getLayer(this.fileId + '-direction')) {
|
||||
this.map.removeLayer(this.fileId + '-direction');
|
||||
if (_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.removeLayer(this.fileId + '-direction');
|
||||
}
|
||||
if (this.map.getLayer(this.fileId)) {
|
||||
this.map.removeLayer(this.fileId);
|
||||
if (_map.getLayer(this.fileId)) {
|
||||
_map.removeLayer(this.fileId);
|
||||
}
|
||||
if (this.map.getSource(this.fileId)) {
|
||||
this.map.removeSource(this.fileId);
|
||||
if (_map.getSource(this.fileId)) {
|
||||
_map.removeSource(this.fileId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,13 +406,17 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
moveToFront() {
|
||||
if (this.map.getLayer(this.fileId)) {
|
||||
this.map.moveLayer(this.fileId);
|
||||
const _map = get(map);
|
||||
if (!_map) {
|
||||
return;
|
||||
}
|
||||
if (this.map.getLayer(this.fileId + '-direction')) {
|
||||
this.map.moveLayer(
|
||||
if (_map.getLayer(this.fileId)) {
|
||||
_map.moveLayer(this.fileId);
|
||||
}
|
||||
if (_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.moveLayer(
|
||||
this.fileId + '-direction',
|
||||
this.map.getLayer('distance-markers') ? 'distance-markers' : undefined
|
||||
_map.getLayer('distance-markers') ? 'distance-markers' : undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -468,7 +474,7 @@ export class GPXLayer {
|
||||
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
||||
)
|
||||
) {
|
||||
dbUtils.split(splitAs.current, this.fileId, trackIndex, segmentIndex, {
|
||||
fileActions.split(get(splitAs), this.fileId, trackIndex, segmentIndex, {
|
||||
lat: e.lngLat.lat,
|
||||
lon: e.lngLat.lng,
|
||||
});
|
||||
@@ -492,9 +498,9 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
if (e.originalEvent.ctrlKey || e.originalEvent.metaKey) {
|
||||
addSelectItem(item);
|
||||
selection.addSelectItem(item);
|
||||
} else {
|
||||
selectItem(item);
|
||||
selection.selectItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
38
website/src/lib/components/map/gpx-layer/gpx-layers.ts
Normal file
38
website/src/lib/components/map/gpx-layer/gpx-layers.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { GPXFileStateCollectionObserver } from '$lib/logic/file-state';
|
||||
import { GPXLayer } from './gpx-layer';
|
||||
|
||||
export class GPXLayerCollection {
|
||||
private _layers: Map<string, GPXLayer>;
|
||||
private _fileStateCollectionObserver: GPXFileStateCollectionObserver | null = null;
|
||||
|
||||
constructor() {
|
||||
this._layers = new Map<string, GPXLayer>();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this._fileStateCollectionObserver) {
|
||||
return;
|
||||
}
|
||||
this._fileStateCollectionObserver = new GPXFileStateCollectionObserver(
|
||||
(fileId, fileState) => {
|
||||
const layer = new GPXLayer(fileId, fileState);
|
||||
this._layers.set(fileId, layer);
|
||||
},
|
||||
(fileId) => {
|
||||
const layer = this._layers.get(fileId);
|
||||
if (layer) {
|
||||
layer.remove();
|
||||
this._layers.delete(fileId);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this._layers.forEach((layer) => {
|
||||
layer.remove();
|
||||
});
|
||||
this._layers.clear();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const gpxLayers = new GPXLayerCollection();
|
||||
Reference in New Issue
Block a user