@raruto/leaflet-elevation 2.4.0 → 2.5.1
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 +3 -3
- 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/distance.js +46 -46
- package/src/handlers/labels.js +3 -3
- package/src/handlers/lineargradient.js +122 -0
- package/src/handlers/temperature.js +62 -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/distance.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
export function Distance() {
|
|
2
|
-
|
|
3
|
-
const _ = L.Control.Elevation.Utils;
|
|
4
|
-
|
|
5
|
-
let opts = this.options;
|
|
6
|
-
let distance = {};
|
|
7
|
-
|
|
8
|
-
opts.distanceFactor = opts.imperial ? this.__mileFactor : (opts.distanceFactor || 1); // 1 km = (1000 m)
|
|
9
|
-
distance.label = opts.imperial ? "mi" : opts.xLabel;
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
name: 'distance',
|
|
13
|
-
required: true,
|
|
14
|
-
attr: 'dist',
|
|
15
|
-
unit: distance.label,
|
|
16
|
-
decimals: 5,
|
|
17
|
-
pointToAttr: (_, i) => (i > 0 ? this._data[i - 1].dist : 0) + (this._data[i].latlng.distanceTo(this._data[i > 0 ? i - 1 : i].latlng) * opts.distanceFactor) / 1000, // convert back km to meters
|
|
18
|
-
// stats: { total: _.iSum },
|
|
19
|
-
onPointAdded: (distance, i) => this.track_info.distance = distance,
|
|
20
|
-
scale: opts.distance && {
|
|
21
|
-
axis : "x",
|
|
22
|
-
position: "bottom",
|
|
23
|
-
scale : "x", // this._chart._x,
|
|
24
|
-
labelY : 25,
|
|
25
|
-
labelX : () => this._width() + 6,
|
|
26
|
-
ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]),
|
|
27
|
-
},
|
|
28
|
-
grid: opts.distance && {
|
|
29
|
-
axis : "x",
|
|
30
|
-
position : "bottom",
|
|
31
|
-
scale : "x" // this._chart._x,
|
|
32
|
-
},
|
|
33
|
-
tooltip: opts.distance && {
|
|
34
|
-
name: 'x',
|
|
35
|
-
chart: (item) => L._("x: ") + _.round(item[opts.xAttr], opts.decimalsX) + " " + distance.label,
|
|
36
|
-
order: 20
|
|
37
|
-
},
|
|
38
|
-
summary: opts.distance && {
|
|
39
|
-
"totlen" : {
|
|
40
|
-
label: "Total Length: ",
|
|
41
|
-
value: (track) => (track.distance || 0).toFixed(2) + ' ' + distance.label,
|
|
42
|
-
order: 10
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
1
|
+
export function Distance() {
|
|
2
|
+
|
|
3
|
+
const _ = L.Control.Elevation.Utils;
|
|
4
|
+
|
|
5
|
+
let opts = this.options;
|
|
6
|
+
let distance = {};
|
|
7
|
+
|
|
8
|
+
opts.distanceFactor = opts.imperial ? this.__mileFactor : (opts.distanceFactor || 1); // 1 km = (1000 m)
|
|
9
|
+
distance.label = opts.imperial ? "mi" : opts.xLabel;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
name: 'distance',
|
|
13
|
+
required: true,
|
|
14
|
+
attr: 'dist',
|
|
15
|
+
unit: distance.label,
|
|
16
|
+
decimals: 5,
|
|
17
|
+
pointToAttr: (_, i) => (i > 0 ? this._data[i - 1].dist : 0) + (this._data[i].latlng.distanceTo(this._data[i > 0 ? i - 1 : i].latlng) * opts.distanceFactor) / 1000, // convert back km to meters
|
|
18
|
+
// stats: { total: _.iSum },
|
|
19
|
+
onPointAdded: (distance, i) => this.track_info.distance = distance,
|
|
20
|
+
scale: opts.distance && {
|
|
21
|
+
axis : "x",
|
|
22
|
+
position: "bottom",
|
|
23
|
+
scale : "x", // this._chart._x,
|
|
24
|
+
labelY : 25,
|
|
25
|
+
labelX : () => this._width() + 6,
|
|
26
|
+
ticks : () => _.clamp(this._chart._xTicks() / 2, [4, +Infinity]),
|
|
27
|
+
},
|
|
28
|
+
grid: opts.distance && {
|
|
29
|
+
axis : "x",
|
|
30
|
+
position : "bottom",
|
|
31
|
+
scale : "x" // this._chart._x,
|
|
32
|
+
},
|
|
33
|
+
tooltip: opts.distance && {
|
|
34
|
+
name: 'x',
|
|
35
|
+
chart: (item) => L._("x: ") + _.round(item[opts.xAttr], opts.decimalsX) + " " + distance.label,
|
|
36
|
+
order: 20
|
|
37
|
+
},
|
|
38
|
+
summary: opts.distance && {
|
|
39
|
+
"totlen" : {
|
|
40
|
+
label: "Total Length: ",
|
|
41
|
+
value: (track) => (track.distance || 0).toFixed(2) + ' ' + distance.label,
|
|
42
|
+
order: 10
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
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
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function Temperature() {
|
|
2
|
+
|
|
3
|
+
const _ = L.Control.Elevation.Utils;
|
|
4
|
+
|
|
5
|
+
let temperature = {};
|
|
6
|
+
let opts = this.options;
|
|
7
|
+
temperature.label = opts.label || L._(opts.imperial ? '°F' : '°C');
|
|
8
|
+
|
|
9
|
+
// Fahrenheit = (Celsius * 9/5) + 32
|
|
10
|
+
opts.temperatureFactor1 = opts.temperatureFactor1 ?? (opts.imperial ? 1.8 : 1);
|
|
11
|
+
opts.temperatureFactor2 = opts.temperatureFactor2 ?? (opts.imperial ? 32 : 0);
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
name: 'temperature', // <-- Your custom option name (eg. "temperature: true")
|
|
15
|
+
unit: temperature.label,
|
|
16
|
+
meta: 'atemps', // <-- point.meta.atemps
|
|
17
|
+
coordinateProperties: ["atemps"], // List of GPX Extensions ("coordinateProperties") to be handled by "@tmcw/toGeoJSON"
|
|
18
|
+
deltaMax: this.options.temperatureDeltaMax,
|
|
19
|
+
clampRange: this.options.temperatureRange,
|
|
20
|
+
decimals: 2,
|
|
21
|
+
pointToAttr: (point, i) => (point.meta.atemps ?? point.meta.atemps ?? point.prev('temperature')) * opts.temperatureFactor1 + opts.temperatureFactor2,
|
|
22
|
+
stats: { max: _.iMax, min: _.iMin, avg: _.iAvg },
|
|
23
|
+
scale: {
|
|
24
|
+
axis : "y",
|
|
25
|
+
position : "right",
|
|
26
|
+
scale : { min: -1, max: +1 },
|
|
27
|
+
tickPadding: 16,
|
|
28
|
+
labelX : +18,
|
|
29
|
+
labelY : -8,
|
|
30
|
+
},
|
|
31
|
+
path: {
|
|
32
|
+
label : temperature.label,
|
|
33
|
+
yAttr : 'temperature',
|
|
34
|
+
scaleX : 'distance',
|
|
35
|
+
scaleY : 'temperature',
|
|
36
|
+
color : 'transparent',
|
|
37
|
+
strokeColor : '#000',
|
|
38
|
+
strokeOpacity: "0.85",
|
|
39
|
+
// fillOpacity : "0.1",
|
|
40
|
+
},
|
|
41
|
+
tooltip: {
|
|
42
|
+
name: 'temperature',
|
|
43
|
+
chart: (item) => L._("Temp: ") + Math.round(item.temperature).toLocaleString() + " " + temperature.label,
|
|
44
|
+
marker: (item) => Math.round(item.temperature).toLocaleString() + " " + temperature.label,
|
|
45
|
+
order: 1
|
|
46
|
+
},
|
|
47
|
+
summary: {
|
|
48
|
+
"mintemp": {
|
|
49
|
+
label: "Min Temp: ",
|
|
50
|
+
value: (track, unit) => Math.round(track.temperature_min || 0) + ' ' + unit,
|
|
51
|
+
},
|
|
52
|
+
"maxtemp": {
|
|
53
|
+
label: "Max Temp: ",
|
|
54
|
+
value: (track, unit) => Math.round(track.temperature_max || 0) + ' ' + unit,
|
|
55
|
+
},
|
|
56
|
+
"avgtemp": {
|
|
57
|
+
label: "Avg Temp: ",
|
|
58
|
+
value: (track, unit) => Math.round(track.temperature_avg || 0) + ' ' + unit,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|