summaryrefslogtreecommitdiff
path: root/assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js
diff options
context:
space:
mode:
authorDennis Fink2026-07-23 00:18:21 +0200
committerDennis Fink2026-07-23 00:18:21 +0200
commit1fe1d13c600b7dfe421b3b99b833b7d5d1232724 (patch)
tree2e8636160cc1719c907be56f5ef8cbc7e0f10a53 /assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js
parentca77de06be474dfd712402c327dc436864d233a3 (diff)
downloaddennisfink.me-1fe1d13c600b7dfe421b3b99b833b7d5d1232724.tar.gz
dennisfink.me-1fe1d13c600b7dfe421b3b99b833b7d5d1232724.zip
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.
Diffstat (limited to 'assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js')
-rw-r--r--assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js457
1 files changed, 457 insertions, 0 deletions
diff --git a/assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js b/assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js
new file mode 100644
index 0000000..1684560
--- /dev/null
+++ b/assets/static/vendor/leaflet-elevation/libs/leaflet-hotline.js
@@ -0,0 +1,457 @@
+/*
+(c) 2017, iosphere GmbH
+Leaflet.hotline, a Leaflet plugin for drawing gradients along polylines.
+https://github.com/iosphere/Leaflet.hotline/
+*/
+
+(function (root, plugin) {
+ /**
+ * UMD wrapper.
+ * When used directly in the Browser it expects Leaflet to be globally
+ * available as `L`. The plugin then adds itself to Leaflet.
+ * When used as a CommonJS module (e.g. with browserify) only the plugin
+ * factory gets exported, so one hast to call the factory manually and pass
+ * Leaflet as the only parameter.
+ * @see {@link https://github.com/umdjs/umd}
+ */
+ if (typeof define === 'function' && define.amd) {
+ define(['leaflet'], plugin);
+ } else if (typeof exports === 'object') {
+ module.exports = plugin;
+ } else {
+ plugin(root.L);
+ }
+}(window, function (L) {
+ // Plugin is already added to Leaflet
+ if (L.Hotline) {
+ return L;
+ }
+
+ /**
+ * Core renderer.
+ * @constructor
+ * @param {HTMLElement | string} canvas - <canvas> element or its id
+ * to initialize the instance on.
+ */
+ var Hotline = function (canvas) {
+ if (!(this instanceof Hotline)) { return new Hotline(canvas); }
+
+ var defaultPalette = {
+ 0.0: 'green',
+ 0.5: 'yellow',
+ 1.0: 'red'
+ };
+
+ this._canvas = canvas = ('string' === typeof canvas)
+ ? document.getElementById(canvas)
+ : canvas;
+
+ this._ctx = canvas.getContext('2d');
+ this._width = canvas.width;
+ this._height = canvas.height;
+
+ this._weight = 5;
+ this._outlineWidth = 1;
+ this._outlineColor = 'black';
+
+ this._min = 0;
+ this._max = 1;
+
+ this._data = [];
+
+ this.palette(defaultPalette);
+ };
+
+ Hotline.prototype = {
+ /**
+ * Sets the width of the canvas. Used when clearing the canvas.
+ * @param {number} width - Width of the canvas.
+ */
+ width: function (width) {
+ this._width = width;
+ return this;
+ },
+
+ /**
+ * Sets the height of the canvas. Used when clearing the canvas.
+ * @param {number} height - Height of the canvas.
+ */
+ height: function (height) {
+ this._height = height;
+ return this;
+ },
+
+ /**
+ * Sets the weight of the path.
+ * @param {number} weight - Weight of the path in px.
+ */
+ weight: function (weight) {
+ this._weight = weight;
+ return this;
+ },
+
+ /**
+ * Sets the width of the outline around the path.
+ * @param {number} outlineWidth - Width of the outline in px.
+ */
+ outlineWidth: function (outlineWidth) {
+ this._outlineWidth = outlineWidth;
+ return this;
+ },
+
+ /**
+ * Sets the color of the outline around the path.
+ * @param {string} outlineColor - A CSS color value.
+ */
+ outlineColor: function (outlineColor) {
+ this._outlineColor = outlineColor;
+ return this;
+ },
+
+ /**
+ * Sets the palette gradient.
+ * @param {Object.<number, string>} palette - Gradient definition.
+ * e.g. { 0.0: 'white', 1.0: 'black' }
+ */
+ palette: function (palette) {
+ var canvas = document.createElement('canvas'),
+ ctx = canvas.getContext('2d'),
+ gradient = ctx.createLinearGradient(0, 0, 0, 256);
+
+ canvas.width = 1;
+ canvas.height = 256;
+
+ for (var i in palette) {
+ gradient.addColorStop(i, palette[i]);
+ }
+
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, 1, 256);
+
+ this._palette = ctx.getImageData(0, 0, 1, 256).data;
+
+ return this;
+ },
+
+ /**
+ * Sets the value used at the start of the palette gradient.
+ * @param {number} min
+ */
+ min: function (min) {
+ this._min = min;
+ return this;
+ },
+
+ /**
+ * Sets the value used at the end of the palette gradient.
+ * @param {number} max
+ */
+ max: function (max) {
+ this._max = max;
+ return this;
+ },
+
+ /**
+ * A path to rander as a hotline.
+ * @typedef Array.<{x:number, y:number, z:number}> Path - Array of x, y and z coordinates.
+ */
+
+ /**
+ * Sets the data that gets drawn on the canvas.
+ * @param {(Path|Path[])} data - A single path or an array of paths.
+ */
+ data: function (data) {
+ this._data = data;
+ return this;
+ },
+
+ /**
+ * Adds a path to the list of paths.
+ * @param {Path} path
+ */
+ add: function (path) {
+ this._data.push(path);
+ return this;
+ },
+
+ /**
+ * Draws the currently set paths.
+ */
+ draw: function () {
+ var ctx = this._ctx;
+
+ ctx.globalCompositeOperation = 'source-over';
+ ctx.lineCap = 'round';
+
+ this._drawOutline(ctx);
+ this._drawHotline(ctx);
+
+ return this;
+ },
+
+ /**
+ * Gets the RGB values of a given z value of the current palette.
+ * @param {number} value - Value to get the color for, should be between min and max.
+ * @returns {Array.<number>} The RGB values as an array [r, g, b]
+ */
+ getRGBForValue: function (value) {
+ var valueRelative = Math.min(Math.max((value - this._min) / (this._max - this._min), 0), 0.999);
+ var paletteIndex = Math.floor(valueRelative * 256) * 4;
+
+ return [
+ this._palette[paletteIndex],
+ this._palette[paletteIndex + 1],
+ this._palette[paletteIndex + 2]
+ ];
+ },
+
+ /**
+ * Draws the outline of the graphs.
+ * @private
+ */
+ _drawOutline: function (ctx) {
+ var i, j, dataLength, path, pathLength, pointStart, pointEnd;
+
+ if (this._outlineWidth) {
+ for (i = 0, dataLength = this._data.length; i < dataLength; i++) {
+ path = this._data[i];
+ ctx.lineWidth = this._weight + 2 * this._outlineWidth;
+
+ for (j = 1, pathLength = path.length; j < pathLength; j++) {
+ pointStart = path[j - 1];
+ pointEnd = path[j];
+
+ ctx.strokeStyle = this._outlineColor;
+ ctx.beginPath();
+ ctx.moveTo(pointStart.x, pointStart.y);
+ ctx.lineTo(pointEnd.x, pointEnd.y);
+ ctx.stroke();
+ }
+ }
+ }
+ },
+
+ /**
+ * Draws the color encoded hotline of the graphs.
+ * @private
+ */
+ _drawHotline: function (ctx) {
+ var i, j, dataLength, path, pathLength, pointStart, pointEnd,
+ gradient, gradientStartRGB, gradientEndRGB;
+
+ ctx.lineWidth = this._weight;
+
+ for (i = 0, dataLength = this._data.length; i < dataLength; i++) {
+ path = this._data[i];
+
+ for (j = 1, pathLength = path.length; j < pathLength; j++) {
+ pointStart = path[j - 1];
+ pointEnd = path[j];
+
+ // Create a gradient for each segment, pick start end end colors from palette gradient
+ gradient = ctx.createLinearGradient(pointStart.x, pointStart.y, pointEnd.x, pointEnd.y);
+ gradientStartRGB = this.getRGBForValue(pointStart.z);
+ gradientEndRGB = this.getRGBForValue(pointEnd.z);
+ gradient.addColorStop(0, 'rgb(' + gradientStartRGB.join(',') + ')');
+ gradient.addColorStop(1, 'rgb(' + gradientEndRGB.join(',') + ')');
+
+ ctx.strokeStyle = gradient;
+ ctx.beginPath();
+ ctx.moveTo(pointStart.x, pointStart.y);
+ ctx.lineTo(pointEnd.x, pointEnd.y);
+ ctx.stroke();
+ }
+ }
+ }
+ };
+
+
+ var Renderer = L.Canvas.extend({
+ _initContainer: function () {
+ L.Canvas.prototype._initContainer.call(this);
+ this._hotline = new Hotline(this._container);
+ },
+
+ _update: function () {
+ L.Canvas.prototype._update.call(this);
+ this._hotline.width(this._container.width);
+ this._hotline.height(this._container.height);
+ },
+
+ _updatePoly: function (layer) {
+ if (!this._drawing) { return; }
+
+ var parts = layer._parts;
+
+ if (!parts.length) { return; }
+
+ this._updateOptions(layer);
+
+ this._hotline
+ .data(parts)
+ .draw();
+ },
+
+ _updateOptions: function (layer) {
+ if (layer.options.min != null) {
+ this._hotline.min(layer.options.min);
+ }
+ if (layer.options.max != null) {
+ this._hotline.max(layer.options.max);
+ }
+ if (layer.options.weight != null) {
+ this._hotline.weight(layer.options.weight);
+ }
+ if (layer.options.outlineWidth != null) {
+ this._hotline.outlineWidth(layer.options.outlineWidth);
+ }
+ if (layer.options.outlineColor != null) {
+ this._hotline.outlineColor(layer.options.outlineColor);
+ }
+ if (layer.options.palette) {
+ this._hotline.palette(layer.options.palette);
+ }
+ }
+ });
+
+ var renderer = function (options) {
+ return L.Browser.canvas ? new Renderer(options) : null;
+ };
+
+
+ var Util = {
+ /**
+ * This is just a copy of the original Leaflet version that support a third z coordinate.
+ * @see {@link http://leafletjs.com/reference.html#lineutil-clipsegment|Leaflet}
+ */
+ clipSegment: function (a, b, bounds, useLastCode, round) {
+ var codeA = useLastCode ? this._lastCode : L.LineUtil._getBitCode(a, bounds),
+ codeB = L.LineUtil._getBitCode(b, bounds),
+ codeOut, p, newCode;
+
+ // save 2nd code to avoid calculating it on the next segment
+ this._lastCode = codeB;
+
+ while (true) {
+ // if a,b is inside the clip window (trivial accept)
+ if (!(codeA | codeB)) {
+ return [a, b];
+ // if a,b is outside the clip window (trivial reject)
+ } else if (codeA & codeB) {
+ return false;
+ // other cases
+ } else {
+ codeOut = codeA || codeB;
+ p = L.LineUtil._getEdgeIntersection(a, b, codeOut, bounds, round);
+ newCode = L.LineUtil._getBitCode(p, bounds);
+
+ if (codeOut === codeA) {
+ p.z = a.z;
+ a = p;
+ codeA = newCode;
+ } else {
+ p.z = b.z;
+ b = p;
+ codeB = newCode;
+ }
+ }
+ }
+ }
+ };
+
+
+ L.Hotline = L.Polyline.extend({
+ statics: {
+ Renderer: Renderer,
+ renderer: renderer
+ },
+
+ options: {
+ renderer: renderer(),
+ min: 0,
+ max: 1,
+ palette: {
+ 0.0: 'green',
+ 0.5: 'yellow',
+ 1.0: 'red'
+ },
+ weight: 5,
+ outlineColor: 'black',
+ outlineWidth: 1
+ },
+
+ getRGBForValue: function (value) {
+ return this._renderer._hotline.getRGBForValue(value);
+ },
+
+ /**
+ * Just like the Leaflet version, but with support for a z coordinate.
+ */
+ _projectLatlngs: function (latlngs, result, projectedBounds) {
+ var flat = latlngs[0] instanceof L.LatLng,
+ len = latlngs.length,
+ i, ring;
+
+ if (flat) {
+ ring = [];
+ for (i = 0; i < len; i++) {
+ ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
+ // Add the altitude of the latLng as the z coordinate to the point
+ ring[i].z = latlngs[i].alt;
+ projectedBounds.extend(ring[i]);
+ }
+ result.push(ring);
+ } else {
+ for (i = 0; i < len; i++) {
+ this._projectLatlngs(latlngs[i], result, projectedBounds);
+ }
+ }
+ },
+
+ /**
+ * Just like the Leaflet version, but uses `Util.clipSegment()`.
+ */
+ _clipPoints: function () {
+ if (this.options.noClip) {
+ this._parts = this._rings;
+ return;
+ }
+
+ this._parts = [];
+
+ var parts = this._parts,
+ bounds = this._renderer._bounds,
+ i, j, k, len, len2, segment, points;
+
+ for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
+ points = this._rings[i];
+
+ for (j = 0, len2 = points.length; j < len2 - 1; j++) {
+ segment = Util.clipSegment(points[j], points[j + 1], bounds, j, true);
+
+ if (!segment) { continue; }
+
+ parts[k] = parts[k] || [];
+ parts[k].push(segment[0]);
+
+ // if segment goes out of screen, or it's the last one, it's the end of the line part
+ if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
+ parts[k].push(segment[1]);
+ k++;
+ }
+ }
+ }
+ },
+
+ _clickTolerance: function () {
+ return this.options.weight / 2 + this.options.outlineWidth + (L.Browser.touch ? 10 : 0);
+ }
+ });
+
+ L.hotline = function (latlngs, options) {
+ return new L.Hotline(latlngs, options);
+ };
+
+
+ return L;
+})); \ No newline at end of file