move crosshair on trail hover

This commit is contained in:
Christian Beutel
2025-01-12 15:29:37 +01:00
parent 860e634a03
commit 7005b3677f
3 changed files with 92 additions and 28 deletions

View File

@@ -97,7 +97,7 @@
!drawing && !drawing &&
fitBounds !== "off" && fitBounds !== "off" &&
data.some((d) => d.bbox !== undefined) data.some((d) => d.bbox !== undefined)
) { ) {
focusTrail(trails[activeTrail]); focusTrail(trails[activeTrail]);
} }
} else if (activeTrail === null && trails.length) { } else if (activeTrail === null && trails.length) {
@@ -179,13 +179,13 @@
return r; return r;
} }
function initMap(mapLoaded: boolean) { function initMap(mapLoaded: boolean) {
if (!map) { if (!map) {
return; return;
} }
refreshElevationProfile(); refreshElevationProfile();
if (showElevation) { if (showElevation && data.length) {
epc?.showProfile(); epc?.showProfile();
} }
@@ -201,22 +201,24 @@
removeTrailLayer(layerId); removeTrailLayer(layerId);
} }
}); });
if ( if (
!drawing && !drawing &&
fitBounds !== "off" && fitBounds !== "off" &&
data.some((d) => d.bbox !== undefined) data.some((d) => d.bbox !== undefined)
) { ) {
if (activeTrail !== null && mapLoaded) { if (activeTrail !== null && mapLoaded) {
focusTrail(trails[activeTrail]); focusTrail(trails[activeTrail]);
} else { } else {
flyToBounds(); flyToBounds();
} }
} else if(drawing && activeTrail !== null && mapLoaded) {
addCaretLayer(trails[activeTrail]?.id)
} }
} }
export function refreshElevationProfile() { export function refreshElevationProfile() {
if (activeTrail !== null && data[activeTrail]) { if (activeTrail !== null && data[activeTrail]) {
epc?.setData(data[activeTrail]!, waypoints); epc?.setData(data[activeTrail]!, waypoints);
} }
} }
@@ -283,6 +285,7 @@
map?.off("mouseleave", id, layers[id].listener.onLeave!); map?.off("mouseleave", id, layers[id].listener.onLeave!);
map?.off("mouseup", id, layers[id].listener.onMouseUp!); map?.off("mouseup", id, layers[id].listener.onMouseUp!);
map?.off("mousemove", id, layers[id].listener.onMouseMove!); map?.off("mousemove", id, layers[id].listener.onMouseMove!);
map?.off("mousedown", id, layers[id].listener.onMouseDown!);
delete layers[id]; delete layers[id];
} }
@@ -347,10 +350,9 @@
highlightTrail(id, trails[activeTrail ?? -1]?.id == id); highlightTrail(id, trails[activeTrail ?? -1]?.id == id);
layers[id].listener.onLeave = (e) => unHighlightTrail(id); layers[id].listener.onLeave = (e) => unHighlightTrail(id);
layers[id].listener.onMouseUp = (e) => { layers[id].listener.onMouseUp = (e) => {
activeTrail = trails.indexOf(trail); activeTrail = trails.findIndex(t => t.id == trail.id);
}; };
layers[id].listener.onMouseMove = layers[id].listener.onMouseMove = moveCrosshairToCursorPosition;
moveElevationMarkerToCursorPosition;
layers[id].listener.onMouseDown = (e) => handDragStart(e, id); layers[id].listener.onMouseDown = (e) => handDragStart(e, id);
map.on("mouseenter", id, layers[id].listener.onEnter); map.on("mouseenter", id, layers[id].listener.onEnter);
@@ -365,6 +367,11 @@
} }
} }
function moveCrosshairToCursorPosition(e: M.MapMouseEvent) {
epc?.moveCrosshair(e.lngLat.lat, e.lngLat.lng);
moveElevationMarkerToCursorPosition(e);
}
function moveElevationMarkerToCursorPosition(e: M.MapMouseEvent) { function moveElevationMarkerToCursorPosition(e: M.MapMouseEvent) {
elevationMarker.setLngLat(e.lngLat); elevationMarker.setLngLat(e.lngLat);
} }
@@ -394,6 +401,7 @@
function handleDragEnd(e: M.MapMouseEvent) { function handleDragEnd(e: M.MapMouseEvent) {
map?.off("mousemove", moveElevationMarkerToCursorPosition); map?.off("mousemove", moveElevationMarkerToCursorPosition);
epc?.hideCrosshair();
dispatch("segmentDragEnd", { segment: draggingSegment, event: e }); dispatch("segmentDragEnd", { segment: draggingSegment, event: e });
draggingSegment = null; draggingSegment = null;
} }
@@ -405,7 +413,7 @@
if (map.getLayer("direction-carets")) { if (map.getLayer("direction-carets")) {
removeCaretLayer(); removeCaretLayer();
} }
map.addLayer({ map.addLayer({
id: "direction-carets", id: "direction-carets",
type: "symbol", type: "symbol",
@@ -459,27 +467,26 @@
return; return;
} }
elevationMarker.setOpacity("0"); elevationMarker.setOpacity("0");
epc?.hideCrosshair();
hoveringTrail = false; hoveringTrail = false;
map?.setPaintProperty(id, "line-width", 5); map?.setPaintProperty(id, "line-width", 5);
// map?.setPaintProperty(id, "line-color", "#648ad5"); // map?.setPaintProperty(id, "line-color", "#648ad5");
} }
function focusTrail(trail: Trail) { function focusTrail(trail: Trail) {
activeTrail = trails.findIndex((t) => t.id == trail.id);
activeTrail = trails.findIndex(t => t.id == trail.id);
if (activeTrail < 0) { if (activeTrail < 0) {
activeTrail = null; activeTrail = null;
return; return;
} }
const currentlyFocussedTrail = trails[activeTrail]; const currentlyFocussedTrail = trails[activeTrail];
if (currentlyFocussedTrail && currentlyFocussedTrail != trail) { if (currentlyFocussedTrail && currentlyFocussedTrail != trail) {
unFocusTrail(currentlyFocussedTrail); unFocusTrail(currentlyFocussedTrail);
} }
dispatch("select", trail); dispatch("select", trail);
try { try {
highlightTrail(trail.id!);
refreshElevationProfile(); refreshElevationProfile();
if (showElevation) { if (showElevation) {
epc?.showProfile(); epc?.showProfile();
@@ -488,7 +495,7 @@
addCaretLayer(trail.id!); addCaretLayer(trail.id!);
flyToBounds(); flyToBounds();
} catch (e) { } catch (e) {
console.warn(e) console.warn(e);
} }
} }
@@ -743,7 +750,11 @@
elevationMarker.setOpacity("0"); elevationMarker.setOpacity("0");
}, },
onMove: (data) => { onMove: (data) => {
elevationMarker.setLngLat(data.position as M.LngLatLike); if (!hoveringTrail) {
elevationMarker.setLngLat(
data.position as M.LngLatLike,
);
}
}, },
}); });
map.addControl(epc); map.addControl(epc);

View File

@@ -2,7 +2,7 @@ import { type ControlPosition, type IControl } from "maplibre-gl";
import * as M from "maplibre-gl"; import * as M from "maplibre-gl";
import { ElevationProfile, type ElevationProfileOptions } from "./elevationprofile"; import { ElevationProfile, type ElevationProfileOptions } from "./elevationprofile";
// @ts-ignore // @ts-ignore
import type { GeoJsonObject } from "geojson"; import type { GeoJsonObject, Position } from "geojson";
import type { Waypoint } from "$lib/models/waypoint"; import type { Waypoint } from "$lib/models/waypoint";
/** /**
@@ -189,6 +189,30 @@ export class ElevationProfileControl implements IControl {
this.isProfileShown = false; this.isProfileShown = false;
} }
moveCrosshair(lat: number, lon: number) {
if (!this.elevationProfileChart) {
return;
}
const chart = this.elevationProfileChart.chart
const point = this.elevationProfileChart.getChartCoordinatesFromPosition(lat, lon)
if (point == null) {
return;
}
const rectangle = chart.canvas.getBoundingClientRect();
const mouseMoveEvent = new MouseEvent('mousemove', {
clientX: rectangle.left + point[0],
clientY: rectangle.top + point[1]
});
chart.canvas.dispatchEvent(mouseMoveEvent);
}
hideCrosshair() {
const mouseOutEvent = new MouseEvent('mouseout');
return this.elevationProfileChart?.chart.canvas.dispatchEvent(mouseOutEvent);
}
onRemove(): void { onRemove(): void {
// remove button // remove button
if (this.buttonContainer?.parentNode) { if (this.buttonContainer?.parentNode) {

View File

@@ -358,7 +358,7 @@ const elevationProfileDefaultOptions: ElevationProfileOptions = {
export class ElevationProfile { export class ElevationProfile {
private canvas: HTMLCanvasElement; private canvas: HTMLCanvasElement;
private settings: ElevationProfileOptions; private settings: ElevationProfileOptions;
private chart: Chart<"line", Array<number>, number>; public chart: Chart<"line", Array<number>, number>;
private elevatedPositions: Position[] = []; private elevatedPositions: Position[] = [];
private elevatedPositionsAdjustedUnit: Position[] = []; private elevatedPositionsAdjustedUnit: Position[] = [];
private cumulatedDistance: number[] = []; private cumulatedDistance: number[] = [];
@@ -483,7 +483,6 @@ export class ElevationProfile {
}, },
onHover: (_e, item) => { onHover: (_e, item) => {
if (typeof this.settings.onMove !== "function") return; if (typeof this.settings.onMove !== "function") return;
try { try {
const i = item[0].index; const i = item[0].index;
@@ -753,6 +752,33 @@ export class ElevationProfile {
} }
} }
getChartCoordinatesFromPosition(lat: number, lon: number) {
let minDistance = Infinity
let bestCandiateIndex: number = -1
for (let i = 0; i < this.elevatedPositionsAdjustedUnit.length; i++) {
const p = this.elevatedPositionsAdjustedUnit[i];
const deltaLat = lat - p[1];
const deltaLon = lon - p[0];
const distance = deltaLat * deltaLat + deltaLon * deltaLon;
if (distance < minDistance) {
minDistance = distance;
bestCandiateIndex = i
}
};
if (bestCandiateIndex < 0) {
return null;
}
const meta = this.chart.getDatasetMeta(0);
const element = meta.data[bestCandiateIndex];
if (element) {
return [element.x, element.y];
}
return null;
}
gradientFromElevation(chart: Chart, force: boolean = false) { gradientFromElevation(chart: Chart, force: boolean = false) {
const ctx = chart.ctx; const ctx = chart.ctx;
const chartArea = chart.chartArea; const chartArea = chart.chartArea;
@@ -864,7 +890,7 @@ export class ElevationProfile {
async setData(data: GeoJsonObject, waypoints?: Waypoint[]) { async setData(data: GeoJsonObject, waypoints?: Waypoint[]) {
// Concatenates the positions that may come from multiple LineStrings or MultiLineString // Concatenates the positions that may come from multiple LineStrings or MultiLineString
const { positions, times } = geoJsonObjectToPositionsAndTimes(data); const { positions, times } = geoJsonObjectToPositionsAndTimes(data);
this.times = times; this.times = times;
this.elevatedPositions = smoothElevations(positions, Math.ceil(positions.length / 100)); this.elevatedPositions = smoothElevations(positions, Math.ceil(positions.length / 100));
@@ -954,14 +980,17 @@ export class ElevationProfile {
const timeStart = this.times[segmentStartIndex] const timeStart = this.times[segmentStartIndex]
const timeEnd = this.times[i] const timeEnd = this.times[i]
const timeDelta = (timeEnd.getTime() - timeStart.getTime()) / 1000 if (timeStart && timeEnd) {
const timeDelta = (timeEnd.getTime() - timeStart.getTime()) / 1000
speed = (distanceDelta / timeDelta) speed = (distanceDelta / timeDelta)
if (this.settings.unit === "imperial") { if (this.settings.unit === "imperial") {
speed = speed * MILES_HOUR_PER_METER_SECOND speed = speed * MILES_HOUR_PER_METER_SECOND
} else { } else {
speed = speed * KILOMETERS_HOUR_PER_METER_SECOND; speed = speed * KILOMETERS_HOUR_PER_METER_SECOND;
}
} }
} }