@raruto/leaflet-elevation 2.0.0 → 2.0.3

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/src/control.js CHANGED
@@ -14,10 +14,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
14
14
  __LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.9.3/src/leaflet.geometryutil.js',
15
15
  __LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
16
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',
17
20
  __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
@@ -25,9 +25,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
25
25
  addData: function(d, layer) {
26
26
  this.import(this.__D3)
27
27
  .then(() => {
28
- if (typeof layer === "undefined" && d.on) {
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 });
@@ -296,51 +294,44 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
296
294
  return;
297
295
  }
298
296
 
299
- /**
300
- * Standard GeoJSON --> doesn't handle "time", "heart", …
301
- */
302
- let geom = d.geometry;
303
- if (geom) {
304
- switch (geom.type) {
305
- case 'LineString':
306
- this._addGeoJSONData(geom.coordinates);
307
- break;
308
-
309
- case 'MultiLineString':
310
- _.each(geom.coordinates, coords => this._addGeoJSONData(coords));
311
- break;
312
-
313
- default:
314
- return 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
+ }
315
309
  }
316
310
  }
317
- if (d.type === "FeatureCollection") {
318
- _.each(d.features, feature => this._addData(feature));
319
- }
320
311
 
321
- /**
322
- * Extended GeoJSON --> rely on leaflet implementation
323
- */
312
+ // Fallback for leaflet layers (eg. L.Gpx)
324
313
  if (d._latlngs) {
325
- this._addGeoJSONData(d._latlngs, d.feature && d.feature.properties);
314
+ return this._addGeoJSONData(d._latlngs, d.feature && d.feature.properties);
326
315
  }
316
+
327
317
  },
328
318
 
329
319
  /*
330
320
  * Parsing of GeoJSON data lines and their elevation in z-coordinate
331
321
  */
332
- _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
+
333
327
  coords.forEach((point, i) => {
334
328
 
335
329
  // GARMIN_EXTENSIONS = ["hr", "cad", "atemp", "wtemp", "depth", "course", "bearing"];
336
330
  point.meta = point.meta ?? { time: null, ele: null };
337
331
 
338
- // "coordinateProperties" property is generated inside "@tmcw/toGeoJSON"
339
- let props = (properties && properties.coordinateProperties) || properties;
340
-
341
332
  point.prev = (attr) => (attr ? this._data[i > 0 ? i - 1 : 0][attr] : this._data[i > 0 ? i - 1 : 0]);
342
333
 
343
- this.fire("elepoint_init", { point: point, props: props, id: i });
334
+ this.fire("elepoint_init", { point: point, props: props, id: i, isMulti: nestingLevel });
344
335
 
345
336
  this._addPoint(
346
337
  point.lat ?? point[1],
@@ -380,10 +371,12 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
380
371
  if (layer && this.options.distanceMarkers && this.options.distanceMarkers.lazy) {
381
372
  layer.on('add remove', ({target, type}) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
382
373
  }
374
+ return layer;
383
375
  },
384
376
 
385
377
  _addMarker: function(marker) {
386
378
  if (marker) this._markers.addLayer(marker)
379
+ return marker;
387
380
  },
388
381
 
389
382
  /**
@@ -600,10 +593,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
600
593
  .on('margins_updated', this._resizeChart, this);
601
594
 
602
595
 
603
- this.fire("elechart_axis");
604
-
605
- if (this.options.legend) this.fire("elechart_legend");
606
-
607
596
  this.fire("elechart_init");
608
597
 
609
598
  map
@@ -716,7 +705,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
716
705
  */
717
706
  _loadModules: function(handlers) {
718
707
  // 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);
708
+ handlers = handlers.map((h) => typeof h === 'string' && typeof Elevation[h] !== "undefined" ? Elevation[h] : h);
720
709
  // Then load optional classes and custom imports (eg. "Cadence" --> import('../src/handlers/cadence.js'))
721
710
  Promise.
722
711
  all(
@@ -727,8 +716,8 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
727
716
  .then((m) => {
728
717
  _.each(m, (exported) => {
729
718
  let fn = Object.keys(exported)[0];
730
- if (fn && typeof L.Control.Elevation[fn] === "undefined" ) {
731
- handlers[fn] = L.Control.Elevation[fn] = exported[fn];
719
+ if (fn && typeof Elevation[fn] === "undefined" ) {
720
+ handlers[fn] = Elevation[fn] = exported[fn];
732
721
 
733
722
  }
734
723
  });
@@ -740,65 +729,48 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
740
729
  * Simple GeoJSON data loader (L.GeoJSON).
741
730
  */
742
731
  _loadLayer: function(geojson) {
743
- let control = this;
732
+ let { polyline, theme, waypoints, wptIcons, wptLabels, distanceMarkers } = this.options;
733
+ let style = L.extend({}, polyline);
734
+
735
+ if (theme) {
736
+ style.className += ' ' + theme;
737
+ }
744
738
 
745
- let { wptIcons, wptLabels } = control.options;
739
+ if (geojson.name) {
740
+ this.track_info.name = geojson.name;
741
+ }
746
742
 
747
743
  let layer = L.geoJson(geojson, {
748
- distanceMarkers: control.options.distanceMarkers,
749
- style: (feature) => {
750
- let style = L.extend({}, control.options.polyline);
751
- if (control.options.theme) {
752
- style.className += ' ' + control.options.theme;
753
- }
754
- return style;
755
- },
744
+ distanceMarkers: distanceMarkers,
745
+ style: style,
756
746
  pointToLayer: (feature, latlng) => {
757
- if (!control.options.waypoints) return;
758
-
759
- let prop = feature.properties;
760
- let desc = prop.desc ?? '';
761
- let name = prop.name ?? '';
762
- let sym = (prop.sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase();
763
-
764
- // Handle chart waypoints (dots)
765
- if ([true, 'dots'].includes(control.options.waypoints)) {
766
- control._registerCheckPoint({
767
- latlng: latlng,
768
- label: ([true, 'dots'].includes(wptLabels) ? name : '')
769
- });
770
- }
771
-
772
- // Handle map waypoints (markers)
773
- if ([true, 'markers'].includes(control.options.waypoints) && wptIcons != false) {
774
- // generate and cache appropriate icon symbol
775
- if (!wptIcons.hasOwnProperty(sym)) {
776
- wptIcons[sym] = L.divIcon(
777
- L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } )
778
- );
747
+ if (waypoints) {
748
+ let { desc, name, sym } = feature.properties;
749
+ desc = desc || '';
750
+ name = name || '';
751
+ // Handle chart waypoints (dots)
752
+ if ([true, 'dots'].includes(waypoints)) {
753
+ this._registerCheckPoint({
754
+ latlng: latlng,
755
+ label : ([true, 'dots'].includes(wptLabels) ? name : '')
756
+ });
779
757
  }
780
- let marker = L.marker(latlng, { icon: wptIcons[sym] });
781
- if ([true, 'markers'].includes(wptLabels) && (name || desc)) {
782
- let content = decodeURI("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : ''));
783
- marker.bindPopup(content, { className: 'elevation-popup', keepInView: true }).openPopup();
784
- marker.bindTooltip(content, { className: 'elevation-tooltip', direction: 'auto', sticky: true, opacity: 1 }).openTooltip();
758
+ // Handle map waypoints (markers)
759
+ if ([true, 'markers'].includes(waypoints) && wptIcons != false) {
760
+ return this._registerMarker({
761
+ latlng : latlng,
762
+ sym : (sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase(),
763
+ content: [true, 'markers'].includes(wptLabels) && (name || desc) && decodeURI("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : ''))
764
+ });
785
765
  }
786
- control._addMarker(marker);
787
- control.fire('waypoint_added', { point: marker, element: latlng, properties: prop });
788
- return marker;
789
766
  }
790
767
  },
791
- onEachFeature: (feature, layer) => {
792
- if (feature.geometry && feature.geometry.type != 'Point') {
793
- control.addData(layer);
794
- control.track_info = L.extend({}, control.track_info, { name: geojson.name });
795
- }
796
- },
768
+ onEachFeature: (feature, layer) => feature.geometry && feature.geometry.type != 'Point' && this.addData(feature, layer),
797
769
  });
798
770
 
799
- control.import(control.__D3).then(() => {
800
- control._initMapIntegrations(control, layer);
801
- control._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: control.track_info.name, track_info: control.track_info });
771
+ this.import(this.__D3).then(() => {
772
+ this._initMapIntegrations(this, layer);
773
+ this._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: this.track_info.name, track_info: this.track_info });
802
774
  });
803
775
 
804
776
  return layer;
@@ -929,8 +901,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
929
901
  geojson = this._parseFromGeoJSONString(data.toString());
930
902
  }
931
903
  if (geojson) {
932
- geojson = this._prepareMultiLineStrings(geojson);
933
- geojson = this._prepareAdditionalProperties(geojson);
904
+ geojson.name = geojson.name || (this._downloadURL || '').split('/').pop().split('#')[0].split('?')[0];
934
905
  }
935
906
  resolve(geojson);
936
907
  })
@@ -958,37 +929,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
958
929
  return geojson;
959
930
  },
960
931
 
961
- /**
962
- * Extend GeoJSON properties (name, time, ...)
963
- */
964
- _prepareAdditionalProperties: function(geojson) {
965
- if (!geojson.name && this._downloadURL) {
966
- geojson.name = this._downloadURL.split('/').pop().split('#')[0].split('?')[0];
967
- }
968
- return geojson;
969
- },
970
-
971
- /**
972
- * Just a temporary fix for MultiLineString data (trk > trkseg + trkseg),
973
- * just split them into seperate LineStrings (trk > trkseg, trk > trkseg)
974
- *
975
- * @link https://github.com/Raruto/leaflet-elevation/issues/56
976
- */
977
- _prepareMultiLineStrings: function(geojson) {
978
- geojson.features.forEach(feauture => {
979
- if (feauture.geometry.type == "MultiLineString") {
980
- feauture.geometry.coordinates.forEach(coords => {
981
- geojson.features.push({
982
- geometry: { type: 'LineString', coordinates: coords },
983
- properties: feauture.properties,
984
- type: 'Feature'
985
- });
986
- });
987
- }
988
- });
989
- return geojson.features.filter(feauture => feauture.geometry.type != "MultiLineString");
990
- },
991
-
992
932
  /**
993
933
  * Add chart profile to diagram
994
934
  */
@@ -1106,60 +1046,79 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1106
1046
  return this._registerHandler(props.call(this));
1107
1047
  }
1108
1048
 
1049
+ let {
1050
+ name,
1051
+ attr,
1052
+ required,
1053
+ skipNull,
1054
+ deltaMax,
1055
+ clampRange,
1056
+ decimals,
1057
+ meta,
1058
+ unit,
1059
+ coordinateProperties,
1060
+ coordPropsToMeta,
1061
+ pointToAttr,
1062
+ onPointAdded,
1063
+ stats,
1064
+ statsName,
1065
+ grid,
1066
+ scale,
1067
+ path,
1068
+ tooltip,
1069
+ summary
1070
+ } = props;
1071
+
1109
1072
  // eg. "altitude" == true
1110
- if (this.options[props.name] || props.required) {
1073
+ if (this.options[name] || required) {
1111
1074
 
1112
1075
  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,
1076
+ name,
1077
+ attr,
1078
+ skipNull,
1079
+ deltaMax,
1080
+ clampRange,
1081
+ decimals,
1082
+ coordPropsToMeta: _.coordPropsToMeta(coordinateProperties, meta || name, coordPropsToMeta),
1083
+ pointToAttr,
1084
+ onPointAdded,
1085
+ stats,
1086
+ statsName,
1124
1087
  });
1125
1088
 
1126
- if (props.grid) {
1127
- props.grid.name = props.grid.name || props.name;
1128
- this._registerAxisGrid(props.grid);
1089
+ if (grid) {
1090
+ this._registerAxisGrid(L.extend({ name }, grid));
1129
1091
  }
1130
1092
 
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
- }
1093
+ if (this.options[name] !== "summary") {
1094
+ if (scale) this._registerAxisScale(L.extend({ name, label: unit }, scale));
1095
+ if (path) this._registerAreaPath(L.extend({ name }, path));
1141
1096
  }
1142
1097
 
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)
1098
+ if (tooltip || props.tooltips) {
1099
+ _.each([tooltip, ...(props.tooltips || [])], t => t && this._registerTooltip(L.extend({ name }, t)));
1153
1100
  }
1154
1101
 
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);
1102
+ if (summary) {
1103
+ _.each(summary, (s, k) => summary[k] = L.extend({ unit }, s));
1104
+ this._registerSummary(summary);
1160
1105
  }
1161
1106
  }
1162
1107
  },
1108
+
1109
+ _registerMarker({latlng, sym, content}) {
1110
+ let { wptIcons } = this.options;
1111
+ // generate and cache appropriate icon symbol
1112
+ if (!wptIcons.hasOwnProperty(sym)) {
1113
+ wptIcons[sym] = L.divIcon(L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } ));
1114
+ }
1115
+ let marker = L.marker(latlng, { icon: wptIcons[sym] });
1116
+ if (content) {
1117
+ marker.bindPopup(content, { className: 'elevation-popup', keepInView: true }).openPopup();
1118
+ marker.bindTooltip(content, { className: 'elevation-tooltip', direction: 'auto', sticky: true, opacity: 1 }).openTooltip();
1119
+ }
1120
+ return this._addMarker(marker)
1121
+ },
1163
1122
 
1164
1123
  /**
1165
1124
  * Add chart or marker tooltip info
@@ -1242,7 +1201,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1242
1201
  _updateChart: function() {
1243
1202
  if (this._chart && this._container) {
1244
1203
  this.fire("elechart_axis");
1245
- this.fire("elechart_area");
1246
1204
 
1247
1205
  this._chart.update({ data: this._data, options: this.options });
1248
1206
 
@@ -1290,27 +1248,26 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1290
1248
  * Update chart summary.
1291
1249
  */
1292
1250
  _updateSummary: function() {
1293
- if (!this._summary) return;
1294
-
1295
- this._summary.reset();
1296
-
1297
- if (this.options.summary) {
1298
- this.fire("elechart_summary");
1299
- this._summary.update();
1251
+ if (this._summary) {
1252
+ this._summary.reset();
1253
+ if (this.options.summary) {
1254
+ this.fire("elechart_summary");
1255
+ this._summary.update();
1256
+ }
1257
+ if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
1258
+ this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
1259
+ _.select('.download a', this._summary._container).onclick = ({preventDefault}) => {
1260
+ preventDefault();
1261
+ let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
1262
+ if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
1263
+ document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
1264
+ } else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
1265
+ event.confirm();
1266
+ }
1267
+ this.fire('eletrack_download', event);
1268
+ };
1269
+ }
1300
1270
  }
1301
- if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
1302
- this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>'
1303
- _.select('.download a', this._summary._container).onclick = ({preventDefault}) => {
1304
- preventDefault();
1305
- let event = { downloadLink: this.options.downloadLink, confirm: _.saveFile.bind(this, this._downloadURL) };
1306
- if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
1307
- document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
1308
- } else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
1309
- event.confirm();
1310
- }
1311
- this.fire('eletrack_download', event);
1312
- };
1313
- };
1314
1271
  },
1315
1272
 
1316
1273
 
@@ -24,7 +24,10 @@ export function Altitude() {
24
24
  // // "alt" property is generated inside "leaflet"
25
25
  // if ("alt" in point) point.meta.ele = point.alt;
26
26
  // },
27
- pointToAttr: (_, i) => this._data[i].z * opts.altitudeFactor,
27
+ pointToAttr: (point, i) => {
28
+ if ("alt" in point) point.meta.ele = point.alt; // "alt" property is generated inside "leaflet"
29
+ return this._data[i].z * opts.altitudeFactor;
30
+ },
28
31
  stats: { max: _.iMax, min: _.iMin, avg: _.iAvg },
29
32
  grid: {
30
33
  axis : "y",
@@ -13,8 +13,8 @@ export function Heart() {
13
13
  axis : "y",
14
14
  position : "left",
15
15
  scale : { min: -1, max: +1 },
16
- tickPadding: 16,
17
- labelX : -18,
16
+ tickPadding: 25,
17
+ labelX : -30,
18
18
  labelY : -8,
19
19
  },
20
20
  path: {
package/src/index.css CHANGED
@@ -60,6 +60,7 @@
60
60
  }
61
61
 
62
62
  .elevation-control.elevation .axis text,
63
+ .elevation-control.elevation .legend text,
63
64
  .elevation-control.elevation .point text {
64
65
  fill: #000;
65
66
  font-weight: 700;
@@ -246,7 +247,7 @@
246
247
  .leaflet-elevation-pane .height-focus,
247
248
  .leaflet-overlay-pane .height-focus {
248
249
  stroke: #000;
249
- fill: var(--ele-area, #FFF);
250
+ fill: var(--ele-circle, var(--ele-area, #FFF));
250
251
  }
251
252
 
252
253
  .leaflet-elevation-pane .height-focus.line,
@@ -334,6 +335,7 @@
334
335
  --ele-area: #3366CC;
335
336
  --ele-alpha: 0.45;
336
337
  --ele-stroke: #4682B4;
338
+ --ele-circle: #fff;
337
339
  }
338
340
 
339
341
  .elevation-detached .lightblue-theme.elevation-control.elevation .area {
package/src/utils.js CHANGED
@@ -88,11 +88,11 @@ export function formatTime(t) {
88
88
  * Loop and extract GPX Extensions handled by "@tmcw/toGeoJSON" (eg. "coordinateProperties" > "times")
89
89
  */
90
90
  export function coordPropsToMeta(coordProps, name, parser) {
91
- return coordProps && (({props, point, id}) => {
91
+ return coordProps && (({props, point, id, isMulti }) => {
92
92
  if (props) {
93
93
  for (const key of coordProps) {
94
94
  if (key in props) {
95
- point.meta[name] = (parser || parseInt).call(this, props[key], id);
95
+ point.meta[name] = (parser || parseInt).call(this, (isMulti ? props[key][isMulti] : props[key]), id);
96
96
  break;
97
97
  }
98
98
  }
@@ -139,9 +139,9 @@ export const iSum = (iVal, sum) => iVal + (sum || 0);
139
139
  /**
140
140
  * Alias for some leaflet core functions
141
141
  */
142
- export const { on, off } = L.DomEvent;
143
- export const { throttle } = L.Util;
144
- export const { hasClass } = L.DomUtil;
142
+ export const { on, off } = L.DomEvent;
143
+ export const { throttle, wrapNum } = L.Util;
144
+ export const { hasClass } = L.DomUtil;
145
145
 
146
146
  /**
147
147
  * Limit floating point precision
@@ -1 +0,0 @@
1
- export function Acceleration(){const _=L.Control.Elevation.Utils;let opts=this.options,acceleration={};return acceleration.label=opts.accelerationLabel||L._(opts.imperial?"ft/s²":"m/s²"),opts.accelerationFactor=opts.accelerationFactor||1,{name:"acceleration",unit:acceleration.label,deltaMax:this.options.accelerationDeltaMax,clampRange:this.options.accelerationRange,decimals:2,pointToAttr:(_,i)=>{let dv=(this._data[i].speed-this._data[i>0?i-1:i].speed)*(1e3/opts.timeFactor),dt=(this._data[i].time-this._data[i>0?i-1:i].time)/1e3;return dt>0?Math.abs(dv/dt)*opts.accelerationFactor:0},stats:{max:_.iMax,min:_.iMin,avg:_.iAvg},scale:{axis:"y",position:"right",scale:{min:0,max:1},tickPadding:16,labelX:25,labelY:-8},path:{label:"Acceleration",yAttr:"acceleration",scaleX:"distance",scaleY:"acceleration",color:"#050402",strokeColor:"#000",strokeOpacity:"0.5",fillOpacity:"0.25"},tooltip:{chart:item=>L._("a: ")+item.acceleration+" "+acceleration.label,marker:item=>Math.round(item.acceleration)+" "+acceleration.label,order:60},summary:{minacceleration:{label:"Min Acceleration: ",value:(track,unit)=>Math.round(track.acceleration_min||0)+"&nbsp;"+unit,order:60},maxacceleration:{label:"Max Acceleration: ",value:(track,unit)=>Math.round(track.acceleration_max||0)+"&nbsp;"+unit,order:61},avgacceleration:{label:"Avg Acceleration: ",value:(track,unit)=>Math.round(track.acceleration_avg||0)+"&nbsp;"+unit,order:62}}}}
@@ -1 +0,0 @@
1
- export function Altitude(){const _=L.Control.Elevation.Utils;let opts=this.options,altitude={},theme=opts.theme.replace("-theme",""),color=_.Colors[theme]||{};return opts.altitudeFactor=opts.imperial?this.__footFactor:opts.altitudeFactor||1,altitude.label=opts.imperial?"ft":opts.yLabel,{name:"altitude",required:this.options.slope,meta:"z",unit:altitude.label,statsName:"elevation",skipNull:this.options.skipNullZCoords,deltaMax:this.options.altitudeDeltaMax,clampRange:this.options.altitudeRange,pointToAttr:(_,i)=>this._data[i].z*opts.altitudeFactor,stats:{max:_.iMax,min:_.iMin,avg:_.iAvg},grid:{axis:"y",position:"left",scale:"y"},scale:{axis:"y",position:"left",scale:"y",labelX:-3,labelY:-8},path:{label:"Altitude",scaleX:"distance",scaleY:"altitude",className:"area",color:color.area||theme,strokeColor:opts.detached?color.stroke:"#000",strokeOpacity:"1",fillOpacity:opts.detached?color.alpha||"0.8":1,preferCanvas:opts.preferCanvas},tooltip:{name:"y",chart:item=>L._("y: ")+_.round(item[opts.yAttr],opts.decimalsY)+" "+altitude.label,marker:item=>_.round(item[opts.yAttr],opts.decimalsY)+" "+altitude.label,order:10},summary:{minele:{label:"Min Elevation: ",value:(track,unit)=>(track.elevation_min||0).toFixed(2)+"&nbsp;"+unit,order:30},maxele:{label:"Max Elevation: ",value:(track,unit)=>(track.elevation_max||0).toFixed(2)+"&nbsp;"+unit,order:31},avgele:{label:"Avg Elevation: ",value:(track,unit)=>(track.elevation_avg||0).toFixed(2)+"&nbsp;"+unit,order:32}}}}
@@ -1 +0,0 @@
1
- export function Distance(){const _=L.Control.Elevation.Utils;let opts=this.options,distance={};return opts.distanceFactor=opts.imperial?this.__mileFactor:opts.distanceFactor||1,distance.label=opts.imperial?"mi":opts.xLabel,{name:"distance",attr:"dist",unit:distance.label,decimals:5,pointToAttr:(_,i)=>(i>0?this._data[i-1].dist:0)+this._data[i].latlng.distanceTo(this._data[i>0?i-1:i].latlng)*opts.distanceFactor/1e3,onPointAdded:(distance,i)=>this.track_info.distance=distance,scale:{axis:"x",position:"bottom",scale:"x",labelY:25,labelX:()=>this._width()+6,ticks:()=>_.clamp(this._chart._xTicks()/2,[4,1/0])},grid:{axis:"x",position:"bottom",scale:"x"},tooltip:{name:"x",chart:item=>L._("x: ")+_.round(item[opts.xAttr],opts.decimalsX)+" "+distance.label,order:20},summary:{totlen:{label:"Total Length: ",value:track=>(track.distance||0).toFixed(2)+"&nbsp;"+distance.label,order:10}}}}
@@ -1 +0,0 @@
1
- export function Slope(){const _=L.Control.Elevation.Utils;let opts=this.options,slope={};return slope.label=opts.slopeLabel||"%",{name:"slope",meta:"slope",unit:slope.label,deltaMax:this.options.slopedDeltaMax,clampRange:this.options.slopeRange,decimals:2,pointToAttr:(_,i)=>{let dx=1e3*(this._data[i].dist-this._data[i>0?i-1:i].dist),dy=this._data[i][this.options.yAttr]-this._data[i>0?i-1:i][this.options.yAttr];return isNaN(dy)||0===dx?0:dy/dx*100},onPointAdded:(_,i)=>{let dz=this._data[i][this.options.yAttr]-this._data[i>0?i-1:i][this.options.yAttr];dz>0?this.track_info.ascent=(this.track_info.ascent||0)+dz:dz<0&&(this.track_info.descent=(this.track_info.descent||0)-dz)},stats:{max:_.iMax,min:_.iMin,avg:_.iAvg},scale:{axis:"y",position:"right",scale:{min:-1,max:1},tickPadding:16,labelX:25,labelY:-8},path:{label:"Slope",yAttr:"slope",scaleX:"distance",scaleY:"slope",color:"#F00",strokeColor:"#000",strokeOpacity:"0.5",fillOpacity:"0.25"},tooltip:{chart:item=>L._("m: ")+item.slope+slope.label,marker:item=>Math.round(item.slope)+slope.label,order:40},summary:{minslope:{label:"Min Slope: ",value:(track,unit)=>Math.round(track.slope_min||0)+"&nbsp;"+unit,order:40},maxslope:{label:"Max Slope: ",value:(track,unit)=>Math.round(track.slope_max||0)+"&nbsp;"+unit,order:41},avgslope:{label:"Avg Slope: ",value:(track,unit)=>Math.round(track.slope_avg||0)+"&nbsp;"+unit,order:42},ascent:{label:"Total Ascent: ",value:(track,unit)=>Math.round(track.ascent||0)+"&nbsp;"+(this.options.imperial?"ft":"m"),order:43},descent:{label:"Total Descent: ",value:(track,unit)=>Math.round(track.descent||0)+"&nbsp;"+(this.options.imperial?"ft":"m"),order:45}}}}
@@ -1 +0,0 @@
1
- export function Speed(){const _=L.Control.Elevation.Utils;let opts=this.options,speed={};return speed.label=opts.speedLabel||L._(opts.imperial?"mph":"km/h"),opts.speedFactor=opts.speedFactor||1,{name:"speed",required:this.options.acceleration,unit:speed.label,deltaMax:this.options.speedDeltaMax,clampRange:this.options.speedRange,decimals:2,pointToAttr:(_,i)=>{let dx=1e3*(this._data[i].dist-this._data[i>0?i-1:i].dist),dt=this._data[i].time-this._data[i>0?i-1:i].time;return dt>0?Math.abs(dx/dt*opts.timeFactor)*opts.speedFactor:0},stats:{max:_.iMax,min:_.iMin,avg:_.iAvg},scale:this.options.speed&&"summary"!=this.options.speed&&{axis:"y",position:"right",scale:{min:0,max:1},tickPadding:16,labelX:25,labelY:-8},path:this.options.speed&&"summary"!=this.options.speed&&{label:"Speed",yAttr:"speed",scaleX:"distance",scaleY:"speed",color:"#03ffff",strokeColor:"#000",strokeOpacity:"0.5",fillOpacity:"0.25"},tooltip:this.options.speed&&{chart:item=>L._("v: ")+item.speed+" "+speed.label,marker:item=>Math.round(item.speed)+" "+speed.label,order:50},summary:this.options.speed&&{minspeed:{label:"Min Speed: ",value:(track,unit)=>Math.round(track.speed_min||0)+"&nbsp;"+unit,order:51},maxspeed:{label:"Max Speed: ",value:(track,unit)=>Math.round(track.speed_max||0)+"&nbsp;"+unit,order:51},avgspeed:{label:"Avg Speed: ",value:(track,unit)=>Math.round(track.speed_avg||0)+"&nbsp;"+unit,order:52}}}}
@@ -1 +0,0 @@
1
- export function Time(){const _=L.Control.Elevation.Utils;let opts=this.options,time={};return time.label=opts.timeLabel||"t",opts.timeFactor=opts.timeFactor||3600,this._timeAVGSpeed=(opts.timeAVGSpeed||3.6)*(opts.speedFactor||1),opts.timeFormat?"time"==opts.timeFormat?opts.timeFormat=time=>new Date(time).toLocaleTimeString():"date"==opts.timeFormat&&(opts.timeFormat=time=>new Date(time).toLocaleDateString()):opts.timeFormat=time=>new Date(time).toLocaleString().replaceAll("/","-").replaceAll(","," "),opts.xTimeFormat=opts.xTimeFormat||(t=>_.formatTime(t).split("'")[0]),{name:"time",required:this.options.speed||this.options.acceleration||this.options.timestamps,coordinateProperties:["coordTimes","times","time"],coordPropsToMeta:_.parseDate,pointToAttr:function(point,i){if(!point.meta||!point.meta.time)if(point.meta=point.meta||{},i>0){let dx=this._data[i].dist-this._data[i-1].dist,t0=this._data[i-1].time.getTime();point.meta.time=new Date(t0+dx/this._timeAVGSpeed*this.options.timeFactor*1e3)}else point.meta.time=new Date(Date.now());let time=point.meta.time.getTime()-60*point.meta.time.getTimezoneOffset()*1e3!=0?point.meta.time:0;return this._data[i].duration=i>0?(this._data[i-1].duration||0)+Math.abs(time-this._data[i-1].time):0,time},onPointAdded:(_,i)=>this.track_info.time=this._data[i].duration,scale:opts.time&&"summary"!=opts.time&&!L.Browser.mobile&&{axis:"x",position:"top",scale:{attr:"duration",min:0},label:time.label,labelY:-10,labelX:()=>this._width(),name:"time",ticks:()=>_.clamp(this._chart._xTicks()/2,[4,1/0]),tickFormat:d=>0==d?"":opts.xTimeFormat(d),onAxisMount:axis=>{axis.select(".domain").remove(),axis.selectAll("text").attr("opacity",.65).style("font-family","Monospace").style("font-size","110%"),axis.selectAll(".tick line").attr("y2",this._height()).attr("stroke-dasharray",2).attr("opacity",.75)}},tooltips:[this.options.time&&{name:"time",chart:item=>L._("T: ")+_.formatTime(item.duration||0),order:20},this.options.timestamps&&{name:"date",chart:item=>L._("t: ")+this.options.timeFormat(item.time),order:21}],summary:this.options.time&&{tottime:{label:"Total Time: ",value:track=>_.formatTime(track.time||0),order:20}}}}