move crosshair on trail hover
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user