@raruto/leaflet-elevation 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +325 -324
- package/dist/leaflet-elevation.js +35 -37
- package/dist/leaflet-elevation.js.map +1 -1
- package/dist/leaflet-elevation.min.js +1 -1
- package/dist/leaflet-elevation.min.js.map +1 -1
- package/libs/leaflet-hotline.js +456 -456
- package/package.json +2 -2
- package/src/components/chart.js +783 -783
- package/src/components/d3.js +649 -641
- package/src/components/marker.js +96 -96
- package/src/control.js +31 -33
- package/src/handlers/labels.js +3 -3
- package/src/handlers/lineargradient.js +122 -0
- package/src/handlers/time.js +102 -102
- package/src/utils.js +2 -2
package/src/components/marker.js
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import * as D3 from './d3.js';
|
|
2
|
-
|
|
3
|
-
const _ = L.Control.Elevation.Utils;
|
|
4
|
-
|
|
5
|
-
export var Marker = L.Class.extend({
|
|
6
|
-
|
|
7
|
-
initialize(options, control) {
|
|
8
|
-
this.options = options;
|
|
9
|
-
this.control = control
|
|
10
|
-
|
|
11
|
-
switch(this.options.marker) {
|
|
12
|
-
case 'elevation-line':
|
|
13
|
-
// this._container = d3.create("g").attr("class", "height-focus-group");
|
|
14
|
-
break;
|
|
15
|
-
case 'position-marker':
|
|
16
|
-
// this._marker = L.circleMarker([0, 0], { pane: 'overlayPane', radius: 6, fillColor: '#fff', fillOpacity:1, color: '#000', weight:1, interactive: false });
|
|
17
|
-
this._marker = L.marker([0, 0], { icon: this.options.markerIcon, zIndexOffset: 1000000, interactive: false });
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
this._labels = {};
|
|
22
|
-
|
|
23
|
-
return this;
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
addTo(map) {
|
|
27
|
-
this._map = map;
|
|
28
|
-
switch(this.options.marker) {
|
|
29
|
-
case 'elevation-line': this._container = d3.select(map.getPane('elevationPane')).select("svg > g").call(D3.PositionMarker({})); break;
|
|
30
|
-
case 'position-marker': this._marker.addTo(map, { pane: 'overlayPane' }); break;
|
|
31
|
-
}
|
|
32
|
-
return this;
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Update position marker ("leaflet-marker").
|
|
37
|
-
*/
|
|
38
|
-
update(props) {
|
|
39
|
-
if (props) this._props = props;
|
|
40
|
-
else props = this._props;
|
|
41
|
-
|
|
42
|
-
if(!props) return;
|
|
43
|
-
|
|
44
|
-
if (props.options) this.options = props.options;
|
|
45
|
-
if (!this._map) this.addTo(props.map);
|
|
46
|
-
|
|
47
|
-
this._latlng = props.item.latlng;
|
|
48
|
-
|
|
49
|
-
switch(this.options.marker) {
|
|
50
|
-
case 'elevation-line':
|
|
51
|
-
if (this._container) {
|
|
52
|
-
let point = this._map.latLngToLayerPoint(this._latlng);
|
|
53
|
-
point = L.extend({}, props.item, this._map._rotate ? this._map.rotatedPointToMapPanePoint(point) : point);
|
|
54
|
-
|
|
55
|
-
let yMax = (this.control._height() / props.yCoordMax * point[this.options.yAttr]);
|
|
56
|
-
|
|
57
|
-
if(!isFinite(yMax) || isNaN(yMax)) yMax = 0;
|
|
58
|
-
|
|
59
|
-
this._container.classed("leaflet-hidden", false);
|
|
60
|
-
this._container.call(D3.PositionMarker({
|
|
61
|
-
theme : this.options.theme,
|
|
62
|
-
xCoord: point.x,
|
|
63
|
-
yCoord: point.y,
|
|
64
|
-
length: point.y - yMax, // normalized Y
|
|
65
|
-
labels: this._labels,
|
|
66
|
-
item: point,
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
break;
|
|
70
|
-
case 'position-marker':
|
|
71
|
-
_.removeClass(this._marker.getElement(), 'leaflet-hidden');
|
|
72
|
-
this._marker.setLatLng(this._latlng);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
/*
|
|
78
|
-
* Hides the position/height indicator marker drawn onto the map
|
|
79
|
-
*/
|
|
80
|
-
remove() {
|
|
81
|
-
this._props = null;
|
|
82
|
-
switch(this.options.marker) {
|
|
83
|
-
case 'elevation-line': this._container && this._container.classed("leaflet-hidden", true); break;
|
|
84
|
-
case 'position-marker': _.addClass(this._marker.getElement(), 'leaflet-hidden'); break;
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
getLatLng() {
|
|
89
|
-
return this._latlng;
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
_registerTooltip(props) {
|
|
93
|
-
this._labels[props.name] = props;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
});
|
|
1
|
+
import * as D3 from './d3.js';
|
|
2
|
+
|
|
3
|
+
const _ = L.Control.Elevation.Utils;
|
|
4
|
+
|
|
5
|
+
export var Marker = L.Class.extend({
|
|
6
|
+
|
|
7
|
+
initialize(options, control) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.control = control
|
|
10
|
+
|
|
11
|
+
switch(this.options.marker) {
|
|
12
|
+
case 'elevation-line':
|
|
13
|
+
// this._container = d3.create("g").attr("class", "height-focus-group");
|
|
14
|
+
break;
|
|
15
|
+
case 'position-marker':
|
|
16
|
+
// this._marker = L.circleMarker([0, 0], { pane: 'overlayPane', radius: 6, fillColor: '#fff', fillOpacity:1, color: '#000', weight:1, interactive: false });
|
|
17
|
+
this._marker = L.marker([0, 0], { icon: this.options.markerIcon, zIndexOffset: 1000000, interactive: false });
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this._labels = {};
|
|
22
|
+
|
|
23
|
+
return this;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
addTo(map) {
|
|
27
|
+
this._map = map;
|
|
28
|
+
switch(this.options.marker) {
|
|
29
|
+
case 'elevation-line': this._container = d3.select(map.getPane('elevationPane')).select("svg > g").call(D3.PositionMarker({})); break;
|
|
30
|
+
case 'position-marker': this._marker.addTo(map, { pane: 'overlayPane' }); break;
|
|
31
|
+
}
|
|
32
|
+
return this;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Update position marker ("leaflet-marker").
|
|
37
|
+
*/
|
|
38
|
+
update(props) {
|
|
39
|
+
if (props) this._props = props;
|
|
40
|
+
else props = this._props;
|
|
41
|
+
|
|
42
|
+
if (!props) return;
|
|
43
|
+
|
|
44
|
+
if (props.options) this.options = props.options;
|
|
45
|
+
if (!this._map) this.addTo(props.map);
|
|
46
|
+
|
|
47
|
+
this._latlng = props.item.latlng;
|
|
48
|
+
|
|
49
|
+
switch(this.options.marker) {
|
|
50
|
+
case 'elevation-line':
|
|
51
|
+
if (this._container) {
|
|
52
|
+
let point = this._map.latLngToLayerPoint(this._latlng);
|
|
53
|
+
point = L.extend({}, props.item, this._map._rotate ? this._map.rotatedPointToMapPanePoint(point) : point);
|
|
54
|
+
|
|
55
|
+
let yMax = (this.control._height() / props.yCoordMax * point[this.options.yAttr]);
|
|
56
|
+
|
|
57
|
+
if (!isFinite(yMax) || isNaN(yMax)) yMax = 0;
|
|
58
|
+
|
|
59
|
+
this._container.classed("leaflet-hidden", false);
|
|
60
|
+
this._container.call(D3.PositionMarker({
|
|
61
|
+
theme : this.options.theme,
|
|
62
|
+
xCoord: point.x,
|
|
63
|
+
yCoord: point.y,
|
|
64
|
+
length: point.y - yMax, // normalized Y
|
|
65
|
+
labels: this._labels,
|
|
66
|
+
item: point,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case 'position-marker':
|
|
71
|
+
_.removeClass(this._marker.getElement(), 'leaflet-hidden');
|
|
72
|
+
this._marker.setLatLng(this._latlng);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* Hides the position/height indicator marker drawn onto the map
|
|
79
|
+
*/
|
|
80
|
+
remove() {
|
|
81
|
+
this._props = null;
|
|
82
|
+
switch(this.options.marker) {
|
|
83
|
+
case 'elevation-line': this._container && this._container.classed("leaflet-hidden", true); break;
|
|
84
|
+
case 'position-marker': _.addClass(this._marker.getElement(), 'leaflet-hidden'); break;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
getLatLng() {
|
|
89
|
+
return this._latlng;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
_registerTooltip(props) {
|
|
93
|
+
this._labels[props.name] = props;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
});
|
package/src/control.js
CHANGED
|
@@ -14,7 +14,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
14
14
|
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
|
|
15
15
|
__footFactor: 3.28084, // 1 m = (3.28084 ft)
|
|
16
16
|
__D3: 'https://unpkg.com/d3@7.8.4/dist/d3.min.js',
|
|
17
|
-
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@5.6.
|
|
17
|
+
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@5.6.2/dist/togeojson.umd.js',
|
|
18
18
|
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.10.1/src/leaflet.geometryutil.js',
|
|
19
19
|
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
|
|
20
20
|
__LHOTLINE: '../libs/leaflet-hotline.min.js',
|
|
@@ -214,6 +214,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
214
214
|
* Javascript scripts downloader (lazy loader)
|
|
215
215
|
*/
|
|
216
216
|
import(src, condition) {
|
|
217
|
+
if (Array.isArray(src)) {
|
|
218
|
+
return Promise.all(src.map(m => this.import(m)));
|
|
219
|
+
}
|
|
217
220
|
switch(src) {
|
|
218
221
|
case this.__D3: condition = typeof d3 !== 'object'; break;
|
|
219
222
|
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
|
@@ -401,38 +404,36 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
401
404
|
* Initialize "L.AlmostOver" integration
|
|
402
405
|
*/
|
|
403
406
|
_initAlmostOverHandler(map, layer) {
|
|
404
|
-
return (map && this.options.almostOver && !L.Browser.mobile) ?
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
|
|
410
|
-
map.almostOver.addLayer(layer);
|
|
411
|
-
map
|
|
412
|
-
.on('almost:move', this._onMouseMoveLayer, this)
|
|
413
|
-
.on('almost:out', this._onMouseOut, this);
|
|
414
|
-
this.once('eledata_clear', () => {
|
|
415
|
-
map.almostOver.removeLayer(layer);
|
|
407
|
+
return (map && this.options.almostOver && !L.Browser.mobile) ? this.import([this.__LGEOMUTIL, this.__LALMOSTOVER])
|
|
408
|
+
.then(() => {
|
|
409
|
+
map.addHandler('almostOver', L.Handler.AlmostOver)
|
|
410
|
+
if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
|
|
411
|
+
map.almostOver.addLayer(layer);
|
|
416
412
|
map
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
413
|
+
.on('almost:move', this._onMouseMoveLayer, this)
|
|
414
|
+
.on('almost:out', this._onMouseOut, this);
|
|
415
|
+
this.once('eledata_clear', () => {
|
|
416
|
+
map.almostOver.removeLayer(layer);
|
|
417
|
+
map
|
|
418
|
+
.off('almost:move', this._onMouseMoveLayer, this)
|
|
419
|
+
.off('almost:out', this._onMouseOut, this);
|
|
420
|
+
})
|
|
421
|
+
}
|
|
422
|
+
}) : Promise.resolve();
|
|
422
423
|
},
|
|
423
424
|
|
|
424
425
|
/**
|
|
425
426
|
* Initialize "L.DistanceMarkers" integration
|
|
426
427
|
*/
|
|
427
428
|
_initDistanceMarkers() {
|
|
428
|
-
return this.options.distanceMarkers ?
|
|
429
|
+
return this.options.distanceMarkers ? this.import([this.__LGEOMUTIL, this.__LDISTANCEM]) : Promise.resolve();
|
|
429
430
|
},
|
|
430
431
|
|
|
431
432
|
/**
|
|
432
433
|
* Initialize "L.Control.EdgeScale" integration
|
|
433
434
|
*/
|
|
434
435
|
_initEdgeScale(map) {
|
|
435
|
-
return this.options.edgeScale ?
|
|
436
|
+
return this.options.edgeScale ? this.import(this.__LEDGESCALE)
|
|
436
437
|
.then(() => {
|
|
437
438
|
map.edgeScaleControl = map.edgeScaleControl || L.control.edgeScale('boolean' !== typeof this.options.edgeScale ? this.options.edgeScale : {}).addTo(map);
|
|
438
439
|
}) : Promise.resolve();
|
|
@@ -443,7 +444,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
443
444
|
return this.options.hotline ? this.import(this.__LHOTLINE)
|
|
444
445
|
.then(() => {
|
|
445
446
|
layer.eachLayer((trkseg) => {
|
|
446
|
-
if(trkseg.feature.geometry.type != "Point") {
|
|
447
|
+
if (trkseg.feature.geometry.type != "Point") {
|
|
447
448
|
let geo = L.geoJson(trkseg.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1))});
|
|
448
449
|
let line = L.hotline(geo.toGeoJSON().features[0].geometry.coordinates, {
|
|
449
450
|
renderer: L.Hotline.renderer(),
|
|
@@ -618,10 +619,9 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
618
619
|
if (opts._maxWidth > clientWidth) opts.width = clientWidth - 30;
|
|
619
620
|
}
|
|
620
621
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
]).then((m) => {
|
|
622
|
+
this
|
|
623
|
+
.import([this.__D3, this.__LCHART])
|
|
624
|
+
.then((m) => {
|
|
625
625
|
|
|
626
626
|
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
|
627
627
|
|
|
@@ -689,13 +689,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
689
689
|
if (this._renderer) this._renderer.remove()
|
|
690
690
|
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
|
691
691
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
this.fire("elechart_marker");
|
|
698
|
-
});
|
|
692
|
+
this.import([this.__D3, this.__LMARKER])
|
|
693
|
+
.then((m) => {
|
|
694
|
+
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
|
695
|
+
this.fire("elechart_marker");
|
|
696
|
+
});
|
|
699
697
|
},
|
|
700
698
|
|
|
701
699
|
/**
|
|
@@ -829,7 +827,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
829
827
|
},
|
|
830
828
|
|
|
831
829
|
_onKeyDown({key}) {
|
|
832
|
-
if(!this.options.detached && key === "Escape"){
|
|
830
|
+
if (!this.options.detached && key === "Escape"){
|
|
833
831
|
this._collapse()
|
|
834
832
|
};
|
|
835
833
|
},
|
package/src/handlers/labels.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @see https://github.com/Raruto/leaflet-elevation/issues/211
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* @example
|
|
5
5
|
* ```js
|
|
6
6
|
* L.control.Elevation({ handlers: [ 'Labels' ], labelsRotation: 25, labelsAlign: 'start' })
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export function Labels() {
|
|
10
10
|
|
|
11
|
-
this.on('
|
|
11
|
+
this.on('elechart_updated', function(e) {
|
|
12
12
|
|
|
13
13
|
const pointG = this._chart._chart.pane('point');
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ export function Labels() {
|
|
|
29
29
|
.attr("stroke", '#000')
|
|
30
30
|
.attr("stroke-width", '1.1');
|
|
31
31
|
|
|
32
|
-
} else if (!isNaN(textRotation)) {
|
|
32
|
+
} else if (!isNaN(textRotation)) {
|
|
33
33
|
|
|
34
34
|
pointG.selectAll('text')
|
|
35
35
|
.attr("dx", "4px")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/Raruto/leaflet-elevation/issues/251
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```js
|
|
6
|
+
* L.control.Elevation({
|
|
7
|
+
* altitude: true,
|
|
8
|
+
* distance: true,
|
|
9
|
+
* handlers: [ 'Altitude', 'Distance', 'LinearGradient', ],
|
|
10
|
+
* linearGradient: {
|
|
11
|
+
* attr: 'z',
|
|
12
|
+
* path: 'altitude',
|
|
13
|
+
* range: { 0.0: '#008800', 0.5: '#ffff00', 1.0: '#ff0000' },
|
|
14
|
+
* min: 'elevation_min',
|
|
15
|
+
* max: 'elevation_max',
|
|
16
|
+
* },
|
|
17
|
+
* })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function LinearGradient() {
|
|
21
|
+
|
|
22
|
+
if (!this.options.linearGradient) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const _ = L.Control.Elevation.Utils;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize gradient color palette.
|
|
30
|
+
*/
|
|
31
|
+
const get_palette = function ({range, min, max, depth = 256}) {
|
|
32
|
+
const canvas = document.createElement('canvas'),
|
|
33
|
+
ctx = canvas.getContext('2d'),
|
|
34
|
+
gradient = ctx.createLinearGradient(0, 0, 0, depth);
|
|
35
|
+
|
|
36
|
+
canvas.width = 1;
|
|
37
|
+
canvas.height = depth;
|
|
38
|
+
|
|
39
|
+
for (let i in range) {
|
|
40
|
+
gradient.addColorStop(i, range[i]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ctx.fillStyle = gradient;
|
|
44
|
+
ctx.fillRect(0, 0, 1, depth);
|
|
45
|
+
|
|
46
|
+
const { data } = ctx.getImageData(0, 0, 1, depth);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
/**
|
|
50
|
+
* Gets the RGB values of a given z value of the current palette.
|
|
51
|
+
*
|
|
52
|
+
* @param {number} value - Value to get the color for, should be between min and max.
|
|
53
|
+
* @returns {string} The RGB values as `rgb(r, g, b)` string
|
|
54
|
+
*/
|
|
55
|
+
getRGBColor(value) {
|
|
56
|
+
const idx = Math.floor(Math.min(Math.max((value - min) / (max - min), 0), 0.999) * depth) * 4;
|
|
57
|
+
return 'rgb(' + [data[idx], data[idx + 1], data[idx + 2]].join(',') + ')';
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const { preferCanvas } = this.options;
|
|
63
|
+
const { attr, path: path_name, range, min, max } = L.extend({
|
|
64
|
+
attr: 'z',
|
|
65
|
+
path: 'altitude',
|
|
66
|
+
range: { 0.0: '#008800', 0.5: '#ffff00', 1.0: '#ff0000' },
|
|
67
|
+
min: 'elevation_min',
|
|
68
|
+
max: 'elevation_max',
|
|
69
|
+
}, (true === this.options.linearGradient) ? {} : this.options.linearGradient);
|
|
70
|
+
|
|
71
|
+
const gradient_id = path_name + '-gradient-' + _.randomId();
|
|
72
|
+
const legend_id = 'legend-' + gradient_id;
|
|
73
|
+
|
|
74
|
+
// Charte profile gradient
|
|
75
|
+
this.on('elechart_axis', () => {
|
|
76
|
+
if (!this._data.length) return;
|
|
77
|
+
|
|
78
|
+
const chart = this._chart;
|
|
79
|
+
const path = chart._paths[path_name];
|
|
80
|
+
const { defs } = chart._chart.utils;
|
|
81
|
+
|
|
82
|
+
const palette = get_palette({
|
|
83
|
+
min: isFinite(this.track_info[min]) ? this.track_info[min] : 0,
|
|
84
|
+
max: isFinite(this.track_info[max]) ? this.track_info[max] : 1,
|
|
85
|
+
range,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let gradient;
|
|
89
|
+
|
|
90
|
+
if (preferCanvas) {
|
|
91
|
+
/** ref: `path.__fillStyle` within L.Control.Elevation.Utils::drawCanvas(ctx, path) */
|
|
92
|
+
path.__fillStyle = gradient = chart._context.createLinearGradient(0, 0, chart._width(), 0);
|
|
93
|
+
} else {
|
|
94
|
+
defs.select('#' + gradient_id).remove();
|
|
95
|
+
gradient = defs.append('svg:linearGradient').attr('id', gradient_id);
|
|
96
|
+
gradient.addColorStop = function(offset, color) { gradient.append('svg:stop').attr('offset', offset).attr('stop-color', color) };
|
|
97
|
+
path.attr('fill', 'url(#' + gradient_id + ')').classed('area', false);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Generate gradient for each segment picking colors from palette
|
|
101
|
+
for (let i = 0, data = this._data; i < data.length; i++) {
|
|
102
|
+
gradient.addColorStop((i) / data.length, palette.getRGBColor(data[i][attr]));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Legend item gradient
|
|
108
|
+
this.on('elechart_updated', () => {
|
|
109
|
+
const chart = this._chart;
|
|
110
|
+
const { defs } = chart._chart.utils;
|
|
111
|
+
defs.select('#' + legend_id).remove();
|
|
112
|
+
const legendGradient = defs.append('svg:linearGradient').attr('id', legend_id);
|
|
113
|
+
Object.keys(range).sort().forEach(i => legendGradient.append('svg:stop').attr('offset', i).attr('stop-color', range[i]));
|
|
114
|
+
|
|
115
|
+
chart._container
|
|
116
|
+
.select('.legend-' + path_name + ' > rect')
|
|
117
|
+
.attr('fill', 'url(#' + legend_id + ')')
|
|
118
|
+
.classed('area', false);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return { };
|
|
122
|
+
}
|
package/src/handlers/time.js
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
export function Time() {
|
|
2
|
-
|
|
3
|
-
const _ = L.Control.Elevation.Utils;
|
|
4
|
-
|
|
5
|
-
let opts = this.options;
|
|
6
|
-
let time = {};
|
|
7
|
-
|
|
8
|
-
time.label = opts.timeLabel || 't';
|
|
9
|
-
opts.timeFactor = opts.timeFactor || 3600;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Common AVG speeds:
|
|
13
|
-
* ----------------------
|
|
14
|
-
* slow walk = 1.8 km/h
|
|
15
|
-
* walking = 3.6 km/h <-- default: 3.6
|
|
16
|
-
* running = 10.8 km/h
|
|
17
|
-
* cycling = 18 km/h
|
|
18
|
-
* driving = 72 km/h
|
|
19
|
-
* ----------------------
|
|
20
|
-
*/
|
|
21
|
-
this._timeAVGSpeed = (opts.timeAVGSpeed || 3.6) * (opts.speedFactor || 1);
|
|
22
|
-
|
|
23
|
-
if (!opts.timeFormat) {
|
|
24
|
-
opts.timeFormat = (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' ');
|
|
25
|
-
} else if (opts.timeFormat == 'time') {
|
|
26
|
-
opts.timeFormat = (time) => (new Date(time)).toLocaleTimeString();
|
|
27
|
-
} else if (opts.timeFormat == 'date') {
|
|
28
|
-
opts.timeFormat = (time) => (new Date(time)).toLocaleDateString();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
opts.xTimeFormat = opts.xTimeFormat || ((t) => _.formatTime(t).split("'")[0]);
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
name: 'time',
|
|
35
|
-
required: (this.options.speed || this.options.acceleration || this.options.timestamps),
|
|
36
|
-
coordinateProperties: ["coordTimes", "times", "time"],
|
|
37
|
-
coordPropsToMeta: _.parseDate,
|
|
38
|
-
pointToAttr: function(point, i) {
|
|
39
|
-
// Add missing timestamps (see: options.timeAVGSpeed)
|
|
40
|
-
if (!point.meta || !point.meta.time) {
|
|
41
|
-
point.meta = point.meta || {};
|
|
42
|
-
if(i > 0) {
|
|
43
|
-
let dx = (this._data[i].dist - this._data[i - 1].dist);
|
|
44
|
-
let t0 = this._data[i - 1].time.getTime();
|
|
45
|
-
point.meta.time = new Date(t0 + ( dx / this._timeAVGSpeed) * this.options.timeFactor * 1000);
|
|
46
|
-
} else {
|
|
47
|
-
point.meta.time = new Date(Date.now())
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// Handle timezone offset
|
|
51
|
-
let time = (point.meta.time.getTime() - point.meta.time.getTimezoneOffset() * 60 * 1000 !== 0) ? point.meta.time : 0;
|
|
52
|
-
// Update duration
|
|
53
|
-
this._data[i].duration = i > 0 ? (this._data[i - 1].duration || 0) + Math.abs(time - this._data[i - 1].time) : 0;
|
|
54
|
-
return time;
|
|
55
|
-
},
|
|
56
|
-
onPointAdded: (_, i) => this.track_info.time = this._data[i].duration,
|
|
57
|
-
scale: (opts.time && opts.time != "summary" && !L.Browser.mobile) && {
|
|
58
|
-
axis : "x",
|
|
59
|
-
position : "top",
|
|
60
|
-
scale : {
|
|
61
|
-
attr : "duration",
|
|
62
|
-
min : 0,
|
|
63
|
-
},
|
|
64
|
-
label : time.label,
|
|
65
|
-
labelY : -10,
|
|
66
|
-
labelX : () => this._width(),
|
|
67
|
-
name : "time",
|
|
68
|
-
ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]),
|
|
69
|
-
tickFormat : (d) => (d == 0 ? '' : opts.xTimeFormat(d)),
|
|
70
|
-
onAxisMount: axis => {
|
|
71
|
-
axis.select(".domain")
|
|
72
|
-
.remove();
|
|
73
|
-
axis.selectAll("text")
|
|
74
|
-
.attr('opacity', 0.65)
|
|
75
|
-
.style('font-family', 'Monospace')
|
|
76
|
-
.style('font-size', '110%');
|
|
77
|
-
axis.selectAll(".tick line")
|
|
78
|
-
.attr('y2', this._height())
|
|
79
|
-
.attr('stroke-dasharray', 2)
|
|
80
|
-
.attr('opacity', 0.75);
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
tooltips: [
|
|
84
|
-
(this.options.time) && {
|
|
85
|
-
name: 'time',
|
|
86
|
-
chart: (item) => L._("T: ") + _.formatTime(item.duration || 0),
|
|
87
|
-
order: 20
|
|
88
|
-
},
|
|
89
|
-
(this.options.timestamps) && {
|
|
90
|
-
name: 'date',
|
|
91
|
-
chart: (item) => L._("t: ") + this.options.timeFormat(item.time),
|
|
92
|
-
order: 21,
|
|
93
|
-
}
|
|
94
|
-
],
|
|
95
|
-
summary: (this.options.time) && {
|
|
96
|
-
"tottime" : {
|
|
97
|
-
label: "Total Time: ",
|
|
98
|
-
value: (track) => _.formatTime(track.time || 0),
|
|
99
|
-
order: 20
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
};
|
|
1
|
+
export function Time() {
|
|
2
|
+
|
|
3
|
+
const _ = L.Control.Elevation.Utils;
|
|
4
|
+
|
|
5
|
+
let opts = this.options;
|
|
6
|
+
let time = {};
|
|
7
|
+
|
|
8
|
+
time.label = opts.timeLabel || 't';
|
|
9
|
+
opts.timeFactor = opts.timeFactor || 3600;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Common AVG speeds:
|
|
13
|
+
* ----------------------
|
|
14
|
+
* slow walk = 1.8 km/h
|
|
15
|
+
* walking = 3.6 km/h <-- default: 3.6
|
|
16
|
+
* running = 10.8 km/h
|
|
17
|
+
* cycling = 18 km/h
|
|
18
|
+
* driving = 72 km/h
|
|
19
|
+
* ----------------------
|
|
20
|
+
*/
|
|
21
|
+
this._timeAVGSpeed = (opts.timeAVGSpeed || 3.6) * (opts.speedFactor || 1);
|
|
22
|
+
|
|
23
|
+
if (!opts.timeFormat) {
|
|
24
|
+
opts.timeFormat = (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' ');
|
|
25
|
+
} else if (opts.timeFormat == 'time') {
|
|
26
|
+
opts.timeFormat = (time) => (new Date(time)).toLocaleTimeString();
|
|
27
|
+
} else if (opts.timeFormat == 'date') {
|
|
28
|
+
opts.timeFormat = (time) => (new Date(time)).toLocaleDateString();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
opts.xTimeFormat = opts.xTimeFormat || ((t) => _.formatTime(t).split("'")[0]);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
name: 'time',
|
|
35
|
+
required: (this.options.speed || this.options.acceleration || this.options.timestamps),
|
|
36
|
+
coordinateProperties: ["coordTimes", "times", "time"],
|
|
37
|
+
coordPropsToMeta: _.parseDate,
|
|
38
|
+
pointToAttr: function(point, i) {
|
|
39
|
+
// Add missing timestamps (see: options.timeAVGSpeed)
|
|
40
|
+
if (!point.meta || !point.meta.time) {
|
|
41
|
+
point.meta = point.meta || {};
|
|
42
|
+
if (i > 0) {
|
|
43
|
+
let dx = (this._data[i].dist - this._data[i - 1].dist);
|
|
44
|
+
let t0 = this._data[i - 1].time.getTime();
|
|
45
|
+
point.meta.time = new Date(t0 + ( dx / this._timeAVGSpeed) * this.options.timeFactor * 1000);
|
|
46
|
+
} else {
|
|
47
|
+
point.meta.time = new Date(Date.now())
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Handle timezone offset
|
|
51
|
+
let time = (point.meta.time.getTime() - point.meta.time.getTimezoneOffset() * 60 * 1000 !== 0) ? point.meta.time : 0;
|
|
52
|
+
// Update duration
|
|
53
|
+
this._data[i].duration = i > 0 ? (this._data[i - 1].duration || 0) + Math.abs(time - this._data[i - 1].time) : 0;
|
|
54
|
+
return time;
|
|
55
|
+
},
|
|
56
|
+
onPointAdded: (_, i) => this.track_info.time = this._data[i].duration,
|
|
57
|
+
scale: (opts.time && opts.time != "summary" && !L.Browser.mobile) && {
|
|
58
|
+
axis : "x",
|
|
59
|
+
position : "top",
|
|
60
|
+
scale : {
|
|
61
|
+
attr : "duration",
|
|
62
|
+
min : 0,
|
|
63
|
+
},
|
|
64
|
+
label : time.label,
|
|
65
|
+
labelY : -10,
|
|
66
|
+
labelX : () => this._width(),
|
|
67
|
+
name : "time",
|
|
68
|
+
ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]),
|
|
69
|
+
tickFormat : (d) => (d == 0 ? '' : opts.xTimeFormat(d)),
|
|
70
|
+
onAxisMount: axis => {
|
|
71
|
+
axis.select(".domain")
|
|
72
|
+
.remove();
|
|
73
|
+
axis.selectAll("text")
|
|
74
|
+
.attr('opacity', 0.65)
|
|
75
|
+
.style('font-family', 'Monospace')
|
|
76
|
+
.style('font-size', '110%');
|
|
77
|
+
axis.selectAll(".tick line")
|
|
78
|
+
.attr('y2', this._height())
|
|
79
|
+
.attr('stroke-dasharray', 2)
|
|
80
|
+
.attr('opacity', 0.75);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
tooltips: [
|
|
84
|
+
(this.options.time) && {
|
|
85
|
+
name: 'time',
|
|
86
|
+
chart: (item) => L._("T: ") + _.formatTime(item.duration || 0),
|
|
87
|
+
order: 20
|
|
88
|
+
},
|
|
89
|
+
(this.options.timestamps) && {
|
|
90
|
+
name: 'date',
|
|
91
|
+
chart: (item) => L._("t: ") + this.options.timeFormat(item.time),
|
|
92
|
+
order: 21,
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
summary: (this.options.time) && {
|
|
96
|
+
"tottime" : {
|
|
97
|
+
label: "Total Time: ",
|
|
98
|
+
value: (track) => _.formatTime(track.time || 0),
|
|
99
|
+
order: 20
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
103
|
}
|