@raruto/leaflet-elevation 2.2.7 → 2.3.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/package.json CHANGED
@@ -1,71 +1,74 @@
1
1
  {
2
- "name": "@raruto/leaflet-elevation",
3
- "version": "2.2.7",
4
- "description": "A Leaflet plugin that allows to add elevation profiles using d3js",
5
- "type": "module",
6
- "main": "dist/leaflet-elevation.min.js",
7
- "browser": "dist/leaflet-elevation.min.js",
8
- "module": "src/index.js",
9
- "scripts": {
10
- "test": "node spec/index.js",
11
- "build": "rollup -c build/rollup.config.js",
12
- "watch": "rollup -c build/rollup.config.js --watch",
13
- "dev": "npm run watch",
14
- "version": "npm run build && git add -A"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "https://github.com/Raruto/leaflet-elevation.git"
19
- },
20
- "keywords": [
21
- "leaflet",
22
- "d3js",
23
- "javascript",
24
- "elevation",
25
- "highchart",
26
- "kml",
27
- "altitude",
28
- "gpx",
29
- "gps-visualizer",
30
- "tcx"
31
- ],
32
- "author": {
33
- "name": "Felix “MrMufflon” Bache",
34
- "url": "https: //github.com/MrMufflon/Leaflet.Elevation"
35
- },
36
- "contributors": [
37
- {
38
- "name": "Raruto",
39
- "url": "https://github.com/Raruto/leaflet-elevation"
40
- },
41
- {
42
- "name": "Felix Bache",
43
- "web": "https://github.com/HostedDinner/Leaflet.Elevation"
44
- }
45
- ],
46
- "license": "GPL-3.0",
47
- "bugs": {
48
- "url": "https://github.com/Raruto/leaflet-elevation/issues"
49
- },
50
- "homepage": "https://github.com/Raruto/leaflet-elevation#readme",
51
- "devDependencies": {
52
- "jsdom": "^20.0.0",
53
- "leaflet": "^1.7.1",
54
- "postcss": "^8.4.16",
55
- "postcss-copy": "^7.1.0",
56
- "postcss-import": "^15.0.0",
57
- "rollup": "^2.79.0",
58
- "rollup-plugin-commonjs": "^10.1.0",
59
- "rollup-plugin-git-version": "^0.3.1",
60
- "rollup-plugin-node-resolve": "^5.2.0",
61
- "rollup-plugin-postcss": "^4.0.2",
62
- "rollup-plugin-terser": "^7.0.2",
63
- "uvu": "^0.5.6"
64
- },
65
- "peerDependencies": {
66
- "leaflet": "^1.7.0",
67
- "leaflet-i18n": "^0.3.1",
68
- "d3": "6.5.0",
69
- "@tmcw/togeojson": "4.6.0"
70
- }
2
+ "name": "@raruto/leaflet-elevation",
3
+ "version": "2.3.0",
4
+ "description": "A Leaflet plugin that allows to add elevation profiles using d3js",
5
+ "type": "module",
6
+ "main": "dist/leaflet-elevation.min.js",
7
+ "browser": "dist/leaflet-elevation.min.js",
8
+ "module": "src/index.js",
9
+ "scripts": {
10
+ "test": "uvu . ^(examples)?(src)?\\.*\\.spec\\.js$",
11
+ "build": "rollup -c build/rollup.config.js",
12
+ "watch": "rollup -c build/rollup.config.js --watch",
13
+ "dev": "npm-run-all --print-label --parallel serve watch",
14
+ "serve": "http-server",
15
+ "version": "npm run build && git add -A"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/Raruto/leaflet-elevation.git"
20
+ },
21
+ "keywords": [
22
+ "leaflet",
23
+ "d3js",
24
+ "javascript",
25
+ "elevation",
26
+ "highchart",
27
+ "kml",
28
+ "altitude",
29
+ "gpx",
30
+ "gps-visualizer",
31
+ "tcx"
32
+ ],
33
+ "author": {
34
+ "name": "Felix “MrMufflon” Bache",
35
+ "url": "https: //github.com/MrMufflon/Leaflet.Elevation"
36
+ },
37
+ "contributors": [
38
+ {
39
+ "name": "Raruto",
40
+ "url": "https://github.com/Raruto/leaflet-elevation"
41
+ },
42
+ {
43
+ "name": "Felix Bache",
44
+ "web": "https://github.com/HostedDinner/Leaflet.Elevation"
45
+ }
46
+ ],
47
+ "license": "GPL-3.0",
48
+ "bugs": {
49
+ "url": "https://github.com/Raruto/leaflet-elevation/issues"
50
+ },
51
+ "homepage": "https://github.com/Raruto/leaflet-elevation#readme",
52
+ "devDependencies": {
53
+ "@rollup/plugin-commonjs": "^24.1.0",
54
+ "@rollup/plugin-node-resolve": "^15.0.2",
55
+ "@rollup/plugin-terser": "^0.4.1",
56
+ "http-server": "^14.1.1",
57
+ "jsdom": "^20.0.0",
58
+ "leaflet": "^1.7.1",
59
+ "npm-run-all": "^4.1.5",
60
+ "playwright": "^1.32.3",
61
+ "postcss": "^8.4.22",
62
+ "postcss-copy": "^7.1.0",
63
+ "postcss-import": "^15.1.0",
64
+ "rollup": "^3.20.6",
65
+ "rollup-plugin-postcss": "^4.0.2",
66
+ "uvu": "^0.5.6"
67
+ },
68
+ "peerDependencies": {
69
+ "@tmcw/togeojson": "4.6.0",
70
+ "d3": "6.5.0",
71
+ "leaflet": "^1.7.0",
72
+ "leaflet-i18n": "^0.3.1"
73
+ }
71
74
  }
@@ -268,7 +268,7 @@ export const LegendSmall = ({
268
268
  // .merge(label.data([{ idx: id }]))
269
269
  // .text(d => items.length ? (items[idx][0].toUpperCase() + items[idx].slice(1)) : '')
270
270
  // .on("mousedown", (e, d) => setIdx(L.Util.wrapNum((idx + 1), [0, items.length])));
271
- label.text(items.length ? (items[idx][0].toUpperCase() + items[idx].slice(1)) : '');
271
+ label.text(items.length ? L._((items[idx][0].toUpperCase() + items[idx].slice(1))) : '');
272
272
  onClick(items[idx]);
273
273
  };
274
274
  setIdx(0);
package/src/control.js CHANGED
@@ -22,6 +22,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
22
22
  __LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
23
23
  __LHOTLINE: '../libs/leaflet-hotline.min.js',
24
24
  __LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
25
+ __LEDGESCALE: '../libs/leaflet-edgescale.min.js',
25
26
  __LCHART: '../src/components/chart.js',
26
27
  __LMARKER: '../src/components/marker.js',
27
28
  __LSUMMARY: '../src/components/summary.js',
@@ -173,7 +174,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
173
174
  */
174
175
  initialize: function(opts) {
175
176
 
176
- opts = L.setOptions(this, opts);
177
+ // opts = L.setOptions(this, opts);
178
+
179
+ // Fixes: https://github.com/Raruto/leaflet-elevation/pull/240
180
+ opts = L.setOptions(this, L.extend({}, structuredClone(Options), opts)); // "deep copy" nested objects (multiple charts)
177
181
 
178
182
  this._data = [];
179
183
  this._layers = L.featureGroup();
@@ -218,6 +222,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
218
222
  case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
219
223
  case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
220
224
  case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
225
+ case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break;
221
226
  case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break;
222
227
  }
223
228
  src = (src.startsWith('../') || src.startsWith('./') ? baseURL : '') + src;
@@ -420,6 +425,16 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
420
425
  return this.options.distanceMarkers ? Promise.all([this.import(this.__LGEOMUTIL), this.import(this.__LDISTANCEM)]) : Promise.resolve();
421
426
  },
422
427
 
428
+ /**
429
+ * Initialize "L.Control.EdgeScale" integration
430
+ */
431
+ _initEdgeScale: function(map) {
432
+ return this.options.edgeScale ? Promise.all([this.import(this.__LEDGESCALE)])
433
+ .then(() => {
434
+ map.edgeScaleControl = map.edgeScaleControl || L.control.edgeScale('boolean' !== typeof this.options.edgeScale ? this.options.edgeScale : {}).addTo(map);
435
+ }) : Promise.resolve();
436
+ },
437
+
423
438
  _initHotLine: function(layer) {
424
439
  let prop = typeof this.options.hotline == 'string' ? this.options.hotline : 'elevation';
425
440
  return this.options.hotline ? this.import(this.__LHOTLINE)
@@ -465,6 +480,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
465
480
  this._initHotLine(layer),
466
481
  this._initAlmostOverHandler(map, layer),
467
482
  this._initDistanceMarkers(),
483
+ this._initEdgeScale(map),
468
484
  ]).then(() => {
469
485
  if (this.options.polyline) {
470
486
  layer.addTo(map);
@@ -990,10 +1006,10 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
990
1006
  }
991
1007
 
992
1008
  // prevent excessive variabile instanstations
993
- let i, curr, prev, attr;
1009
+ let i, curr, prev, attr = props.attr || props.name;
994
1010
 
995
1011
  // save here a reference to last used point
996
- let lastValid = null;
1012
+ let lastValid = {};
997
1013
 
998
1014
  // iteration
999
1015
  this.on("elepoint_added", ({index, point}) => {
@@ -1001,42 +1017,33 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1001
1017
 
1002
1018
  prev = curr ?? this._data[i]; // same as: this._data[i > 0 ? i - 1 : i]
1003
1019
  curr = this._data[i];
1004
- attr = props.attr || props.name;
1020
+
1021
+ // retrieve point value
1022
+ curr[attr] = props.pointToAttr.call(this, point, i);
1005
1023
 
1006
1024
  // check and fix missing data on last added point
1007
- if (props.skipNull === false) {
1008
- let curr = this._data[i][attr];
1009
- if(i > 0) {
1010
- let prev = this._data[i - 1][attr];
1011
- if (isNaN(prev)) {
1012
- if (!isNaN(lastValid) && !isNaN(curr)) {
1013
- prev = (lastValid + curr) / 2;
1014
- } else if (!isNaN(lastValid)) {
1015
- prev = lastValid;
1016
- } else if (!isNaN(curr)) {
1017
- prev = curr;
1018
- }
1019
- if (!isNaN(prev)) return this._data.splice(i - 1, 1);
1020
- this._data[i - 1][attr] = prev;
1021
- }
1025
+ if (i > 0 && isNaN(prev[attr])) {
1026
+ if (!isNaN(lastValid[attr]) && !isNaN(curr[attr])) {
1027
+ prev[attr] = (lastValid[attr] + curr[attr]) / 2;
1028
+ } else if (!isNaN(lastValid[attr])) {
1029
+ prev[attr] = lastValid[attr];
1030
+ } else if (!isNaN(curr[attr])) {
1031
+ prev[attr] = curr[attr];
1022
1032
  }
1023
- // update reference to last used point (ie. if it has data)
1024
- if (!isNaN(curr)) {
1025
- lastValid = curr;
1033
+ // update "yAttr" and "xAttr"
1034
+ if (props.meta) {
1035
+ prev[props.meta] = prev[attr];
1026
1036
  }
1027
1037
  }
1028
1038
 
1029
- // retrieve point value
1030
- curr[attr] = props.pointToAttr.call(this, point, i);
1031
-
1032
- // update "track_info" stats (min, max, avg, ...)
1033
- if (props.stats) {
1034
- for (const key in props.stats) {
1035
- let sname = (props.statsName || attr) + (key != '' ? '_' : '');
1036
- this.track_info[sname + key] = props.stats[key].call(this, curr[attr], this.track_info[sname + key], this._data.length);
1037
- }
1039
+ // skip to next iteration for invalid or missing data (eg. i == 0)
1040
+ if (isNaN(curr[attr])) {
1041
+ return;
1038
1042
  }
1039
1043
 
1044
+ // update reference to last used point
1045
+ lastValid[attr] = curr[attr];
1046
+
1040
1047
  // Limit "crazy" delta values.
1041
1048
  if (props.deltaMax) {
1042
1049
  curr[attr] =_.wrapDelta(curr[attr], prev[attr], props.deltaMax);
@@ -1052,8 +1059,16 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1052
1059
  curr[attr] = _.round(curr[attr], props.decimals);
1053
1060
  }
1054
1061
 
1062
+ // update "track_info" stats (min, max, avg, ...)
1063
+ if (props.stats) {
1064
+ for (const key in props.stats) {
1065
+ let sname = (props.statsName || attr) + (key != '' ? '_' : '');
1066
+ this.track_info[sname + key] = props.stats[key].call(this, curr[attr], this.track_info[sname + key], this._data.length);
1067
+ }
1068
+ }
1069
+
1055
1070
  // update here some mixins (eg. complex "track_info" stuff)
1056
- if(props.onPointAdded) props.onPointAdded.call(this, curr[attr], i, point);
1071
+ if (props.onPointAdded) props.onPointAdded.call(this, curr[attr], i, point);
1057
1072
  });
1058
1073
  },
1059
1074
 
@@ -1071,7 +1086,6 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1071
1086
  name,
1072
1087
  attr,
1073
1088
  required,
1074
- skipNull,
1075
1089
  deltaMax,
1076
1090
  clampRange,
1077
1091
  decimals,
@@ -1096,7 +1110,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
1096
1110
  this._registerDataAttribute({
1097
1111
  name,
1098
1112
  attr,
1099
- skipNull,
1113
+ meta,
1100
1114
  deltaMax,
1101
1115
  clampRange,
1102
1116
  decimals,
@@ -17,7 +17,7 @@ export function Acceleration() {
17
17
  pointToAttr: (_, i) => {
18
18
  let dv = (this._data[i].speed - this._data[i > 0 ? i - 1 : i].speed) * (1000 / opts.timeFactor);
19
19
  let dt = (this._data[i].time - this._data[i > 0 ? i - 1 : i].time) / 1000;
20
- return dt > 0 ? Math.abs((dv / dt)) * opts.accelerationFactor : 0;
20
+ return dt > 0 ? Math.abs((dv / dt)) * opts.accelerationFactor : NaN;
21
21
  },
22
22
  stats: { max: _.iMax, min: _.iMin, avg: _.iAvg },
23
23
  scale: {
@@ -17,7 +17,6 @@ export function Altitude() {
17
17
  meta: 'z',
18
18
  unit: altitude.label,
19
19
  statsName: 'elevation',
20
- skipNull: this.options.skipNullZCoords,
21
20
  deltaMax: this.options.altitudeDeltaMax,
22
21
  clampRange: this.options.altitudeRange,
23
22
  // init: ({point}) => {
@@ -17,7 +17,7 @@ export function Pace() {
17
17
  pointToAttr: (_, i) => {
18
18
  let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000;
19
19
  let dt = this._data[i].time - this._data[ i > 0 ? i - 1 : i].time;
20
- return dx > 0 ? Math.abs((dt / dx) / opts.paceFactor) : 0;
20
+ return dx > 0 ? Math.abs((dt / dx) / opts.paceFactor) : NaN;
21
21
  },
22
22
  stats: { max: _.iMax, min: _.iMin, avg: _.iAvg },
23
23
  scale : (this.options.pace && this.options.pace != "summary") && {
@@ -17,7 +17,7 @@ export function Slope() {
17
17
  pointToAttr: (_, i) => { // slope in % = ( dy / dx ) * 100;
18
18
  let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000;
19
19
  let dy = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr];
20
- return (!isNaN(dy) && dx !== 0) ? (dy / dx) * 100 : 0;
20
+ return dx !== 0 ? (dy / dx) * 100 : NaN;
21
21
  },
22
22
  onPointAdded: (_, i) => {
23
23
  let dz = this._data[i][this.options.yAttr] - this._data[i > 0 ? i - 1 : i][this.options.yAttr];
@@ -18,7 +18,7 @@ export function Speed() {
18
18
  pointToAttr: (_, i) => {
19
19
  let dx = (this._data[i].dist - this._data[i > 0 ? i - 1 : i].dist) * 1000;
20
20
  let dt = this._data[i].time - this._data[ i > 0 ? i - 1 : i].time;
21
- return dt > 0 ? Math.abs((dx / dt) * opts.timeFactor) * opts.speedFactor : 0;
21
+ return dt > 0 ? Math.abs((dx / dt) * opts.timeFactor) * opts.speedFactor : NaN;
22
22
  },
23
23
  stats: { max: _.iMax, min: _.iMin, avg: _.iAvg },
24
24
  scale : (this.options.speed && this.options.speed != "summary") && {
package/src/options.js CHANGED
@@ -14,6 +14,7 @@ export var Options = {
14
14
  dragging: !L.Browser.mobile,
15
15
  downloadLink: 'link',
16
16
  elevationDiv: "#elevation-div",
17
+ edgeScale: { bar: true, icon: false, coords: false },
17
18
  followMarker: true,
18
19
  imperial: false,
19
20
  legend: true,
@@ -79,7 +80,6 @@ export var Options = {
79
80
  decimalsY: 0,
80
81
  forceAxisBounds: false,
81
82
  interpolation: "curveLinear",
82
- skipNullZCoords: false,
83
83
  yAxisMax: undefined,
84
84
  yAxisMin: undefined,
85
85
  };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * src/utils.js
3
+ */
4
+
5
+ import { suite } from 'uvu';
6
+ import * as assert from 'uvu/assert';
7
+ import '../test/setup/jsdom.js'
8
+ import { iAvg, iMin, iMax, iSum } from "../src/utils.js";
9
+
10
+ const toFixed = (n) => +n.toFixed(2);
11
+
12
+ const test = suite('src/utils.js');
13
+
14
+ test('iAvg()', () => {
15
+ let avg;
16
+ avg = iAvg(100, undefined, 1); assert.is(toFixed(avg), 100); // average for [100] is 100
17
+ avg = iAvg(100, avg, 2); assert.is(toFixed(avg), 100); // average for [100, 100] is 100
18
+ avg = iAvg(200, avg, 3); assert.is(toFixed(avg), 133.33); // average for [100, 100, 200] is 133.33
19
+ avg = iAvg(200, avg, 4); assert.is(toFixed(avg), 150); // average for [100, 100, 200, 200] is 150
20
+ avg = iAvg(NaN, avg, 5); assert.ok(isNaN(avg)); // average for [100, 100, 200, 200, NaN] is NaN
21
+ });
22
+
23
+ test('iMin()', () => {
24
+ let min;
25
+ min = iMin(100, undefined); assert.is(toFixed(min), 100); // min for [100] is 100
26
+ min = iMin(NaN, min); assert.is(toFixed(min), 100); // min for [100, NaN] is 100
27
+ min = iMin(0, min); assert.is(toFixed(min), 0); // min for [100, NaN, 0] is 100
28
+ min = iMin(-200, min); assert.is(toFixed(min), -200); // min for [100, NaN, 0, -200] is -200
29
+ min = iMin(200, min); assert.is(toFixed(min), -200); // min for [100, NaN, -100, -200, 200] is -200
30
+ });
31
+
32
+ test('iMax()', () => {
33
+ let max;
34
+ max = iMax(100, undefined); assert.is(toFixed(max), 100); // max for [100] is 100
35
+ max = iMax(NaN, max); assert.is(toFixed(max), 100); // max for [100, NaN] is 100
36
+ max = iMax(0, max); assert.is(toFixed(max), 100); // max for [100, NaN, 0] is 100
37
+ max = iMax(-200, max); assert.is(toFixed(max), 100); // max for [100, NaN, 0, -200] is 100
38
+ max = iMax(200, max); assert.is(toFixed(max), 200); // max for [100, NaN, -100, -200, 200] is 200
39
+ });
40
+
41
+ test('iSum()', () => {
42
+ let sum;
43
+ sum = iSum(10.25, undefined); assert.is(toFixed(sum), 10.25); // sum for [10.25] is 10.25
44
+ sum = iSum(0, sum); assert.is(toFixed(sum), 10.25); // sum for [10.25, 0] is 10.25
45
+ sum = iSum(-0.25, sum); assert.is(toFixed(sum), 10); // sum for [10.25, 0, -0.25] is 10
46
+ sum = iSum(-10, sum); assert.is(toFixed(sum), 0); // sum for [10.25, 0, -0.25, -10] is 0
47
+ sum = iSum(NaN, sum); assert.ok(isNaN(sum)); // sum for [10.25, 0, -0.25, -10, NaN] is NaN
48
+ });
49
+
50
+ test.run();