From bc298698848bdcc81657a6b6707f4c3ee7d669c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:03:46 +0000 Subject: [PATCH 1/3] Initial plan From f6f775c2defbf7d5bf6302fcfdd4ff70726c626e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:18:02 +0000 Subject: [PATCH 2/3] Fix photo viewer in public shared Maps by passing shareToken to Viewer Co-authored-by: tacruc <402891+tacruc@users.noreply.github.com> --- .../Sidebar/PhotoSuggestionsSidebarTab.vue | 13 +++++++++- src/components/map/PhotoSuggestionsLayer.vue | 26 +++++++++++++++++-- src/components/map/PhotosLayer.vue | 26 +++++++++++++++++-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue b/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue index bb1336e72..6c4f94a44 100644 --- a/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue +++ b/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue @@ -294,7 +294,18 @@ export default { }, onListItemClick(photo) { if (OCA.Viewer && OCA.Viewer.open) { - OCA.Viewer.open({ path: photo.path, list: this.photoSuggestionsSelected }) + const token = getToken() + if (token) { + // For public shares, pass the share token to the Viewer + OCA.Viewer.open({ + path: photo.path, + list: this.photoSuggestionsSelected, + shareToken: token + }) + } else { + // For logged-in users, use the standard approach + OCA.Viewer.open({ path: photo.path, list: this.photoSuggestionsSelected }) + } } }, subtracks(t) { diff --git a/src/components/map/PhotoSuggestionsLayer.vue b/src/components/map/PhotoSuggestionsLayer.vue index da3729497..2f4457b49 100644 --- a/src/components/map/PhotoSuggestionsLayer.vue +++ b/src/components/map/PhotoSuggestionsLayer.vue @@ -317,7 +317,18 @@ export default { return a.dateTaken - b.dateTaken }) this.$emit('open-sidebar', photoList[0].path) - OCA.Viewer.open({ path: photoList[0].path, list: photoList }) + const token = getToken() + if (token) { + // For public shares, pass the share token to the Viewer + OCA.Viewer.open({ + path: photoList[0].path, + list: photoList, + shareToken: token + }) + } else { + // For logged-in users, use the standard approach + OCA.Viewer.open({ path: photoList[0].path, list: photoList }) + } this.map.closePopup() }, getClusterMarkerIcon(cluster) { @@ -382,7 +393,18 @@ export default { }, viewPhoto(photo) { if (OCA.Viewer && OCA.Viewer.open) { - OCA.Viewer.open({ path: photo.path, list: [photo] }) + const token = getToken() + if (token) { + // For public shares, pass the share token to the Viewer + OCA.Viewer.open({ + path: photo.path, + list: [photo], + shareToken: token + }) + } else { + // For logged-in users, use the standard approach + OCA.Viewer.open({ path: photo.path, list: [photo] }) + } this.map.closePopup() } }, diff --git a/src/components/map/PhotosLayer.vue b/src/components/map/PhotosLayer.vue index 1e56c319f..0c5154252 100644 --- a/src/components/map/PhotosLayer.vue +++ b/src/components/map/PhotosLayer.vue @@ -307,7 +307,18 @@ export default { return a.dateTaken - b.dateTaken }) // this.$emit('open-sidebar', photoList[0].path) - OCA.Viewer.open({ path: photoList[0].path, list: photoList }) + const token = getToken() + if (token) { + // For public shares, pass the share token to the Viewer + OCA.Viewer.open({ + path: photoList[0].path, + list: photoList, + shareToken: token + }) + } else { + // For logged-in users, use the standard approach + OCA.Viewer.open({ path: photoList[0].path, list: photoList }) + } this.map.closePopup() }, getClusterMarkerIcon(cluster) { @@ -364,7 +375,18 @@ export default { }, viewPhoto(photo) { if (OCA.Viewer && OCA.Viewer.open) { - OCA.Viewer.open({ path: photo.path, list: [photo] }) + const token = getToken() + if (token) { + // For public shares, pass the share token to the Viewer + OCA.Viewer.open({ + path: photo.path, + list: [photo], + shareToken: token + }) + } else { + // For logged-in users, use the standard approach + OCA.Viewer.open({ path: photo.path, list: [photo] }) + } this.map.closePopup() } }, From f8c37e6b3db7e302fbff4ccd1db76468e44de967 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:20:50 +0000 Subject: [PATCH 3/3] Complete fix for photo viewer in public shared Maps Co-authored-by: tacruc <402891+tacruc@users.noreply.github.com> --- src/components/Map.vue | 28 +- .../Sidebar/PhotoSuggestionsSidebarTab.vue | 6 +- .../leaflet-elevation/src/components/chart.js | 748 +++++++++--------- .../leaflet-elevation/src/components/d3.js | 612 +++++++------- .../src/components/marker.js | 123 +-- .../src/components/summary.js | 42 +- .../src/handlers/acceleration.js | 68 +- .../src/handlers/altitude.js | 74 +- .../leaflet-elevation/src/handlers/cadence.js | 100 +-- .../src/handlers/distance.js | 44 +- .../leaflet-elevation/src/handlers/heart.js | 98 +-- .../leaflet-elevation/src/handlers/labels.js | 48 +- .../src/handlers/lineargradient.js | 209 ++--- .../leaflet-elevation/src/handlers/pace.js | 72 +- .../leaflet-elevation/src/handlers/runner.js | 198 ++--- .../leaflet-elevation/src/handlers/slope.js | 84 +- .../leaflet-elevation/src/handlers/speed.js | 72 +- .../src/handlers/temperature.js | 66 +- .../leaflet-elevation/src/handlers/time.js | 98 +-- src/components/map/PhotoSuggestionsLayer.vue | 12 +- src/components/map/PhotosLayer.vue | 12 +- 21 files changed, 1412 insertions(+), 1402 deletions(-) diff --git a/src/components/Map.vue b/src/components/Map.vue index 4c461ff8a..a23f2081a 100644 --- a/src/components/Map.vue +++ b/src/components/Map.vue @@ -170,7 +170,7 @@ import { getCurrentUser } from '@nextcloud/auth' import axios from '@nextcloud/axios' import { showError, showSuccess } from '@nextcloud/dialogs' -import { LocateControl } from "leaflet.locatecontrol" +import { LocateControl } from 'leaflet.locatecontrol' import 'leaflet.locatecontrol/dist/L.Control.Locate.min.css' import L from 'leaflet' import 'mapbox-gl/dist/mapbox-gl' @@ -210,22 +210,22 @@ import PhotoSuggestionsLayer from './map/PhotoSuggestionsLayer.vue' // exclude dynamic imports from webpack-processing L.Control.Elevation.include({ - import: function(src, condition) { + import(src, condition) { if (Array.isArray(src)) { - return Promise.all(src.map(m => this.import(m))); + return Promise.all(src.map(m => this.import(m))) } - switch(src) { - case this.__D3: condition = typeof d3 !== 'object'; break; - case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break; - case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break; - case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break; - case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break; - case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break; - case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break; + switch (src) { + case this.__D3: condition = typeof d3 !== 'object'; break + case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break + case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break + case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break + case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break + case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break + case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break } - let url = (new URL(src, (src.startsWith('../') || src.startsWith('./')) ? this.options.srcFolder : undefined)).toString(); - return condition !== false ? import(/* webpackIgnore: true */ url) : Promise.resolve(); - } + const url = (new URL(src, (src.startsWith('../') || src.startsWith('./')) ? this.options.srcFolder : undefined)).toString() + return condition !== false ? import(/* webpackIgnore: true */ url) : Promise.resolve() + }, }) export default { diff --git a/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue b/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue index 6c4f94a44..51d424f0b 100644 --- a/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue +++ b/src/components/Sidebar/PhotoSuggestionsSidebarTab.vue @@ -297,10 +297,10 @@ export default { const token = getToken() if (token) { // For public shares, pass the share token to the Viewer - OCA.Viewer.open({ - path: photo.path, + OCA.Viewer.open({ + path: photo.path, list: this.photoSuggestionsSelected, - shareToken: token + shareToken: token, }) } else { // For logged-in users, use the standard approach diff --git a/src/components/leaflet-elevation/src/components/chart.js b/src/components/leaflet-elevation/src/components/chart.js index 2897f8b96..8c6ad5875 100644 --- a/src/components/leaflet-elevation/src/components/chart.js +++ b/src/components/leaflet-elevation/src/components/chart.js @@ -1,179 +1,180 @@ -import * as D3 from './d3.js'; +import * as D3 from './d3.js' -const _ = L.Control.Elevation.Utils; +const _ = L.Control.Elevation.Utils export var Chart = L.Control.Elevation.Chart = L.Class.extend({ includes: L.Evented ? L.Evented.prototype : L.Mixin.Events, initialize(opts, control) { - this.options = opts; - this.control = control; + this.options = opts + this.control = control - this._data = control._data || []; + this._data = control._data || [] // cache registered components - this._props = { - scales : {}, - paths : {}, - areas : {}, - grids : {}, - axes : {}, - legendItems : {}, + this._props = { + scales: {}, + paths: {}, + areas: {}, + grids: {}, + axes: {}, + legendItems: {}, tooltipItems: {}, - }; + } + + this._scales = {} + this._domains = {} + this._ranges = {} + this._paths = {} - this._scales = {}; - this._domains = {}; - this._ranges = {}; - this._paths = {}; + this._brushEnabled = opts.dragging + this._zoomEnabled = opts.zooming - this._brushEnabled = opts.dragging; - this._zoomEnabled = opts.zooming; + opts.xTicks = this._xTicks() + opts.yTicks = this._yTicks() - opts.xTicks = this._xTicks(); - opts.yTicks = this._yTicks(); - - let chart = this._chart = D3.Chart(opts); + const chart = this._chart = D3.Chart(opts) // SVG Container - this._container = chart.svg; + this._container = chart.svg // Panes - this._grid = chart.pane('grid'); - this._area = chart.pane('area'); - this._point = chart.pane('point'); - this._axis = chart.pane('axis'); - this._legend = chart.pane('legend'); - this._tooltip = chart.pane('tooltip'); - this._ruler = chart.pane('ruler'); + this._grid = chart.pane('grid') + this._area = chart.pane('area') + this._point = chart.pane('point') + this._axis = chart.pane('axis') + this._legend = chart.pane('legend') + this._tooltip = chart.pane('tooltip') + this._ruler = chart.pane('ruler') // Scales - this._initScale(); + this._initScale() // Helpers - this._mask = chart.get('mask'); - this._context = chart.get('context'); - this._brush = chart.get('brush'); + this._mask = chart.get('mask') + this._context = chart.get('context') + this._brush = chart.get('brush') - this._zoom = d3.zoom(); - this._drag = d3.drag(); + this._zoom = d3.zoom() + this._drag = d3.drag() // Interactions - this._initInteractions(); + this._initInteractions() // svg.on('resize', (e)=>console.log(e.detail)); // Handle multi-track segments - this._maskGaps = []; - control.on('eletrack_added', ({index}) => { - this._maskGaps.push(index); - control.once('elepoint_added', ({index}) => this._maskGaps.push(index)); - }); + this._maskGaps = [] + control.on('eletrack_added', ({ index }) => { + this._maskGaps.push(index) + control.once('elepoint_added', ({ index }) => this._maskGaps.push(index)) + }) }, update(props) { if (props) { - if (props.data) this._data = props.data; - if (props.options) this.options = props.options; + if (props.data) this._data = props.data + if (props.options) this.options = props.options } - this.options.xTicks = this._xTicks(); - this.options.yTicks = this._yTicks(); + this.options.xTicks = this._xTicks() + this.options.yTicks = this._yTicks() - this._updateScale(); - this._updateAxis(); - this._updateMargins(); - this._updateLegend(); - this._updateClipper(); - this._updateArea(); + this._updateScale() + this._updateAxis() + this._updateMargins() + this._updateLegend() + this._updateClipper() + this._updateArea() - return this; + return this }, render() { - return container => container.append(() => this._container.node()); + return container => container.append(() => this._container.node()) }, clear() { - this._resetDrag(); + this._resetDrag() this._hideDiagramIndicator() - this._area.selectAll('path').attr("d", "M0 0"); - this._context.clearRect(0, 0, this._width(), this._height()); - this._mask.selectAll(".gap").remove(); - this._maskGaps = []; + this._area.selectAll('path').attr('d', 'M0 0') + this._context.clearRect(0, 0, this._width(), this._height()) + this._mask.selectAll('.gap').remove() + this._maskGaps = [] // if (this._path) { - // this._x.domain([0, 1]); - // this._y.domain([0, 1]); + // this._x.domain([0, 1]); + // this._y.domain([0, 1]); // } }, _drawPath(name) { - let path = this._paths[name]; - let area = this._props.areas[name]; + const path = this._paths[name] + const area = this._props.areas[name] - path.datum(this._data).attr("d", + path.datum(this._data).attr('d', D3.Area( L.extend({}, area, { - width : this._width(), - height : this._height(), - scaleX : this._scales[area.scaleX], - scaleY : this._scales[area.scaleY] - }) - ) - ); + width: this._width(), + height: this._height(), + scaleX: this._scales[area.scaleX], + scaleY: this._scales[area.scaleY], + }), + ), + ) if (!path.classed('leaflet-hidden')) { this.options.preferCanvas ? _.drawCanvas(this._context, path) - : _.append(this._area.node(), path.node()); + : _.append(this._area.node(), path.node()) } }, _hasActiveLayers() { - const paths = this._paths; - for (var i in paths) { + const paths = this._paths + for (const i in paths) { if (!paths[i].classed('leaflet-hidden')) { - return true; + return true } } - return false; + return false }, /** * Initialize "d3-brush". + * @param e */ _initBrush(e) { - const brush = ({selection}) => { + const brush = ({ selection }) => { if (selection && this._data.length) { - let start = this._findIndexForXCoord(selection[0]); - let end = this._findIndexForXCoord(selection[1]); - this.fire('dragged', { dragstart: this._data[start], dragend: this._data[end] }); + const start = this._findIndexForXCoord(selection[0]) + const end = this._findIndexForXCoord(selection[1]) + this.fire('dragged', { dragstart: this._data[start], dragend: this._data[end] }) } } - const focus = (e) => { - if (this._data.length && (e.type != 'brush' /*|| e.sourceEvent*/)) { - let rect = this._chart.panes.brush.select('.overlay').node(); - let coords = d3.pointers(e, rect)[0]; - let xCoord = coords[0]; - let item = this._data[this._findIndexForXCoord(xCoord)]; + const focus = (e) => { + if (this._data.length && (e.type != 'brush' /* || e.sourceEvent */)) { + const rect = this._chart.panes.brush.select('.overlay').node() + const coords = d3.pointers(e, rect)[0] + const xCoord = coords[0] + const item = this._data[this._findIndexForXCoord(xCoord)] - this.fire("mouse_move", { item: item, xCoord: xCoord }); + this.fire('mouse_move', { item, xCoord }) } - }; + } this._brush - .filter(({shiftKey, button}) => !shiftKey && !button && this._brushEnabled) - .on("end.update", brush) - .on("brush.update", focus); + .filter(({ shiftKey, button }) => !shiftKey && !button && this._brushEnabled) + .on('end.update', brush) + .on('brush.update', focus) this._chart.panes.brush - .on("mouseenter.focus touchstart.focus", this.fire.bind(this, "mouse_enter")) - .on("mouseout.focus touchend.focus", this.fire.bind(this, "mouse_out") ) - .on("mousemove.focus touchmove.focus", focus ); + .on('mouseenter.focus touchstart.focus', this.fire.bind(this, 'mouse_enter')) + .on('mouseout.focus touchend.focus', this.fire.bind(this, 'mouse_out')) + .on('mousemove.focus touchmove.focus', focus) }, @@ -181,134 +182,134 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({ * Initialize "d3-zoom" */ _initClipper() { - let svg = this._container; - let margin = this.options.margins; + const svg = this._container + const margin = this.options.margins - const zoom = this._zoom; + const zoom = this._zoom - const onStart = ({transform, sourceEvent}) => { - if (sourceEvent && sourceEvent.type == "mousedown") svg.style('cursor', 'grabbing'); + const onStart = ({ transform, sourceEvent }) => { + if (sourceEvent && sourceEvent.type == 'mousedown') svg.style('cursor', 'grabbing') if (transform.k == 1 && transform.x == 0) { - this._container.classed('zoomed', true); + this._container.classed('zoomed', true) // Apply d3-zoom and bind if (this._mask) { - this._point.attr('mask', 'url(#' + this._mask.attr('id') + ')'); + this._point.attr('mask', 'url(#' + this._mask.attr('id') + ')') } } - this.zooming = true; - }; + this.zooming = true + } - const onEnd = ({transform}) => { - if (transform.k ==1 && transform.x == 0) { - this._container.classed('zoomed', false); + const onEnd = ({ transform }) => { + if (transform.k == 1 && transform.x == 0) { + this._container.classed('zoomed', false) // Reset d3-zoom and remove if (this._mask) { - this._point.attr('mask', null); + this._point.attr('mask', null) } } - this.zooming = false; - svg.style('cursor', ''); - }; + this.zooming = false + svg.style('cursor', '') + } - const onZoom = ({transform, sourceEvent}) => { + const onZoom = ({ transform, sourceEvent }) => { // TODO: find a faster way to redraw the chart. - this.zooming = false; - this._updateScale(); // hacky way for restoring x scale when zooming out - this.zooming = true; - this._scales.distance = this._x = transform.rescaleX(this._x); // calculate x scale at zoom level - if (this._scales.time) this._scales.time = transform.rescaleX(this._scales.time); // calculate x scale at zoom level - this._resetDrag(); - if (sourceEvent && sourceEvent.type == "mousemove") { - this._hideDiagramIndicator(); + this.zooming = false + this._updateScale() // hacky way for restoring x scale when zooming out + this.zooming = true + this._scales.distance = this._x = transform.rescaleX(this._x) // calculate x scale at zoom level + if (this._scales.time) this._scales.time = transform.rescaleX(this._scales.time) // calculate x scale at zoom level + this._resetDrag() + if (sourceEvent && sourceEvent.type == 'mousemove') { + this._hideDiagramIndicator() } - this.fire('zoom'); - }; + this.fire('zoom') + } zoom .scaleExtent([1, 10]) .extent([ [margin.left, 0], - [this._width() - margin.right, this._height()] + [this._width() - margin.right, this._height()], ]) .translateExtent([ [margin.left, -Infinity], - [this._width() - margin.right, Infinity] + [this._width() - margin.right, Infinity], ]) - .filter(({shiftKey, buttons}) => (shiftKey || buttons == 4) && this._zoomEnabled) - .on("start", onStart) - .on("end", onEnd) - .on("zoom", onZoom); + .filter(({ shiftKey, buttons }) => (shiftKey || buttons == 4) && this._zoomEnabled) + .on('start', onStart) + .on('end', onEnd) + .on('zoom', onZoom) - svg.call(zoom); // add zoom functionality to "svg" group + svg.call(zoom) // add zoom functionality to "svg" group // d3.select("body").on("keydown.grabzoom keyup.grabzoom", (e) => svg.style('cursor', e.shiftKey ? 'move' : '')); }, _initInteractions() { - this._initBrush(); - this._initRuler(); - this._initClipper(); - this._initLegend(); + this._initBrush() + this._initRuler() + this._initClipper() + this._initLegend() }, /** * Toggle chart data on legend click */ _initLegend() { - this._container.on('legend_clicked', ({detail}) => { - let { path, legend, name, enabled } = detail; + this._container.on('legend_clicked', ({ detail }) => { + const { path, legend, name, enabled } = detail if (path) { - let label = _.select('text', legend); - let rect = _.select('rect', legend); - _.toggleStyle(label, 'text-decoration-line', 'line-through', enabled); - _.toggleStyle(rect, 'fill-opacity', '0', enabled); - _.toggleClass(path, 'leaflet-hidden', enabled); - this._updateArea(); - this.fire("elepath_toggle", { path, name, legend, enabled }) + const label = _.select('text', legend) + const rect = _.select('rect', legend) + _.toggleStyle(label, 'text-decoration-line', 'line-through', enabled) + _.toggleStyle(rect, 'fill-opacity', '0', enabled) + _.toggleClass(path, 'leaflet-hidden', enabled) + this._updateArea() + this.fire('elepath_toggle', { path, name, legend, enabled }) } - }); + }) }, /** * Initialize "ruler". */ _initRuler() { - if (!this.options.ruler) return; + if (!this.options.ruler) return // const yMax = this._height(); - const formatNum = d3.format(".0f"); - const drag = this._drag; - - const label = (e, d) => { - let yMax = this._height(); - let y = this._ruler.data()[0].y; - if (y >= yMax || y <= 0) this._ruler.select(".horizontal-drag-label").text(''); - this._hideDiagramIndicator(); - }; + const formatNum = d3.format('.0f') + const drag = this._drag + + const label = (e, d) => { + const yMax = this._height() + const y = this._ruler.data()[0].y + if (y >= yMax || y <= 0) this._ruler.select('.horizontal-drag-label').text('') + this._hideDiagramIndicator() + } - const position = (e, d) => { - let yCoord = d3.pointers(e, this._area.node())[0][1]; - let yMax = this._height(); - let y = _.clamp(yCoord, [0, yMax]); + const position = (e, d) => { + const yCoord = d3.pointers(e, this._area.node())[0][1] + const yMax = this._height() + const y = _.clamp(yCoord, [0, yMax]) this._ruler - .data([L.extend(this._ruler.data()[0], { y: y })]) - .attr("transform", d => "translate(" + d.x + "," + d.y + ")") - .classed('active', y < yMax); + .data([L.extend(this._ruler.data()[0], { y })]) + .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')') + .classed('active', y < yMax) this._container - .select(".horizontal-drag-label") - .text(formatNum(this._y.invert(y)) + " " + (this.options.imperial ? 'ft' : 'm')); + .select('.horizontal-drag-label') + .text(formatNum(this._y.invert(y)) + ' ' + (this.options.imperial ? 'ft' : 'm')) - this.fire('ruler_filter', { coords: yCoord < yMax && yCoord > 0 ? this._findCoordsForY(yCoord) : [] }); + this.fire('ruler_filter', { coords: yCoord < yMax && yCoord > 0 ? this._findCoordsForY(yCoord) : [] }) } drag - .on("start end", label) - .on("drag", position); + .on('start end', label) + .on('drag', position) - this._ruler.call(drag); + this._ruler.call(drag) }, @@ -316,376 +317,377 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({ * Initialize x and y scales */ _initScale() { - let opts = this.options; + const opts = this.options this._registerAxisScale({ - axis : 'x', + axis: 'x', position: 'bottom', - attr : opts.xAttr, - min : opts.xAxisMin, - max : opts.xAxisMax, - name : 'distance' - }); + attr: opts.xAttr, + min: opts.xAxisMin, + max: opts.xAxisMax, + name: 'distance', + }) this._registerAxisScale({ - axis : 'y', - position : 'left', - attr : opts.yAttr, - min : opts.yAxisMin, - max : opts.yAxisMax, - name : 'altitude' - }); + axis: 'y', + position: 'left', + attr: opts.yAttr, + min: opts.yAxisMin, + max: opts.yAxisMax, + name: 'altitude', + }) - this._x = this._scales.distance; - this._y = this._scales.altitude; + this._x = this._scales.distance + this._y = this._scales.altitude }, _registerAreaPath(props) { - if (props.scale == 'y') props.scale = this._y; - else if (props.scale == 'x') props.scale = this._x; + if (props.scale == 'y') props.scale = this._y + else if (props.scale == 'x') props.scale = this._x - let opts = this.options; + const opts = this.options - if (!props.xAttr) props.xAttr = opts.xAttr; - if (!props.yAttr) props.yAttr = opts.yAttr; - if (typeof props.preferCanvas === "undefined") props.preferCanvas = opts.preferCanvas; + if (!props.xAttr) props.xAttr = opts.xAttr + if (!props.yAttr) props.yAttr = opts.yAttr + if (typeof props.preferCanvas === 'undefined') props.preferCanvas = opts.preferCanvas // Save paths in memory for latter usage - this._paths[props.name] = D3.Path(props); - this._props.areas[props.name] = props; + this._paths[props.name] = D3.Path(props) + this._props.areas[props.name] = props if (opts.legend) { this._props.legendItems[props.name] = { - name : props.name, - label : props.label, - color : props.color, + name: props.name, + label: props.label, + color: props.color, className: props.className, - path : this._paths[props.name] - }; + path: this._paths[props.name], + } } }, _registerAxisGrid(props) { - if (props.scale == 'y') props.scale = this._y; - else if (props.scale == 'x') props.scale = this._x; + if (props.scale == 'y') props.scale = this._y + else if (props.scale == 'x') props.scale = this._x - this._props.grids[props.name || props.axis] = props; + this._props.grids[props.name || props.axis] = props }, _registerAxisScale(props) { - if (props.scale == 'y') props.scale = this._y; - else if (props.scale == 'x') props.scale = this._x; + if (props.scale == 'y') props.scale = this._y + else if (props.scale == 'x') props.scale = this._x - let opts = this.options; - let scale = props.scale; + const opts = this.options + let scale = props.scale if (typeof this._scales[props.name] === 'function') { - props.scale = this._scales[props.name]; // retrieve cached scale + props.scale = this._scales[props.name] // retrieve cached scale } else if (typeof scale !== 'function') { - scale = L.extend({ - data : this._data, - forceBounds: opts.forceAxisBounds - }, scale); + scale = L.extend({ + data: this._data, + forceBounds: opts.forceAxisBounds, + }, scale) - scale.attr = scale.attr || props.name; + scale.attr = scale.attr || props.name - let domain = this._domains[props.name] = D3.Domain(props); - let range = this._ranges[props.name] = D3.Range(props); - scale.range = scale.range || range(this._width(),this._height()); - scale.domain = scale.domain || domain(this._data); + const domain = this._domains[props.name] = D3.Domain(props) + const range = this._ranges[props.name] = D3.Range(props) + scale.range = scale.range || range(this._width(), this._height()) + scale.domain = scale.domain || domain(this._data) - this._props.scales[props.name] = scale; + this._props.scales[props.name] = scale - props.scale = this._scales[props.name] = D3.Scale(scale); + props.scale = this._scales[props.name] = D3.Scale(scale) } if (!props.ticks) { - if (props.axis == 'x') props.ticks = this._xTicks.bind(this); - else if (props.axis == 'y') props.ticks = this._yTicks.bind(this); + if (props.axis == 'x') props.ticks = this._xTicks.bind(this) + else if (props.axis == 'y') props.ticks = this._yTicks.bind(this) } - this._props.axes[props.name] = props; + this._props.axes[props.name] = props - if (props.name == this.options.yScale) this._y = scale; - if (props.name == this.options.xScale) this._x = scale; + if (props.name == this.options.yScale) this._y = scale + if (props.name == this.options.xScale) this._x = scale - return scale; + return scale }, /** * Add a point of interest over the chart + * @param point */ _registerCheckPoint(point) { - if (!this._data.length) return; + if (!this._data.length) return - const {xAttr, yAttr} = this.options; - let item, x, y; + const { xAttr, yAttr } = this.options + let item, x, y if (point.latlng) { - item = this._data[this._findIndexForLatLng(point.latlng)]; - x = this._x(item[xAttr]); - y = this._y(item[yAttr]); + item = this._data[this._findIndexForLatLng(point.latlng)] + x = this._x(item[xAttr]) + y = this._y(item[yAttr]) } else if (!isNaN(point[xAttr])) { - x = this._x(point[xAttr]); + x = this._x(point[xAttr]) item = this._data[this._findIndexForXCoord(x)] - y = this._y(item[yAttr]); - } + y = this._y(item[yAttr]) + } this._point.call(D3.CheckPoint({ - point: point, + point, width: this._width(), height: this._height(), - x: x, - y: y, - })); + x, + y, + })) }, _registerTooltip(props) { - props.order = props.order ?? 1000; - this._props.tooltipItems[props.name] = props; + props.order = props.order ?? 1000 + this._props.tooltipItems[props.name] = props }, _updateArea() { // Reset and update chart profiles - this._context.clearRect(0, 0, this._width(), this._height()); - _.each(this._paths, (path, i) => !path.classed('leaflet-hidden') && this._drawPath(i)); + this._context.clearRect(0, 0, this._width(), this._height()) + _.each(this._paths, (path, i) => !path.classed('leaflet-hidden') && this._drawPath(i)) }, _updateAxis() { - let opts = this.options; + const opts = this.options const gridOpts = { - width : this._width(), - height : this._height(), - tickFormat: "", - }; + width: this._width(), + height: this._height(), + tickFormat: '', + } - const axesOpts = { - width : this._width(), - height : this._height(), - }; + const axesOpts = { + width: this._width(), + height: this._height(), + } // Reset grids - this._grid.selectAll('g').remove(); + this._grid.selectAll('g').remove() _.each(this._props.grids, (grid, i) => { if (opts[i] !== false && opts[i] !== 'summary') { this._grid.call(D3.Grid(L.extend({}, gridOpts, grid))) } - }); + }) // Rest axis - this._axis.selectAll('g').remove(); - _.each(this._props.axes, (axis, i) => { + this._axis.selectAll('g').remove() + _.each(this._props.axes, (axis, i) => { if (opts[i] !== false && opts[i] !== 'summary') { - this._axis.call(D3.Axis(L.extend({}, axesOpts, axis))); + this._axis.call(D3.Axis(L.extend({}, axesOpts, axis))) } - }); + }) // Adjust axis scale positions this._axis .selectAll('.y.axis.right') .each((d, i, n) => { - let axis = d3.select(n[i]); - let transform = axis.attr('transform'); - let translate = transform.substring(transform.indexOf("(") + 1, transform.indexOf(")")).split(","); + const axis = d3.select(n[i]) + const transform = axis.attr('transform') + const translate = transform.substring(transform.indexOf('(') + 1, transform.indexOf(')')).split(',') axis.attr('transform', 'translate(' + (+translate[0] + (i * 40)) + ',' + translate[1] + ')') if (i > 0) { - axis.select(':scope > path') .attr('opacity', 0.25); - axis.selectAll(':scope > .tick line').attr('opacity', 0.75); + axis.select(':scope > path').attr('opacity', 0.25) + axis.selectAll(':scope > .tick line').attr('opacity', 0.75) } - }); + }) }, _updateClipper() { - const { xAttr, margins } = this.options; - const data = this._data; + const { xAttr, margins } = this.options + const data = this._data this._zoom .scaleExtent([1, 10]) .extent([ [margins.left, 0], - [this._width() - margins.right, this._height()] + [this._width() - margins.right, this._height()], ]) .translateExtent([ [margins.left, -Infinity], - [this._width() - margins.right, Infinity] - ]); + [this._width() - margins.right, Infinity], + ]) // Apply svg mask on multi-track segments - this._mask.selectAll(".gap").remove() + this._mask.selectAll('.gap').remove() this._maskGaps.forEach((d, i) => { - if (i >= this._maskGaps.length - 2) return; - let x1 = this._x(data[this._findIndexForLatLng(data[this._maskGaps[i]].latlng)][xAttr]); - let x2 = this._x(data[this._findIndexForLatLng(data[this._maskGaps[i + 1]].latlng)][xAttr]); + if (i >= this._maskGaps.length - 2) return + const x1 = this._x(data[this._findIndexForLatLng(data[this._maskGaps[i]].latlng)][xAttr]) + const x2 = this._x(data[this._findIndexForLatLng(data[this._maskGaps[i + 1]].latlng)][xAttr]) this._mask - .append("rect") - .attr("x", x1) - .attr("y", 0) - .attr("width", x2 - x1 ) - .attr("height", this._height()) + .append('rect') + .attr('x', x1) + .attr('y', 0) + .attr('width', x2 - x1) + .attr('height', this._height()) .attr('class', 'gap') .attr('fill-opacity', '0.8') - .attr("fill", 'black'); // hide = black (mask) - }); + .attr('fill', 'black') // hide = black (mask) + }) }, - _updateLegend: function () { - let xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length; - + _updateLegend() { + const xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length + // Get legend items - let items = Object.keys(this._paths); + const items = Object.keys(this._paths) // Calculate legend item positions - let n = items.length; - let v = Array(Math.floor(n / 2)).fill(null).map((d, i) => (i + 1) * 2 - (1 - Math.sign(n % 2))); - let rev = v.slice().reverse().map((d) => -(d)); + const n = items.length + let v = Array(Math.floor(n / 2)).fill(null).map((d, i) => (i + 1) * 2 - (1 - Math.sign(n % 2))) + const rev = v.slice().reverse().map((d) => -(d)) // push a fake element to handle center alignment (odd numbers) if (n % 2 !== 0) { - rev.push(0); + rev.push(0) } - v = rev.concat(v); + v = rev.concat(v) // Reset legend items - this._legend.selectAll('g').remove(); + this._legend.selectAll('g').remove() // Render legend items _.each(this._props.legendItems, (legend) => { - this._legend.append("g").call( + this._legend.append('g').call( D3.LegendItem(L.extend({ - width : this._width(), - height : this._height(), + width: this._width(), + height: this._height(), margins: this.options.margins, - }, legend)) - ); - }); + }, legend)), + ) + }) // Render legend item switcher if (n > 1) { - this._legend.append("g").call( + this._legend.append('g').call( D3.LegendSmall({ - width : this._width(), - height : this._height(), - items : items, + width: this._width(), + height: this._height(), + items, onClick: (selected) => { - _.each(items, name => this._togglePath(name, selected == name, true)); - this._updateArea(); - } - }) - ); + _.each(items, name => this._togglePath(name, selected == name, true)) + this._updateArea() + }, + }), + ) } - + _.each(items, (name, i) => { // Adjust legend item positions - this._legend.select('[data-name=' + name + ']').attr("transform", "translate(" + (v[i] * 55) + ", " + (xAxesB * 2) + ")"); + this._legend.select('[data-name=' + name + ']').attr('transform', 'translate(' + (v[i] * 55) + ', ' + (xAxesB * 2) + ')') // Set initial state (disabled controls) - this._togglePath(name, !(name in this.options && this.options[name] == 'disabled'), true); - }); + this._togglePath(name, !(name in this.options && this.options[name] == 'disabled'), true) + }) }, _updateMargins() { // Get chart margins - let xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length; - let xAxesL = this._axis.selectAll('.y.axis.left').nodes().length; - let xAxesR = this._axis.selectAll('.y.axis.right').nodes().length; - let marginB = 60 + (xAxesB * 2); - let marginL = 10 + (xAxesL * 30); - let marginR = 40 + (xAxesR * 30); + const xAxesB = this._axis.selectAll('.x.axis.bottom').nodes().length + const xAxesL = this._axis.selectAll('.y.axis.left').nodes().length + const xAxesR = this._axis.selectAll('.y.axis.right').nodes().length + const marginB = 60 + (xAxesB * 2) + const marginL = 10 + (xAxesL * 30) + const marginR = 40 + (xAxesR * 30) let marginsUpdated = false // Adjust right margin if (xAxesR && this.options.margins.right < marginR) { - this.options.margins.right = marginR; - marginsUpdated = true; + this.options.margins.right = marginR + marginsUpdated = true } // Adjust left margin if (xAxesL && this.options.margins.left < marginL) { - this.options.margins.left = marginL; - marginsUpdated = true; + this.options.margins.left = marginL + marginsUpdated = true } // Adjust bottom margin if (xAxesB && this.options.margins.bottom < marginB) { - this.options.margins.bottom = marginB; - marginsUpdated = true; + this.options.margins.bottom = marginB + marginsUpdated = true } if (marginsUpdated) { - this.fire('margins_updated'); + this.fire('margins_updated') } }, _updateScale() { - if (this.zooming) return { x: this._x, y: this._y }; + if (this.zooming) return { x: this._x, y: this._y } - for (let i in this._scales) { + for (const i in this._scales) { this._scales[i] .domain(this._domains[i](this._data)) .range(this._ranges[i](this._width(), this._height())) } - return { x: this._x, y: this._y }; + return { x: this._x, y: this._y } }, /** * Calculates chart width. */ _width() { - if (this._chart) this._chart._width; - const { width, margins } = this.options; - return width - margins.left - margins.right; + if (this._chart) this._chart._width + const { width, margins } = this.options + return width - margins.left - margins.right }, /** * Calculates chart height. */ _height() { - if (this._chart) return this._chart._height; - const { height, margins } = this.options; - return height - margins.top - margins.bottom; + if (this._chart) return this._chart._height + const { height, margins } = this.options + return height - margins.top - margins.bottom }, /* * Finds data entries above a given y-elevation value and returns geo-coordinates */ _findCoordsForY(y) { - let data = this._data; - let z = this._y.invert(y); + const data = this._data + const z = this._y.invert(y) // save indexes of elevation values above the horizontal line const list = data.reduce((array, item, index) => { - if (item[this.options.yAttr] >= z) array.push(index); - return array; - }, []); + if (item[this.options.yAttr] >= z) array.push(index) + return array + }, []) - let start = 0; - let next; + let start = 0 + let next // split index list into blocks of coordinates const coords = list.reduce((array, _, curr) => { - next = curr + 1; + next = curr + 1 if (list[next] !== list[curr] + 1 || next === list.length) { array.push( list - .slice(start, next) - .map(i => data[i].latlng) - ); - start = next; + .slice(start, next) + .map(i => data[i].latlng), + ) + start = next } - return array; - }, []); + return array + }, []) - return coords; + return coords }, /* @@ -694,90 +696,92 @@ export var Chart = L.Control.Elevation.Chart = L.Class.extend({ _findIndexForXCoord(x) { return d3 .bisector(d => d[this.options.xAttr]) - .left(this._data || [0, 1], this._x.invert(x)); + .left(this._data || [0, 1], this._x.invert(x)) }, /* * Finds a data entry for a given latlng of the map */ _findIndexForLatLng(latlng) { - let result = null; - let d = Infinity; + let result = null + let d = Infinity this._data.forEach((item, index) => { - let dist = latlng.distanceTo(item.latlng); + const dist = latlng.distanceTo(item.latlng) if (dist < d) { - d = dist; - result = index; + d = dist + result = index } - }); - return result; + }) + return result }, /* * Removes the drag rectangle and zoms back to the total extent of the data. */ _resetDrag() { - if (this._chart.panes.brush.select(".selection").attr('width')) { - this._chart.panes.brush.call(this._brush.clear); - this._hideDiagramIndicator(); - this.fire('reset_drag'); + if (this._chart.panes.brush.select('.selection').attr('width')) { + this._chart.panes.brush.call(this._brush.clear) + this._hideDiagramIndicator() + this.fire('reset_drag') } }, _resetZoom() { if (this._zoom) { - this._zoom.transform(this._chart.svg, d3.zoomIdentity); + this._zoom.transform(this._chart.svg, d3.zoomIdentity) } }, /** * Display distance and altitude level ("focus-rect"). + * @param item + * @param xCoordinate */ _showDiagramIndicator(item, xCoordinate) { this._tooltip - .attr("display", null) + .attr('display', null) .call(D3.Tooltip({ xCoord: xCoordinate, yCoord: this._y(item[this.options.yAttr]), height: this._height(), - width : this._width(), + width: this._width(), labels: this._props.tooltipItems, - item: item - })); + item, + })) }, _togglePath(name, enabled = true, lazy = false) { - let path = this._paths[name]; - let legend = this._container.select('.legend [data-name=' + name + ']'); - path.classed('leaflet-hidden', !enabled); - legend.select('text').style('text-decoration-line', enabled ? '' : 'line-through'); - legend.select('rect').style('fill-opacity', enabled ? '': '0'); + const path = this._paths[name] + const legend = this._container.select('.legend [data-name=' + name + ']') + path.classed('leaflet-hidden', !enabled) + legend.select('text').style('text-decoration-line', enabled ? '' : 'line-through') + legend.select('rect').style('fill-opacity', enabled ? '' : '0') // Apply d3-zoom (bind mask) if (this._mask) { - path.attr('mask', 'url(#' + this._mask.attr('id') + ')'); + path.attr('mask', 'url(#' + this._mask.attr('id') + ')') } if (!lazy) { - this._updateArea(); + this._updateArea() } }, _hideDiagramIndicator() { - this._tooltip.attr("display", 'none'); + this._tooltip.attr('display', 'none') }, /** * Calculate chart xTicks */ _xTicks() { - if (this.__xTicks) this.__xTicks = this.options.xTicks; - return this.__xTicks || Math.round(this._width() / 75); -}, + if (this.__xTicks) this.__xTicks = this.options.xTicks + return this.__xTicks || Math.round(this._width() / 75) + }, /** * Calculate chart yTicks */ _yTicks() { - if (this.__yTicks) this.__yTicks = this.options.yTicks; - return this.__yTicks || Math.round(this._height() / 30); - } -}); + if (this.__yTicks) this.__yTicks = this.options.yTicks + return this.__yTicks || Math.round(this._height() / 30) + }, +}) diff --git a/src/components/leaflet-elevation/src/components/d3.js b/src/components/leaflet-elevation/src/components/d3.js index 2c35d5704..db08e036c 100644 --- a/src/components/leaflet-elevation/src/components/d3.js +++ b/src/components/leaflet-elevation/src/components/d3.js @@ -5,14 +5,14 @@ export const Area = ({ yAttr, scaleX, scaleY, - interpolation = "curveLinear" + interpolation = 'curveLinear', }) => { return d3.area() .curve(typeof interpolation === 'string' ? d3[interpolation] : interpolation) .x(d => (d.xDiagCoord = scaleX(d[xAttr]))) .y0(height) - .y1(d => scaleY(d[yAttr])); -}; + .y1(d => scaleY(d[yAttr])) +} export const Path = ({ name, @@ -20,25 +20,25 @@ export const Path = ({ strokeColor, strokeOpacity, fillOpacity, - className = '' + className = '', }) => { - let path = d3.create('svg:path') + const path = d3.create('svg:path') - if (name) path.classed(name + ' ' + className, true); + if (name) path.classed(name + ' ' + className, true) - path.style("pointer-events", "none"); + path.style('pointer-events', 'none') path - .attr("fill", color || '#3366CC') - .attr("stroke", strokeColor || '#000') - .attr("stroke-opacity", strokeOpacity || '1') - .attr("fill-opacity", fillOpacity || '0.8'); + .attr('fill', color || '#3366CC') + .attr('stroke', strokeColor || '#000') + .attr('stroke-opacity', strokeOpacity || '1') + .attr('fill-opacity', fillOpacity || '0.8') - return path; -}; + return path +} export const Axis = ({ - type = "axis", + type = 'axis', tickSize = 6, tickPadding = 3, position, @@ -51,51 +51,51 @@ export const Axis = ({ label, labelX, labelY, - name = "", + name = '', onAxisMount, }) => { return g => { - let [w, h] = [0, 0]; - if (position == "bottom") h = height; - if (position == "right") w = width; - - if (axis == "x" && type == "grid") { - tickSize = -height; - } else if (axis == "y" && type == "grid") { - tickSize = -width; + let [w, h] = [0, 0] + if (position == 'bottom') h = height + if (position == 'right') w = width + + if (axis == 'x' && type == 'grid') { + tickSize = -height + } else if (axis == 'y' && type == 'grid') { + tickSize = -width } - let axisScale = d3["axis" + position.replace(/\b\w/g, l => l.toUpperCase())]() + const axisScale = d3['axis' + position.replace(/\b\w/g, l => l.toUpperCase())]() .scale(scale) .ticks(typeof ticks === 'function' ? ticks() : ticks) .tickPadding(tickPadding) .tickSize(tickSize) - .tickFormat(tickFormat); + .tickFormat(tickFormat) - let axisGroup = g.append("g") - .attr("class", [axis, type, position, name].join(" ")) - .attr("transform", "translate(" + w + "," + h + ")") - .call(axisScale); + const axisGroup = g.append('g') + .attr('class', [axis, type, position, name].join(' ')) + .attr('transform', 'translate(' + w + ',' + h + ')') + .call(axisScale) if (label) { - axisGroup.append("svg:text") - .attr("x", labelX) - .attr("y", labelY) - .text(label); + axisGroup.append('svg:text') + .attr('x', labelX) + .attr('y', labelY) + .text(label) } if (onAxisMount) { - axisGroup.call(onAxisMount); + axisGroup.call(onAxisMount) } - return axisGroup; - }; -}; + return axisGroup + } +} export const Grid = (props) => { - props.type = "grid"; - return Axis(props); -}; + props.type = 'grid' + return Axis(props) +} export const PositionMarker = ({ theme, @@ -107,62 +107,62 @@ export const PositionMarker = ({ }) => { return g => { - g.attr("class", "height-focus-group"); + g.attr('class', 'height-focus-group') - let line = g.select('.height-focus.line'); - let circle = g.select('.height-focus.circle-lower'); - let text = g.select('.height-focus-label'); + let line = g.select('.height-focus.line') + let circle = g.select('.height-focus.circle-lower') + let text = g.select('.height-focus-label') - if (!line.node()) line = g.append('svg:line'); - if (!circle.node()) circle = g.append('svg:circle'); - if (!text.node()) text = g.append('svg:text'); + if (!line.node()) line = g.append('svg:line') + if (!circle.node()) circle = g.append('svg:circle') + if (!text.node()) text = g.append('svg:text') - if (isNaN(xCoord) || isNaN(yCoord)) return g; + if (isNaN(xCoord) || isNaN(yCoord)) return g circle - .attr("class", theme + " height-focus circle-lower") - .attr("transform", "translate(" + xCoord + "," + yCoord + ")") - .attr("r", 6) - .attr("cx", 0) - .attr("cy", 0); + .attr('class', theme + ' height-focus circle-lower') + .attr('transform', 'translate(' + xCoord + ',' + yCoord + ')') + .attr('r', 6) + .attr('cx', 0) + .attr('cy', 0) line - .attr("class", theme + " height-focus line") - .attr("x1", xCoord) - .attr("x2", xCoord) - .attr("y1", yCoord) - .attr("y2", length); + .attr('class', theme + ' height-focus line') + .attr('x1', xCoord) + .attr('x2', xCoord) + .attr('y1', yCoord) + .attr('y2', length) text - .attr("class", theme + " height-focus-label") - .style("pointer-events", "none") - .attr("x", xCoord + 5) - .attr("y", length); + .attr('class', theme + ' height-focus-label') + .style('pointer-events', 'none') + .attr('x', xCoord + 5) + .attr('y', length) - let label; + let label Object .keys(labels) .sort((a, b) => labels[a].order - labels[b].order) // TODO: any performance issues? - .forEach((i)=> { - label = text.select(".height-focus-" + labels[i].name); + .forEach((i) => { + label = text.select('.height-focus-' + labels[i].name) if (!label.size()) { - label = text.append("svg:tspan") - .attr("class", "height-focus-" + labels[i].name /*+ " " + "order-" + labels[i].order*/) - .attr("dy", "1.5em"); + label = text.append('svg:tspan') + .attr('class', 'height-focus-' + labels[i].name /* + " " + "order-" + labels[i].order */) + .attr('dy', '1.5em') } - label.text(typeof labels[i].value !== "function" ? labels[i].value : labels[i].value(item)); + label.text(typeof labels[i].value !== 'function' ? labels[i].value : labels[i].value(item)) - }); + }) - text.select('tspan').attr("dy", text.selectAll('tspan').size() > 1 ? "-1.5em" : "0em" ); - text.selectAll('tspan').attr("x", xCoord + 5); + text.select('tspan').attr('dy', text.selectAll('tspan').size() > 1 ? '-1.5em' : '0em') + text.selectAll('tspan').attr('x', xCoord + 5) - return g; + return g } -}; +} export const LegendItem = ({ name, @@ -172,93 +172,93 @@ export const LegendItem = ({ margins = {}, color, path, - className = '' + className = '', }) => { return g => { g - .attr("class", "legend-item legend-" + name.toLowerCase()) - .attr("data-name", name); + .attr('class', 'legend-item legend-' + name.toLowerCase()) + .attr('data-name', name) g.on('click.legend', () => d3.select(g.node().ownerSVGElement || g) - .dispatch("legend_clicked", { + .dispatch('legend_clicked', { detail: { path: path.node(), - name: name, + name, legend: g.node(), enabled: !path.classed('leaflet-hidden'), - } - }) - ); - - g.append("svg:rect") - .attr("x", (width / 2) - 50) - .attr("y", height + margins.bottom / 2) - .attr("width", 50) - .attr("height", 10) - .attr("fill", color) - .attr("stroke", "#000") - .attr("stroke-opacity", "0.5") - .attr("fill-opacity", "0.25") - .attr("class", className); + }, + }), + ) + + g.append('svg:rect') + .attr('x', (width / 2) - 50) + .attr('y', height + margins.bottom / 2) + .attr('width', 50) + .attr('height', 10) + .attr('fill', color) + .attr('stroke', '#000') + .attr('stroke-opacity', '0.5') + .attr('fill-opacity', '0.25') + .attr('class', className) g.append('svg:text') .text(L._(label || name)) - .attr("x", (width / 2) + 5) - .attr("font-size", 10) - .style("font-weight", "700") + .attr('x', (width / 2) + 5) + .attr('font-size', 10) + .style('font-weight', '700') .attr('y', height + margins.bottom / 2) - .attr('dy', "0.75em"); + .attr('dy', '0.75em') - return g; + return g } -}; +} export const LegendSmall = ({ width, height, items, - onClick + onClick, }) => { return g => { - let idx = 0; + let idx = 0 g.data([{ x: width, - y: height + 40 - }]).attr("transform", d => "translate(" + d.x + "," + d.y + ")").classed('legend-switcher'); + y: height + 40, + }]).attr('transform', d => 'translate(' + d.x + ',' + d.y + ')').classed('legend-switcher') // let label = g.selectAll(".legend-switcher-label") .data([{ idx: 0 }]); - let label = g.append('svg:text') - .attr("class", "legend-switcher-label") - .attr("text-anchor", "end") - .attr("x", -25) - .attr("y", 5) - .on("mousedown", (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length]))); - - let symbol = g.selectAll(".legend-switcher-symbol").data([ - { type: d3.symbolTriangle, x: 0, y: 3, angle: 0, size: 50, id: "down" }, - { type: d3.symbolTriangle, x: -13, y: 0, angle: 180, size: 50, id: "up" } - ]); - - symbol.exit().remove(); - label.exit().remove(); - + const label = g.append('svg:text') + .attr('class', 'legend-switcher-label') + .attr('text-anchor', 'end') + .attr('x', -25) + .attr('y', 5) + .on('mousedown', (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length]))) + + const symbol = g.selectAll('.legend-switcher-symbol').data([ + { type: d3.symbolTriangle, x: 0, y: 3, angle: 0, size: 50, id: 'down' }, + { type: d3.symbolTriangle, x: -13, y: 0, angle: 180, size: 50, id: 'up' }, + ]) + + symbol.exit().remove() + label.exit().remove() + symbol.enter() - .append("svg:path") - .attr("class", "legend-switcher-symbol") - .attr("cursor", 'pointer') - .attr("fill", "#000") + .append('svg:path') + .attr('class', 'legend-switcher-symbol') + .attr('cursor', 'pointer') + .attr('fill', '#000') .merge(symbol) - .attr("d", + .attr('d', d3.symbol() - .type(d => d.type) - .size(d => d.size) + .type(d => d.type) + .size(d => d.size), ) - .attr("transform", d => "translate(" + d.x + "," + d.y + ") rotate(" + d.angle + ")") - .on("mousedown", (e, d) => setIdx(L.Util.wrapNum((d.id === "up" ? idx + 1 : idx - 1), [0, items.length]))); + .attr('transform', d => 'translate(' + d.x + ',' + d.y + ') rotate(' + d.angle + ')') + .on('mousedown', (e, d) => setIdx(L.Util.wrapNum((d.id === 'up' ? idx + 1 : idx - 1), [0, items.length]))) const setIdx = (id) => { - idx = id; + idx = id // label.enter() // .append('text') // .attr("class", "legend-switcher-label") @@ -268,14 +268,14 @@ export const LegendSmall = ({ // .merge(label.data([{ idx: id }])) // .text(d => items.length ? (items[idx][0].toUpperCase() + items[idx].slice(1)) : '') // .on("mousedown", (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length]))); - label.text(items.length ? L._((items[idx][0].toUpperCase() + items[idx].slice(1))) : ''); - onClick(items[idx]); - }; - setIdx(0); - - return g; - }; -}; + label.text(items.length ? L._((items[idx][0].toUpperCase() + items[idx].slice(1))) : '') + onClick(items[idx]) + } + setIdx(0) + + return g + } +} export const Tooltip = ({ xCoord, @@ -287,74 +287,74 @@ export const Tooltip = ({ }) => { return g => { - let line = g.select('.mouse-focus-line'); - let box = g.select('.mouse-focus-label'); + let line = g.select('.mouse-focus-line') + let box = g.select('.mouse-focus-label') - if (!line.node()) line = g.append('svg:line'); - if (!box.node()) box = g.append("g"); + if (!line.node()) line = g.append('svg:line') + if (!box.node()) box = g.append('g') - let rect = box.select(".mouse-focus-label-rect"); - let text = box.select(".mouse-focus-label-text"); + let rect = box.select('.mouse-focus-label-rect') + let text = box.select('.mouse-focus-label-text') - if (!rect.node()) rect = box.append("svg:rect"); - if (!text.node()) text = box.append("svg:text"); + if (!rect.node()) rect = box.append('svg:rect') + if (!text.node()) text = box.append('svg:text') // Sets focus-label-text position to the left / right of the mouse-focus-line - let xAlign = 0; - let yAlign = 0; - let bbox = { width: 0, height: 0 }; - try { bbox = text.node().getBBox(); } catch (e) { return g; } + let xAlign = 0 + let yAlign = 0 + let bbox = { width: 0, height: 0 } + try { bbox = text.node().getBBox() } catch (e) { return g } - if (xCoord) xAlign = xCoord + (xCoord < width / 2 ? 10 : -bbox.width - 10); - if (yCoord) yAlign = Math.max(yCoord - bbox.height, L.Browser.webkit ? 0 : -Infinity); + if (xCoord) xAlign = xCoord + (xCoord < width / 2 ? 10 : -bbox.width - 10) + if (yCoord) yAlign = Math.max(yCoord - bbox.height, L.Browser.webkit ? 0 : -Infinity) line .attr('class', 'mouse-focus-line') .attr('x2', xCoord) .attr('y2', 0) .attr('x1', xCoord) - .attr('y1', height); + .attr('y1', height) box - .attr('class', 'mouse-focus-label'); + .attr('class', 'mouse-focus-label') rect - .attr("class", "mouse-focus-label-rect") - .attr("x", xAlign - 5) - .attr("y", yAlign - 5) - .attr("width", bbox.width + 10) - .attr("height", bbox.height + 10) - .attr("rx", 3) - .attr("ry", 3); + .attr('class', 'mouse-focus-label-rect') + .attr('x', xAlign - 5) + .attr('y', yAlign - 5) + .attr('width', bbox.width + 10) + .attr('height', bbox.height + 10) + .attr('rx', 3) + .attr('ry', 3) text - .attr("class", "mouse-focus-label-text") - .style("font-weight", "700") - .attr("y", yAlign); + .attr('class', 'mouse-focus-label-text') + .style('font-weight', '700') + .attr('y', yAlign) - let label; + let label Object .keys(labels) .sort((a, b) => labels[a].order - labels[b].order) // TODO: any performance issues? - .forEach((i)=> { - label = text.select(".mouse-focus-label-" + labels[i].name); + .forEach((i) => { + label = text.select('.mouse-focus-label-' + labels[i].name) if (!label.size()) { - label = text.append("svg:tspan", ".mouse-focus-label-x") - .attr("class", "mouse-focus-label-" + labels[i].name /*+ " " + "order-" + labels[i].order*/) - .attr("dy", "1.5em"); + label = text.append('svg:tspan', '.mouse-focus-label-x') + .attr('class', 'mouse-focus-label-' + labels[i].name /* + " " + "order-" + labels[i].order */) + .attr('dy', '1.5em') } - label.text(typeof labels[i].value !== "function" ? labels[i].value : labels[i].value(item)); - }); + label.text(typeof labels[i].value !== 'function' ? labels[i].value : labels[i].value(item)) + }) - text.select('tspan').attr("dy", "1em"); - text.selectAll('tspan').attr("x", xAlign); + text.select('tspan').attr('dy', '1em') + text.selectAll('tspan').attr('x', xAlign) - return g; - }; -}; + return g + } +} export const Ruler = ({ height, @@ -363,128 +363,128 @@ export const Ruler = ({ return g => { g.data([{ - x: 0, - y: height, - }]) - .attr("transform", d => "translate(" + d.x + "," + d.y + ")"); - - let rect = g.selectAll('.horizontal-drag-rect').data([{ w: width }]); - let line = g.selectAll('.horizontal-drag-line').data([{ w: width }]); - let label = g.selectAll('.horizontal-drag-label').data([{ w: width - 8 }]); - let symbol = g.selectAll('.horizontal-drag-symbol') + x: 0, + y: height, + }]) + .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')') + + const rect = g.selectAll('.horizontal-drag-rect').data([{ w: width }]) + const line = g.selectAll('.horizontal-drag-line').data([{ w: width }]) + const label = g.selectAll('.horizontal-drag-label').data([{ w: width - 8 }]) + const symbol = g.selectAll('.horizontal-drag-symbol') .data([{ type: d3.symbolTriangle, x: width + 7, y: 0, angle: -90, - size: 50 - }]); + size: 50, + }]) - rect.exit().remove(); - line.exit().remove(); - label.exit().remove(); - symbol.exit().remove(); + rect.exit().remove() + line.exit().remove() + label.exit().remove() + symbol.exit().remove() rect.enter() - .append("svg:rect") - .attr("class", "horizontal-drag-rect") - .attr("x", 0) - .attr("y", -8) - .attr("height", 8) + .append('svg:rect') + .attr('class', 'horizontal-drag-rect') + .attr('x', 0) + .attr('y', -8) + .attr('height', 8) .attr('fill', 'none') .attr('pointer-events', 'all') .merge(rect) - .attr("width", d => d.w); + .attr('width', d => d.w) line.enter() - .append("svg:line") - .attr("class", "horizontal-drag-line") - .attr("x1", 0) + .append('svg:line') + .attr('class', 'horizontal-drag-line') + .attr('x1', 0) .merge(line) - .attr("x2", d => d.w); + .attr('x2', d => d.w) label.enter() - .append("svg:text") - .attr("class", "horizontal-drag-label") - .attr("text-anchor", "end") - .attr("y", -8) + .append('svg:text') + .attr('class', 'horizontal-drag-label') + .attr('text-anchor', 'end') + .attr('y', -8) .merge(label) - .attr("x", d => d.w) + .attr('x', d => d.w) symbol .enter() - .append("svg:path") - .attr("class", "horizontal-drag-symbol") + .append('svg:path') + .attr('class', 'horizontal-drag-symbol') .merge(symbol) - .attr("d", + .attr('d', d3.symbol() - .type(d => d.type) - .size(d => d.size) + .type(d => d.type) + .size(d => d.size), ) - .attr("transform", d => "translate(" + d.x + "," + d.y + ") rotate(" + d.angle + ")"); + .attr('transform', d => 'translate(' + d.x + ',' + d.y + ') rotate(' + d.angle + ')') - return g; + return g } -}; +} export const CheckPoint = ({ point, height, width, x, - y + y, }) => { return g => { - if (isNaN(x) || isNaN(y)) return g; + if (isNaN(x) || isNaN(y)) return g if (!point.item || !point.item.property('isConnected')) { - point.position = point.position || "bottom"; + point.position = point.position || 'bottom' - point.item = g.append('g'); + point.item = g.append('g') - point.item.append("svg:line") - .attr("y1", 0) - .attr("x1", 0) - .attr("style","stroke: rgb(51, 51, 51); stroke-width: 0.5; stroke-dasharray: 2, 2;"); + point.item.append('svg:line') + .attr('y1', 0) + .attr('x1', 0) + .attr('style', 'stroke: rgb(51, 51, 51); stroke-width: 0.5; stroke-dasharray: 2, 2;') point.item - .append("svg:circle") - .attr("class", " height-focus circle-lower") - .attr("r", 3); + .append('svg:circle') + .attr('class', ' height-focus circle-lower') + .attr('r', 3) if (point.label) { - point.item.append("svg:text") - .attr("dx", "4px") - .attr("dy", "-4px"); + point.item.append('svg:text') + .attr('dx', '4px') + .attr('dy', '-4px') } } point.item .datum({ pos: point.position, - x: x, - y: y + x, + y, }) - .attr("class", d => "point " + d.pos) - .attr("transform", d => "translate(" + d.x + "," + d.y + ")"); + .attr('class', d => 'point ' + d.pos) + .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')') point.item.select('line') .datum({ - y2: ({'top': -y, 'bottom': height - y})[point.position], - x2: ({'left': -x, 'right': width - x})[point.position] || 0 + y2: ({ top: -y, bottom: height - y })[point.position], + x2: ({ left: -x, right: width - x })[point.position] || 0, }) - .attr("y2", d => d.y2) - .attr("x2", d => d.x2) + .attr('y2', d => d.y2) + .attr('x2', d => d.x2) if (point.label) { point.item.select('text') - .text(point.label); + .text(point.label) } - return g; + return g } -}; +} export const Domain = ({ min, @@ -492,25 +492,25 @@ export const Domain = ({ attr, name, forceBounds, - scale + scale, }) => function(data) { - attr = (scale && scale.attr) || attr || name; - let domain = data && data.length ? d3.extent(data, d => d[attr]) : [0, 1]; - if (typeof min !== "undefined" && (min < domain[0] || forceBounds)) { - domain[0] = min; + attr = (scale && scale.attr) || attr || name + const domain = data && data.length ? d3.extent(data, d => d[attr]) : [0, 1] + if (typeof min !== 'undefined' && (min < domain[0] || forceBounds)) { + domain[0] = min } - if (typeof max !== "undefined" && (max > domain[1] || forceBounds)) { - domain[1] = max; + if (typeof max !== 'undefined' && (max > domain[1] || forceBounds)) { + domain[1] = max } - return domain; -}; + return domain +} export const Range = ({ - axis + axis, }) => function(width, height) { - if (axis == 'x') return [0, width]; - else if (axis == 'y') return [height, 0]; -}; + if (axis == 'x') return [0, width] + else if (axis == 'y') return [height, 0] +} export const Scale = ({ data, @@ -522,19 +522,19 @@ export const Scale = ({ }) => { return d3.scaleLinear() .range(range) - .domain(Domain({min, max, attr, forceBounds})(data)); -}; + .domain(Domain({ min, max, attr, forceBounds })(data)) +} export const Bisect = ({ data = [0, 1], scale, x, - attr + attr, }) => { return d3 .bisector(d => d[attr]) - .left(data, scale.invert(x)); -}; + .left(data, scale.invert(x)) +} export const Chart = ({ width, @@ -543,107 +543,107 @@ export const Chart = ({ ruler, }) => { - const w = width - margins.left - margins.right; - const h = height - margins.top - margins.bottom; + const w = width - margins.left - margins.right + const h = height - margins.top - margins.bottom // SVG Container - const svg = d3.create("svg:svg").attr("class", "background"); + const svg = d3.create('svg:svg').attr('class', 'background') - const defs = svg.append("svg:defs"); + const defs = svg.append('svg:defs') // SVG Groups - const g = svg.append("g"); + const g = svg.append('g') const panes = { - grid : g.append("g").attr("class", "grid"), - area : g.append('g').attr("class", "area"), - axis : g.append('g').attr("class", "axis"), - point : g.append('g').attr("class", "point"), - brush : g.append("g").attr("class", "brush"), - tooltip: g.append("g").attr("class", "tooltip").attr('display', 'none'), - ruler : g.append('g').attr('class', 'ruler'), - legend : g.append('g').attr("class", "legend"), - }; + grid: g.append('g').attr('class', 'grid'), + area: g.append('g').attr('class', 'area'), + axis: g.append('g').attr('class', 'axis'), + point: g.append('g').attr('class', 'point'), + brush: g.append('g').attr('class', 'brush'), + tooltip: g.append('g').attr('class', 'tooltip').attr('display', 'none'), + ruler: g.append('g').attr('class', 'ruler'), + legend: g.append('g').attr('class', 'legend'), + } // SVG Paths - const mask = panes.area .append("svg:mask") .attr("id", 'elevation-clipper-' + Math.random().toString(36).substr(2, 9)).attr('fill-opacity', 1); - const maskRect = mask .append("svg:rect") .attr('class', 'zoom') .attr('fill', 'white'); // white = transparent + const mask = panes.area.append('svg:mask').attr('id', 'elevation-clipper-' + Math.random().toString(36).substr(2, 9)).attr('fill-opacity', 1) + const maskRect = mask.append('svg:rect').attr('class', 'zoom').attr('fill', 'white') // white = transparent // Canvas Paths - const foreignObject = panes.area .append('svg:foreignObject').attr('mask', 'url(#' + mask.attr('id') + ')'); - const canvas = foreignObject.append('xhtml:canvas') .attr('class', 'canvas-plot'); - const context = canvas.node().getContext('2d'); + const foreignObject = panes.area.append('svg:foreignObject').attr('mask', 'url(#' + mask.attr('id') + ')') + const canvas = foreignObject.append('xhtml:canvas').attr('class', 'canvas-plot') + const context = canvas.node().getContext('2d') // Add tooltip - panes.tooltip.call(Tooltip({ xCoord: 0, yCoord: 0, height: h, width : w, labels: {} })); + panes.tooltip.call(Tooltip({ xCoord: 0, yCoord: 0, height: h, width: w, labels: {} })) // Add the brushing - let brush = d3.brushX().on('start.cursor end.cursor brush.cursor', () => panes.brush.select(".overlay").attr('cursor', null)); + const brush = d3.brushX().on('start.cursor end.cursor brush.cursor', () => panes.brush.select('.overlay').attr('cursor', null)) // Scales - const scale = (opts) => ({ x: Scale(opts.x), y: Scale(opts.y)}); + const scale = (opts) => ({ x: Scale(opts.x), y: Scale(opts.y) }) - let utils = { + const utils = { defs, mask, canvas, context, brush, - }; + } - let chart = { + const chart = { svg, g, panes, utils, scale, - }; + } // Resize - chart._resize = ({ + chart._resize = ({ width, height, margins = {}, ruler, }) => { - const w = width - margins.left - margins.right; - const h = height - margins.top - margins.bottom; + const w = width - margins.left - margins.right + const h = height - margins.top - margins.bottom - svg .attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`); - g .attr("transform", "translate(" + margins.left + "," + margins.top + ")"); + svg.attr('width', width).attr('height', height).attr('viewBox', `0 0 ${width} ${height}`) + g.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')') // Fix: https://github.com/Raruto/leaflet-elevation/issues/123 // Fix: https://github.com/Raruto/leaflet-elevation/issues/232 if ( - /Mac|iPod|iPhone|iPad/.test(navigator.platform) && - /AppleWebkit/i.test(navigator.userAgent) && - !/Chrome/.test(navigator.userAgent) + /Mac|iPod|iPhone|iPad/.test(navigator.platform) + && /AppleWebkit/i.test(navigator.userAgent) + && !/Chrome/.test(navigator.userAgent) ) { - canvas .style("transform", "translate(" + margins.left + "px," + margins.top + "px)"); + canvas.style('transform', 'translate(' + margins.left + 'px,' + margins.top + 'px)') } - maskRect .attr("width", w).attr("height", h).attr("x", 0).attr("y", 0); - foreignObject.attr('width', w).attr('height', h); - canvas .attr('width', w).attr('height', h); + maskRect.attr('width', w).attr('height', h).attr('x', 0).attr('y', 0) + foreignObject.attr('width', w).attr('height', h) + canvas.attr('width', w).attr('height', h) if (ruler) { - panes.ruler.call(Ruler({ height: h, width: w })); + panes.ruler.call(Ruler({ height: h, width: w })) } - panes.brush.call(brush.extent( [ [0,0], [w, h] ] )); - panes.brush.select(".overlay").attr('cursor', null); + panes.brush.call(brush.extent([[0, 0], [w, h]])) + panes.brush.select('.overlay').attr('cursor', null) - chart._width = w; - chart._height = h; + chart._width = w + chart._height = h - chart.svg.dispatch('resize', { detail: { width: w, height: h } } ); + chart.svg.dispatch('resize', { detail: { width: w, height: h } }) - }; + } - chart.pane = (name) => (panes[name] || (panes[name] = g.append('g').attr("class", name))); - chart.get = (name) => utils[name]; + chart.pane = (name) => (panes[name] || (panes[name] = g.append('g').attr('class', name))) + chart.get = (name) => utils[name] - chart._resize({ width, height, margins}); + chart._resize({ width, height, margins }) - return chart; -}; + return chart +} diff --git a/src/components/leaflet-elevation/src/components/marker.js b/src/components/leaflet-elevation/src/components/marker.js index 6f1f6af5e..092f447d4 100644 --- a/src/components/leaflet-elevation/src/components/marker.js +++ b/src/components/leaflet-elevation/src/components/marker.js @@ -1,76 +1,77 @@ -import * as D3 from './d3.js'; +import * as D3 from './d3.js' -const _ = L.Control.Elevation.Utils; +const _ = L.Control.Elevation.Utils export var Marker = L.Class.extend({ initialize(options, control) { - this.options = options; + 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; + 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 = {}; + this._labels = {} - return this; + 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; + 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; + return this }, /** * Update position marker ("leaflet-marker"). + * @param props */ 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; + 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 } }, @@ -78,19 +79,19 @@ export var Marker = L.Class.extend({ * 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; + 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; + return this._latlng }, _registerTooltip(props) { - this._labels[props.name] = props; - } + this._labels[props.name] = props + }, -}); +}) diff --git a/src/components/leaflet-elevation/src/components/summary.js b/src/components/leaflet-elevation/src/components/summary.js index e8da57f8c..af29e5ca2 100644 --- a/src/components/leaflet-elevation/src/components/summary.js +++ b/src/components/leaflet-elevation/src/components/summary.js @@ -1,43 +1,43 @@ -const _ = L.Control.Elevation.Utils; +const _ = L.Control.Elevation.Utils export var Summary = L.Class.extend({ initialize(opts, control) { - this.options = opts; - this.control = control; - this.labels = {}; + this.options = opts + this.control = control + this.labels = {} - let summary = this._container = _.create("div", "elevation-summary " + (opts.summary ? opts.summary + "-summary" : '')); - _.style(summary, 'max-width', opts.width ? opts.width + 'px' : ''); + const summary = this._container = _.create('div', 'elevation-summary ' + (opts.summary ? opts.summary + '-summary' : '')) + _.style(summary, 'max-width', opts.width ? opts.width + 'px' : '') }, render() { - return container => container.append(() => this._container); + return container => container.append(() => this._container) }, reset() { - this._container.innerHTML = ''; + this._container.innerHTML = '' }, append(className, label, value) { - this._container.innerHTML += `${label}${value}`; - return this; + this._container.innerHTML += `${label}${value}` + return this }, update() { Object - .keys(this.labels) - .sort((a, b) => this.labels[a].order - this.labels[b].order) // TODO: any performance issues? - .forEach((i)=> { - let value = typeof this.labels[i].value !== "function" ? this.labels[i].value : this.labels[i].value(this.control.track_info, this.labels[i].unit || ''); - this.append(i /*+ " order-" + this.labels[i].order*/, L._(this.labels[i].label), value, this.labels[i].order); - }); + .keys(this.labels) + .sort((a, b) => this.labels[a].order - this.labels[b].order) // TODO: any performance issues? + .forEach((i) => { + const value = typeof this.labels[i].value !== 'function' ? this.labels[i].value : this.labels[i].value(this.control.track_info, this.labels[i].unit || '') + this.append(i /* + " order-" + this.labels[i].order */, L._(this.labels[i].label), value, this.labels[i].order) + }) }, _registerSummary(data) { - for (let i in data) { - data[i].order = data[i].order ?? 1000; - this.labels[i] = data[i]; + for (const i in data) { + data[i].order = data[i].order ?? 1000 + this.labels[i] = data[i] } - } + }, -}); +}) diff --git a/src/components/leaflet-elevation/src/handlers/acceleration.js b/src/components/leaflet-elevation/src/handlers/acceleration.js index 9b31b5c64..58e559bc1 100644 --- a/src/components/leaflet-elevation/src/handlers/acceleration.js +++ b/src/components/leaflet-elevation/src/handlers/acceleration.js @@ -1,12 +1,12 @@ export function Acceleration() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let acceleration = {}; + const opts = this.options + const acceleration = {} - acceleration.label = opts.accelerationLabel || L._(opts.imperial ? 'ft/s²' : 'm/s²'); - opts.accelerationFactor = opts.accelerationFactor || 1; + acceleration.label = opts.accelerationLabel || L._(opts.imperial ? 'ft/s²' : 'm/s²') + opts.accelerationFactor = opts.accelerationFactor || 1 return { name: 'acceleration', @@ -15,50 +15,50 @@ export function Acceleration() { clampRange: this.options.accelerationRange, decimals: 2, pointToAttr: (_, i) => { - let dv = (this._data[i].speed - this._data[i > 0 ? i - 1 : i].speed) * (1000 / opts.timeFactor); - let dt = (this._data[i].time - this._data[i > 0 ? i - 1 : i].time) / 1000; - return dt > 0 ? Math.abs((dv / dt)) * opts.accelerationFactor : NaN; + const dv = (this._data[i].speed - this._data[i > 0 ? i - 1 : i].speed) * (1000 / opts.timeFactor) + const dt = (this._data[i].time - this._data[i > 0 ? i - 1 : i].time) / 1000 + return dt > 0 ? Math.abs((dv / dt)) * opts.accelerationFactor : NaN }, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, scale: { - axis : "y", - position : "right", - scale : { min: 0, max: +1 }, + axis: 'y', + position: 'right', + scale: { min: 0, max: +1 }, tickPadding: 16, - labelX : 25, - labelY : -8, + labelX: 25, + labelY: -8, }, path: { - label : 'Acceleration', - yAttr : 'acceleration', - scaleX : 'distance', - scaleY : 'acceleration', - color : '#050402', - strokeColor : '#000', - strokeOpacity: "0.5", - fillOpacity : "0.25", + label: 'Acceleration', + yAttr: 'acceleration', + scaleX: 'distance', + scaleY: 'acceleration', + color: '#050402', + strokeColor: '#000', + strokeOpacity: '0.5', + fillOpacity: '0.25', }, tooltip: { - chart: (item) => L._("a: ") + item.acceleration + " " + acceleration.label, - marker: (item) => Math.round(item.acceleration) + " " + acceleration.label, + chart: (item) => L._('a: ') + item.acceleration + ' ' + acceleration.label, + marker: (item) => Math.round(item.acceleration) + ' ' + acceleration.label, order: 60, }, summary: { - "minacceleration" : { - label: "Min Acceleration: ", + minacceleration: { + label: 'Min Acceleration: ', value: (track, unit) => Math.round(track.acceleration_min || 0) + ' ' + unit, - order: 60 + order: 60, }, - "maxacceleration" : { - label: "Max Acceleration: ", + maxacceleration: { + label: 'Max Acceleration: ', value: (track, unit) => Math.round(track.acceleration_max || 0) + ' ' + unit, - order: 61 + order: 61, }, - "avgacceleration": { - label: "Avg Acceleration: ", + avgacceleration: { + label: 'Avg Acceleration: ', value: (track, unit) => Math.round(track.acceleration_avg || 0) + ' ' + unit, - order: 62 + order: 62, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/altitude.js b/src/components/leaflet-elevation/src/handlers/altitude.js index b7fe6e34a..0fa0ad351 100644 --- a/src/components/leaflet-elevation/src/handlers/altitude.js +++ b/src/components/leaflet-elevation/src/handlers/altitude.js @@ -1,15 +1,15 @@ export function Altitude() { - - const _ = L.Control.Elevation.Utils; - let opts = this.options; - let altitude = {}; + const _ = L.Control.Elevation.Utils - let theme = opts.theme.split(' ')[0].replace('-theme', ''); - let color = _.Colors[theme] || {}; + const opts = this.options + const altitude = {} - opts.altitudeFactor = opts.imperial ? this.__footFactor : (opts.altitudeFactor || 1); // 1 m = (1 m) - altitude.label = opts.imperial ? "ft" : opts.yLabel; + const theme = opts.theme.split(' ')[0].replace('-theme', '') + const color = _.Colors[theme] || {} + + opts.altitudeFactor = opts.imperial ? this.__footFactor : (opts.altitudeFactor || 1) // 1 m = (1 m) + altitude.label = opts.imperial ? 'ft' : opts.yLabel return { name: 'altitude', @@ -24,55 +24,55 @@ export function Altitude() { // if ("alt" in point) point.meta.ele = point.alt; // }, pointToAttr: (point, i) => { - if ("alt" in point) point.meta.ele = point.alt; // "alt" property is generated inside "leaflet" - return this._data[i].z *= opts.altitudeFactor; + if ('alt' in point) point.meta.ele = point.alt // "alt" property is generated inside "leaflet" + return this._data[i].z *= opts.altitudeFactor }, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, grid: { - axis : "y", - position : "left", - scale : "y" // this._chart._y, + axis: 'y', + position: 'left', + scale: 'y', // this._chart._y, }, scale: { - axis : "y", - position: "left", - scale : "y", // this._chart._y, - labelX : -3, - labelY : -8, + axis: 'y', + position: 'left', + scale: 'y', // this._chart._y, + labelX: -3, + labelY: -8, }, path: { - label : 'Altitude', - scaleX : 'distance', - scaleY : 'altitude', - className : 'area', - color : color.area || theme, - strokeColor : opts.detached ? color.stroke : '#000', - strokeOpacity: "1", - fillOpacity : opts.detached ? (color.alpha || '0.8') : 1, - preferCanvas : opts.preferCanvas, + label: 'Altitude', + scaleX: 'distance', + scaleY: 'altitude', + className: 'area', + color: color.area || theme, + strokeColor: opts.detached ? color.stroke : '#000', + strokeOpacity: '1', + fillOpacity: opts.detached ? (color.alpha || '0.8') : 1, + preferCanvas: opts.preferCanvas, }, tooltip: { name: 'y', - chart: (item) => L._("y: ") + _.round(item[opts.yAttr], opts.decimalsY) + " " + altitude.label, - marker: (item) => _.round(item[opts.yAttr], opts.decimalsY) + " " + altitude.label, + chart: (item) => L._('y: ') + _.round(item[opts.yAttr], opts.decimalsY) + ' ' + altitude.label, + marker: (item) => _.round(item[opts.yAttr], opts.decimalsY) + ' ' + altitude.label, order: 10, }, summary: { - "minele" : { - label: "Min Elevation: ", + minele: { + label: 'Min Elevation: ', value: (track, unit) => (track.elevation_min || 0).toFixed(2) + ' ' + unit, order: 30, }, - "maxele" : { - label: "Max Elevation: ", + maxele: { + label: 'Max Elevation: ', value: (track, unit) => (track.elevation_max || 0).toFixed(2) + ' ' + unit, order: 31, }, - "avgele" : { - label: "Avg Elevation: ", + avgele: { + label: 'Avg Elevation: ', value: (track, unit) => (track.elevation_avg || 0).toFixed(2) + ' ' + unit, order: 32, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/cadence.js b/src/components/leaflet-elevation/src/handlers/cadence.js index 0ad7704dc..acf73ab12 100644 --- a/src/components/leaflet-elevation/src/handlers/cadence.js +++ b/src/components/leaflet-elevation/src/handlers/cadence.js @@ -1,54 +1,54 @@ export function Cadence() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - return { - name: 'cadence', // <-- Your custom option name (eg. "cadence: true") - unit: 'rpm', - meta: 'cad', // <-- point.meta.cad - coordinateProperties: ["cads", "cadences", "cad", "cadence"], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" - pointToAttr: (point, i) => (point.cad ?? point.meta.cad ?? point.prev('cadence')) || 0, + return { + name: 'cadence', // <-- Your custom option name (eg. "cadence: true") + unit: 'rpm', + meta: 'cad', // <-- point.meta.cad + coordinateProperties: ['cads', 'cadences', 'cad', 'cadence'], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" + pointToAttr: (point, i) => (point.cad ?? point.meta.cad ?? point.prev('cadence')) || 0, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, - scale: { - axis : "y", - position : "right", - scale : { min: -1, max: +1 }, - tickPadding: 16, - labelX : 25, - labelY : -8, - }, - path: { - label : 'RPM', - yAttr : 'cadence', - scaleX : 'distance', - scaleY : 'cadence', - color : '#FFF', - strokeColor : 'blue', - strokeOpacity: "0.85", - fillOpacity : "0.1", - }, - tooltip: { - name: 'cadence', - chart: (item) => L._("cad: ") + item.cadence + " " + 'rpm', - marker: (item) => Math.round(item.cadence) + " " + 'rpm', - order: 1 - }, - summary: { - "minrpm": { - label: "Min RPM: ", - value: (track, unit) => Math.round(track.cadence_min || 0) + ' ' + unit, - // order: 30 - }, - "maxrpm": { - label: "Max RPM: ", - value: (track, unit) => Math.round(track.cadence_max || 0) + ' ' + unit, - // order: 30 - }, - "avgrpm": { - label: "Avg RPM: ", - value: (track, unit) => Math.round(track.cadence_avg || 0) + ' ' + unit, - // order: 20 - }, - } - }; -} \ No newline at end of file + scale: { + axis: 'y', + position: 'right', + scale: { min: -1, max: +1 }, + tickPadding: 16, + labelX: 25, + labelY: -8, + }, + path: { + label: 'RPM', + yAttr: 'cadence', + scaleX: 'distance', + scaleY: 'cadence', + color: '#FFF', + strokeColor: 'blue', + strokeOpacity: '0.85', + fillOpacity: '0.1', + }, + tooltip: { + name: 'cadence', + chart: (item) => L._('cad: ') + item.cadence + ' ' + 'rpm', + marker: (item) => Math.round(item.cadence) + ' ' + 'rpm', + order: 1, + }, + summary: { + minrpm: { + label: 'Min RPM: ', + value: (track, unit) => Math.round(track.cadence_min || 0) + ' ' + unit, + // order: 30 + }, + maxrpm: { + label: 'Max RPM: ', + value: (track, unit) => Math.round(track.cadence_max || 0) + ' ' + unit, + // order: 30 + }, + avgrpm: { + label: 'Avg RPM: ', + value: (track, unit) => Math.round(track.cadence_avg || 0) + ' ' + unit, + // order: 20 + }, + }, + } +} diff --git a/src/components/leaflet-elevation/src/handlers/distance.js b/src/components/leaflet-elevation/src/handlers/distance.js index fa6fa1df7..184781d42 100644 --- a/src/components/leaflet-elevation/src/handlers/distance.js +++ b/src/components/leaflet-elevation/src/handlers/distance.js @@ -1,12 +1,12 @@ export function Distance() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let distance = {}; + const opts = this.options + const distance = {} - opts.distanceFactor = opts.imperial ? this.__mileFactor : (opts.distanceFactor || 1); // 1 km = (1000 m) - distance.label = opts.imperial ? "mi" : opts.xLabel; + opts.distanceFactor = opts.imperial ? this.__mileFactor : (opts.distanceFactor || 1) // 1 km = (1000 m) + distance.label = opts.imperial ? 'mi' : opts.xLabel return { name: 'distance', @@ -18,29 +18,29 @@ export function Distance() { // stats: { total: _.iSum }, onPointAdded: (distance, i) => this.track_info.distance = distance, scale: opts.distance && { - axis : "x", - position: "bottom", - scale : "x", // this._chart._x, - labelY : 25, - labelX : () => this._width() + 6, - ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]), + axis: 'x', + position: 'bottom', + scale: 'x', // this._chart._x, + labelY: 25, + labelX: () => this._width() + 6, + ticks: () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]), }, grid: opts.distance && { - axis : "x", - position : "bottom", - scale : "x" // this._chart._x, + axis: 'x', + position: 'bottom', + scale: 'x', // this._chart._x, }, tooltip: opts.distance && { name: 'x', - chart: (item) => L._("x: ") + _.round(item[opts.xAttr], opts.decimalsX) + " " + distance.label, - order: 20 + chart: (item) => L._('x: ') + _.round(item[opts.xAttr], opts.decimalsX) + ' ' + distance.label, + order: 20, }, summary: opts.distance && { - "totlen" : { - label: "Total Length: ", + totlen: { + label: 'Total Length: ', value: (track) => (track.distance || 0).toFixed(2) + ' ' + distance.label, - order: 10 - } - } - }; + order: 10, + }, + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/heart.js b/src/components/leaflet-elevation/src/handlers/heart.js index 1930f6017..3cee1feb2 100644 --- a/src/components/leaflet-elevation/src/handlers/heart.js +++ b/src/components/leaflet-elevation/src/handlers/heart.js @@ -1,53 +1,53 @@ export function Heart() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - return { - name: 'heart', // <-- Your custom option name (eg. "heart: true") - unit: 'bpm', - meta: 'hr', // <-- point.meta.hr - coordinateProperties: ["heart", "heartRates", "heartRate"], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" - pointToAttr: (point, i) => (point.hr ?? point.meta.hr ?? point.prev('heart')) || 0, + return { + name: 'heart', // <-- Your custom option name (eg. "heart: true") + unit: 'bpm', + meta: 'hr', // <-- point.meta.hr + coordinateProperties: ['heart', 'heartRates', 'heartRate'], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" + pointToAttr: (point, i) => (point.hr ?? point.meta.hr ?? point.prev('heart')) || 0, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, - scale: { - axis : "y", - position : "left", - scale : { min: -1, max: +1 }, - tickPadding: 25, - labelX : -30, - labelY : -8, - }, - path: { - label : 'ECG', - yAttr : 'heart', - scaleX : 'distance', - scaleY : 'heart', - color : 'white', - strokeColor : 'red', - strokeOpacity: "0.85", - fillOpacity : "0.1", - }, - tooltip: { - chart: (item) => L._("hr: ") + item.heart + " " + 'bpm', - marker: (item) => Math.round(item.heart) + " " + 'bpm', - order: 1 - }, - summary: { - "minbpm": { - label: "Min BPM: ", - value: (track, unit) => Math.round(track.heart_min || 0) + ' ' + unit, - // order: 30 - }, - "maxbpm": { - label: "Max BPM: ", - value: (track, unit) => Math.round(track.heart_max || 0) + ' ' + unit, - // order: 30 - }, - "avgbpm": { - label: "Avg BPM: ", - value: (track, unit) => Math.round(track.heart_avg || 0) + ' ' + unit, - // order: 20 - }, - } - }; -}; \ No newline at end of file + scale: { + axis: 'y', + position: 'left', + scale: { min: -1, max: +1 }, + tickPadding: 25, + labelX: -30, + labelY: -8, + }, + path: { + label: 'ECG', + yAttr: 'heart', + scaleX: 'distance', + scaleY: 'heart', + color: 'white', + strokeColor: 'red', + strokeOpacity: '0.85', + fillOpacity: '0.1', + }, + tooltip: { + chart: (item) => L._('hr: ') + item.heart + ' ' + 'bpm', + marker: (item) => Math.round(item.heart) + ' ' + 'bpm', + order: 1, + }, + summary: { + minbpm: { + label: 'Min BPM: ', + value: (track, unit) => Math.round(track.heart_min || 0) + ' ' + unit, + // order: 30 + }, + maxbpm: { + label: 'Max BPM: ', + value: (track, unit) => Math.round(track.heart_max || 0) + ' ' + unit, + // order: 30 + }, + avgbpm: { + label: 'Avg BPM: ', + value: (track, unit) => Math.round(track.heart_avg || 0) + ' ' + unit, + // order: 20 + }, + }, + } +} diff --git a/src/components/leaflet-elevation/src/handlers/labels.js b/src/components/leaflet-elevation/src/handlers/labels.js index 31b3cd65e..c83c278b2 100644 --- a/src/components/leaflet-elevation/src/handlers/labels.js +++ b/src/components/leaflet-elevation/src/handlers/labels.js @@ -8,38 +8,38 @@ */ export function Labels() { - this.on('elechart_updated', function(e) { + this.on('elechart_updated', function(e) { - const pointG = this._chart._chart.pane('point'); + const pointG = this._chart._chart.pane('point') - const textRotation = this.options.labelsRotation ?? -90; - const textAnchor = this.options.labelsAlign; + const textRotation = this.options.labelsRotation ?? -90 + const textAnchor = this.options.labelsAlign - if (90 == Math.abs(textRotation)) { + if (Math.abs(textRotation) == 90) { - pointG.selectAll('text') - .attr('dy', '4px') - .attr("dx", (d, i, el) => Math.sign(textRotation) * (this._height() - d3.select(el[i].parentElement).datum().y - 8) + 'px') - .attr('text-anchor', textRotation > 0 ? 'end' : 'start') - .attr('transform', 'rotate(' + textRotation + ')') + pointG.selectAll('text') + .attr('dy', '4px') + .attr('dx', (d, i, el) => Math.sign(textRotation) * (this._height() - d3.select(el[i].parentElement).datum().y - 8) + 'px') + .attr('text-anchor', textRotation > 0 ? 'end' : 'start') + .attr('transform', 'rotate(' + textRotation + ')') - pointG.selectAll('circle') - .attr('r', '2.5') - .attr('fill', '#fff') - .attr("stroke", '#000') - .attr("stroke-width", '1.1'); + pointG.selectAll('circle') + .attr('r', '2.5') + .attr('fill', '#fff') + .attr('stroke', '#000') + .attr('stroke-width', '1.1') - } else if (!isNaN(textRotation)) { + } else if (!isNaN(textRotation)) { - pointG.selectAll('text') - .attr("dx", "4px") - .attr("dy", "-9px") - .attr('text-anchor', textAnchor ?? (0 == textRotation ? 'start' : 'middle')) - .attr('transform', 'rotate('+ -textRotation +')') + pointG.selectAll('text') + .attr('dx', '4px') + .attr('dy', '-9px') + .attr('text-anchor', textAnchor ?? (textRotation == 0 ? 'start' : 'middle')) + .attr('transform', 'rotate(' + -textRotation + ')') - } + } - }); + }) - return { }; + return { } } diff --git a/src/components/leaflet-elevation/src/handlers/lineargradient.js b/src/components/leaflet-elevation/src/handlers/lineargradient.js index 90e33ced6..183e28a9c 100644 --- a/src/components/leaflet-elevation/src/handlers/lineargradient.js +++ b/src/components/leaflet-elevation/src/handlers/lineargradient.js @@ -1,6 +1,6 @@ /** * @see https://github.com/Raruto/leaflet-elevation/issues/251 - * + * * @example * ```js * L.control.Elevation({ @@ -19,104 +19,109 @@ */ export function LinearGradient() { - if (!this.options.linearGradient) { - return {}; - } - - const _ = L.Control.Elevation.Utils; - - /** - * Initialize gradient color palette. - */ - const get_palette = function ({range, min, max, depth = 256}) { - const canvas = document.createElement('canvas'), - ctx = canvas.getContext('2d'), - gradient = ctx.createLinearGradient(0, 0, 0, depth); - - canvas.width = 1; - canvas.height = depth; - - for (let i in range) { - gradient.addColorStop(i, range[i]); - } - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, 1, depth); - - const { data } = ctx.getImageData(0, 0, 1, depth); - - return { - /** - * 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 {string} The RGB values as `rgb(r, g, b)` string - */ - getRGBColor(value) { - const idx = Math.floor(Math.min(Math.max((value - min) / (max - min), 0), 0.999) * depth) * 4; - return 'rgb(' + [data[idx], data[idx + 1], data[idx + 2]].join(',') + ')'; - } - }; - }; - - const { preferCanvas } = this.options; - const { attr, path: path_name, range, min, max } = L.extend({ - attr: 'z', - path: 'altitude', - range: { 0.0: '#008800', 0.5: '#ffff00', 1.0: '#ff0000' }, - min: 'elevation_min', - max: 'elevation_max', - }, (true === this.options.linearGradient) ? {} : this.options.linearGradient); - - const gradient_id = path_name + '-gradient-' + _.randomId(); - const legend_id = 'legend-' + gradient_id; - - // Charte profile gradient - this.on('elechart_axis', () => { - if (!this._data.length) return; - - const chart = this._chart; - const path = chart._paths[path_name]; - const { defs } = chart._chart.utils; - - const palette = get_palette({ - min: isFinite(this.track_info[min]) ? this.track_info[min] : 0, - max: isFinite(this.track_info[max]) ? this.track_info[max] : 1, - range, - }); - - let gradient; - - if (preferCanvas) { - /** ref: `path.__fillStyle` within L.Control.Elevation.Utils::drawCanvas(ctx, path) */ - path.__fillStyle = gradient = chart._context.createLinearGradient(0, 0, chart._width(), 0); - } else { - defs.select('#' + gradient_id).remove(); - gradient = defs.append('svg:linearGradient').attr('id', gradient_id); - gradient.addColorStop = function(offset, color) { gradient.append('svg:stop').attr('offset', offset).attr('stop-color', color) }; - path.attr('fill', 'url(#' + gradient_id + ')').classed('area', false); - } - - // Generate gradient for each segment picking colors from palette - for (let i = 0, data = this._data; i < data.length; i++) { - gradient.addColorStop((i) / data.length, palette.getRGBColor(data[i][attr])); - } - - }); - - // Legend item gradient - this.on('elechart_updated', () => { - const chart = this._chart; - const { defs } = chart._chart.utils; - defs.select('#' + legend_id).remove(); - const legendGradient = defs.append('svg:linearGradient').attr('id', legend_id); - Object.keys(range).sort().forEach(i => legendGradient.append('svg:stop').attr('offset', i).attr('stop-color', range[i])); - - chart._container - .select('.legend-' + path_name + ' > rect') - .attr('fill', 'url(#' + legend_id + ')') - .classed('area', false); - }); - - return { }; -} \ No newline at end of file + if (!this.options.linearGradient) { + return {} + } + + const _ = L.Control.Elevation.Utils + + /** + * Initialize gradient color palette. + * @param root0 + * @param root0.range + * @param root0.min + * @param root0.max + * @param root0.depth + */ + const get_palette = function({ range, min, max, depth = 256 }) { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + const gradient = ctx.createLinearGradient(0, 0, 0, depth) + + canvas.width = 1 + canvas.height = depth + + for (const i in range) { + gradient.addColorStop(i, range[i]) + } + + ctx.fillStyle = gradient + ctx.fillRect(0, 0, 1, depth) + + const { data } = ctx.getImageData(0, 0, 1, depth) + + return { + /** + * 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. + * @return {string} The RGB values as `rgb(r, g, b)` string + */ + getRGBColor(value) { + const idx = Math.floor(Math.min(Math.max((value - min) / (max - min), 0), 0.999) * depth) * 4 + return 'rgb(' + [data[idx], data[idx + 1], data[idx + 2]].join(',') + ')' + }, + } + } + + const { preferCanvas } = this.options + const { attr, path: path_name, range, min, max } = L.extend({ + attr: 'z', + path: 'altitude', + range: { 0.0: '#008800', 0.5: '#ffff00', 1.0: '#ff0000' }, + min: 'elevation_min', + max: 'elevation_max', + }, (this.options.linearGradient === true) ? {} : this.options.linearGradient) + + const gradient_id = path_name + '-gradient-' + _.randomId() + const legend_id = 'legend-' + gradient_id + + // Charte profile gradient + this.on('elechart_axis', () => { + if (!this._data.length) return + + const chart = this._chart + const path = chart._paths[path_name] + const { defs } = chart._chart.utils + + const palette = get_palette({ + min: isFinite(this.track_info[min]) ? this.track_info[min] : 0, + max: isFinite(this.track_info[max]) ? this.track_info[max] : 1, + range, + }) + + let gradient + + if (preferCanvas) { + /** ref: `path.__fillStyle` within L.Control.Elevation.Utils::drawCanvas(ctx, path) */ + path.__fillStyle = gradient = chart._context.createLinearGradient(0, 0, chart._width(), 0) + } else { + defs.select('#' + gradient_id).remove() + gradient = defs.append('svg:linearGradient').attr('id', gradient_id) + gradient.addColorStop = function(offset, color) { gradient.append('svg:stop').attr('offset', offset).attr('stop-color', color) } + path.attr('fill', 'url(#' + gradient_id + ')').classed('area', false) + } + + // Generate gradient for each segment picking colors from palette + for (let i = 0, data = this._data; i < data.length; i++) { + gradient.addColorStop((i) / data.length, palette.getRGBColor(data[i][attr])) + } + + }) + + // Legend item gradient + this.on('elechart_updated', () => { + const chart = this._chart + const { defs } = chart._chart.utils + defs.select('#' + legend_id).remove() + const legendGradient = defs.append('svg:linearGradient').attr('id', legend_id) + Object.keys(range).sort().forEach(i => legendGradient.append('svg:stop').attr('offset', i).attr('stop-color', range[i])) + + chart._container + .select('.legend-' + path_name + ' > rect') + .attr('fill', 'url(#' + legend_id + ')') + .classed('area', false) + }) + + return { } +} diff --git a/src/components/leaflet-elevation/src/handlers/pace.js b/src/components/leaflet-elevation/src/handlers/pace.js index 9017f3965..86f0de5bd 100644 --- a/src/components/leaflet-elevation/src/handlers/pace.js +++ b/src/components/leaflet-elevation/src/handlers/pace.js @@ -1,12 +1,12 @@ export function Pace() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let pace = {}; + const opts = this.options + const pace = {} - pace.label = opts.paceLabel || L._(opts.imperial ? 'min/mi' : 'min/km'); - opts.paceFactor = opts.paceFactor || 60; // 1 min = 60 sec + pace.label = opts.paceLabel || L._(opts.imperial ? 'min/mi' : 'min/km') + opts.paceFactor = opts.paceFactor || 60 // 1 min = 60 sec return { name: 'pace', @@ -15,51 +15,51 @@ export function Pace() { clampRange: this.options.paceRange, decimals: 2, pointToAttr: (_, i) => { - let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000; - let dt = this._data[i].time - this._data[ i > 0 ? i - 1 : i].time; - return dx > 0 ? Math.abs((dt / dx) / opts.paceFactor) : NaN; + const dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000 + const dt = this._data[i].time - this._data[i > 0 ? i - 1 : i].time + return dx > 0 ? Math.abs((dt / dx) / opts.paceFactor) : NaN }, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, - scale : (this.options.pace && this.options.pace != "summary") && { - axis : "y", - position : "right", - scale : { min : 0, max : +1 }, + scale: (this.options.pace && this.options.pace != 'summary') && { + axis: 'y', + position: 'right', + scale: { min: 0, max: +1 }, tickPadding: 16, - labelX : 25, - labelY : -8, + labelX: 25, + labelY: -8, }, - path: (this.options.pace && this.options.pace != "summary") && { + path: (this.options.pace && this.options.pace != 'summary') && { // name : 'pace', - label : 'Pace', - yAttr : "pace", - scaleX : 'distance', - scaleY : 'pace', - color : '#03ffff', - strokeColor : '#000', - strokeOpacity: "0.5", - fillOpacity : "0.25", + label: 'Pace', + yAttr: 'pace', + scaleX: 'distance', + scaleY: 'pace', + color: '#03ffff', + strokeColor: '#000', + strokeOpacity: '0.5', + fillOpacity: '0.25', }, tooltip: (this.options.pace) && { - chart: (item) => L._('pace: ') + item.pace + " " + pace.label, - marker: (item) => Math.round(item.pace) + " " + pace.label, + chart: (item) => L._('pace: ') + item.pace + ' ' + pace.label, + marker: (item) => Math.round(item.pace) + ' ' + pace.label, order: 50, }, summary: (this.options.pace) && { - "minpace" : { - label: "Min Pace: ", + minpace: { + label: 'Min Pace: ', value: (track, unit) => Math.round(track.pace_min || 0) + ' ' + unit, - order: 51 + order: 51, }, - "maxpace" : { - label: "Max Pace: ", + maxpace: { + label: 'Max Pace: ', value: (track, unit) => Math.round(track.pace_max || 0) + ' ' + unit, - order: 51 + order: 51, }, - "avgpace": { - label: "Avg Pace: ", + avgpace: { + label: 'Avg Pace: ', value: (track, unit) => Math.round(track.pace_avg || 0) + ' ' + unit, - order: 52 + order: 52, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/runner.js b/src/components/leaflet-elevation/src/handlers/runner.js index 30dd80a06..8896c7b9d 100644 --- a/src/components/leaflet-elevation/src/handlers/runner.js +++ b/src/components/leaflet-elevation/src/handlers/runner.js @@ -1,6 +1,6 @@ /** * @see https://github.com/Igor-Vladyka/leaflet.motion - * + * * @example * ```js * L.control.Elevation({ handlers: [ 'Runner' ], runnerOptions: { polyline: {..}, motion: {..}, marker {..} } }) @@ -8,102 +8,102 @@ */ export async function Runner() { - await this.import(this.__LMOTION || 'https://unpkg.com/leaflet.motion@0.3.2/dist/leaflet.motion.min.js', typeof L.Motion !== 'object') - - let { runnerOptions } = this.options; - - runnerOptions = L.extend( - { polyline: {}, motion: {}, marker: undefined }, - 'object' === typeof runnerOptions ? runnerOptions : {} - ); - - // Custom tooltips - this._registerTooltip({ - name: 'here', - marker: (item) => L._("You are here: "), - order: 1, - }); - - this._registerTooltip({ - name: 'distance', - marker: (item) => Math.round(item.dist) + " " + this.options.xLabel, - order: 2, - }); - - this.addCheckpoint = function (checkpoint) { - return this._registerCheckPoint({ // <-- NB these are private functions use them at your own risk! - latlng: this._findItemForX(this._x(checkpoint.dist)).latlng, - label: checkpoint.label || '' - }); - } - - this.addRunner = function (runner) { - let x = this._x(runner.dist); - let y = this._y(this._findItemForX(x).z) - let g = d3.select(this._container) - .select('svg > g') - .append("svg:circle") - .attr("class", "runner " + this.options.theme + " height-focus circle-lower") - .attr("r", 6) - .attr("cx", x) - .attr("cy", y); - return g; - } - - this.setPositionFromLatLng = function (latlng) { - this._onMouseMoveLayer({ latlng: latlng }); // Update map and chart "markers" from latlng - }; - - this.tick = function (runner, dist = 0, inc = 0.1) { - dist = (dist <= this.track_info.distance - inc) ? dist + inc : 0; - this.updateRunnerPos(runner, dist); - setTimeout(() => this.tick(runner, dist), 150); - }; - - this.updateRunnerPos = function (runner, pos) { - let curr, x; - - if (pos instanceof L.LatLng) { - curr = this._findItemForLatLng(pos); - x = this._x(curr.dist); - } else { - x = this._x(pos); - curr = this._findItemForX(x); - } - - runner - .attr("cx", x) - .attr("cy", this._y(curr.z)); - this.setPositionFromLatLng(curr.latlng); - }; - - this.animate = function (layer, speed = 1500) { - - if (this._runner) { - this._runner.remove(); - } - - layer.setStyle({ opacity: 0.5 }); - - const geo = L.geoJson(layer.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1)) }); - this._runner = L.motion.polyline( - geo.toGeoJSON().features[0].geometry.coordinates, - L.extend({}, { color: 'red', pane: 'elevationPane', attribution: '' }, runnerOptions.polyline), - L.extend({}, { auto: true, speed: speed, }, runnerOptions.motion), - runnerOptions.marker || undefined - ); - - // Override default function behavior: `L.Motion.Polyline::_drawMarker()` - this._runner._drawMarker = new Proxy(this._runner._drawMarker, { - apply: (target, thisArg, argArray) => { - thisArg._runner = thisArg._runner || this.addRunner({ dist: 0 }); - this.updateRunnerPos(thisArg._runner, argArray[0]); - return target.apply(thisArg, argArray); - } - }); - - this._runner.addTo(this._map); - }; - - return {}; + await this.import(this.__LMOTION || 'https://unpkg.com/leaflet.motion@0.3.2/dist/leaflet.motion.min.js', typeof L.Motion !== 'object') + + let { runnerOptions } = this.options + + runnerOptions = L.extend( + { polyline: {}, motion: {}, marker: undefined }, + typeof runnerOptions === 'object' ? runnerOptions : {}, + ) + + // Custom tooltips + this._registerTooltip({ + name: 'here', + marker: (item) => L._('You are here: '), + order: 1, + }) + + this._registerTooltip({ + name: 'distance', + marker: (item) => Math.round(item.dist) + ' ' + this.options.xLabel, + order: 2, + }) + + this.addCheckpoint = function(checkpoint) { + return this._registerCheckPoint({ // <-- NB these are private functions use them at your own risk! + latlng: this._findItemForX(this._x(checkpoint.dist)).latlng, + label: checkpoint.label || '', + }) + } + + this.addRunner = function(runner) { + const x = this._x(runner.dist) + const y = this._y(this._findItemForX(x).z) + const g = d3.select(this._container) + .select('svg > g') + .append('svg:circle') + .attr('class', 'runner ' + this.options.theme + ' height-focus circle-lower') + .attr('r', 6) + .attr('cx', x) + .attr('cy', y) + return g + } + + this.setPositionFromLatLng = function(latlng) { + this._onMouseMoveLayer({ latlng }) // Update map and chart "markers" from latlng + } + + this.tick = function(runner, dist = 0, inc = 0.1) { + dist = (dist <= this.track_info.distance - inc) ? dist + inc : 0 + this.updateRunnerPos(runner, dist) + setTimeout(() => this.tick(runner, dist), 150) + } + + this.updateRunnerPos = function(runner, pos) { + let curr, x + + if (pos instanceof L.LatLng) { + curr = this._findItemForLatLng(pos) + x = this._x(curr.dist) + } else { + x = this._x(pos) + curr = this._findItemForX(x) + } + + runner + .attr('cx', x) + .attr('cy', this._y(curr.z)) + this.setPositionFromLatLng(curr.latlng) + } + + this.animate = function(layer, speed = 1500) { + + if (this._runner) { + this._runner.remove() + } + + layer.setStyle({ opacity: 0.5 }) + + const geo = L.geoJson(layer.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1)) }) + this._runner = L.motion.polyline( + geo.toGeoJSON().features[0].geometry.coordinates, + L.extend({}, { color: 'red', pane: 'elevationPane', attribution: '' }, runnerOptions.polyline), + L.extend({}, { auto: true, speed }, runnerOptions.motion), + runnerOptions.marker || undefined, + ) + + // Override default function behavior: `L.Motion.Polyline::_drawMarker()` + this._runner._drawMarker = new Proxy(this._runner._drawMarker, { + apply: (target, thisArg, argArray) => { + thisArg._runner = thisArg._runner || this.addRunner({ dist: 0 }) + this.updateRunnerPos(thisArg._runner, argArray[0]) + return target.apply(thisArg, argArray) + }, + }) + + this._runner.addTo(this._map) + } + + return {} } diff --git a/src/components/leaflet-elevation/src/handlers/slope.js b/src/components/leaflet-elevation/src/handlers/slope.js index 37291b300..dcd815dcc 100644 --- a/src/components/leaflet-elevation/src/handlers/slope.js +++ b/src/components/leaflet-elevation/src/handlers/slope.js @@ -1,11 +1,11 @@ export function Slope() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let slope = {}; + const opts = this.options + const slope = {} - slope.label = opts.slopeLabel || '%'; + slope.label = opts.slopeLabel || '%' return { name: 'slope', @@ -15,65 +15,65 @@ export function Slope() { clampRange: this.options.slopeRange, decimals: 2, pointToAttr: (_, i) => { // slope in % = ( dy / dx ) * 100; - let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000; - let dy = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr]; - return dx !== 0 ? (dy / dx) * 100 : NaN; + const dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000 + const dy = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr] + return dx !== 0 ? (dy / dx) * 100 : NaN }, onPointAdded: (_, i) => { - let dz = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr]; - if (dz > 0) this.track_info.ascent = (this.track_info.ascent || 0) + dz; // Total Ascent - else if (dz < 0) this.track_info.descent = (this.track_info.descent || 0) - dz; // Total Descent + const dz = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr] + if (dz > 0) this.track_info.ascent = (this.track_info.ascent || 0) + dz // Total Ascent + else if (dz < 0) this.track_info.descent = (this.track_info.descent || 0) - dz // Total Descent }, - stats: { max: _.iMax, min: _.iMin, avg: _.iAvg, }, + stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, scale: { - axis : "y", - position : "right", - scale : { min: -1, max: +1 }, + axis: 'y', + position: 'right', + scale: { min: -1, max: +1 }, tickPadding: 16, - labelX : 25, - labelY : -8, + labelX: 25, + labelY: -8, }, path: { - label : 'Slope', - yAttr : 'slope', - scaleX : 'distance', - scaleY : 'slope', - color : '#F00', - strokeColor : '#000', - strokeOpacity: "0.5", - fillOpacity : "0.25", + label: 'Slope', + yAttr: 'slope', + scaleX: 'distance', + scaleY: 'slope', + color: '#F00', + strokeColor: '#000', + strokeOpacity: '0.5', + fillOpacity: '0.25', }, tooltip: { - chart: (item) => L._("m: ") + item.slope + slope.label, + chart: (item) => L._('m: ') + item.slope + slope.label, marker: (item) => Math.round(item.slope) + slope.label, order: 40, }, summary: { - "minslope": { - label: "Min Slope: ", + minslope: { + label: 'Min Slope: ', value: (track, unit) => Math.round(track.slope_min || 0) + ' ' + unit, - order: 40 + order: 40, }, - "maxslope": { - label: "Max Slope: ", + maxslope: { + label: 'Max Slope: ', value: (track, unit) => Math.round(track.slope_max || 0) + ' ' + unit, - order: 41 + order: 41, }, - "avgslope": { - label: "Avg Slope: ", + avgslope: { + label: 'Avg Slope: ', value: (track, unit) => Math.round(track.slope_avg || 0) + ' ' + unit, - order: 42 + order: 42, }, - "ascent" : { - label: "Total Ascent: ", + ascent: { + label: 'Total Ascent: ', value: (track, unit) => Math.round(track.ascent || 0) + ' ' + (this.options.imperial ? 'ft' : 'm'), - order: 43 + order: 43, }, - "descent" : { - label: "Total Descent: ", + descent: { + label: 'Total Descent: ', value: (track, unit) => Math.round(track.descent || 0) + ' ' + (this.options.imperial ? 'ft' : 'm'), - order: 45 + order: 45, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/speed.js b/src/components/leaflet-elevation/src/handlers/speed.js index 534b340b4..e53d5a866 100644 --- a/src/components/leaflet-elevation/src/handlers/speed.js +++ b/src/components/leaflet-elevation/src/handlers/speed.js @@ -1,12 +1,12 @@ export function Speed() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let speed = {}; + const opts = this.options + const speed = {} - speed.label = opts.speedLabel || L._(opts.imperial ? 'mph' : 'km/h'); - opts.speedFactor = opts.speedFactor || 1; + speed.label = opts.speedLabel || L._(opts.imperial ? 'mph' : 'km/h') + opts.speedFactor = opts.speedFactor || 1 return { name: 'speed', @@ -16,51 +16,51 @@ export function Speed() { clampRange: this.options.speedRange, decimals: 2, pointToAttr: (_, i) => { - let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000; - let dt = this._data[i].time - this._data[ i > 0 ? i - 1 : i].time; - return dt > 0 ? Math.abs((dx / dt) * opts.timeFactor) * opts.speedFactor : NaN; + const dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000 + const dt = this._data[i].time - this._data[i > 0 ? i - 1 : i].time + return dt > 0 ? Math.abs((dx / dt) * opts.timeFactor) * opts.speedFactor : NaN }, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, - scale : (this.options.speed && this.options.speed != "summary") && { - axis : "y", - position : "right", - scale : { min : 0, max : +1 }, + scale: (this.options.speed && this.options.speed != 'summary') && { + axis: 'y', + position: 'right', + scale: { min: 0, max: +1 }, tickPadding: 16, - labelX : 25, - labelY : -8, + labelX: 25, + labelY: -8, }, - path: (this.options.speed && this.options.speed != "summary") && { + path: (this.options.speed && this.options.speed != 'summary') && { // name : 'speed', - label : 'Speed', - yAttr : "speed", - scaleX : 'distance', - scaleY : 'speed', - color : '#03ffff', - strokeColor : '#000', - strokeOpacity: "0.5", - fillOpacity : "0.25", + label: 'Speed', + yAttr: 'speed', + scaleX: 'distance', + scaleY: 'speed', + color: '#03ffff', + strokeColor: '#000', + strokeOpacity: '0.5', + fillOpacity: '0.25', }, tooltip: (this.options.speed) && { - chart: (item) => L._('v: ') + item.speed + " " + speed.label, - marker: (item) => Math.round(item.speed) + " " + speed.label, + chart: (item) => L._('v: ') + item.speed + ' ' + speed.label, + marker: (item) => Math.round(item.speed) + ' ' + speed.label, order: 50, }, summary: (this.options.speed) && { - "minspeed" : { - label: "Min Speed: ", + minspeed: { + label: 'Min Speed: ', value: (track, unit) => Math.round(track.speed_min || 0) + ' ' + unit, - order: 51 + order: 51, }, - "maxspeed" : { - label: "Max Speed: ", + maxspeed: { + label: 'Max Speed: ', value: (track, unit) => Math.round(track.speed_max || 0) + ' ' + unit, - order: 51 + order: 51, }, - "avgspeed": { - label: "Avg Speed: ", + avgspeed: { + label: 'Avg Speed: ', value: (track, unit) => Math.round(track.speed_avg || 0) + ' ' + unit, - order: 52 + order: 52, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/temperature.js b/src/components/leaflet-elevation/src/handlers/temperature.js index cf05308b5..0f366cdfa 100644 --- a/src/components/leaflet-elevation/src/handlers/temperature.js +++ b/src/components/leaflet-elevation/src/handlers/temperature.js @@ -1,62 +1,62 @@ export function Temperature() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let temperature = {}; - let opts = this.options; - temperature.label = opts.label || L._(opts.imperial ? '°F' : '°C'); + const temperature = {} + const opts = this.options + temperature.label = opts.label || L._(opts.imperial ? '°F' : '°C') // Fahrenheit = (Celsius * 9/5) + 32 - opts.temperatureFactor1 = opts.temperatureFactor1 ?? (opts.imperial ? 1.8 : 1); - opts.temperatureFactor2 = opts.temperatureFactor2 ?? (opts.imperial ? 32 : 0); + opts.temperatureFactor1 = opts.temperatureFactor1 ?? (opts.imperial ? 1.8 : 1) + opts.temperatureFactor2 = opts.temperatureFactor2 ?? (opts.imperial ? 32 : 0) return { - name: 'temperature', // <-- Your custom option name (eg. "temperature: true") + name: 'temperature', // <-- Your custom option name (eg. "temperature: true") unit: temperature.label, - meta: 'atemps', // <-- point.meta.atemps - coordinateProperties: ["atemps"], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" + meta: 'atemps', // <-- point.meta.atemps + coordinateProperties: ['atemps'], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON" deltaMax: this.options.temperatureDeltaMax, clampRange: this.options.temperatureRange, decimals: 2, pointToAttr: (point, i) => (point.meta.atemps ?? point.meta.atemps ?? point.prev('temperature')) * opts.temperatureFactor1 + opts.temperatureFactor2, stats: { max: _.iMax, min: _.iMin, avg: _.iAvg }, scale: { - axis : "y", - position : "right", - scale : { min: -1, max: +1 }, + axis: 'y', + position: 'right', + scale: { min: -1, max: +1 }, tickPadding: 16, - labelX : +18, - labelY : -8, + labelX: +18, + labelY: -8, }, path: { - label : temperature.label, - yAttr : 'temperature', - scaleX : 'distance', - scaleY : 'temperature', - color : 'transparent', - strokeColor : '#000', - strokeOpacity: "0.85", + label: temperature.label, + yAttr: 'temperature', + scaleX: 'distance', + scaleY: 'temperature', + color: 'transparent', + strokeColor: '#000', + strokeOpacity: '0.85', // fillOpacity : "0.1", }, tooltip: { name: 'temperature', - chart: (item) => L._("Temp: ") + Math.round(item.temperature).toLocaleString() + " " + temperature.label, - marker: (item) => Math.round(item.temperature).toLocaleString() + " " + temperature.label, - order: 1 + chart: (item) => L._('Temp: ') + Math.round(item.temperature).toLocaleString() + ' ' + temperature.label, + marker: (item) => Math.round(item.temperature).toLocaleString() + ' ' + temperature.label, + order: 1, }, summary: { - "mintemp": { - label: "Min Temp: ", - value: (track, unit) => Math.round(track.temperature_min || 0) + ' ' + unit, + mintemp: { + label: 'Min Temp: ', + value: (track, unit) => Math.round(track.temperature_min || 0) + ' ' + unit, }, - "maxtemp": { - label: "Max Temp: ", + maxtemp: { + label: 'Max Temp: ', value: (track, unit) => Math.round(track.temperature_max || 0) + ' ' + unit, }, - "avgtemp": { - label: "Avg Temp: ", + avgtemp: { + label: 'Avg Temp: ', value: (track, unit) => Math.round(track.temperature_avg || 0) + ' ' + unit, }, - } - }; + }, + } } diff --git a/src/components/leaflet-elevation/src/handlers/time.js b/src/components/leaflet-elevation/src/handlers/time.js index d94b13631..1949ccbb7 100644 --- a/src/components/leaflet-elevation/src/handlers/time.js +++ b/src/components/leaflet-elevation/src/handlers/time.js @@ -1,12 +1,12 @@ export function Time() { - const _ = L.Control.Elevation.Utils; + const _ = L.Control.Elevation.Utils - let opts = this.options; - let time = {}; + const opts = this.options + const time = {} - time.label = opts.timeLabel || 't'; - opts.timeFactor = opts.timeFactor || 3600; + time.label = opts.timeLabel || 't' + opts.timeFactor = opts.timeFactor || 3600 /** * Common AVG speeds: @@ -18,86 +18,86 @@ export function Time() { * driving = 72 km/h * ---------------------- */ - this._timeAVGSpeed = (opts.timeAVGSpeed || 3.6) * (opts.speedFactor || 1); + this._timeAVGSpeed = (opts.timeAVGSpeed || 3.6) * (opts.speedFactor || 1) if (!opts.timeFormat) { - opts.timeFormat = (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' '); + opts.timeFormat = (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' ') } else if (opts.timeFormat == 'time') { - opts.timeFormat = (time) => (new Date(time)).toLocaleTimeString(); + opts.timeFormat = (time) => (new Date(time)).toLocaleTimeString() } else if (opts.timeFormat == 'date') { - opts.timeFormat = (time) => (new Date(time)).toLocaleDateString(); + opts.timeFormat = (time) => (new Date(time)).toLocaleDateString() } - opts.xTimeFormat = opts.xTimeFormat || ((t) => _.formatTime(t).split("'")[0]); + opts.xTimeFormat = opts.xTimeFormat || ((t) => _.formatTime(t).split("'")[0]) return { name: 'time', required: (this.options.speed || this.options.acceleration || this.options.timestamps), - coordinateProperties: ["coordTimes", "times", "time"], + coordinateProperties: ['coordTimes', 'times', 'time'], coordPropsToMeta: _.parseDate, - pointToAttr: function(point, i) { + pointToAttr(point, i) { // Add missing timestamps (see: options.timeAVGSpeed) if (!point.meta || !point.meta.time) { - point.meta = point.meta || {}; + point.meta = point.meta || {} if (i > 0) { - let dx = (this._data[i].dist - this._data[i - 1].dist); - let t0 = this._data[i - 1].time.getTime(); - point.meta.time = new Date(t0 + ( dx / this._timeAVGSpeed) * this.options.timeFactor * 1000); + const dx = (this._data[i].dist - this._data[i - 1].dist) + const t0 = this._data[i - 1].time.getTime() + point.meta.time = new Date(t0 + (dx / this._timeAVGSpeed) * this.options.timeFactor * 1000) } else { point.meta.time = new Date(Date.now()) } } // Handle timezone offset - let time = (point.meta.time.getTime() - point.meta.time.getTimezoneOffset() * 60 * 1000 !== 0) ? point.meta.time : 0; + const time = (point.meta.time.getTime() - point.meta.time.getTimezoneOffset() * 60 * 1000 !== 0) ? point.meta.time : 0 // Update duration - this._data[i].duration = i > 0 ? (this._data[i - 1].duration || 0) + Math.abs(time - this._data[i - 1].time) : 0; - return time; + this._data[i].duration = i > 0 ? (this._data[i - 1].duration || 0) + Math.abs(time - this._data[i - 1].time) : 0 + return time }, onPointAdded: (_, i) => this.track_info.time = this._data[i].duration, - scale: (opts.time && opts.time != "summary" && !L.Browser.mobile) && { - axis : "x", - position : "top", - scale : { - attr : "duration", - min : 0, + scale: (opts.time && opts.time != 'summary' && !L.Browser.mobile) && { + axis: 'x', + position: 'top', + scale: { + attr: 'duration', + min: 0, }, - label : time.label, - labelY : -10, - labelX : () => this._width(), - name : "time", - ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]), - tickFormat : (d) => (d == 0 ? '' : opts.xTimeFormat(d)), + label: time.label, + labelY: -10, + labelX: () => this._width(), + name: 'time', + ticks: () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]), + tickFormat: (d) => (d == 0 ? '' : opts.xTimeFormat(d)), onAxisMount: axis => { - axis.select(".domain") - .remove(); - axis.selectAll("text") + axis.select('.domain') + .remove() + axis.selectAll('text') .attr('opacity', 0.65) .style('font-family', 'Monospace') - .style('font-size', '110%'); - axis.selectAll(".tick line") + .style('font-size', '110%') + axis.selectAll('.tick line') .attr('y2', this._height()) .attr('stroke-dasharray', 2) - .attr('opacity', 0.75); - } + .attr('opacity', 0.75) + }, }, tooltips: [ (this.options.time) && { name: 'time', - chart: (item) => L._("T: ") + _.formatTime(item.duration || 0), - order: 20 + chart: (item) => L._('T: ') + _.formatTime(item.duration || 0), + order: 20, }, (this.options.timestamps) && { name: 'date', - chart: (item) => L._("t: ") + this.options.timeFormat(item.time), + chart: (item) => L._('t: ') + this.options.timeFormat(item.time), order: 21, - } + }, ], summary: (this.options.time) && { - "tottime" : { - label: "Total Time: ", + tottime: { + label: 'Total Time: ', value: (track) => _.formatTime(track.time || 0), - order: 20 - } - } - }; -} \ No newline at end of file + order: 20, + }, + }, + } +} diff --git a/src/components/map/PhotoSuggestionsLayer.vue b/src/components/map/PhotoSuggestionsLayer.vue index 2f4457b49..59387e8da 100644 --- a/src/components/map/PhotoSuggestionsLayer.vue +++ b/src/components/map/PhotoSuggestionsLayer.vue @@ -320,10 +320,10 @@ export default { const token = getToken() if (token) { // For public shares, pass the share token to the Viewer - OCA.Viewer.open({ - path: photoList[0].path, + OCA.Viewer.open({ + path: photoList[0].path, list: photoList, - shareToken: token + shareToken: token, }) } else { // For logged-in users, use the standard approach @@ -396,10 +396,10 @@ export default { const token = getToken() if (token) { // For public shares, pass the share token to the Viewer - OCA.Viewer.open({ - path: photo.path, + OCA.Viewer.open({ + path: photo.path, list: [photo], - shareToken: token + shareToken: token, }) } else { // For logged-in users, use the standard approach diff --git a/src/components/map/PhotosLayer.vue b/src/components/map/PhotosLayer.vue index 0c5154252..a50932aea 100644 --- a/src/components/map/PhotosLayer.vue +++ b/src/components/map/PhotosLayer.vue @@ -310,10 +310,10 @@ export default { const token = getToken() if (token) { // For public shares, pass the share token to the Viewer - OCA.Viewer.open({ - path: photoList[0].path, + OCA.Viewer.open({ + path: photoList[0].path, list: photoList, - shareToken: token + shareToken: token, }) } else { // For logged-in users, use the standard approach @@ -378,10 +378,10 @@ export default { const token = getToken() if (token) { // For public shares, pass the share token to the Viewer - OCA.Viewer.open({ - path: photo.path, + OCA.Viewer.open({ + path: photo.path, list: [photo], - shareToken: token + shareToken: token, }) } else { // For logged-in users, use the standard approach