diff --git a/website/src/lib/components/ElevationProfile.svelte b/website/src/lib/components/ElevationProfile.svelte index 3885816f..f434e73a 100644 --- a/website/src/lib/components/ElevationProfile.svelte +++ b/website/src/lib/components/ElevationProfile.svelte @@ -3,7 +3,7 @@ import Tooltip from '$lib/components/Tooltip.svelte'; import Chart from 'chart.js/auto'; import mapboxgl from 'mapbox-gl'; - import { map } from '$lib/stores'; + import { hoveredTrackPoint, map } from '$lib/stores'; import { onDestroy, onMount } from 'svelte'; import { BrickWall, @@ -127,7 +127,7 @@ }, label: function (context: Chart.TooltipContext) { let point = context.raw; - if (context.datasetIndex === 0) { + if (context.datasetIndex === 0 && $hoveredTrackPoint === undefined) { if ($map && marker) { if (dragging) { marker.remove(); @@ -603,6 +603,32 @@ $: $slicedGPXStatistics, $mode, updateOverlay(); + $: if (chart) { + if ($hoveredTrackPoint) { + let index = chart._metasets[0].data.findIndex( + (point) => + $gpxStatistics.local.points[point.raw.index]._data.index === + $hoveredTrackPoint.point._data.index && + $hoveredTrackPoint.point.getLongitude() === point.raw.coordinates.lon && + $hoveredTrackPoint.point.getLatitude() === point.raw.coordinates.lat + ); + if (index >= 0) { + chart.tooltip?.setActiveElements( + [ + { + datasetIndex: 0, + index + } + ], + { x: 0, y: 0 } + ); + } + } else { + chart.tooltip?.setActiveElements([], { x: 0, y: 0 }); + } + chart.update(); + } + onDestroy(() => { if (chart) { chart.destroy(); diff --git a/website/src/lib/components/gpx-layer/GPXLayer.ts b/website/src/lib/components/gpx-layer/GPXLayer.ts index 6da90ed7..d867b8ff 100644 --- a/website/src/lib/components/gpx-layer/GPXLayer.ts +++ b/website/src/lib/components/gpx-layer/GPXLayer.ts @@ -1,4 +1,4 @@ -import { currentTool, map, Tool } from "$lib/stores"; +import { currentTool, hoveredTrackPoint, map, Tool } from "$lib/stores"; import { settings, type GPXFileWithStatistics, dbUtils } from "$lib/db"; import { get, type Readable } from "svelte/store"; import mapboxgl from "mapbox-gl"; @@ -6,7 +6,7 @@ import { currentPopupWaypoint, deleteWaypoint, waypointPopup } from "./WaypointP import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection"; import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList"; import type { Waypoint } from "gpx"; -import { getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils"; +import { getClosestLinePoint, getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils"; import { font } from "$lib/assets/layers"; import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte"; import { MapPin, Square } from "lucide-static"; @@ -80,6 +80,7 @@ export class GPXLayer { updateBinded: () => void = this.update.bind(this); layerOnMouseEnterBinded: (e: any) => void = this.layerOnMouseEnter.bind(this); + layerOnMouseMoveBinded: (e: any) => void = this.layerOnMouseMove.bind(this); layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this); layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this); maybeHideWaypointPopupBinded: (e: any) => void = this.maybeHideWaypointPopup.bind(this); @@ -155,6 +156,7 @@ export class GPXLayer { this.map.on('click', this.fileId, this.layerOnClickBinded); this.map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded); + this.map.on('mousemove', this.fileId, this.layerOnMouseMoveBinded); this.map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); } @@ -302,6 +304,7 @@ export class GPXLayer { if (get(map)) { this.map.off('click', this.fileId, this.layerOnClickBinded); this.map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded); + this.map.off('mousemove', this.fileId, this.layerOnMouseMoveBinded); this.map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); this.map.off('style.load', this.updateBinded); @@ -345,8 +348,28 @@ export class GPXLayer { } } + layerOnMouseMove(e: any) { + let trackIndex = e.features[0].properties.trackIndex; + let segmentIndex = e.features[0].properties.segmentIndex; + + if (get(selection).hasAnyParent(new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex))) { + let file = get(this.file)?.file; + if (file) { + let segment = file.trk[trackIndex].trkseg[segmentIndex]; + let point = getClosestLinePoint(segment.trkpt, { lat: e.lngLat.lat, lon: e.lngLat.lng }); + hoveredTrackPoint.set({ + fileId: this.fileId, + trackIndex, + segmentIndex, + point + }); + } + } + } + layerOnMouseLeave() { resetCursor(); + hoveredTrackPoint.set(undefined); } layerOnClick(e: any) { diff --git a/website/src/lib/stores.ts b/website/src/lib/stores.ts index 397ae336..f98d1634 100644 --- a/website/src/lib/stores.ts +++ b/website/src/lib/stores.ts @@ -1,7 +1,7 @@ import { writable, get, type Writable, derived } from 'svelte/store'; import mapboxgl from 'mapbox-gl'; -import { GPXFile, buildGPX, parseGPX, GPXStatistics, type Coordinates } from 'gpx'; +import { GPXFile, buildGPX, parseGPX, GPXStatistics, type Coordinates, TrackPoint } from 'gpx'; import { tick } from 'svelte'; import { _ } from 'svelte-i18n'; import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer'; @@ -19,6 +19,7 @@ export const selectFiles = writable<{ [key: string]: (fileId?: string) => void } export const gpxStatistics: Writable = writable(new GPXStatistics()); export const slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined> = writable(undefined); +export const hoveredTrackPoint = writable<{ fileId: string, trackIndex: number, segmentIndex: number, point: TrackPoint, matchedPoint?: TrackPoint } | undefined>(undefined); export function updateGPXData() { let statistics = new GPXStatistics();