@raruto/leaflet-elevation 2.3.4 → 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 -323
- package/dist/leaflet-elevation.js +1619 -1575
- 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-gpxgroup.js +346 -340
- package/libs/leaflet-gpxgroup.min.js +1 -1
- package/libs/leaflet-hotline.js +456 -456
- package/package.json +74 -74
- 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 +1335 -1332
- package/src/handlers/labels.js +45 -0
- package/src/handlers/lineargradient.js +122 -0
- package/src/handlers/runner.js +109 -0
- package/src/handlers/time.js +102 -102
- package/src/options.js +88 -88
- package/src/utils.js +203 -163
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/Raruto/leaflet-elevation/issues/211
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```js
|
|
6
|
+
* L.control.Elevation({ handlers: [ 'Labels' ], labelsRotation: 25, labelsAlign: 'start' })
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export function Labels() {
|
|
10
|
+
|
|
11
|
+
this.on('elechart_updated', function(e) {
|
|
12
|
+
|
|
13
|
+
const pointG = this._chart._chart.pane('point');
|
|
14
|
+
|
|
15
|
+
const textRotation = this.options.labelsRotation ?? -90;
|
|
16
|
+
const textAnchor = this.options.labelsAlign;
|
|
17
|
+
|
|
18
|
+
if (90 == Math.abs(textRotation)) {
|
|
19
|
+
|
|
20
|
+
pointG.selectAll('text')
|
|
21
|
+
.attr('dy', '4px')
|
|
22
|
+
.attr("dx", (d, i, el) => Math.sign(textRotation) * (this._height() - d3.select(el[i].parentElement).datum().y - 8) + 'px')
|
|
23
|
+
.attr('text-anchor', textRotation > 0 ? 'end' : 'start')
|
|
24
|
+
.attr('transform', 'rotate(' + textRotation + ')')
|
|
25
|
+
|
|
26
|
+
pointG.selectAll('circle')
|
|
27
|
+
.attr('r', '2.5')
|
|
28
|
+
.attr('fill', '#fff')
|
|
29
|
+
.attr("stroke", '#000')
|
|
30
|
+
.attr("stroke-width", '1.1');
|
|
31
|
+
|
|
32
|
+
} else if (!isNaN(textRotation)) {
|
|
33
|
+
|
|
34
|
+
pointG.selectAll('text')
|
|
35
|
+
.attr("dx", "4px")
|
|
36
|
+
.attr("dy", "-9px")
|
|
37
|
+
.attr('text-anchor', textAnchor ?? (0 == textRotation ? 'start' : 'middle'))
|
|
38
|
+
.attr('transform', 'rotate('+ -textRotation +')')
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return { };
|
|
45
|
+
}
|
|
@@ -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,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://github.com/Igor-Vladyka/leaflet.motion
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```js
|
|
6
|
+
* L.control.Elevation({ handlers: [ 'Runner' ], runnerOptions: { polyline: {..}, motion: {..}, marker {..} } })
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export async function Runner() {
|
|
10
|
+
|
|
11
|
+
await this.import(this.__LMOTION || 'https://unpkg.com/leaflet.motion@0.3.2/dist/leaflet.motion.min.js', typeof L.Motion !== 'object')
|
|
12
|
+
|
|
13
|
+
let { runnerOptions } = this.options;
|
|
14
|
+
|
|
15
|
+
runnerOptions = L.extend(
|
|
16
|
+
{ polyline: {}, motion: {}, marker: undefined },
|
|
17
|
+
'object' === typeof runnerOptions ? runnerOptions : {}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Custom tooltips
|
|
21
|
+
this._registerTooltip({
|
|
22
|
+
name: 'here',
|
|
23
|
+
marker: (item) => L._("You are here: "),
|
|
24
|
+
order: 1,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
this._registerTooltip({
|
|
28
|
+
name: 'distance',
|
|
29
|
+
marker: (item) => Math.round(item.dist) + " " + this.options.xLabel,
|
|
30
|
+
order: 2,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.addCheckpoint = function (checkpoint) {
|
|
34
|
+
return this._registerCheckPoint({ // <-- NB these are private functions use them at your own risk!
|
|
35
|
+
latlng: this._findItemForX(this._x(checkpoint.dist)).latlng,
|
|
36
|
+
label: checkpoint.label || ''
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.addRunner = function (runner) {
|
|
41
|
+
let x = this._x(runner.dist);
|
|
42
|
+
let y = this._y(this._findItemForX(x).z)
|
|
43
|
+
let g = d3.select(this._container)
|
|
44
|
+
.select('svg > g')
|
|
45
|
+
.append("svg:circle")
|
|
46
|
+
.attr("class", "runner " + this.options.theme + " height-focus circle-lower")
|
|
47
|
+
.attr("r", 6)
|
|
48
|
+
.attr("cx", x)
|
|
49
|
+
.attr("cy", y);
|
|
50
|
+
return g;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.setPositionFromLatLng = function (latlng) {
|
|
54
|
+
this._onMouseMoveLayer({ latlng: latlng }); // Update map and chart "markers" from latlng
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
this.tick = function (runner, dist = 0, inc = 0.1) {
|
|
58
|
+
dist = (dist <= this.track_info.distance - inc) ? dist + inc : 0;
|
|
59
|
+
this.updateRunnerPos(runner, dist);
|
|
60
|
+
setTimeout(() => this.tick(runner, dist), 150);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
this.updateRunnerPos = function (runner, pos) {
|
|
64
|
+
let curr, x;
|
|
65
|
+
|
|
66
|
+
if (pos instanceof L.LatLng) {
|
|
67
|
+
curr = this._findItemForLatLng(pos);
|
|
68
|
+
x = this._x(curr.dist);
|
|
69
|
+
} else {
|
|
70
|
+
x = this._x(pos);
|
|
71
|
+
curr = this._findItemForX(x);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
runner
|
|
75
|
+
.attr("cx", x)
|
|
76
|
+
.attr("cy", this._y(curr.z));
|
|
77
|
+
this.setPositionFromLatLng(curr.latlng);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
this.animate = function (layer, speed = 1500) {
|
|
81
|
+
|
|
82
|
+
if (this._runner) {
|
|
83
|
+
this._runner.remove();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
layer.setStyle({ opacity: 0.5 });
|
|
87
|
+
|
|
88
|
+
const geo = L.geoJson(layer.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2] * (this.options.altitudeFactor || 1)) });
|
|
89
|
+
this._runner = L.motion.polyline(
|
|
90
|
+
geo.toGeoJSON().features[0].geometry.coordinates,
|
|
91
|
+
L.extend({}, { color: 'red', pane: 'elevationPane', attribution: '' }, runnerOptions.polyline),
|
|
92
|
+
L.extend({}, { auto: true, speed: speed, }, runnerOptions.motion),
|
|
93
|
+
runnerOptions.marker || undefined
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Override default function behavior: `L.Motion.Polyline::_drawMarker()`
|
|
97
|
+
this._runner._drawMarker = new Proxy(this._runner._drawMarker, {
|
|
98
|
+
apply: (target, thisArg, argArray) => {
|
|
99
|
+
thisArg._runner = thisArg._runner || this.addRunner({ dist: 0 });
|
|
100
|
+
this.updateRunnerPos(thisArg._runner, argArray[0]);
|
|
101
|
+
return target.apply(thisArg, argArray);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
this._runner.addTo(this._map);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return {};
|
|
109
|
+
}
|
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
|
}
|
package/src/options.js
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import * as _ from './utils';
|
|
2
|
-
|
|
3
|
-
export var Options = {
|
|
4
|
-
autofitBounds: true,
|
|
5
|
-
autohide: false,
|
|
6
|
-
autohideMarker: true,
|
|
7
|
-
almostover: true,
|
|
8
|
-
altitude: true,
|
|
9
|
-
closeBtn: true,
|
|
10
|
-
collapsed: false,
|
|
11
|
-
detached: true,
|
|
12
|
-
distance: true,
|
|
13
|
-
distanceMarkers: { lazy: true, distance: true, direction: true },
|
|
14
|
-
dragging: !L.Browser.mobile,
|
|
15
|
-
downloadLink: 'link',
|
|
16
|
-
elevationDiv: "#elevation-div",
|
|
17
|
-
edgeScale: { bar: true, icon: false, coords: false },
|
|
18
|
-
followMarker: true,
|
|
19
|
-
imperial: false,
|
|
20
|
-
legend: true,
|
|
21
|
-
handlers: ["Distance", "Time", "Altitude", "Slope", "Speed", "Acceleration"],
|
|
22
|
-
hotline: 'elevation',
|
|
23
|
-
marker: 'elevation-line',
|
|
24
|
-
markerIcon: L.divIcon({
|
|
25
|
-
className: 'elevation-position-marker',
|
|
26
|
-
html: '<i class="elevation-position-icon"></i>',
|
|
27
|
-
iconSize: [32, 32],
|
|
28
|
-
iconAnchor: [16, 16],
|
|
29
|
-
}),
|
|
30
|
-
position: "topright",
|
|
31
|
-
polyline: {
|
|
32
|
-
className: 'elevation-polyline',
|
|
33
|
-
color: '#000',
|
|
34
|
-
opacity: 0.75,
|
|
35
|
-
weight: 5,
|
|
36
|
-
lineCap: 'round'
|
|
37
|
-
},
|
|
38
|
-
polylineSegments: {
|
|
39
|
-
className: 'elevation-polyline-segments',
|
|
40
|
-
color: '#F00',
|
|
41
|
-
interactive: false,
|
|
42
|
-
},
|
|
43
|
-
preferCanvas: false,
|
|
44
|
-
reverseCoords: false,
|
|
45
|
-
ruler: true,
|
|
46
|
-
theme: "lightblue-theme",
|
|
47
|
-
summary: 'inline',
|
|
48
|
-
slope: false,
|
|
49
|
-
speed: false,
|
|
50
|
-
time: true,
|
|
51
|
-
timeFactor: 3600,
|
|
52
|
-
timestamps: false,
|
|
53
|
-
trkStart: { className: 'start-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#00d800', fillOpacity: 1, interactive: false },
|
|
54
|
-
trkEnd: { className: 'end-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#ff0606', fillOpacity: 1, interactive: false },
|
|
55
|
-
waypoints: true,
|
|
56
|
-
wptIcons: {
|
|
57
|
-
'': L.divIcon({
|
|
58
|
-
className: 'elevation-waypoint-marker',
|
|
59
|
-
html: '<i class="elevation-waypoint-icon default"></i>',
|
|
60
|
-
iconSize: [30, 30],
|
|
61
|
-
iconAnchor: [8, 30],
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
|
-
wptLabels: true,
|
|
65
|
-
xAttr: "dist",
|
|
66
|
-
xLabel: "km",
|
|
67
|
-
yAttr: "z",
|
|
68
|
-
yLabel: "m",
|
|
69
|
-
zFollow: false,
|
|
70
|
-
zooming: !L.Browser.Mobile,
|
|
71
|
-
|
|
72
|
-
// Quite uncommon and undocumented options
|
|
73
|
-
margins: { top: 30, right: 30, bottom: 30, left: 40 },
|
|
74
|
-
height: (screen.height * 0.3) || 200,
|
|
75
|
-
width: (screen.width * 0.6) || 600,
|
|
76
|
-
xTicks: undefined,
|
|
77
|
-
yTicks: undefined,
|
|
78
|
-
|
|
79
|
-
decimalsX: 2,
|
|
80
|
-
decimalsY: 0,
|
|
81
|
-
forceAxisBounds: false,
|
|
82
|
-
interpolation: "curveLinear",
|
|
83
|
-
yAxisMax: undefined,
|
|
84
|
-
yAxisMin: undefined,
|
|
85
|
-
|
|
86
|
-
// Prevent CORS issues for relative locations (dynamic import)
|
|
87
|
-
srcFolder: ((document.currentScript && document.currentScript.src) || (import.meta && import.meta.url)).split("/").slice(0,-1).join("/") + '/',
|
|
88
|
-
};
|
|
1
|
+
import * as _ from './utils';
|
|
2
|
+
|
|
3
|
+
export var Options = {
|
|
4
|
+
autofitBounds: true,
|
|
5
|
+
autohide: false,
|
|
6
|
+
autohideMarker: true,
|
|
7
|
+
almostover: true,
|
|
8
|
+
altitude: true,
|
|
9
|
+
closeBtn: true,
|
|
10
|
+
collapsed: false,
|
|
11
|
+
detached: true,
|
|
12
|
+
distance: true,
|
|
13
|
+
distanceMarkers: { lazy: true, distance: true, direction: true },
|
|
14
|
+
dragging: !L.Browser.mobile,
|
|
15
|
+
downloadLink: 'link',
|
|
16
|
+
elevationDiv: "#elevation-div",
|
|
17
|
+
edgeScale: { bar: true, icon: false, coords: false },
|
|
18
|
+
followMarker: true,
|
|
19
|
+
imperial: false,
|
|
20
|
+
legend: true,
|
|
21
|
+
handlers: ["Distance", "Time", "Altitude", "Slope", "Speed", "Acceleration"],
|
|
22
|
+
hotline: 'elevation',
|
|
23
|
+
marker: 'elevation-line',
|
|
24
|
+
markerIcon: L.divIcon({
|
|
25
|
+
className: 'elevation-position-marker',
|
|
26
|
+
html: '<i class="elevation-position-icon"></i>',
|
|
27
|
+
iconSize: [32, 32],
|
|
28
|
+
iconAnchor: [16, 16],
|
|
29
|
+
}),
|
|
30
|
+
position: "topright",
|
|
31
|
+
polyline: {
|
|
32
|
+
className: 'elevation-polyline',
|
|
33
|
+
color: '#000',
|
|
34
|
+
opacity: 0.75,
|
|
35
|
+
weight: 5,
|
|
36
|
+
lineCap: 'round'
|
|
37
|
+
},
|
|
38
|
+
polylineSegments: {
|
|
39
|
+
className: 'elevation-polyline-segments',
|
|
40
|
+
color: '#F00',
|
|
41
|
+
interactive: false,
|
|
42
|
+
},
|
|
43
|
+
preferCanvas: false,
|
|
44
|
+
reverseCoords: false,
|
|
45
|
+
ruler: true,
|
|
46
|
+
theme: "lightblue-theme",
|
|
47
|
+
summary: 'inline',
|
|
48
|
+
slope: false,
|
|
49
|
+
speed: false,
|
|
50
|
+
time: true,
|
|
51
|
+
timeFactor: 3600,
|
|
52
|
+
timestamps: false,
|
|
53
|
+
trkStart: { className: 'start-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#00d800', fillOpacity: 1, interactive: false },
|
|
54
|
+
trkEnd: { className: 'end-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#ff0606', fillOpacity: 1, interactive: false },
|
|
55
|
+
waypoints: true,
|
|
56
|
+
wptIcons: {
|
|
57
|
+
'': L.divIcon({
|
|
58
|
+
className: 'elevation-waypoint-marker',
|
|
59
|
+
html: '<i class="elevation-waypoint-icon default"></i>',
|
|
60
|
+
iconSize: [30, 30],
|
|
61
|
+
iconAnchor: [8, 30],
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
wptLabels: true,
|
|
65
|
+
xAttr: "dist",
|
|
66
|
+
xLabel: "km",
|
|
67
|
+
yAttr: "z",
|
|
68
|
+
yLabel: "m",
|
|
69
|
+
zFollow: false,
|
|
70
|
+
zooming: !L.Browser.Mobile,
|
|
71
|
+
|
|
72
|
+
// Quite uncommon and undocumented options
|
|
73
|
+
margins: { top: 30, right: 30, bottom: 30, left: 40 },
|
|
74
|
+
height: (screen.height * 0.3) || 200,
|
|
75
|
+
width: (screen.width * 0.6) || 600,
|
|
76
|
+
xTicks: undefined,
|
|
77
|
+
yTicks: undefined,
|
|
78
|
+
|
|
79
|
+
decimalsX: 2,
|
|
80
|
+
decimalsY: 0,
|
|
81
|
+
forceAxisBounds: false,
|
|
82
|
+
interpolation: "curveLinear",
|
|
83
|
+
yAxisMax: undefined,
|
|
84
|
+
yAxisMin: undefined,
|
|
85
|
+
|
|
86
|
+
// Prevent CORS issues for relative locations (dynamic import)
|
|
87
|
+
srcFolder: ((document.currentScript && document.currentScript.src) || (import.meta && import.meta.url)).split("/").slice(0,-1).join("/") + '/',
|
|
88
|
+
};
|