@raruto/leaflet-elevation 1.9.4 → 2.0.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 +8 -7
- package/dist/leaflet-elevation.css +51 -161
- package/dist/leaflet-elevation.js +1622 -3767
- package/dist/leaflet-elevation.js.map +1 -1
- package/dist/leaflet-elevation.min.css +1 -1
- package/dist/leaflet-elevation.min.js +1 -3
- package/dist/leaflet-elevation.min.js.map +1 -1
- package/images/checkered.png +0 -0
- package/libs/leaflet-distance-marker.js +17 -18
- package/libs/leaflet-distance-marker.min.js +1 -1
- package/libs/leaflet-gpxgroup.js +2 -2
- package/libs/leaflet-ruler.css +42 -0
- package/libs/leaflet-ruler.js +183 -0
- package/libs/leaflet-ruler.min.css +1 -0
- package/libs/leaflet-ruler.min.js +1 -0
- package/package.json +9 -11
- package/src/chart.js +156 -143
- package/src/components.js +55 -76
- package/src/control.js +503 -345
- package/src/handlers/acceleration.js +64 -0
- package/src/handlers/acceleration.min.js +1 -0
- package/src/handlers/altitude.js +76 -0
- package/src/handlers/altitude.min.js +1 -0
- package/src/handlers/cadence.js +54 -0
- package/src/handlers/distance.js +45 -0
- package/src/handlers/distance.min.js +1 -0
- package/src/handlers/heart.js +53 -0
- package/src/handlers/slope.js +79 -0
- package/src/handlers/slope.min.js +1 -0
- package/src/handlers/speed.js +66 -0
- package/src/handlers/speed.min.js +1 -0
- package/src/handlers/time.js +103 -0
- package/src/handlers/time.min.js +1 -0
- package/src/index.css +51 -161
- package/src/index.js +0 -24
- package/src/marker.js +11 -14
- package/src/options.js +20 -28
- package/src/summary.js +14 -4
- package/src/utils.js +74 -102
- package/src/acceleration.js +0 -108
- package/src/altitude.js +0 -120
- package/src/distance.js +0 -78
- package/src/slope.js +0 -113
- package/src/speed.js +0 -110
- package/src/time.js +0 -124
package/src/control.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import 'leaflet-i18n';
|
|
2
2
|
import * as _ from './utils';
|
|
3
|
-
import * as D3 from './components';
|
|
4
|
-
import { Chart } from './chart';
|
|
5
|
-
import { Marker } from './marker';
|
|
6
|
-
import { Summary } from './summary';
|
|
7
3
|
import { Options } from './options';
|
|
8
4
|
|
|
9
5
|
export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
@@ -11,19 +7,23 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
11
7
|
includes: L.Evented ? L.Evented.prototype : L.Mixin.Events,
|
|
12
8
|
|
|
13
9
|
options: Options,
|
|
14
|
-
__mileFactor: 0.621371,
|
|
15
|
-
__footFactor: 3.28084,
|
|
16
|
-
__D3:
|
|
17
|
-
__TOGEOJSON:
|
|
18
|
-
__LGEOMUTIL:
|
|
19
|
-
__LALMOSTOVER:
|
|
20
|
-
__LDISTANCEM:
|
|
10
|
+
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
|
|
11
|
+
__footFactor: 3.28084, // 1 m = (3.28084 ft)
|
|
12
|
+
__D3: 'https://unpkg.com/d3@6.5.0/dist/d3.min.js',
|
|
13
|
+
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@4.6.0/dist/togeojson.umd.js',
|
|
14
|
+
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.9.3/src/leaflet.geometryutil.js',
|
|
15
|
+
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
|
|
16
|
+
__LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
|
|
17
|
+
__modulesFolder: '../src/handlers/',
|
|
18
|
+
__LCHART: '../src/chart.js',
|
|
19
|
+
__LMARKER: '../src/marker.js',
|
|
20
|
+
__LSUMMARY: '../src/summary.js',
|
|
21
21
|
|
|
22
22
|
/*
|
|
23
23
|
* Add data to the diagram either from GPX or GeoJSON and update the axis domain and data
|
|
24
24
|
*/
|
|
25
25
|
addData: function(d, layer) {
|
|
26
|
-
this.
|
|
26
|
+
this.import(this.__D3)
|
|
27
27
|
.then(() => {
|
|
28
28
|
if (typeof layer === "undefined" && d.on) {
|
|
29
29
|
layer = d;
|
|
@@ -52,10 +52,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
52
52
|
* Reset data and display
|
|
53
53
|
*/
|
|
54
54
|
clear: function() {
|
|
55
|
-
if (this._marker)
|
|
56
|
-
if (this._chart)
|
|
57
|
-
if (this._layers)
|
|
58
|
-
if (this._markers)
|
|
55
|
+
if (this._marker) this._marker.remove();
|
|
56
|
+
if (this._chart) this._clearChart();
|
|
57
|
+
if (this._layers) this._clearLayers(this._layers);
|
|
58
|
+
if (this._markers) this._clearLayers(this._markers);
|
|
59
|
+
if (this._circleMarkers) this._clearLayers(this._circleMarkers);
|
|
59
60
|
|
|
60
61
|
this._data = [];
|
|
61
62
|
this.track_info = {};
|
|
@@ -67,7 +68,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
67
68
|
|
|
68
69
|
_clearChart: function() {
|
|
69
70
|
if (this._events && this._events.elechart_updated) {
|
|
70
|
-
this._events.elechart_updated.forEach(
|
|
71
|
+
this._events.elechart_updated.forEach(({fn, ctx}) => this.off('elechart_updated', fn, ctx));
|
|
71
72
|
}
|
|
72
73
|
if (this._chart && this._chart._container) {
|
|
73
74
|
this._chart._container.selectAll('g.point .point').remove();
|
|
@@ -75,19 +76,29 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
75
76
|
}
|
|
76
77
|
},
|
|
77
78
|
|
|
78
|
-
_clearLayers: function() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
_clearLayers: function(l) {
|
|
80
|
+
l = l || this._layers;
|
|
81
|
+
if (l && l.eachLayer) {
|
|
82
|
+
l.eachLayer(f => f.remove())
|
|
83
|
+
l.clearLayers();
|
|
82
84
|
}
|
|
83
85
|
},
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
/**
|
|
88
|
+
* TODO: Create a base class to handle custom data attributes (heart rate, cadence, temperature, ...)
|
|
89
|
+
*
|
|
90
|
+
* @link https://leafletjs.com/examples/extending/extending-3-controls.html#handlers
|
|
91
|
+
*/
|
|
92
|
+
// addHandler: function (name, HandlerClass) {
|
|
93
|
+
// if (HandlerClass) {
|
|
94
|
+
// let handler = this[name] = new HandlerClass(this);
|
|
95
|
+
// this.handlers.push(handler);
|
|
96
|
+
// if (this.options[name]) {
|
|
97
|
+
// handler.enable();
|
|
98
|
+
// }
|
|
99
|
+
// }
|
|
100
|
+
// return this;
|
|
101
|
+
// },
|
|
91
102
|
|
|
92
103
|
/**
|
|
93
104
|
* Disable chart brushing.
|
|
@@ -128,12 +139,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
128
139
|
},
|
|
129
140
|
|
|
130
141
|
getBounds: function(data) {
|
|
131
|
-
|
|
132
|
-
return L.latLngBounds(data.map((d) => d.latlng));
|
|
142
|
+
return L.latLngBounds((data || this._data).map((d) => d.latlng));
|
|
133
143
|
},
|
|
134
144
|
|
|
135
145
|
/**
|
|
136
|
-
* Get default zoom level
|
|
146
|
+
* Get default zoom level (followMarker: true).
|
|
137
147
|
*/
|
|
138
148
|
getZFollow: function() {
|
|
139
149
|
return this.options.zFollow;
|
|
@@ -149,66 +159,58 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
149
159
|
/**
|
|
150
160
|
* Initialize chart control "options" and "container".
|
|
151
161
|
*/
|
|
152
|
-
initialize: function(
|
|
162
|
+
initialize: function(opts) {
|
|
163
|
+
|
|
164
|
+
opts = L.setOptions(this, opts);
|
|
165
|
+
|
|
153
166
|
this._data = [];
|
|
154
167
|
this._layers = L.featureGroup();
|
|
155
168
|
this._markers = L.featureGroup();
|
|
169
|
+
this._circleMarkers = L.featureGroup();
|
|
156
170
|
this._markedSegments = L.polyline([]);
|
|
157
|
-
this.
|
|
171
|
+
this._start = (opts.trkStart || Options.trkStart);
|
|
172
|
+
this._end = (opts.trkEnd || Options.trkEnd);
|
|
173
|
+
this._chartEnabled = true;
|
|
174
|
+
this._yCoordMax = -Infinity;
|
|
158
175
|
this.track_info = {};
|
|
176
|
+
// this.handlers = [];
|
|
159
177
|
|
|
160
|
-
|
|
178
|
+
if (opts.followMarker) this._setMapView = _.throttle(this._setMapView, 300, this);
|
|
179
|
+
if (opts.legend) opts.margins.bottom += 30;
|
|
180
|
+
if (opts.theme) opts.polylineSegments.className += ' ' + opts.theme;
|
|
181
|
+
if (opts.wptIcons === true) opts.wptIcons = Options.wptIcons;
|
|
182
|
+
if (opts.distanceMarkers === true) opts.distanceMarkers = Options.distanceMarkers;
|
|
183
|
+
if (opts.trkStart) this._start.addTo(this._circleMarkers);
|
|
184
|
+
if (opts.trkEnd) this._end.addTo(this._circleMarkers);
|
|
161
185
|
|
|
162
|
-
if (this.options.followMarker) this._setMapView = L.Util.throttle(this._setMapView, 300, this);
|
|
163
|
-
if (this.options.legend) this.options.margins.bottom += 30;
|
|
164
|
-
if (this.options.theme) this.options.polylineSegments.className += ' ' + this.options.theme;
|
|
165
|
-
if (this.options.wptIcons === true) this.options.wptIcons = Options.wptIcons;
|
|
166
|
-
if (this.options.distanceMarkers === true) this.options.distanceMarkers = Options.distanceMarkers;
|
|
167
186
|
|
|
168
|
-
this._markedSegments.setStyle(
|
|
187
|
+
this._markedSegments.setStyle(opts.polylineSegments);
|
|
169
188
|
|
|
170
189
|
// Leaflet canvas renderer colors
|
|
171
|
-
L.extend(
|
|
190
|
+
L.extend(_.Colors, opts.colors || {});
|
|
172
191
|
|
|
173
192
|
// Various stuff
|
|
174
193
|
this._fixCanvasPaths();
|
|
175
194
|
this._fixTooltipSize();
|
|
195
|
+
|
|
176
196
|
},
|
|
177
197
|
|
|
178
198
|
/**
|
|
179
199
|
* Javascript scripts downloader (lazy loader)
|
|
180
200
|
*/
|
|
181
|
-
|
|
182
|
-
if (!this.options.lazyLoadJS) {
|
|
183
|
-
return Promise.resolve();
|
|
184
|
-
}
|
|
201
|
+
import: function(src, condition) {
|
|
185
202
|
switch(src) {
|
|
186
|
-
case this.__D3:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
break;
|
|
190
|
-
case this.
|
|
191
|
-
loader = '_togeojsonLazyLoader';
|
|
192
|
-
condition = typeof toGeoJSON !== 'object';
|
|
193
|
-
break;
|
|
194
|
-
case this.__LGEOMUTIL:
|
|
195
|
-
loader = '_geomutilLazyLoader';
|
|
196
|
-
condition = typeof L.GeometryUtil !== 'object';
|
|
197
|
-
break;
|
|
198
|
-
case this.__LALMOSTOVER:
|
|
199
|
-
loader = '_almostoverLazyLoader';
|
|
200
|
-
condition = typeof L.Handler.AlmostOver !== 'function';
|
|
201
|
-
break;
|
|
202
|
-
case this.__LDISTANCEM:
|
|
203
|
-
loader = '_distanceMarkersLazyLoader';
|
|
204
|
-
condition = typeof L.DistanceMarkers !== 'function';
|
|
205
|
-
break;
|
|
203
|
+
case this.__D3: condition = typeof d3 !== 'object'; break;
|
|
204
|
+
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
|
205
|
+
case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
|
|
206
|
+
case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
|
|
207
|
+
case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
|
|
206
208
|
}
|
|
207
|
-
return
|
|
209
|
+
return condition !== false ? import(src) : Promise.resolve();
|
|
208
210
|
},
|
|
209
211
|
|
|
210
212
|
/**
|
|
211
|
-
* Load elevation data (GPX, GeoJSON or
|
|
213
|
+
* Load elevation data (GPX, GeoJSON, KML or TCX).
|
|
212
214
|
*/
|
|
213
215
|
load: function(data) {
|
|
214
216
|
this._parseFromString(data).then( geojson => geojson ? this._loadLayer(geojson) : this._loadFile(data));
|
|
@@ -222,33 +224,23 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
222
224
|
this._map = map;
|
|
223
225
|
|
|
224
226
|
let container = this._container = _.create("div", "elevation-control elevation " + this.options.theme + " " + (this.options.detached ? '' : 'leaflet-control'));
|
|
225
|
-
|
|
226
|
-
this.lazyLoad(this.__D3)
|
|
227
|
-
.then(() => {
|
|
228
|
-
this._initButton(container);
|
|
229
|
-
this._initChart(container);
|
|
230
|
-
this._initSummary(container);
|
|
231
|
-
this._initMarker(map);
|
|
232
|
-
this._initLayer(map);
|
|
233
227
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this._updateSummary();
|
|
251
|
-
});
|
|
228
|
+
let modules = [
|
|
229
|
+
"Distance",
|
|
230
|
+
"Time",
|
|
231
|
+
"Altitude",
|
|
232
|
+
"Slope",
|
|
233
|
+
"Speed",
|
|
234
|
+
"Acceleration",
|
|
235
|
+
...this.options.handlers
|
|
236
|
+
].filter(m => false !== this.options[typeof m == "string" && m.toLowerCase()]);
|
|
237
|
+
|
|
238
|
+
this._loadModules(modules); // Inject here required modules (data handlers)
|
|
239
|
+
this._initButton(container);
|
|
240
|
+
this._initChart(container);
|
|
241
|
+
this._initSummary(container);
|
|
242
|
+
this._initMarker(map);
|
|
243
|
+
this._initLayer(map);
|
|
252
244
|
|
|
253
245
|
return container;
|
|
254
246
|
},
|
|
@@ -261,18 +253,18 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
261
253
|
this._container = null;
|
|
262
254
|
|
|
263
255
|
map
|
|
264
|
-
.off('zoom viewreset zoomanim',
|
|
265
|
-
.off('resize',
|
|
266
|
-
.off('resize',
|
|
267
|
-
.off('mousedown',
|
|
256
|
+
.off('zoom viewreset zoomanim', this._hideMarker, this)
|
|
257
|
+
.off('resize', this._resetView, this)
|
|
258
|
+
.off('resize', this._resizeChart, this)
|
|
259
|
+
.off('mousedown', this._resetDrag, this);
|
|
268
260
|
|
|
269
|
-
_.off(map.getContainer(), 'mousewheel',
|
|
270
|
-
_.off(map.getContainer(), 'touchstart',
|
|
271
|
-
_.off(document, 'keydown',
|
|
261
|
+
_.off(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
262
|
+
_.off(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
263
|
+
_.off(document, 'keydown', this._onKeyDown, this);
|
|
272
264
|
|
|
273
265
|
this
|
|
274
|
-
.off('eledata_added eledata_loaded',
|
|
275
|
-
.off('eledata_added eledata_loaded',
|
|
266
|
+
.off('eledata_added eledata_loaded', this._updateChart, this)
|
|
267
|
+
.off('eledata_added eledata_loaded', this._updateSummary, this);
|
|
276
268
|
},
|
|
277
269
|
|
|
278
270
|
/**
|
|
@@ -283,7 +275,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
283
275
|
},
|
|
284
276
|
|
|
285
277
|
/**
|
|
286
|
-
* Set default zoom level
|
|
278
|
+
* Set default zoom level (followMarker: true).
|
|
287
279
|
*/
|
|
288
280
|
setZFollow: function(zoom) {
|
|
289
281
|
this.options.zFollow = zoom;
|
|
@@ -319,7 +311,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
319
311
|
break;
|
|
320
312
|
|
|
321
313
|
default:
|
|
322
|
-
console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
|
314
|
+
return console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
|
323
315
|
}
|
|
324
316
|
}
|
|
325
317
|
if (d.type === "FeatureCollection") {
|
|
@@ -340,23 +332,15 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
340
332
|
_addGeoJSONData: function(coords, properties) {
|
|
341
333
|
coords.forEach((point, i) => {
|
|
342
334
|
|
|
343
|
-
//
|
|
344
|
-
point.meta = point.meta ?? { time: null, ele: null
|
|
335
|
+
// GARMIN_EXTENSIONS = ["hr", "cad", "atemp", "wtemp", "depth", "course", "bearing"];
|
|
336
|
+
point.meta = point.meta ?? { time: null, ele: null };
|
|
345
337
|
|
|
346
|
-
// "alt" property is generated inside "leaflet"
|
|
347
|
-
if ("alt" in point) point.meta.ele = point.alt;
|
|
348
|
-
|
|
349
338
|
// "coordinateProperties" property is generated inside "@tmcw/toGeoJSON"
|
|
350
|
-
let props
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if ("heartRates" in props) point.meta.hr = parseInt(props.heartRates[i]);
|
|
356
|
-
else if("heartRate" in props) point.meta.hr = parseInt((typeof props.heartRate === 'object' ? props.heartRate[i] : props.heartRate));
|
|
357
|
-
else if("heart" in props) point.meta.hr = parseInt((typeof props.heart === 'object' ? props.heart[i] : props.heart));
|
|
358
|
-
// TODO: ask to "@tmcw/toGeoJSON" for "cadence" and "temperature" implementation
|
|
359
|
-
}
|
|
339
|
+
let props = (properties && properties.coordinateProperties) || properties;
|
|
340
|
+
|
|
341
|
+
point.prev = (attr) => (attr ? this._data[i > 0 ? i - 1 : 0][attr] : this._data[i > 0 ? i - 1 : 0]);
|
|
342
|
+
|
|
343
|
+
this.fire("elepoint_init", { point: point, props: props, id: i });
|
|
360
344
|
|
|
361
345
|
this._addPoint(
|
|
362
346
|
point.lat ?? point[1],
|
|
@@ -364,10 +348,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
364
348
|
point.alt ?? point.meta.ele ?? point[2]
|
|
365
349
|
);
|
|
366
350
|
|
|
367
|
-
this.
|
|
351
|
+
this.fire("elepoint_added", { point: point, index: this._data.length - 1 });
|
|
352
|
+
|
|
353
|
+
if (this._yCoordMax < this._data[this._data.length - 1][this.options.yAttr]) this._yCoordMax = this._data[this._data.length - 1][this.options.yAttr];
|
|
368
354
|
});
|
|
369
355
|
|
|
370
|
-
this.
|
|
356
|
+
this.fire("eletrack_added", { coords: coords, index: this._data.length - 1 });
|
|
371
357
|
},
|
|
372
358
|
|
|
373
359
|
/*
|
|
@@ -385,14 +371,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
385
371
|
latlng: L.latLng(x, y, z)
|
|
386
372
|
});
|
|
387
373
|
|
|
388
|
-
this.
|
|
374
|
+
this.fire("eledata_updated", { index: this._data.length - 1 });
|
|
389
375
|
},
|
|
390
376
|
|
|
391
377
|
_addLayer: function(layer) {
|
|
392
378
|
if (layer) this._layers.addLayer(layer)
|
|
393
379
|
// Postpone adding the distance markers (lazy: true)
|
|
394
380
|
if (layer && this.options.distanceMarkers && this.options.distanceMarkers.lazy) {
|
|
395
|
-
layer.on('add remove', (
|
|
381
|
+
layer.on('add remove', ({target, type}) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
|
|
396
382
|
}
|
|
397
383
|
},
|
|
398
384
|
|
|
@@ -405,17 +391,18 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
405
391
|
*/
|
|
406
392
|
_initAlmostOverHandler: function(map, layer) {
|
|
407
393
|
if(!map || !this.options.almostOver) return;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
394
|
+
Promise.all([
|
|
395
|
+
this.import(this.__LGEOMUTIL),
|
|
396
|
+
this.import(this.__LALMOSTOVER)
|
|
397
|
+
]).then(() => {
|
|
398
|
+
map.addHandler('almostOver', L.Handler.AlmostOver)
|
|
399
|
+
if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
|
|
400
|
+
map.almostOver.addLayer(layer);
|
|
401
|
+
map
|
|
402
|
+
.on('almost:move', (e) => this._onMouseMoveLayer(e))
|
|
403
|
+
.on('almost:out', (e) => this._onMouseOut(e));
|
|
404
|
+
}
|
|
405
|
+
});
|
|
419
406
|
},
|
|
420
407
|
|
|
421
408
|
/**
|
|
@@ -424,9 +411,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
424
411
|
_initDistanceMarkers: function(map, layer) {
|
|
425
412
|
if (!map) return;
|
|
426
413
|
if (this.options.distanceMarkers) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
.
|
|
414
|
+
Promise.all([
|
|
415
|
+
this.import(this.__LGEOMUTIL),
|
|
416
|
+
this.import(this.__LDISTANCEM)
|
|
417
|
+
]).then(() => this.options.polyline && layer.addTo(map) && this._circleMarkers.bringToFront());
|
|
430
418
|
} else if (this.options.polyline) {
|
|
431
419
|
layer.addTo(map);
|
|
432
420
|
}
|
|
@@ -452,7 +440,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
452
440
|
*/
|
|
453
441
|
_initMapIntegrations: function(control, layer) {
|
|
454
442
|
let map = control._map;
|
|
455
|
-
if (control.
|
|
443
|
+
if (control._data.length) {
|
|
444
|
+
control._start.setLatLng(control._data[0].latlng);
|
|
445
|
+
control._end.setLatLng(control._data[control._data.length -1].latlng);
|
|
446
|
+
}
|
|
447
|
+
if (map) {
|
|
448
|
+
control._circleMarkers.addTo(map);
|
|
456
449
|
map.once('layeradd', (e) => control.options.autofitBounds && control.fitBounds(layer.getBounds()));
|
|
457
450
|
if (!L.Browser.mobile) {
|
|
458
451
|
control._initAlmostOverHandler(map, layer);
|
|
@@ -485,12 +478,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
485
478
|
_fixCanvasPaths: function() {
|
|
486
479
|
let oldProto = L.Canvas.prototype._fillStroke;
|
|
487
480
|
let control = this;
|
|
481
|
+
|
|
482
|
+
let theme = this.options.theme.replace('-theme', '');
|
|
483
|
+
let color = _.Colors[theme] || {};
|
|
484
|
+
|
|
488
485
|
L.Canvas.include({
|
|
489
486
|
_fillStroke: function(ctx, layer) {
|
|
490
487
|
if (control._layers.hasLayer(layer)) {
|
|
491
488
|
|
|
492
|
-
let theme = control.options.theme.replace('-theme', '');
|
|
493
|
-
let color = D3.Colors[theme] || {};
|
|
494
489
|
let options = layer.options;
|
|
495
490
|
|
|
496
491
|
options.color = color.line || color.area || theme;
|
|
@@ -521,12 +516,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
521
516
|
*/
|
|
522
517
|
_fixTooltipSize: function() {
|
|
523
518
|
this.on('elechart_init', () =>
|
|
524
|
-
this.once('elechart_change elechart_hover',
|
|
519
|
+
this.once('elechart_change elechart_hover', ({data, xCoord}) => {
|
|
525
520
|
if (this._chartEnabled) {
|
|
526
|
-
this._chart._showDiagramIndicator(
|
|
527
|
-
this._chart._showDiagramIndicator(
|
|
521
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
522
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
528
523
|
}
|
|
529
|
-
this._updateMarker(
|
|
524
|
+
this._updateMarker(data);
|
|
530
525
|
})
|
|
531
526
|
);
|
|
532
527
|
},
|
|
@@ -553,15 +548,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
553
548
|
if (this._map) this._map.fire(type, data, propagate);
|
|
554
549
|
},
|
|
555
550
|
|
|
556
|
-
/**
|
|
557
|
-
* Calculates chart height.
|
|
558
|
-
*/
|
|
559
|
-
_height: function() {
|
|
560
|
-
if (this._chart) return this._chart._height();
|
|
561
|
-
let opts = this.options;
|
|
562
|
-
return opts.height - opts.margins.top - opts.margins.bottom;
|
|
563
|
-
},
|
|
564
|
-
|
|
565
551
|
/*
|
|
566
552
|
* Hides the position/height indicator marker drawn onto the map
|
|
567
553
|
*/
|
|
@@ -572,14 +558,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
572
558
|
},
|
|
573
559
|
|
|
574
560
|
/**
|
|
575
|
-
* Generate "svg" chart DOM element.
|
|
561
|
+
* Generate "svg" chart (DOM element).
|
|
576
562
|
*/
|
|
577
563
|
_initChart: function(container) {
|
|
578
564
|
let opts = this.options;
|
|
579
565
|
|
|
580
|
-
opts.xTicks = this._xTicks();
|
|
581
|
-
opts.yTicks = this._yTicks();
|
|
582
|
-
|
|
583
566
|
if (opts.detached) {
|
|
584
567
|
let { offsetWidth, offsetHeight} = this.eleDiv;
|
|
585
568
|
if (offsetWidth > 0) opts.width = offsetWidth;
|
|
@@ -591,41 +574,66 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
591
574
|
if (opts._maxWidth > clientWidth) opts.width = clientWidth - 30;
|
|
592
575
|
}
|
|
593
576
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
577
|
+
Promise.all([
|
|
578
|
+
this.import(this.__D3),
|
|
579
|
+
this.import(this.__LCHART)
|
|
580
|
+
]).then((m) => {
|
|
581
|
+
|
|
582
|
+
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
|
583
|
+
|
|
584
|
+
this._x = this._chart._x;
|
|
585
|
+
this._y = this._chart._y;
|
|
586
|
+
|
|
587
|
+
d3
|
|
588
|
+
.select(container)
|
|
589
|
+
.call(chart.render())
|
|
590
|
+
|
|
591
|
+
chart
|
|
592
|
+
.on('reset_drag', this._hideMarker, this)
|
|
593
|
+
.on('mouse_enter', this._onMouseEnter, this)
|
|
594
|
+
.on('dragged', this._onDragEnd, this)
|
|
595
|
+
.on('mouse_move', this._onMouseMove, this)
|
|
596
|
+
.on('mouse_out', this._onMouseOut, this)
|
|
597
|
+
.on('ruler_filter', this._onRulerFilter, this)
|
|
598
|
+
.on('zoom', this._updateChart, this)
|
|
599
|
+
.on('elepath_toggle', this._onToggleChart, this)
|
|
600
|
+
.on('margins_updated', this._resizeChart, this);
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
this.fire("elechart_axis");
|
|
604
|
+
|
|
605
|
+
if (this.options.legend) this.fire("elechart_legend");
|
|
606
|
+
|
|
607
|
+
this.fire("elechart_init");
|
|
608
|
+
|
|
609
|
+
map
|
|
610
|
+
.on('zoom viewreset zoomanim', this._hideMarker, this)
|
|
611
|
+
.on('resize', this._resetView, this)
|
|
612
|
+
.on('resize', this._resizeChart, this)
|
|
613
|
+
.on('rotate', this._rotateMarker, this)
|
|
614
|
+
.on('mousedown', this._resetDrag, this);
|
|
615
|
+
|
|
616
|
+
_.on(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
617
|
+
_.on(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
618
|
+
_.on(document, 'keydown', this._onKeyDown, this);
|
|
619
|
+
|
|
620
|
+
this
|
|
621
|
+
.on('eledata_added eledata_loaded', this._updateChart, this)
|
|
622
|
+
.on('eledata_added eledata_loaded', this._updateSummary, this);
|
|
623
|
+
|
|
624
|
+
this._updateChart();
|
|
625
|
+
this._updateSummary();
|
|
626
|
+
});
|
|
617
627
|
|
|
618
|
-
this._fireEvt("elechart_init");
|
|
619
628
|
},
|
|
620
629
|
|
|
621
630
|
_initLayer: function() {
|
|
622
631
|
this._layers
|
|
623
|
-
.on('layeradd layerremove', (
|
|
624
|
-
let layer = e.layer
|
|
632
|
+
.on('layeradd layerremove', ({layer, type}) => {
|
|
625
633
|
let node = layer.getElement && layer.getElement();
|
|
626
|
-
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme,
|
|
627
|
-
_.toggleEvent(layer, "mousemove", this.
|
|
628
|
-
_.toggleEvent(layer, "mouseout", this.
|
|
634
|
+
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme, type == 'layeradd');
|
|
635
|
+
_.toggleEvent(layer, "mousemove", this._onMouseMoveLayer.bind(this), type == 'layeradd')
|
|
636
|
+
_.toggleEvent(layer, "mouseout", this._onMouseOut.bind(this), type == 'layeradd');
|
|
629
637
|
});
|
|
630
638
|
},
|
|
631
639
|
|
|
@@ -639,9 +647,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
639
647
|
|
|
640
648
|
if (this._renderer) this._renderer.remove()
|
|
641
649
|
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
|
642
|
-
this._marker = new Marker(this.options);
|
|
643
650
|
|
|
644
|
-
|
|
651
|
+
Promise.all([
|
|
652
|
+
this.import(this.__D3),
|
|
653
|
+
this.import(this.__LMARKER)
|
|
654
|
+
]).then((m) => {
|
|
655
|
+
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
|
656
|
+
this.fire("elechart_marker");
|
|
657
|
+
});
|
|
645
658
|
},
|
|
646
659
|
|
|
647
660
|
/**
|
|
@@ -676,15 +689,13 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
676
689
|
},
|
|
677
690
|
|
|
678
691
|
_initSummary: function(container) {
|
|
679
|
-
this.
|
|
680
|
-
|
|
681
|
-
d3.select(container).call(this._summary.render());
|
|
682
|
-
},
|
|
692
|
+
this.import(this.__LSUMMARY).then((m)=>{
|
|
693
|
+
this._summary = new (m || Elevation).Summary({ summary: this.options.summary }, this);
|
|
683
694
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
};
|
|
695
|
+
this.on('elechart_init', () => {
|
|
696
|
+
d3.select(container).call(this._summary.render());
|
|
697
|
+
});
|
|
698
|
+
});
|
|
688
699
|
},
|
|
689
700
|
|
|
690
701
|
/**
|
|
@@ -700,6 +711,31 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
700
711
|
}).catch((err) => console.warn(err));
|
|
701
712
|
},
|
|
702
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Dynamically import only required javascript modules (code splitting)
|
|
716
|
+
*/
|
|
717
|
+
_loadModules: function(handlers) {
|
|
718
|
+
// First map known classnames (eg. "Altitude" --> L.Control.Elevation.Altitude)
|
|
719
|
+
handlers = handlers.map((h) => typeof h === 'string' && typeof L.Control.Elevation[h] !== "undefined" ? L.Control.Elevation[h] : h);
|
|
720
|
+
// Then load optional classes and custom imports (eg. "Cadence" --> import('../src/handlers/cadence.js'))
|
|
721
|
+
Promise.
|
|
722
|
+
all(
|
|
723
|
+
handlers
|
|
724
|
+
.filter(h => typeof h === 'string' || h instanceof Promise)
|
|
725
|
+
.map(file => file instanceof Promise ? file : this.import(this.__modulesFolder + file.toLowerCase() + '.js'))
|
|
726
|
+
)
|
|
727
|
+
.then((m) => {
|
|
728
|
+
_.each(m, (exported) => {
|
|
729
|
+
let fn = Object.keys(exported)[0];
|
|
730
|
+
if (fn && typeof L.Control.Elevation[fn] === "undefined" ) {
|
|
731
|
+
handlers[fn] = L.Control.Elevation[fn] = exported[fn];
|
|
732
|
+
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
_.each(handlers, h => this._registerHandler(h));
|
|
736
|
+
});
|
|
737
|
+
},
|
|
738
|
+
|
|
703
739
|
/**
|
|
704
740
|
* Simple GeoJSON data loader (L.GeoJSON).
|
|
705
741
|
*/
|
|
@@ -760,71 +796,78 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
760
796
|
},
|
|
761
797
|
});
|
|
762
798
|
|
|
763
|
-
control.
|
|
799
|
+
control.import(control.__D3).then(() => {
|
|
764
800
|
control._initMapIntegrations(control, layer);
|
|
765
|
-
control._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: control.track_info.name, track_info: control.track_info })
|
|
801
|
+
control._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: control.track_info.name, track_info: control.track_info });
|
|
766
802
|
});
|
|
767
803
|
|
|
768
804
|
return layer;
|
|
769
805
|
},
|
|
770
806
|
|
|
771
|
-
|
|
807
|
+
_onDragEnd: function({ dragstart, dragend}) {
|
|
772
808
|
this._hideMarker();
|
|
773
|
-
this.fitBounds(L.latLngBounds([
|
|
809
|
+
this.fitBounds(L.latLngBounds([dragstart.latlng, dragend.latlng]));
|
|
774
810
|
|
|
775
|
-
this.
|
|
811
|
+
this.fire("elechart_dragged");
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
_onKeyDown: function({key}) {
|
|
815
|
+
if(!this.options.detached && key === "Escape"){
|
|
816
|
+
this._collapse()
|
|
817
|
+
};
|
|
818
|
+
},
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Trigger mouseenter event.
|
|
822
|
+
*/
|
|
823
|
+
_onMouseEnter: function() {
|
|
824
|
+
this.fire('elechart_enter');
|
|
776
825
|
},
|
|
777
826
|
|
|
778
827
|
/*
|
|
779
828
|
* Handles the moueseover the chart and displays distance and altitude level.
|
|
780
829
|
*/
|
|
781
|
-
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
if (item) {
|
|
788
|
-
let xCoord = e.xCoord;
|
|
830
|
+
_onMouseMove: function({xCoord}) {
|
|
831
|
+
if (this._chartEnabled && this._data.length) {
|
|
832
|
+
let item = this._findItemForX(xCoord);
|
|
833
|
+
if (item) {
|
|
834
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
789
835
|
|
|
790
|
-
|
|
836
|
+
this._updateMarker(item);
|
|
837
|
+
this._setMapView(item);
|
|
791
838
|
|
|
792
|
-
|
|
793
|
-
|
|
839
|
+
if (this._map) {
|
|
840
|
+
_.addClass(this._map.getContainer(), 'elechart-hover');
|
|
841
|
+
}
|
|
794
842
|
|
|
795
|
-
|
|
796
|
-
|
|
843
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
844
|
+
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
|
797
845
|
}
|
|
798
|
-
|
|
799
|
-
this._fireEvt("elechart_change", { data: item, xCoord: xCoord });
|
|
800
|
-
this._fireEvt("elechart_hover", { data: item, xCoord: xCoord });
|
|
801
846
|
}
|
|
802
847
|
},
|
|
803
848
|
|
|
804
849
|
/*
|
|
805
850
|
* Handles mouseover events of the data layers on the map.
|
|
806
851
|
*/
|
|
807
|
-
|
|
808
|
-
if (
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
this._fireEvt("elechart_change", { data: item, xCoord: xCoord });
|
|
852
|
+
_onMouseMoveLayer: function({latlng}) {
|
|
853
|
+
if (this._data.length) {
|
|
854
|
+
let item = this._findItemForLatLng(latlng);
|
|
855
|
+
if (item) {
|
|
856
|
+
let xCoord = item.xDiagCoord;
|
|
857
|
+
|
|
858
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
859
|
+
|
|
860
|
+
this._updateMarker(item);
|
|
861
|
+
|
|
862
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
863
|
+
}
|
|
821
864
|
}
|
|
822
865
|
},
|
|
823
866
|
|
|
824
867
|
/*
|
|
825
868
|
* Handles the moueseout over the chart.
|
|
826
869
|
*/
|
|
827
|
-
|
|
870
|
+
_onMouseOut: function() {
|
|
828
871
|
if (!this.options.detached) {
|
|
829
872
|
this._hideMarker();
|
|
830
873
|
this._chart._hideDiagramIndicator();
|
|
@@ -834,7 +877,34 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
834
877
|
_.removeClass(this._map.getContainer(), 'elechart-hover');
|
|
835
878
|
}
|
|
836
879
|
|
|
837
|
-
this.
|
|
880
|
+
this.fire("elechart_leave");
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Handles the drag event over the ruler filter.
|
|
885
|
+
*/
|
|
886
|
+
_onRulerFilter: function({coords}) {
|
|
887
|
+
this._updateMapSegments(coords);
|
|
888
|
+
},
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Toggle chart data on legend click
|
|
892
|
+
*/
|
|
893
|
+
_onToggleChart: function({ name, enabled }) {
|
|
894
|
+
|
|
895
|
+
this._chartEnabled = this._chart._hasActiveLayers();
|
|
896
|
+
|
|
897
|
+
// toggle layer visibility on empty chart
|
|
898
|
+
this._layers.eachLayer(layer => _.toggleClass(layer.getElement && layer.getElement(), this.options.polyline.className + ' ' + this.options.theme, this._chartEnabled));
|
|
899
|
+
|
|
900
|
+
// toggle option value (eg. altitude = { 'disabled' || 'enabled' })
|
|
901
|
+
this.options[name] = !enabled && this.options[name] == 'disabled' ? 'enabled' : 'disabled';
|
|
902
|
+
|
|
903
|
+
// remove marker on empty chart
|
|
904
|
+
if (!this._chartEnabled) {
|
|
905
|
+
this._chart._hideDiagramIndicator();
|
|
906
|
+
this._marker.remove();
|
|
907
|
+
}
|
|
838
908
|
},
|
|
839
909
|
|
|
840
910
|
/**
|
|
@@ -851,7 +921,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
851
921
|
*/
|
|
852
922
|
_parseFromString: function(data) {
|
|
853
923
|
return new Promise(resolve =>
|
|
854
|
-
this.
|
|
924
|
+
this.import(this.__TOGEOJSON).then(() => {
|
|
855
925
|
let geojson;
|
|
856
926
|
try {
|
|
857
927
|
geojson = this._parseFromXMLString(data.trim());
|
|
@@ -919,13 +989,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
919
989
|
return geojson.features.filter(feauture => feauture.geometry.type != "MultiLineString");
|
|
920
990
|
},
|
|
921
991
|
|
|
922
|
-
/**
|
|
923
|
-
* Add a point of interest over the diagram
|
|
924
|
-
*/
|
|
925
|
-
_registerCheckPoint: function(props) {
|
|
926
|
-
this.on("elechart_updated", () => this._chart._registerCheckPoint(props));
|
|
927
|
-
},
|
|
928
|
-
|
|
929
992
|
/**
|
|
930
993
|
* Add chart profile to diagram
|
|
931
994
|
*/
|
|
@@ -948,18 +1011,163 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
948
1011
|
},
|
|
949
1012
|
|
|
950
1013
|
/**
|
|
951
|
-
* Add
|
|
1014
|
+
* Add a point of interest over the diagram
|
|
952
1015
|
*/
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1016
|
+
_registerCheckPoint: function(props) {
|
|
1017
|
+
this.on("elechart_updated", () => this._chart._registerCheckPoint(props));
|
|
1018
|
+
},
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Base handler for iterative track statistics (dist, time, z, slope, speed, acceleration, ...)
|
|
1023
|
+
*/
|
|
1024
|
+
_registerDataAttribute: function(props) {
|
|
1025
|
+
|
|
1026
|
+
// parse of "coordinateProperties" for later usage
|
|
1027
|
+
if (props.coordPropsToMeta) {
|
|
1028
|
+
this.on("elepoint_init", (e) => props.coordPropsToMeta.call(this, e));
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// prevent excessive variabile instanstations
|
|
1032
|
+
let i, curr, prev, attr;
|
|
1033
|
+
|
|
1034
|
+
// save here a reference to last used point
|
|
1035
|
+
let lastValid = null;
|
|
1036
|
+
|
|
1037
|
+
// iteration
|
|
1038
|
+
this.on("elepoint_added", ({index, point}) => {
|
|
1039
|
+
i = index;
|
|
1040
|
+
|
|
1041
|
+
prev = curr ?? this._data[i]; // same as: this._data[i > 0 ? i - 1 : i]
|
|
1042
|
+
curr = this._data[i];
|
|
1043
|
+
attr = props.attr || props.name;
|
|
1044
|
+
|
|
1045
|
+
// check and fix missing data on last added point
|
|
1046
|
+
if (props.skipNull === false) {
|
|
1047
|
+
let curr = this._data[i][attr];
|
|
1048
|
+
if(i > 0) {
|
|
1049
|
+
let prev = this._data[i - 1][attr];
|
|
1050
|
+
if (isNaN(prev)) {
|
|
1051
|
+
if (!isNaN(lastValid) && !isNaN(curr)) {
|
|
1052
|
+
prev = (lastValid + curr) / 2;
|
|
1053
|
+
} else if (!isNaN(lastValid)) {
|
|
1054
|
+
prev = lastValid;
|
|
1055
|
+
} else if (!isNaN(curr)) {
|
|
1056
|
+
prev = curr;
|
|
1057
|
+
}
|
|
1058
|
+
if (!isNaN(prev)) return this._data.splice(i - 1, 1);
|
|
1059
|
+
this._data[i - 1][attr] = prev;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
// update reference to last used point (ie. if it has data)
|
|
1063
|
+
if (!isNaN(curr)) {
|
|
1064
|
+
lastValid = curr;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// retrieve point value
|
|
1069
|
+
curr[attr] = props.pointToAttr.call(this, point, i);
|
|
1070
|
+
|
|
1071
|
+
// update "track_info" stats (min, max, avg, ...)
|
|
1072
|
+
if (props.stats) {
|
|
1073
|
+
for (const key in props.stats) {
|
|
1074
|
+
let sname = (props.statsName || attr) + (key != '' ? '_' : '');
|
|
1075
|
+
this.track_info[sname + key] = props.stats[key].call(this, curr[attr], this.track_info[sname + key]);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Limit "crazy" delta values.
|
|
1080
|
+
if (props.deltaMax) {
|
|
1081
|
+
curr[attr] =_.wrapDelta(curr[attr], prev[attr], props.deltaMax);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// Range of acceptable values.
|
|
1085
|
+
if (props.clampRange) {
|
|
1086
|
+
curr[attr] = _.clamp(curr[attr], props.clampRange);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Limit floating point precision.
|
|
1090
|
+
if (!isNaN(props.decimals)) {
|
|
1091
|
+
curr[attr] = _.round(curr[attr], props.decimals);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// update here some mixins (eg. complex "track_info" stuff)
|
|
1095
|
+
if(props.onPointAdded) props.onPointAdded.call(this, curr[attr], i, point);
|
|
1096
|
+
});
|
|
1097
|
+
},
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Parse a module definition and attach related function listeners
|
|
1101
|
+
*/
|
|
1102
|
+
_registerHandler: function(props) {
|
|
1103
|
+
|
|
1104
|
+
// eg. L.Control.Altitude
|
|
1105
|
+
if (typeof props === "function") {
|
|
1106
|
+
return this._registerHandler(props.call(this));
|
|
957
1107
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1108
|
+
|
|
1109
|
+
// eg. "altitude" == true
|
|
1110
|
+
if (this.options[props.name] || props.required) {
|
|
1111
|
+
|
|
1112
|
+
this._registerDataAttribute({
|
|
1113
|
+
name: props.name,
|
|
1114
|
+
attr: props.attr,
|
|
1115
|
+
skipNull: props.skipNull,
|
|
1116
|
+
deltaMax: props.deltaMax,
|
|
1117
|
+
clampRange: props.clampRange,
|
|
1118
|
+
decimals: props.decimals,
|
|
1119
|
+
coordPropsToMeta: _.coordPropsToMeta(props.coordinateProperties, props.meta || props.name, props.coordPropsToMeta),
|
|
1120
|
+
pointToAttr: props.pointToAttr,
|
|
1121
|
+
onPointAdded: props.onPointAdded,
|
|
1122
|
+
stats: props.stats,
|
|
1123
|
+
statsName: props.statsName,
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
if (props.grid) {
|
|
1127
|
+
props.grid.name = props.grid.name || props.name;
|
|
1128
|
+
this._registerAxisGrid(props.grid);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (this.options[props.name] !== "summary") {
|
|
1132
|
+
if (props.scale) {
|
|
1133
|
+
props.scale.name = props.scale.name || props.name;
|
|
1134
|
+
props.scale.label = props.scale.label || props.unit;
|
|
1135
|
+
this._registerAxisScale(props.scale);
|
|
1136
|
+
}
|
|
1137
|
+
if (props.path) {
|
|
1138
|
+
props.path.name = props.path.name || props.name;
|
|
1139
|
+
this._registerAreaPath(props.path);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (props.tooltips) {
|
|
1144
|
+
_.each(props.tooltips, t => {
|
|
1145
|
+
if (t) {
|
|
1146
|
+
t.name = t.name || props.name;
|
|
1147
|
+
this._registerTooltip(t)
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
} else if (props.tooltip) {
|
|
1151
|
+
props.tooltip.name = props.tooltip.name || props.name;
|
|
1152
|
+
this._registerTooltip(props.tooltip)
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
if (props.summary) {
|
|
1156
|
+
for (const key in props.summary) {
|
|
1157
|
+
props.summary[key].unit = props.summary[key].unit || props.unit;
|
|
1158
|
+
}
|
|
1159
|
+
this._registerSummary(props.summary);
|
|
1160
|
+
}
|
|
961
1161
|
}
|
|
962
1162
|
},
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Add chart or marker tooltip info
|
|
1166
|
+
*/
|
|
1167
|
+
_registerTooltip: function(props) {
|
|
1168
|
+
props.chart && this.on("elechart_init", () => this._chart._registerTooltip(L.extend({}, props, { value: props.chart })));
|
|
1169
|
+
props.marker && this.on("elechart_marker", () => this._marker._registerTooltip(L.extend({}, props, { value: props.marker })));
|
|
1170
|
+
},
|
|
963
1171
|
|
|
964
1172
|
/**
|
|
965
1173
|
* Add summary info to diagram
|
|
@@ -992,85 +1200,39 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
992
1200
|
* Hacky way for handling chart resize. Deletes it and redraw chart.
|
|
993
1201
|
*/
|
|
994
1202
|
_resizeChart: function() {
|
|
995
|
-
if(
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
if (opts.detached) {
|
|
1005
|
-
newWidth = (this.eleDiv || this._container).offsetWidth;
|
|
1006
|
-
} else {
|
|
1007
|
-
let { clientWidth } = this._map.getContainer();
|
|
1008
|
-
newWidth = opts._maxWidth > clientWidth ? clientWidth - 30 : opts._maxWidth;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
if (newWidth) {
|
|
1012
|
-
let chart = this._chart;
|
|
1013
|
-
opts.width = newWidth;
|
|
1014
|
-
if (chart && chart._chart) {
|
|
1015
|
-
chart._chart._resize(opts);
|
|
1016
|
-
opts.xTicks = this._xTicks();
|
|
1017
|
-
opts.yTicks = this._yTicks();
|
|
1018
|
-
this._updateChart();
|
|
1203
|
+
if (this._container && _.style(this._container, "display") != "none") {
|
|
1204
|
+
let opts = this.options;
|
|
1205
|
+
let newWidth = opts.detached ? (this.eleDiv || this._container).offsetWidth : _.clamp(opts._maxWidth, [0, this._map.getContainer().clientWidth - 30]);
|
|
1206
|
+
if (newWidth) {
|
|
1207
|
+
opts.width = newWidth;
|
|
1208
|
+
if (this._chart && this._chart._chart) {
|
|
1209
|
+
this._chart._chart._resize(opts);
|
|
1210
|
+
this._updateChart();
|
|
1211
|
+
}
|
|
1019
1212
|
}
|
|
1213
|
+
this._updateMapSegments();
|
|
1020
1214
|
}
|
|
1021
|
-
|
|
1022
|
-
this._updateMapSegments();
|
|
1023
|
-
},
|
|
1024
|
-
|
|
1025
|
-
/**
|
|
1026
|
-
* Handles the drag event over the ruler filter.
|
|
1027
|
-
*/
|
|
1028
|
-
_rulerFilterHandler: function(e) {
|
|
1029
|
-
this._updateMapSegments(e.coords);
|
|
1030
1215
|
},
|
|
1031
1216
|
|
|
1032
1217
|
/**
|
|
1033
|
-
* Collapse or Expand
|
|
1218
|
+
* Collapse or Expand chart control.
|
|
1034
1219
|
*/
|
|
1035
1220
|
_toggle: function() {
|
|
1036
|
-
|
|
1037
|
-
this._collapse();
|
|
1038
|
-
else
|
|
1039
|
-
this._expand();
|
|
1221
|
+
_.hasClass(this._container, "elevation-expanded") ? this._collapse() : this._expand();
|
|
1040
1222
|
},
|
|
1041
1223
|
|
|
1042
1224
|
/**
|
|
1043
|
-
*
|
|
1225
|
+
* Update map center and zoom (followMarker: true)
|
|
1044
1226
|
*/
|
|
1045
1227
|
_setMapView: function(item) {
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
},
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* Toggle chart data on legend click
|
|
1058
|
-
*/
|
|
1059
|
-
_toggleChartHandler: function(e) {
|
|
1060
|
-
let { name, enabled } = e;
|
|
1061
|
-
|
|
1062
|
-
this._chartEnabled = this._chart._hasActiveLayers();
|
|
1063
|
-
|
|
1064
|
-
// toggle layer visibility on empty chart
|
|
1065
|
-
this._layers.eachLayer(layer => _.toggleClass(layer.getElement && layer.getElement(), this.options.polyline.className + ' ' + this.options.theme, this._chartEnabled));
|
|
1066
|
-
|
|
1067
|
-
// toggle option value (eg. altitude = { 'disabled' || 'disabled' })
|
|
1068
|
-
this.options[name] = !enabled && this.options[name] == 'disabled' ? 'enabled' : 'disabled';
|
|
1069
|
-
|
|
1070
|
-
// remove marker on empty chart
|
|
1071
|
-
if (!this._chartEnabled) {
|
|
1072
|
-
this._chart._hideDiagramIndicator();
|
|
1073
|
-
this._marker.remove();
|
|
1228
|
+
if (this._map && this.options.followMarker) {
|
|
1229
|
+
let zoom = this._map.getZoom();
|
|
1230
|
+
let z = this.options.zFollow;
|
|
1231
|
+
if (typeof z === "number") {
|
|
1232
|
+
this._map.setView(item.latlng, (zoom < z ? z : zoom), { animate: true, duration: 0.25 });
|
|
1233
|
+
} else if (!this._map.getBounds().contains(item.latlng)) {
|
|
1234
|
+
this._map.setView(item.latlng, zoom, { animate: true, duration: 0.25 });
|
|
1235
|
+
}
|
|
1074
1236
|
}
|
|
1075
1237
|
},
|
|
1076
1238
|
|
|
@@ -1078,17 +1240,17 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1078
1240
|
* Calculates [x, y] domain and then update chart.
|
|
1079
1241
|
*/
|
|
1080
1242
|
_updateChart: function() {
|
|
1081
|
-
if (
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1243
|
+
if (this._chart && this._container) {
|
|
1244
|
+
this.fire("elechart_axis");
|
|
1245
|
+
this.fire("elechart_area");
|
|
1246
|
+
|
|
1247
|
+
this._chart.update({ data: this._data, options: this.options });
|
|
1248
|
+
|
|
1249
|
+
this._x = this._chart._x;
|
|
1250
|
+
this._y = this._chart._y;
|
|
1251
|
+
|
|
1252
|
+
this.fire('elechart_updated');
|
|
1253
|
+
}
|
|
1092
1254
|
},
|
|
1093
1255
|
|
|
1094
1256
|
/*
|
|
@@ -1099,7 +1261,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1099
1261
|
this._marker.update({
|
|
1100
1262
|
map : this._map,
|
|
1101
1263
|
item : item,
|
|
1102
|
-
|
|
1264
|
+
yCoordMax : this._yCoordMax || 0,
|
|
1103
1265
|
options : this.options
|
|
1104
1266
|
});
|
|
1105
1267
|
}
|
|
@@ -1128,22 +1290,25 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1128
1290
|
* Update chart summary.
|
|
1129
1291
|
*/
|
|
1130
1292
|
_updateSummary: function() {
|
|
1293
|
+
if (!this._summary) return;
|
|
1294
|
+
|
|
1131
1295
|
this._summary.reset();
|
|
1132
1296
|
|
|
1133
1297
|
if (this.options.summary) {
|
|
1134
|
-
this.
|
|
1298
|
+
this.fire("elechart_summary");
|
|
1299
|
+
this._summary.update();
|
|
1135
1300
|
}
|
|
1136
1301
|
if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
|
|
1137
1302
|
this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
|
|
1138
|
-
_.select('.download a', this._summary._container).onclick = (
|
|
1139
|
-
|
|
1303
|
+
_.select('.download a', this._summary._container).onclick = ({preventDefault}) => {
|
|
1304
|
+
preventDefault();
|
|
1140
1305
|
let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
|
|
1141
1306
|
if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
|
|
1142
1307
|
document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
|
|
1143
1308
|
} else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
|
|
1144
1309
|
event.confirm();
|
|
1145
1310
|
}
|
|
1146
|
-
this.
|
|
1311
|
+
this.fire('eletrack_download', event);
|
|
1147
1312
|
};
|
|
1148
1313
|
};
|
|
1149
1314
|
},
|
|
@@ -1154,24 +1319,17 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1154
1319
|
*/
|
|
1155
1320
|
_width: function() {
|
|
1156
1321
|
if (this._chart) return this._chart._width();
|
|
1157
|
-
|
|
1158
|
-
return
|
|
1322
|
+
const { width, margins } = this.options;
|
|
1323
|
+
return width - margins.left - margins.right;
|
|
1159
1324
|
},
|
|
1160
1325
|
|
|
1161
1326
|
/**
|
|
1162
|
-
*
|
|
1327
|
+
* Calculates chart height.
|
|
1163
1328
|
*/
|
|
1164
|
-
|
|
1165
|
-
if (this.
|
|
1166
|
-
|
|
1329
|
+
_height: function() {
|
|
1330
|
+
if (this._chart) return this._chart._height();
|
|
1331
|
+
const { height, margins } = this.options;
|
|
1332
|
+
return height - margins.top - margins.bottom;
|
|
1167
1333
|
},
|
|
1168
1334
|
|
|
1169
|
-
/**
|
|
1170
|
-
* Calculate chart yTicks
|
|
1171
|
-
*/
|
|
1172
|
-
_yTicks: function() {
|
|
1173
|
-
if (this.__yTicks) this.__yTicks = this.options.yTicks;
|
|
1174
|
-
return this.__yTicks || Math.round(this._height() / 30);
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
1335
|
});
|