From 1fe1d13c600b7dfe421b3b99b833b7d5d1232724 Mon Sep 17 00:00:00 2001 From: Dennis Fink Date: Thu, 23 Jul 2026 00:18:21 +0200 Subject: Add GPX trail maps to blog posts This adds a trail flow block so posts can include attached GPX files as interactive maps with route display, elevation charts, activity summaries, and start/finish markers. The trail assets are loaded only when needed, keeping the shared templates clean while vendoring the Leaflet dependencies required for offline, self-hosted map rendering. --- .../leaflet-elevation/src/components/marker.js | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 assets/static/vendor/leaflet-elevation/src/components/marker.js (limited to 'assets/static/vendor/leaflet-elevation/src/components/marker.js') diff --git a/assets/static/vendor/leaflet-elevation/src/components/marker.js b/assets/static/vendor/leaflet-elevation/src/components/marker.js new file mode 100644 index 0000000..6f1f6af --- /dev/null +++ b/assets/static/vendor/leaflet-elevation/src/components/marker.js @@ -0,0 +1,96 @@ +import * as D3 from './d3.js'; + +const _ = L.Control.Elevation.Utils; + +export var Marker = L.Class.extend({ + + initialize(options, control) { + this.options = options; + this.control = control + + switch(this.options.marker) { + case 'elevation-line': + // this._container = d3.create("g").attr("class", "height-focus-group"); + break; + case 'position-marker': + // this._marker = L.circleMarker([0, 0], { pane: 'overlayPane', radius: 6, fillColor: '#fff', fillOpacity:1, color: '#000', weight:1, interactive: false }); + this._marker = L.marker([0, 0], { icon: this.options.markerIcon, zIndexOffset: 1000000, interactive: false }); + break; + } + + this._labels = {}; + + return this; + }, + + addTo(map) { + this._map = map; + switch(this.options.marker) { + case 'elevation-line': this._container = d3.select(map.getPane('elevationPane')).select("svg > g").call(D3.PositionMarker({})); break; + case 'position-marker': this._marker.addTo(map, { pane: 'overlayPane' }); break; + } + return this; + }, + + /** + * Update position marker ("leaflet-marker"). + */ + update(props) { + if (props) this._props = props; + else props = this._props; + + if (!props) return; + + if (props.options) this.options = props.options; + if (!this._map) this.addTo(props.map); + + this._latlng = props.item.latlng; + + switch(this.options.marker) { + case 'elevation-line': + if (this._container) { + let point = this._map.latLngToLayerPoint(this._latlng); + point = L.extend({}, props.item, this._map._rotate ? this._map.rotatedPointToMapPanePoint(point) : point); + + let yMax = (this.control._height() / props.yCoordMax * point[this.options.yAttr]); + + if (!isFinite(yMax) || isNaN(yMax)) yMax = 0; + + this._container.classed("leaflet-hidden", false); + this._container.call(D3.PositionMarker({ + theme : this.options.theme, + xCoord: point.x, + yCoord: point.y, + length: point.y - yMax, // normalized Y + labels: this._labels, + item: point, + })); + } + break; + case 'position-marker': + _.removeClass(this._marker.getElement(), 'leaflet-hidden'); + this._marker.setLatLng(this._latlng); + break; + } + }, + + /* + * Hides the position/height indicator marker drawn onto the map + */ + remove() { + this._props = null; + switch(this.options.marker) { + case 'elevation-line': this._container && this._container.classed("leaflet-hidden", true); break; + case 'position-marker': _.addClass(this._marker.getElement(), 'leaflet-hidden'); break; + } + }, + + getLatLng() { + return this._latlng; + }, + + _registerTooltip(props) { + this._labels[props.name] = props; + } + +}); -- cgit v1.3.1