@raruto/leaflet-elevation 1.9.6 → 2.0.2
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 +7 -5
- package/dist/leaflet-elevation.css +52 -161
- package/dist/leaflet-elevation.js +1550 -3884
- 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 +97 -92
- package/libs/leaflet-distance-marker.min.js +1 -1
- package/libs/leaflet-gpxgroup.js +2 -2
- package/package.json +9 -11
- package/src/{chart.js → components/chart.js} +147 -144
- package/src/{components.js → components/d3.js} +43 -71
- package/src/{marker.js → components/marker.js} +11 -14
- package/src/{summary.js → components/summary.js} +4 -5
- package/src/control.js +492 -434
- package/src/handlers/acceleration.js +64 -0
- package/src/handlers/altitude.js +79 -0
- package/src/handlers/cadence.js +54 -0
- package/src/handlers/distance.js +45 -0
- package/src/handlers/heart.js +53 -0
- package/src/handlers/slope.js +79 -0
- package/src/handlers/speed.js +66 -0
- package/src/{time.js → handlers/time.js} +31 -54
- package/src/index.css +52 -161
- package/src/index.js +0 -24
- package/src/options.js +20 -28
- package/src/utils.js +74 -102
- package/src/acceleration.js +0 -113
- package/src/altitude.js +0 -119
- package/src/distance.js +0 -69
- package/src/slope.js +0 -129
- package/src/speed.js +0 -118
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,23 +7,25 @@ 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
|
+
__LCHART: '../src/components/chart.js',
|
|
18
|
+
__LMARKER: '../src/components/marker.js',
|
|
19
|
+
__LSUMMARY: '../src/components/summary.js',
|
|
20
|
+
__modulesFolder: '../src/handlers/',
|
|
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
|
-
|
|
29
|
-
layer = d;
|
|
30
|
-
}
|
|
28
|
+
layer = layer ?? (d.on && d);
|
|
31
29
|
this._addData(d);
|
|
32
30
|
this._addLayer(layer);
|
|
33
31
|
this._fireEvt("eledata_added", { data: d, layer: layer, track_info: this.track_info });
|
|
@@ -52,10 +50,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
52
50
|
* Reset data and display
|
|
53
51
|
*/
|
|
54
52
|
clear: function() {
|
|
55
|
-
if (this._marker)
|
|
56
|
-
if (this._chart)
|
|
57
|
-
if (this._layers)
|
|
58
|
-
if (this._markers)
|
|
53
|
+
if (this._marker) this._marker.remove();
|
|
54
|
+
if (this._chart) this._clearChart();
|
|
55
|
+
if (this._layers) this._clearLayers(this._layers);
|
|
56
|
+
if (this._markers) this._clearLayers(this._markers);
|
|
57
|
+
if (this._circleMarkers) this._clearLayers(this._circleMarkers);
|
|
59
58
|
|
|
60
59
|
this._data = [];
|
|
61
60
|
this.track_info = {};
|
|
@@ -67,7 +66,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
67
66
|
|
|
68
67
|
_clearChart: function() {
|
|
69
68
|
if (this._events && this._events.elechart_updated) {
|
|
70
|
-
this._events.elechart_updated.forEach(
|
|
69
|
+
this._events.elechart_updated.forEach(({fn, ctx}) => this.off('elechart_updated', fn, ctx));
|
|
71
70
|
}
|
|
72
71
|
if (this._chart && this._chart._container) {
|
|
73
72
|
this._chart._container.selectAll('g.point .point').remove();
|
|
@@ -75,17 +74,11 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
75
74
|
}
|
|
76
75
|
},
|
|
77
76
|
|
|
78
|
-
_clearLayers: function() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
_clearMarkers: function() {
|
|
86
|
-
if (this._markers && this._markers.eachLayer) {
|
|
87
|
-
this._markers.eachLayer(l => l.remove())
|
|
88
|
-
this._markers.clearLayers();
|
|
77
|
+
_clearLayers: function(l) {
|
|
78
|
+
l = l || this._layers;
|
|
79
|
+
if (l && l.eachLayer) {
|
|
80
|
+
l.eachLayer(f => f.remove())
|
|
81
|
+
l.clearLayers();
|
|
89
82
|
}
|
|
90
83
|
},
|
|
91
84
|
|
|
@@ -144,8 +137,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
144
137
|
},
|
|
145
138
|
|
|
146
139
|
getBounds: function(data) {
|
|
147
|
-
|
|
148
|
-
return L.latLngBounds(data.map((d) => d.latlng));
|
|
140
|
+
return L.latLngBounds((data || this._data).map((d) => d.latlng));
|
|
149
141
|
},
|
|
150
142
|
|
|
151
143
|
/**
|
|
@@ -165,63 +157,54 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
165
157
|
/**
|
|
166
158
|
* Initialize chart control "options" and "container".
|
|
167
159
|
*/
|
|
168
|
-
initialize: function(
|
|
160
|
+
initialize: function(opts) {
|
|
161
|
+
|
|
162
|
+
opts = L.setOptions(this, opts);
|
|
163
|
+
|
|
169
164
|
this._data = [];
|
|
170
165
|
this._layers = L.featureGroup();
|
|
171
166
|
this._markers = L.featureGroup();
|
|
167
|
+
this._circleMarkers = L.featureGroup();
|
|
172
168
|
this._markedSegments = L.polyline([]);
|
|
173
|
-
this.
|
|
169
|
+
this._start = (opts.trkStart || Options.trkStart);
|
|
170
|
+
this._end = (opts.trkEnd || Options.trkEnd);
|
|
171
|
+
this._chartEnabled = true;
|
|
172
|
+
this._yCoordMax = -Infinity;
|
|
174
173
|
this.track_info = {};
|
|
175
|
-
this.handlers = [];
|
|
174
|
+
// this.handlers = [];
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
if (opts.followMarker) this._setMapView = _.throttle(this._setMapView, 300, this);
|
|
177
|
+
if (opts.legend) opts.margins.bottom += 30;
|
|
178
|
+
if (opts.theme) opts.polylineSegments.className += ' ' + opts.theme;
|
|
179
|
+
if (opts.wptIcons === true) opts.wptIcons = Options.wptIcons;
|
|
180
|
+
if (opts.distanceMarkers === true) opts.distanceMarkers = Options.distanceMarkers;
|
|
181
|
+
if (opts.trkStart) this._start.addTo(this._circleMarkers);
|
|
182
|
+
if (opts.trkEnd) this._end.addTo(this._circleMarkers);
|
|
178
183
|
|
|
179
|
-
if (this.options.followMarker) this._setMapView = L.Util.throttle(this._setMapView, 300, this);
|
|
180
|
-
if (this.options.legend) this.options.margins.bottom += 30;
|
|
181
|
-
if (this.options.theme) this.options.polylineSegments.className += ' ' + this.options.theme;
|
|
182
|
-
if (this.options.wptIcons === true) this.options.wptIcons = Options.wptIcons;
|
|
183
|
-
if (this.options.distanceMarkers === true) this.options.distanceMarkers = Options.distanceMarkers;
|
|
184
184
|
|
|
185
|
-
this._markedSegments.setStyle(
|
|
185
|
+
this._markedSegments.setStyle(opts.polylineSegments);
|
|
186
186
|
|
|
187
187
|
// Leaflet canvas renderer colors
|
|
188
|
-
L.extend(
|
|
188
|
+
L.extend(_.Colors, opts.colors || {});
|
|
189
189
|
|
|
190
190
|
// Various stuff
|
|
191
191
|
this._fixCanvasPaths();
|
|
192
192
|
this._fixTooltipSize();
|
|
193
|
+
|
|
193
194
|
},
|
|
194
195
|
|
|
195
196
|
/**
|
|
196
197
|
* Javascript scripts downloader (lazy loader)
|
|
197
198
|
*/
|
|
198
|
-
|
|
199
|
-
if (!this.options.lazyLoadJS) {
|
|
200
|
-
return Promise.resolve();
|
|
201
|
-
}
|
|
199
|
+
import: function(src, condition) {
|
|
202
200
|
switch(src) {
|
|
203
|
-
case this.__D3:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
break;
|
|
207
|
-
case this.
|
|
208
|
-
loader = '_togeojsonLazyLoader';
|
|
209
|
-
condition = typeof toGeoJSON !== 'object';
|
|
210
|
-
break;
|
|
211
|
-
case this.__LGEOMUTIL:
|
|
212
|
-
loader = '_geomutilLazyLoader';
|
|
213
|
-
condition = typeof L.GeometryUtil !== 'object';
|
|
214
|
-
break;
|
|
215
|
-
case this.__LALMOSTOVER:
|
|
216
|
-
loader = '_almostoverLazyLoader';
|
|
217
|
-
condition = typeof L.Handler.AlmostOver !== 'function';
|
|
218
|
-
break;
|
|
219
|
-
case this.__LDISTANCEM:
|
|
220
|
-
loader = '_distanceMarkersLazyLoader';
|
|
221
|
-
condition = typeof L.DistanceMarkers !== 'function';
|
|
222
|
-
break;
|
|
201
|
+
case this.__D3: condition = typeof d3 !== 'object'; break;
|
|
202
|
+
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
|
203
|
+
case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
|
|
204
|
+
case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
|
|
205
|
+
case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
|
|
223
206
|
}
|
|
224
|
-
return
|
|
207
|
+
return condition !== false ? import(src) : Promise.resolve();
|
|
225
208
|
},
|
|
226
209
|
|
|
227
210
|
/**
|
|
@@ -239,33 +222,23 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
239
222
|
this._map = map;
|
|
240
223
|
|
|
241
224
|
let container = this._container = _.create("div", "elevation-control elevation " + this.options.theme + " " + (this.options.detached ? '' : 'leaflet-control'));
|
|
242
|
-
|
|
243
|
-
this.lazyLoad(this.__D3)
|
|
244
|
-
.then(() => {
|
|
245
|
-
this._initButton(container);
|
|
246
|
-
this._initChart(container);
|
|
247
|
-
this._initSummary(container);
|
|
248
|
-
this._initMarker(map);
|
|
249
|
-
this._initLayer(map);
|
|
250
225
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this._updateSummary();
|
|
268
|
-
});
|
|
226
|
+
let modules = [
|
|
227
|
+
"Distance",
|
|
228
|
+
"Time",
|
|
229
|
+
"Altitude",
|
|
230
|
+
"Slope",
|
|
231
|
+
"Speed",
|
|
232
|
+
"Acceleration",
|
|
233
|
+
...this.options.handlers
|
|
234
|
+
].filter(m => false !== this.options[typeof m == "string" && m.toLowerCase()]);
|
|
235
|
+
|
|
236
|
+
this._loadModules(modules); // Inject here required modules (data handlers)
|
|
237
|
+
this._initButton(container);
|
|
238
|
+
this._initChart(container);
|
|
239
|
+
this._initSummary(container);
|
|
240
|
+
this._initMarker(map);
|
|
241
|
+
this._initLayer(map);
|
|
269
242
|
|
|
270
243
|
return container;
|
|
271
244
|
},
|
|
@@ -278,18 +251,18 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
278
251
|
this._container = null;
|
|
279
252
|
|
|
280
253
|
map
|
|
281
|
-
.off('zoom viewreset zoomanim',
|
|
282
|
-
.off('resize',
|
|
283
|
-
.off('resize',
|
|
284
|
-
.off('mousedown',
|
|
254
|
+
.off('zoom viewreset zoomanim', this._hideMarker, this)
|
|
255
|
+
.off('resize', this._resetView, this)
|
|
256
|
+
.off('resize', this._resizeChart, this)
|
|
257
|
+
.off('mousedown', this._resetDrag, this);
|
|
285
258
|
|
|
286
|
-
_.off(map.getContainer(), 'mousewheel',
|
|
287
|
-
_.off(map.getContainer(), 'touchstart',
|
|
288
|
-
_.off(document, 'keydown',
|
|
259
|
+
_.off(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
260
|
+
_.off(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
261
|
+
_.off(document, 'keydown', this._onKeyDown, this);
|
|
289
262
|
|
|
290
263
|
this
|
|
291
|
-
.off('eledata_added eledata_loaded',
|
|
292
|
-
.off('eledata_added eledata_loaded',
|
|
264
|
+
.off('eledata_added eledata_loaded', this._updateChart, this)
|
|
265
|
+
.off('eledata_added eledata_loaded', this._updateSummary, this);
|
|
293
266
|
},
|
|
294
267
|
|
|
295
268
|
/**
|
|
@@ -321,49 +294,44 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
321
294
|
return;
|
|
322
295
|
}
|
|
323
296
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this._addGeoJSONData(geom.coordinates);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
break;
|
|
337
|
-
|
|
338
|
-
default:
|
|
339
|
-
console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
|
297
|
+
// Standard GeoJSON
|
|
298
|
+
if (d.type === "FeatureCollection" ) {
|
|
299
|
+
return _.each(d.features, feature => this._addData(feature));
|
|
300
|
+
} else if (d.type === "Feature") {
|
|
301
|
+
let geom = d.geometry;
|
|
302
|
+
if (geom) {
|
|
303
|
+
switch (geom.type) {
|
|
304
|
+
case 'LineString': return this._addGeoJSONData(geom.coordinates, d.properties);
|
|
305
|
+
case 'MultiLineString': return _.each(geom.coordinates, (coords, i) => this._addGeoJSONData(coords, d.properties, i));
|
|
306
|
+
case 'Point':
|
|
307
|
+
default: return console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
|
308
|
+
}
|
|
340
309
|
}
|
|
341
310
|
}
|
|
342
|
-
if (d.type === "FeatureCollection") {
|
|
343
|
-
_.each(d.features, feature => this._addData(feature));
|
|
344
|
-
}
|
|
345
311
|
|
|
346
|
-
|
|
347
|
-
* Extended GeoJSON --> rely on leaflet implementation
|
|
348
|
-
*/
|
|
312
|
+
// Fallback for leaflet layers (eg. L.Gpx)
|
|
349
313
|
if (d._latlngs) {
|
|
350
|
-
this._addGeoJSONData(d._latlngs, d.feature && d.feature.properties);
|
|
314
|
+
return this._addGeoJSONData(d._latlngs, d.feature && d.feature.properties);
|
|
351
315
|
}
|
|
316
|
+
|
|
352
317
|
},
|
|
353
318
|
|
|
354
319
|
/*
|
|
355
320
|
* Parsing of GeoJSON data lines and their elevation in z-coordinate
|
|
356
321
|
*/
|
|
357
|
-
_addGeoJSONData: function(coords, properties) {
|
|
322
|
+
_addGeoJSONData: function(coords, properties, nestingLevel) {
|
|
323
|
+
|
|
324
|
+
// "coordinateProperties" property is generated inside "@tmcw/toGeoJSON"
|
|
325
|
+
let props = (properties && properties.coordinateProperties) || properties;
|
|
326
|
+
|
|
358
327
|
coords.forEach((point, i) => {
|
|
359
328
|
|
|
360
|
-
//
|
|
361
|
-
point.meta = point.meta ?? { time: null, ele: null
|
|
329
|
+
// GARMIN_EXTENSIONS = ["hr", "cad", "atemp", "wtemp", "depth", "course", "bearing"];
|
|
330
|
+
point.meta = point.meta ?? { time: null, ele: null };
|
|
362
331
|
|
|
363
|
-
|
|
364
|
-
let props = (properties && properties.coordinateProperties) || properties;
|
|
332
|
+
point.prev = (attr) => (attr ? this._data[i > 0 ? i - 1 : 0][attr] : this._data[i > 0 ? i - 1 : 0]);
|
|
365
333
|
|
|
366
|
-
this.fire("elepoint_init", { point: point, props: props, id: i });
|
|
334
|
+
this.fire("elepoint_init", { point: point, props: props, id: i, isMulti: nestingLevel });
|
|
367
335
|
|
|
368
336
|
this._addPoint(
|
|
369
337
|
point.lat ?? point[1],
|
|
@@ -372,6 +340,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
372
340
|
);
|
|
373
341
|
|
|
374
342
|
this.fire("elepoint_added", { point: point, index: this._data.length - 1 });
|
|
343
|
+
|
|
344
|
+
if (this._yCoordMax < this._data[this._data.length - 1][this.options.yAttr]) this._yCoordMax = this._data[this._data.length - 1][this.options.yAttr];
|
|
375
345
|
});
|
|
376
346
|
|
|
377
347
|
this.fire("eletrack_added", { coords: coords, index: this._data.length - 1 });
|
|
@@ -399,12 +369,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
399
369
|
if (layer) this._layers.addLayer(layer)
|
|
400
370
|
// Postpone adding the distance markers (lazy: true)
|
|
401
371
|
if (layer && this.options.distanceMarkers && this.options.distanceMarkers.lazy) {
|
|
402
|
-
layer.on('add remove', (
|
|
372
|
+
layer.on('add remove', ({target, type}) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
|
|
403
373
|
}
|
|
374
|
+
return layer;
|
|
404
375
|
},
|
|
405
376
|
|
|
406
377
|
_addMarker: function(marker) {
|
|
407
378
|
if (marker) this._markers.addLayer(marker)
|
|
379
|
+
return marker;
|
|
408
380
|
},
|
|
409
381
|
|
|
410
382
|
/**
|
|
@@ -412,17 +384,18 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
412
384
|
*/
|
|
413
385
|
_initAlmostOverHandler: function(map, layer) {
|
|
414
386
|
if(!map || !this.options.almostOver) return;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
387
|
+
Promise.all([
|
|
388
|
+
this.import(this.__LGEOMUTIL),
|
|
389
|
+
this.import(this.__LALMOSTOVER)
|
|
390
|
+
]).then(() => {
|
|
391
|
+
map.addHandler('almostOver', L.Handler.AlmostOver)
|
|
392
|
+
if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
|
|
393
|
+
map.almostOver.addLayer(layer);
|
|
394
|
+
map
|
|
395
|
+
.on('almost:move', (e) => this._onMouseMoveLayer(e))
|
|
396
|
+
.on('almost:out', (e) => this._onMouseOut(e));
|
|
397
|
+
}
|
|
398
|
+
});
|
|
426
399
|
},
|
|
427
400
|
|
|
428
401
|
/**
|
|
@@ -431,9 +404,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
431
404
|
_initDistanceMarkers: function(map, layer) {
|
|
432
405
|
if (!map) return;
|
|
433
406
|
if (this.options.distanceMarkers) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
.
|
|
407
|
+
Promise.all([
|
|
408
|
+
this.import(this.__LGEOMUTIL),
|
|
409
|
+
this.import(this.__LDISTANCEM)
|
|
410
|
+
]).then(() => this.options.polyline && layer.addTo(map) && this._circleMarkers.bringToFront());
|
|
437
411
|
} else if (this.options.polyline) {
|
|
438
412
|
layer.addTo(map);
|
|
439
413
|
}
|
|
@@ -459,7 +433,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
459
433
|
*/
|
|
460
434
|
_initMapIntegrations: function(control, layer) {
|
|
461
435
|
let map = control._map;
|
|
462
|
-
if (control.
|
|
436
|
+
if (control._data.length) {
|
|
437
|
+
control._start.setLatLng(control._data[0].latlng);
|
|
438
|
+
control._end.setLatLng(control._data[control._data.length -1].latlng);
|
|
439
|
+
}
|
|
440
|
+
if (map) {
|
|
441
|
+
control._circleMarkers.addTo(map);
|
|
463
442
|
map.once('layeradd', (e) => control.options.autofitBounds && control.fitBounds(layer.getBounds()));
|
|
464
443
|
if (!L.Browser.mobile) {
|
|
465
444
|
control._initAlmostOverHandler(map, layer);
|
|
@@ -492,12 +471,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
492
471
|
_fixCanvasPaths: function() {
|
|
493
472
|
let oldProto = L.Canvas.prototype._fillStroke;
|
|
494
473
|
let control = this;
|
|
474
|
+
|
|
475
|
+
let theme = this.options.theme.replace('-theme', '');
|
|
476
|
+
let color = _.Colors[theme] || {};
|
|
477
|
+
|
|
495
478
|
L.Canvas.include({
|
|
496
479
|
_fillStroke: function(ctx, layer) {
|
|
497
480
|
if (control._layers.hasLayer(layer)) {
|
|
498
481
|
|
|
499
|
-
let theme = control.options.theme.replace('-theme', '');
|
|
500
|
-
let color = D3.Colors[theme] || {};
|
|
501
482
|
let options = layer.options;
|
|
502
483
|
|
|
503
484
|
options.color = color.line || color.area || theme;
|
|
@@ -528,12 +509,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
528
509
|
*/
|
|
529
510
|
_fixTooltipSize: function() {
|
|
530
511
|
this.on('elechart_init', () =>
|
|
531
|
-
this.once('elechart_change elechart_hover',
|
|
512
|
+
this.once('elechart_change elechart_hover', ({data, xCoord}) => {
|
|
532
513
|
if (this._chartEnabled) {
|
|
533
|
-
this._chart._showDiagramIndicator(
|
|
534
|
-
this._chart._showDiagramIndicator(
|
|
514
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
515
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
535
516
|
}
|
|
536
|
-
this._updateMarker(
|
|
517
|
+
this._updateMarker(data);
|
|
537
518
|
})
|
|
538
519
|
);
|
|
539
520
|
},
|
|
@@ -560,15 +541,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
560
541
|
if (this._map) this._map.fire(type, data, propagate);
|
|
561
542
|
},
|
|
562
543
|
|
|
563
|
-
/**
|
|
564
|
-
* Calculates chart height.
|
|
565
|
-
*/
|
|
566
|
-
_height: function() {
|
|
567
|
-
if (this._chart) return this._chart._height();
|
|
568
|
-
let opts = this.options;
|
|
569
|
-
return opts.height - opts.margins.top - opts.margins.bottom;
|
|
570
|
-
},
|
|
571
|
-
|
|
572
544
|
/*
|
|
573
545
|
* Hides the position/height indicator marker drawn onto the map
|
|
574
546
|
*/
|
|
@@ -584,9 +556,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
584
556
|
_initChart: function(container) {
|
|
585
557
|
let opts = this.options;
|
|
586
558
|
|
|
587
|
-
opts.xTicks = this._xTicks();
|
|
588
|
-
opts.yTicks = this._yTicks();
|
|
589
|
-
|
|
590
559
|
if (opts.detached) {
|
|
591
560
|
let { offsetWidth, offsetHeight} = this.eleDiv;
|
|
592
561
|
if (offsetWidth > 0) opts.width = offsetWidth;
|
|
@@ -598,42 +567,66 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
598
567
|
if (opts._maxWidth > clientWidth) opts.width = clientWidth - 30;
|
|
599
568
|
}
|
|
600
569
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
570
|
+
Promise.all([
|
|
571
|
+
this.import(this.__D3),
|
|
572
|
+
this.import(this.__LCHART)
|
|
573
|
+
]).then((m) => {
|
|
605
574
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
.
|
|
575
|
+
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
|
576
|
+
|
|
577
|
+
this._x = this._chart._x;
|
|
578
|
+
this._y = this._chart._y;
|
|
579
|
+
|
|
580
|
+
d3
|
|
581
|
+
.select(container)
|
|
582
|
+
.call(chart.render())
|
|
583
|
+
|
|
584
|
+
chart
|
|
585
|
+
.on('reset_drag', this._hideMarker, this)
|
|
586
|
+
.on('mouse_enter', this._onMouseEnter, this)
|
|
587
|
+
.on('dragged', this._onDragEnd, this)
|
|
588
|
+
.on('mouse_move', this._onMouseMove, this)
|
|
589
|
+
.on('mouse_out', this._onMouseOut, this)
|
|
590
|
+
.on('ruler_filter', this._onRulerFilter, this)
|
|
591
|
+
.on('zoom', this._updateChart, this)
|
|
592
|
+
.on('elepath_toggle', this._onToggleChart, this)
|
|
593
|
+
.on('margins_updated', this._resizeChart, this);
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
this.fire("elechart_axis");
|
|
597
|
+
|
|
598
|
+
if (this.options.legend) this.fire("elechart_legend");
|
|
599
|
+
|
|
600
|
+
this.fire("elechart_init");
|
|
609
601
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
.on('ruler_filter', this._rulerFilterHandler, this)
|
|
617
|
-
.on('zoom', this._updateChart, this)
|
|
618
|
-
.on('elepath_toggle', this._toggleChartHandler, this)
|
|
619
|
-
.on('margins_updated',this._resizeChart, this);
|
|
602
|
+
map
|
|
603
|
+
.on('zoom viewreset zoomanim', this._hideMarker, this)
|
|
604
|
+
.on('resize', this._resetView, this)
|
|
605
|
+
.on('resize', this._resizeChart, this)
|
|
606
|
+
.on('rotate', this._rotateMarker, this)
|
|
607
|
+
.on('mousedown', this._resetDrag, this);
|
|
620
608
|
|
|
609
|
+
_.on(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
610
|
+
_.on(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
611
|
+
_.on(document, 'keydown', this._onKeyDown, this);
|
|
621
612
|
|
|
622
|
-
|
|
613
|
+
this
|
|
614
|
+
.on('eledata_added eledata_loaded', this._updateChart, this)
|
|
615
|
+
.on('eledata_added eledata_loaded', this._updateSummary, this);
|
|
623
616
|
|
|
624
|
-
|
|
617
|
+
this._updateChart();
|
|
618
|
+
this._updateSummary();
|
|
619
|
+
});
|
|
625
620
|
|
|
626
|
-
this.fire("elechart_init");
|
|
627
621
|
},
|
|
628
622
|
|
|
629
623
|
_initLayer: function() {
|
|
630
624
|
this._layers
|
|
631
|
-
.on('layeradd layerremove', (
|
|
632
|
-
let layer = e.layer
|
|
625
|
+
.on('layeradd layerremove', ({layer, type}) => {
|
|
633
626
|
let node = layer.getElement && layer.getElement();
|
|
634
|
-
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme,
|
|
635
|
-
_.toggleEvent(layer, "mousemove", this.
|
|
636
|
-
_.toggleEvent(layer, "mouseout", this.
|
|
627
|
+
_.toggleClass(node, this.options.polyline.className + ' ' + this.options.theme, type == 'layeradd');
|
|
628
|
+
_.toggleEvent(layer, "mousemove", this._onMouseMoveLayer.bind(this), type == 'layeradd')
|
|
629
|
+
_.toggleEvent(layer, "mouseout", this._onMouseOut.bind(this), type == 'layeradd');
|
|
637
630
|
});
|
|
638
631
|
},
|
|
639
632
|
|
|
@@ -647,9 +640,14 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
647
640
|
|
|
648
641
|
if (this._renderer) this._renderer.remove()
|
|
649
642
|
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
|
650
|
-
this._marker = new Marker(this.options);
|
|
651
643
|
|
|
652
|
-
|
|
644
|
+
Promise.all([
|
|
645
|
+
this.import(this.__D3),
|
|
646
|
+
this.import(this.__LMARKER)
|
|
647
|
+
]).then((m) => {
|
|
648
|
+
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
|
649
|
+
this.fire("elechart_marker");
|
|
650
|
+
});
|
|
653
651
|
},
|
|
654
652
|
|
|
655
653
|
/**
|
|
@@ -684,15 +682,13 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
684
682
|
},
|
|
685
683
|
|
|
686
684
|
_initSummary: function(container) {
|
|
687
|
-
this.
|
|
685
|
+
this.import(this.__LSUMMARY).then((m)=>{
|
|
686
|
+
this._summary = new (m || Elevation).Summary({ summary: this.options.summary }, this);
|
|
688
687
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if(!this.options.detached && e.key === "Escape"){
|
|
694
|
-
this._collapse()
|
|
695
|
-
};
|
|
688
|
+
this.on('elechart_init', () => {
|
|
689
|
+
d3.select(container).call(this._summary.render());
|
|
690
|
+
});
|
|
691
|
+
});
|
|
696
692
|
},
|
|
697
693
|
|
|
698
694
|
/**
|
|
@@ -708,138 +704,146 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
708
704
|
}).catch((err) => console.warn(err));
|
|
709
705
|
},
|
|
710
706
|
|
|
707
|
+
/**
|
|
708
|
+
* Dynamically import only required javascript modules (code splitting)
|
|
709
|
+
*/
|
|
710
|
+
_loadModules: function(handlers) {
|
|
711
|
+
// First map known classnames (eg. "Altitude" --> L.Control.Elevation.Altitude)
|
|
712
|
+
handlers = handlers.map((h) => typeof h === 'string' && typeof Elevation[h] !== "undefined" ? Elevation[h] : h);
|
|
713
|
+
// Then load optional classes and custom imports (eg. "Cadence" --> import('../src/handlers/cadence.js'))
|
|
714
|
+
Promise.
|
|
715
|
+
all(
|
|
716
|
+
handlers
|
|
717
|
+
.filter(h => typeof h === 'string' || h instanceof Promise)
|
|
718
|
+
.map(file => file instanceof Promise ? file : this.import(this.__modulesFolder + file.toLowerCase() + '.js'))
|
|
719
|
+
)
|
|
720
|
+
.then((m) => {
|
|
721
|
+
_.each(m, (exported) => {
|
|
722
|
+
let fn = Object.keys(exported)[0];
|
|
723
|
+
if (fn && typeof Elevation[fn] === "undefined" ) {
|
|
724
|
+
handlers[fn] = Elevation[fn] = exported[fn];
|
|
725
|
+
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
_.each(handlers, h => this._registerHandler(h));
|
|
729
|
+
});
|
|
730
|
+
},
|
|
731
|
+
|
|
711
732
|
/**
|
|
712
733
|
* Simple GeoJSON data loader (L.GeoJSON).
|
|
713
734
|
*/
|
|
714
735
|
_loadLayer: function(geojson) {
|
|
715
|
-
let
|
|
736
|
+
let { polyline, theme, waypoints, wptIcons, wptLabels, distanceMarkers } = this.options;
|
|
737
|
+
let style = L.extend({}, polyline);
|
|
716
738
|
|
|
717
|
-
|
|
739
|
+
if (theme) {
|
|
740
|
+
style.className += ' ' + theme;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (geojson.name) {
|
|
744
|
+
this.track_info.name = geojson.name;
|
|
745
|
+
}
|
|
718
746
|
|
|
719
747
|
let layer = L.geoJson(geojson, {
|
|
720
|
-
distanceMarkers:
|
|
721
|
-
style:
|
|
722
|
-
let style = L.extend({}, control.options.polyline);
|
|
723
|
-
if (control.options.theme) {
|
|
724
|
-
style.className += ' ' + control.options.theme;
|
|
725
|
-
}
|
|
726
|
-
return style;
|
|
727
|
-
},
|
|
748
|
+
distanceMarkers: distanceMarkers,
|
|
749
|
+
style: style,
|
|
728
750
|
pointToLayer: (feature, latlng) => {
|
|
729
|
-
if (
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
latlng: latlng,
|
|
740
|
-
label: ([true, 'dots'].includes(wptLabels) ? name : '')
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// Handle map waypoints (markers)
|
|
745
|
-
if ([true, 'markers'].includes(control.options.waypoints) && wptIcons != false) {
|
|
746
|
-
// generate and cache appropriate icon symbol
|
|
747
|
-
if (!wptIcons.hasOwnProperty(sym)) {
|
|
748
|
-
wptIcons[sym] = L.divIcon(
|
|
749
|
-
L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } )
|
|
750
|
-
);
|
|
751
|
+
if (waypoints) {
|
|
752
|
+
let { desc, name, sym } = feature.properties;
|
|
753
|
+
desc = desc || '';
|
|
754
|
+
name = name || '';
|
|
755
|
+
// Handle chart waypoints (dots)
|
|
756
|
+
if ([true, 'dots'].includes(waypoints)) {
|
|
757
|
+
this._registerCheckPoint({
|
|
758
|
+
latlng: latlng,
|
|
759
|
+
label : ([true, 'dots'].includes(wptLabels) ? name : '')
|
|
760
|
+
});
|
|
751
761
|
}
|
|
752
|
-
|
|
753
|
-
if ([true, 'markers'].includes(
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
762
|
+
// Handle map waypoints (markers)
|
|
763
|
+
if ([true, 'markers'].includes(waypoints) && wptIcons != false) {
|
|
764
|
+
return this._registerMarker({
|
|
765
|
+
latlng : latlng,
|
|
766
|
+
sym : (sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase(),
|
|
767
|
+
content: [true, 'markers'].includes(wptLabels) && (name || desc) && decodeURI("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : ''))
|
|
768
|
+
});
|
|
757
769
|
}
|
|
758
|
-
control._addMarker(marker);
|
|
759
|
-
control.fire('waypoint_added', { point: marker, element: latlng, properties: prop });
|
|
760
|
-
return marker;
|
|
761
770
|
}
|
|
762
771
|
},
|
|
763
|
-
onEachFeature: (feature, layer) =>
|
|
764
|
-
if (feature.geometry && feature.geometry.type != 'Point') {
|
|
765
|
-
control.addData(layer);
|
|
766
|
-
control.track_info = L.extend({}, control.track_info, { name: geojson.name });
|
|
767
|
-
}
|
|
768
|
-
},
|
|
772
|
+
onEachFeature: (feature, layer) => feature.geometry && feature.geometry.type != 'Point' && this.addData(feature, layer),
|
|
769
773
|
});
|
|
770
774
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
775
|
+
this.import(this.__D3).then(() => {
|
|
776
|
+
this._initMapIntegrations(this, layer);
|
|
777
|
+
this._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: this.track_info.name, track_info: this.track_info });
|
|
774
778
|
});
|
|
775
779
|
|
|
776
780
|
return layer;
|
|
777
781
|
},
|
|
778
782
|
|
|
779
|
-
|
|
783
|
+
_onDragEnd: function({ dragstart, dragend}) {
|
|
780
784
|
this._hideMarker();
|
|
781
|
-
this.fitBounds(L.latLngBounds([
|
|
785
|
+
this.fitBounds(L.latLngBounds([dragstart.latlng, dragend.latlng]));
|
|
782
786
|
|
|
783
787
|
this.fire("elechart_dragged");
|
|
784
788
|
},
|
|
785
789
|
|
|
790
|
+
_onKeyDown: function({key}) {
|
|
791
|
+
if(!this.options.detached && key === "Escape"){
|
|
792
|
+
this._collapse()
|
|
793
|
+
};
|
|
794
|
+
},
|
|
795
|
+
|
|
786
796
|
/**
|
|
787
|
-
* Trigger mouseenter event
|
|
797
|
+
* Trigger mouseenter event.
|
|
788
798
|
*/
|
|
789
|
-
|
|
799
|
+
_onMouseEnter: function() {
|
|
790
800
|
this.fire('elechart_enter');
|
|
791
801
|
},
|
|
792
802
|
|
|
793
803
|
/*
|
|
794
804
|
* Handles the moueseover the chart and displays distance and altitude level.
|
|
795
805
|
*/
|
|
796
|
-
|
|
797
|
-
if (
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (item) {
|
|
803
|
-
let xCoord = e.xCoord;
|
|
806
|
+
_onMouseMove: function({xCoord}) {
|
|
807
|
+
if (this._chartEnabled && this._data.length) {
|
|
808
|
+
let item = this._findItemForX(xCoord);
|
|
809
|
+
if (item) {
|
|
810
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
804
811
|
|
|
805
|
-
|
|
812
|
+
this._updateMarker(item);
|
|
813
|
+
this._setMapView(item);
|
|
806
814
|
|
|
807
|
-
|
|
808
|
-
|
|
815
|
+
if (this._map) {
|
|
816
|
+
_.addClass(this._map.getContainer(), 'elechart-hover');
|
|
817
|
+
}
|
|
809
818
|
|
|
810
|
-
|
|
811
|
-
|
|
819
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
820
|
+
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
|
812
821
|
}
|
|
813
|
-
|
|
814
|
-
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
815
|
-
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
|
816
822
|
}
|
|
817
823
|
},
|
|
818
824
|
|
|
819
825
|
/*
|
|
820
826
|
* Handles mouseover events of the data layers on the map.
|
|
821
827
|
*/
|
|
822
|
-
|
|
823
|
-
if (
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
828
|
+
_onMouseMoveLayer: function({latlng}) {
|
|
829
|
+
if (this._data.length) {
|
|
830
|
+
let item = this._findItemForLatLng(latlng);
|
|
831
|
+
if (item) {
|
|
832
|
+
let xCoord = item.xDiagCoord;
|
|
833
|
+
|
|
834
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
835
|
+
|
|
836
|
+
this._updateMarker(item);
|
|
837
|
+
|
|
838
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
839
|
+
}
|
|
836
840
|
}
|
|
837
841
|
},
|
|
838
842
|
|
|
839
843
|
/*
|
|
840
844
|
* Handles the moueseout over the chart.
|
|
841
845
|
*/
|
|
842
|
-
|
|
846
|
+
_onMouseOut: function() {
|
|
843
847
|
if (!this.options.detached) {
|
|
844
848
|
this._hideMarker();
|
|
845
849
|
this._chart._hideDiagramIndicator();
|
|
@@ -852,6 +856,33 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
852
856
|
this.fire("elechart_leave");
|
|
853
857
|
},
|
|
854
858
|
|
|
859
|
+
/**
|
|
860
|
+
* Handles the drag event over the ruler filter.
|
|
861
|
+
*/
|
|
862
|
+
_onRulerFilter: function({coords}) {
|
|
863
|
+
this._updateMapSegments(coords);
|
|
864
|
+
},
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* Toggle chart data on legend click
|
|
868
|
+
*/
|
|
869
|
+
_onToggleChart: function({ name, enabled }) {
|
|
870
|
+
|
|
871
|
+
this._chartEnabled = this._chart._hasActiveLayers();
|
|
872
|
+
|
|
873
|
+
// toggle layer visibility on empty chart
|
|
874
|
+
this._layers.eachLayer(layer => _.toggleClass(layer.getElement && layer.getElement(), this.options.polyline.className + ' ' + this.options.theme, this._chartEnabled));
|
|
875
|
+
|
|
876
|
+
// toggle option value (eg. altitude = { 'disabled' || 'enabled' })
|
|
877
|
+
this.options[name] = !enabled && this.options[name] == 'disabled' ? 'enabled' : 'disabled';
|
|
878
|
+
|
|
879
|
+
// remove marker on empty chart
|
|
880
|
+
if (!this._chartEnabled) {
|
|
881
|
+
this._chart._hideDiagramIndicator();
|
|
882
|
+
this._marker.remove();
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
|
|
855
886
|
/**
|
|
856
887
|
* Simple GeoJSON Parser
|
|
857
888
|
*/
|
|
@@ -866,7 +897,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
866
897
|
*/
|
|
867
898
|
_parseFromString: function(data) {
|
|
868
899
|
return new Promise(resolve =>
|
|
869
|
-
this.
|
|
900
|
+
this.import(this.__TOGEOJSON).then(() => {
|
|
870
901
|
let geojson;
|
|
871
902
|
try {
|
|
872
903
|
geojson = this._parseFromXMLString(data.trim());
|
|
@@ -874,8 +905,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
874
905
|
geojson = this._parseFromGeoJSONString(data.toString());
|
|
875
906
|
}
|
|
876
907
|
if (geojson) {
|
|
877
|
-
geojson = this.
|
|
878
|
-
geojson = this._prepareAdditionalProperties(geojson);
|
|
908
|
+
geojson.name = geojson.name || (this._downloadURL || '').split('/').pop().split('#')[0].split('?')[0];
|
|
879
909
|
}
|
|
880
910
|
resolve(geojson);
|
|
881
911
|
})
|
|
@@ -903,37 +933,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
903
933
|
return geojson;
|
|
904
934
|
},
|
|
905
935
|
|
|
906
|
-
/**
|
|
907
|
-
* Extend GeoJSON properties (name, time, ...)
|
|
908
|
-
*/
|
|
909
|
-
_prepareAdditionalProperties: function(geojson) {
|
|
910
|
-
if (!geojson.name && this._downloadURL) {
|
|
911
|
-
geojson.name = this._downloadURL.split('/').pop().split('#')[0].split('?')[0];
|
|
912
|
-
}
|
|
913
|
-
return geojson;
|
|
914
|
-
},
|
|
915
|
-
|
|
916
|
-
/**
|
|
917
|
-
* Just a temporary fix for MultiLineString data (trk > trkseg + trkseg),
|
|
918
|
-
* just split them into seperate LineStrings (trk > trkseg, trk > trkseg)
|
|
919
|
-
*
|
|
920
|
-
* @link https://github.com/Raruto/leaflet-elevation/issues/56
|
|
921
|
-
*/
|
|
922
|
-
_prepareMultiLineStrings: function(geojson) {
|
|
923
|
-
geojson.features.forEach(feauture => {
|
|
924
|
-
if (feauture.geometry.type == "MultiLineString") {
|
|
925
|
-
feauture.geometry.coordinates.forEach(coords => {
|
|
926
|
-
geojson.features.push({
|
|
927
|
-
geometry: { type: 'LineString', coordinates: coords },
|
|
928
|
-
properties: feauture.properties,
|
|
929
|
-
type: 'Feature'
|
|
930
|
-
});
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
return geojson.features.filter(feauture => feauture.geometry.type != "MultiLineString");
|
|
935
|
-
},
|
|
936
|
-
|
|
937
936
|
/**
|
|
938
937
|
* Add chart profile to diagram
|
|
939
938
|
*/
|
|
@@ -967,22 +966,31 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
967
966
|
* Base handler for iterative track statistics (dist, time, z, slope, speed, acceleration, ...)
|
|
968
967
|
*/
|
|
969
968
|
_registerDataAttribute: function(props) {
|
|
970
|
-
// save here a reference to last used point
|
|
971
|
-
let lastValid = null;
|
|
972
969
|
|
|
973
970
|
// parse of "coordinateProperties" for later usage
|
|
974
|
-
if (props.
|
|
975
|
-
this.on("elepoint_init", (e) => props.
|
|
971
|
+
if (props.coordPropsToMeta) {
|
|
972
|
+
this.on("elepoint_init", (e) => props.coordPropsToMeta.call(this, e));
|
|
976
973
|
}
|
|
977
974
|
|
|
975
|
+
// prevent excessive variabile instanstations
|
|
976
|
+
let i, curr, prev, attr;
|
|
977
|
+
|
|
978
|
+
// save here a reference to last used point
|
|
979
|
+
let lastValid = null;
|
|
980
|
+
|
|
978
981
|
// iteration
|
|
979
|
-
this.on("elepoint_added", (
|
|
982
|
+
this.on("elepoint_added", ({index, point}) => {
|
|
983
|
+
i = index;
|
|
984
|
+
|
|
985
|
+
prev = curr ?? this._data[i]; // same as: this._data[i > 0 ? i - 1 : i]
|
|
986
|
+
curr = this._data[i];
|
|
987
|
+
attr = props.attr || props.name;
|
|
980
988
|
|
|
981
989
|
// check and fix missing data on last added point
|
|
982
990
|
if (props.skipNull === false) {
|
|
983
|
-
let curr = this._data[
|
|
984
|
-
if(
|
|
985
|
-
let prev = this._data[
|
|
991
|
+
let curr = this._data[i][attr];
|
|
992
|
+
if(i > 0) {
|
|
993
|
+
let prev = this._data[i - 1][attr];
|
|
986
994
|
if (isNaN(prev)) {
|
|
987
995
|
if (!isNaN(lastValid) && !isNaN(curr)) {
|
|
988
996
|
prev = (lastValid + curr) / 2;
|
|
@@ -991,8 +999,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
991
999
|
} else if (!isNaN(curr)) {
|
|
992
1000
|
prev = curr;
|
|
993
1001
|
}
|
|
994
|
-
if (!isNaN(prev)) return this._data.splice(
|
|
995
|
-
this._data[
|
|
1002
|
+
if (!isNaN(prev)) return this._data.splice(i - 1, 1);
|
|
1003
|
+
this._data[i - 1][attr] = prev;
|
|
996
1004
|
}
|
|
997
1005
|
}
|
|
998
1006
|
// update reference to last used point (ie. if it has data)
|
|
@@ -1002,25 +1010,126 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1002
1010
|
}
|
|
1003
1011
|
|
|
1004
1012
|
// retrieve point value
|
|
1005
|
-
|
|
1013
|
+
curr[attr] = props.pointToAttr.call(this, point, i);
|
|
1014
|
+
|
|
1015
|
+
// update "track_info" stats (min, max, avg, ...)
|
|
1016
|
+
if (props.stats) {
|
|
1017
|
+
for (const key in props.stats) {
|
|
1018
|
+
let sname = (props.statsName || attr) + (key != '' ? '_' : '');
|
|
1019
|
+
this.track_info[sname + key] = props.stats[key].call(this, curr[attr], this.track_info[sname + key]);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1006
1022
|
|
|
1007
|
-
//
|
|
1008
|
-
if(props.
|
|
1023
|
+
// Limit "crazy" delta values.
|
|
1024
|
+
if (props.deltaMax) {
|
|
1025
|
+
curr[attr] =_.wrapDelta(curr[attr], prev[attr], props.deltaMax);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Range of acceptable values.
|
|
1029
|
+
if (props.clampRange) {
|
|
1030
|
+
curr[attr] = _.clamp(curr[attr], props.clampRange);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Limit floating point precision.
|
|
1034
|
+
if (!isNaN(props.decimals)) {
|
|
1035
|
+
curr[attr] = _.round(curr[attr], props.decimals);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// update here some mixins (eg. complex "track_info" stuff)
|
|
1039
|
+
if(props.onPointAdded) props.onPointAdded.call(this, curr[attr], i, point);
|
|
1009
1040
|
});
|
|
1010
1041
|
},
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Parse a module definition and attach related function listeners
|
|
1045
|
+
*/
|
|
1046
|
+
_registerHandler: function(props) {
|
|
1047
|
+
|
|
1048
|
+
// eg. L.Control.Altitude
|
|
1049
|
+
if (typeof props === "function") {
|
|
1050
|
+
return this._registerHandler(props.call(this));
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
let {
|
|
1054
|
+
name,
|
|
1055
|
+
attr,
|
|
1056
|
+
required,
|
|
1057
|
+
skipNull,
|
|
1058
|
+
deltaMax,
|
|
1059
|
+
clampRange,
|
|
1060
|
+
decimals,
|
|
1061
|
+
meta,
|
|
1062
|
+
unit,
|
|
1063
|
+
coordinateProperties,
|
|
1064
|
+
coordPropsToMeta,
|
|
1065
|
+
pointToAttr,
|
|
1066
|
+
onPointAdded,
|
|
1067
|
+
stats,
|
|
1068
|
+
statsName,
|
|
1069
|
+
grid,
|
|
1070
|
+
scale,
|
|
1071
|
+
path,
|
|
1072
|
+
tooltip,
|
|
1073
|
+
summary
|
|
1074
|
+
} = props;
|
|
1075
|
+
|
|
1076
|
+
// eg. "altitude" == true
|
|
1077
|
+
if (this.options[name] || required) {
|
|
1078
|
+
|
|
1079
|
+
this._registerDataAttribute({
|
|
1080
|
+
name,
|
|
1081
|
+
attr,
|
|
1082
|
+
skipNull,
|
|
1083
|
+
deltaMax,
|
|
1084
|
+
clampRange,
|
|
1085
|
+
decimals,
|
|
1086
|
+
coordPropsToMeta: _.coordPropsToMeta(coordinateProperties, meta || name, coordPropsToMeta),
|
|
1087
|
+
pointToAttr,
|
|
1088
|
+
onPointAdded,
|
|
1089
|
+
stats,
|
|
1090
|
+
statsName,
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
if (grid) {
|
|
1094
|
+
this._registerAxisGrid(L.extend({ name }, grid));
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (this.options[name] !== "summary") {
|
|
1098
|
+
if (scale) this._registerAxisScale(L.extend({ name, label: unit }, scale));
|
|
1099
|
+
if (path) this._registerAreaPath(L.extend({ name }, path));
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
if (tooltip || props.tooltips) {
|
|
1103
|
+
_.each([tooltip, ...(props.tooltips || [])], t => t && this._registerTooltip(L.extend({ name }, t)));
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (summary) {
|
|
1107
|
+
_.each(summary, (s, k) => summary[k] = L.extend({ unit }, s));
|
|
1108
|
+
this._registerSummary(summary);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
|
|
1113
|
+
_registerMarker({latlng, sym, content}) {
|
|
1114
|
+
let { wptIcons } = this.options;
|
|
1115
|
+
// generate and cache appropriate icon symbol
|
|
1116
|
+
if (!wptIcons.hasOwnProperty(sym)) {
|
|
1117
|
+
wptIcons[sym] = L.divIcon(L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } ));
|
|
1118
|
+
}
|
|
1119
|
+
let marker = L.marker(latlng, { icon: wptIcons[sym] });
|
|
1120
|
+
if (content) {
|
|
1121
|
+
marker.bindPopup(content, { className: 'elevation-popup', keepInView: true }).openPopup();
|
|
1122
|
+
marker.bindTooltip(content, { className: 'elevation-tooltip', direction: 'auto', sticky: true, opacity: 1 }).openTooltip();
|
|
1123
|
+
}
|
|
1124
|
+
return this._addMarker(marker)
|
|
1125
|
+
},
|
|
1011
1126
|
|
|
1012
1127
|
/**
|
|
1013
1128
|
* Add chart or marker tooltip info
|
|
1014
1129
|
*/
|
|
1015
1130
|
_registerTooltip: function(props) {
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
this.on("elechart_init", () => this._chart._registerTooltip(label));
|
|
1019
|
-
}
|
|
1020
|
-
if (props.marker) {
|
|
1021
|
-
let label = L.extend({}, props, { value: props.marker });
|
|
1022
|
-
this.on("elechart_marker", () => this._marker._registerTooltip(label));
|
|
1023
|
-
}
|
|
1131
|
+
props.chart && this.on("elechart_init", () => this._chart._registerTooltip(L.extend({}, props, { value: props.chart })));
|
|
1132
|
+
props.marker && this.on("elechart_marker", () => this._marker._registerTooltip(L.extend({}, props, { value: props.marker })));
|
|
1024
1133
|
},
|
|
1025
1134
|
|
|
1026
1135
|
/**
|
|
@@ -1054,85 +1163,39 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1054
1163
|
* Hacky way for handling chart resize. Deletes it and redraw chart.
|
|
1055
1164
|
*/
|
|
1056
1165
|
_resizeChart: function() {
|
|
1057
|
-
if(
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
if (opts.detached) {
|
|
1067
|
-
newWidth = (this.eleDiv || this._container).offsetWidth;
|
|
1068
|
-
} else {
|
|
1069
|
-
let { clientWidth } = this._map.getContainer();
|
|
1070
|
-
newWidth = opts._maxWidth > clientWidth ? clientWidth - 30 : opts._maxWidth;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
if (newWidth) {
|
|
1074
|
-
let chart = this._chart;
|
|
1075
|
-
opts.width = newWidth;
|
|
1076
|
-
if (chart && chart._chart) {
|
|
1077
|
-
chart._chart._resize(opts);
|
|
1078
|
-
opts.xTicks = this._xTicks();
|
|
1079
|
-
opts.yTicks = this._yTicks();
|
|
1080
|
-
this._updateChart();
|
|
1166
|
+
if (this._container && _.style(this._container, "display") != "none") {
|
|
1167
|
+
let opts = this.options;
|
|
1168
|
+
let newWidth = opts.detached ? (this.eleDiv || this._container).offsetWidth : _.clamp(opts._maxWidth, [0, this._map.getContainer().clientWidth - 30]);
|
|
1169
|
+
if (newWidth) {
|
|
1170
|
+
opts.width = newWidth;
|
|
1171
|
+
if (this._chart && this._chart._chart) {
|
|
1172
|
+
this._chart._chart._resize(opts);
|
|
1173
|
+
this._updateChart();
|
|
1174
|
+
}
|
|
1081
1175
|
}
|
|
1176
|
+
this._updateMapSegments();
|
|
1082
1177
|
}
|
|
1083
|
-
|
|
1084
|
-
this._updateMapSegments();
|
|
1085
|
-
},
|
|
1086
|
-
|
|
1087
|
-
/**
|
|
1088
|
-
* Handles the drag event over the ruler filter.
|
|
1089
|
-
*/
|
|
1090
|
-
_rulerFilterHandler: function(e) {
|
|
1091
|
-
this._updateMapSegments(e.coords);
|
|
1092
1178
|
},
|
|
1093
1179
|
|
|
1094
1180
|
/**
|
|
1095
1181
|
* Collapse or Expand chart control.
|
|
1096
1182
|
*/
|
|
1097
1183
|
_toggle: function() {
|
|
1098
|
-
|
|
1099
|
-
this._collapse();
|
|
1100
|
-
else
|
|
1101
|
-
this._expand();
|
|
1184
|
+
_.hasClass(this._container, "elevation-expanded") ? this._collapse() : this._expand();
|
|
1102
1185
|
},
|
|
1103
1186
|
|
|
1104
1187
|
/**
|
|
1105
1188
|
* Update map center and zoom (followMarker: true)
|
|
1106
1189
|
*/
|
|
1107
1190
|
_setMapView: function(item) {
|
|
1108
|
-
if (
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
},
|
|
1117
|
-
|
|
1118
|
-
/**
|
|
1119
|
-
* Toggle chart data on legend click
|
|
1120
|
-
*/
|
|
1121
|
-
_toggleChartHandler: function(e) {
|
|
1122
|
-
let { name, enabled } = e;
|
|
1123
|
-
|
|
1124
|
-
this._chartEnabled = this._chart._hasActiveLayers();
|
|
1125
|
-
|
|
1126
|
-
// toggle layer visibility on empty chart
|
|
1127
|
-
this._layers.eachLayer(layer => _.toggleClass(layer.getElement && layer.getElement(), this.options.polyline.className + ' ' + this.options.theme, this._chartEnabled));
|
|
1128
|
-
|
|
1129
|
-
// toggle option value (eg. altitude = { 'disabled' || 'disabled' })
|
|
1130
|
-
this.options[name] = !enabled && this.options[name] == 'disabled' ? 'enabled' : 'disabled';
|
|
1131
|
-
|
|
1132
|
-
// remove marker on empty chart
|
|
1133
|
-
if (!this._chartEnabled) {
|
|
1134
|
-
this._chart._hideDiagramIndicator();
|
|
1135
|
-
this._marker.remove();
|
|
1191
|
+
if (this._map && this.options.followMarker) {
|
|
1192
|
+
let zoom = this._map.getZoom();
|
|
1193
|
+
let z = this.options.zFollow;
|
|
1194
|
+
if (typeof z === "number") {
|
|
1195
|
+
this._map.setView(item.latlng, (zoom < z ? z : zoom), { animate: true, duration: 0.25 });
|
|
1196
|
+
} else if (!this._map.getBounds().contains(item.latlng)) {
|
|
1197
|
+
this._map.setView(item.latlng, zoom, { animate: true, duration: 0.25 });
|
|
1198
|
+
}
|
|
1136
1199
|
}
|
|
1137
1200
|
},
|
|
1138
1201
|
|
|
@@ -1140,17 +1203,17 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1140
1203
|
* Calculates [x, y] domain and then update chart.
|
|
1141
1204
|
*/
|
|
1142
1205
|
_updateChart: function() {
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1206
|
+
if (this._chart && this._container) {
|
|
1207
|
+
this.fire("elechart_axis");
|
|
1208
|
+
this.fire("elechart_area");
|
|
1209
|
+
|
|
1210
|
+
this._chart.update({ data: this._data, options: this.options });
|
|
1211
|
+
|
|
1212
|
+
this._x = this._chart._x;
|
|
1213
|
+
this._y = this._chart._y;
|
|
1214
|
+
|
|
1215
|
+
this.fire('elechart_updated');
|
|
1216
|
+
}
|
|
1154
1217
|
},
|
|
1155
1218
|
|
|
1156
1219
|
/*
|
|
@@ -1161,7 +1224,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1161
1224
|
this._marker.update({
|
|
1162
1225
|
map : this._map,
|
|
1163
1226
|
item : item,
|
|
1164
|
-
|
|
1227
|
+
yCoordMax : this._yCoordMax || 0,
|
|
1165
1228
|
options : this.options
|
|
1166
1229
|
});
|
|
1167
1230
|
}
|
|
@@ -1190,6 +1253,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1190
1253
|
* Update chart summary.
|
|
1191
1254
|
*/
|
|
1192
1255
|
_updateSummary: function() {
|
|
1256
|
+
if (!this._summary) return;
|
|
1257
|
+
|
|
1193
1258
|
this._summary.reset();
|
|
1194
1259
|
|
|
1195
1260
|
if (this.options.summary) {
|
|
@@ -1198,8 +1263,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1198
1263
|
}
|
|
1199
1264
|
if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
|
|
1200
1265
|
this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
|
|
1201
|
-
_.select('.download a', this._summary._container).onclick = (
|
|
1202
|
-
|
|
1266
|
+
_.select('.download a', this._summary._container).onclick = ({preventDefault}) => {
|
|
1267
|
+
preventDefault();
|
|
1203
1268
|
let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
|
|
1204
1269
|
if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
|
|
1205
1270
|
document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
|
|
@@ -1217,24 +1282,17 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
|
|
|
1217
1282
|
*/
|
|
1218
1283
|
_width: function() {
|
|
1219
1284
|
if (this._chart) return this._chart._width();
|
|
1220
|
-
|
|
1221
|
-
return
|
|
1285
|
+
const { width, margins } = this.options;
|
|
1286
|
+
return width - margins.left - margins.right;
|
|
1222
1287
|
},
|
|
1223
1288
|
|
|
1224
1289
|
/**
|
|
1225
|
-
*
|
|
1290
|
+
* Calculates chart height.
|
|
1226
1291
|
*/
|
|
1227
|
-
|
|
1228
|
-
if (this.
|
|
1229
|
-
|
|
1292
|
+
_height: function() {
|
|
1293
|
+
if (this._chart) return this._chart._height();
|
|
1294
|
+
const { height, margins } = this.options;
|
|
1295
|
+
return height - margins.top - margins.bottom;
|
|
1230
1296
|
},
|
|
1231
1297
|
|
|
1232
|
-
/**
|
|
1233
|
-
* Calculate chart yTicks
|
|
1234
|
-
*/
|
|
1235
|
-
_yTicks: function() {
|
|
1236
|
-
if (this.__yTicks) this.__yTicks = this.options.yTicks;
|
|
1237
|
-
return this.__yTicks || Math.round(this._height() / 30);
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
1298
|
});
|