@raruto/leaflet-elevation 1.7.5 → 1.8.1

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@raruto/leaflet-elevation",
3
- "version": "1.7.5",
3
+ "version": "1.8.1",
4
4
  "description": "A Leaflet plugin that allows to add elevation profiles using d3js",
5
5
  "main": "dist/leaflet-elevation.min.js",
6
6
  "module": "src/index.js",
@@ -10,8 +10,8 @@ Elevation.addInitHook(function() {
10
10
  let opts = this.options;
11
11
  let acceleration = {};
12
12
 
13
- acceleration.label = opts.accelerationLabel || L._(this.options.imperial ? 'ft/s²' : 'm/s²');
14
- this._accelerationFactor = opts.accelerationFactor || 1;
13
+ acceleration.label = opts.accelerationLabel || L._(opts.imperial ? 'ft/s²' : 'm/s²');
14
+ opts.accelerationFactor = opts.accelerationFactor || 1;
15
15
 
16
16
  if (this.options.acceleration != "summary") {
17
17
 
@@ -61,8 +61,8 @@ Elevation.addInitHook(function() {
61
61
  if (deltaT > 0) {
62
62
  let curr = data[i].speed;
63
63
  let prev = i > 0 ? data[i - 1].speed : curr;
64
- let delta = (curr - prev) * (1000 / this._timeFactor);
65
- acceleration = Math.abs((delta / deltaT)) * this._accelerationFactor;
64
+ let delta = (curr - prev) * (1000 / opts.timeFactor);
65
+ acceleration = Math.abs((delta / deltaT)) * opts.accelerationFactor;
66
66
  }
67
67
 
68
68
  // Try to smooth "crazy" acceleration values.
@@ -75,28 +75,18 @@ Elevation.addInitHook(function() {
75
75
  }
76
76
 
77
77
  // Range of acceptable acceleration values.
78
- if (this.options.accelerationRange) {
79
- let range = this.options.accelerationRange;
80
- if (acceleration < range[0]) acceleration = range[0];
81
- else if (acceleration > range[1]) acceleration = range[1];
82
- }
83
-
84
- acceleration = L.Util.formatNum(acceleration, 2);
85
-
86
- if (acceleration > sMax) sMax = acceleration;
87
- if (acceleration < sMin) sMin = acceleration;
88
- sAvg = (acceleration + sAvg) / 2.0;
78
+ acceleration = _.clamp(acceleration, this.options.accelerationRange);
89
79
 
90
- data[i].acceleration = acceleration;
80
+ track.acceleration_max = acceleration > sMax ? acceleration : sMax;
81
+ track.acceleration_min = acceleration < sMin ? acceleration : sMin;
82
+ track.acceleration_avg = (acceleration + sAvg) / 2.0;
91
83
 
92
- track.acceleration_max = sMax;
93
- track.acceleration_min = sMin;
94
- track.acceleration_avg = sAvg;
84
+ data[i].acceleration = L.Util.formatNum(acceleration, 2);
95
85
  });
96
86
 
97
- this._registerFocusLabel({
87
+ this._registerTooltip({
98
88
  name : 'acceleration',
99
- chart : (item) => item.acceleration + " " + acceleration.label,
89
+ chart: (item) => L._("a: ") + item.acceleration + " " + acceleration.label,
100
90
  marker: (item) => Math.round(item.acceleration) + " " + acceleration.label,
101
91
  });
102
92
 
package/src/altitude.js CHANGED
@@ -9,21 +9,21 @@ Elevation.addInitHook(function() {
9
9
  let altitude = {};
10
10
 
11
11
  if (opts.imperial) {
12
- this._heightFactor = this.__footFactor;
13
- this._yLabel = "ft";
12
+ opts.altitudeFactor = this.__footFactor;
13
+ altitude.label = "ft";
14
14
  } else {
15
- this._heightFactor = opts.heightFactor;
16
- this._yLabel = opts.yLabel;
15
+ opts.altitudeFactor = opts.heightFactor || opts.altitudeFactor || 1;
16
+ altitude.label = opts.yLabel;
17
17
  }
18
18
 
19
19
  this.on("eledata_updated", function(e) {
20
20
  let data = this._data;
21
21
  let i = e.index;
22
- let z = data[i].z * this._heightFactor;
22
+ let z = data[i].z * opts.altitudeFactor;
23
23
 
24
24
  let track = this.track_info;
25
- let eleMax = track.elevation_max || -Infinity;
26
- let eleMin = track.elevation_min || +Infinity;
25
+ let zMax = track.elevation_max || -Infinity;
26
+ let zMin = track.elevation_min || +Infinity;
27
27
 
28
28
  // check and fix missing elevation data on last added point
29
29
  if (!this.options.skipNullZCoords && i > 0) {
@@ -44,15 +44,13 @@ Elevation.addInitHook(function() {
44
44
  }
45
45
  // skip point if it has not elevation
46
46
  if (!isNaN(z)) {
47
- eleMax = eleMax < z ? z : eleMax;
48
- eleMin = eleMin > z ? z : eleMin;
49
47
  this._lastValidZ = z;
50
48
  }
51
49
 
52
- data[i].z = z;
50
+ track.elevation_max = z > zMax ? z : zMax;
51
+ track.elevation_min = z < zMin ? z : zMin;
53
52
 
54
- track.elevation_max = eleMax;
55
- track.elevation_min = eleMin;
53
+ data[i].z = z;
56
54
  });
57
55
 
58
56
  this.on("elechart_axis", function() {
@@ -69,13 +67,10 @@ Elevation.addInitHook(function() {
69
67
 
70
68
  this.on("elechart_axis", function() {
71
69
 
72
- altitude.y = this._chart._y;
73
- altitude.label = this._yLabel;
74
-
75
70
  this._chart._registerAxisScale({
76
71
  axis : "y",
77
72
  position: "left",
78
- scale : altitude.y,
73
+ scale : this._chart._y,
79
74
  label : altitude.label,
80
75
  labelX : -3,
81
76
  labelY : -8,
@@ -88,8 +83,6 @@ Elevation.addInitHook(function() {
88
83
 
89
84
  let theme = this.options.theme.replace('-theme', '');
90
85
  let color = D3.Colors[theme] || {};
91
- let alpha = (this.options.detached ? (color.alpha || '0.8') : 1);
92
- let stroke = (this.options.detached ? color.stroke : false);
93
86
 
94
87
  this._chart._registerAreaPath({
95
88
  name : 'altitude',
@@ -97,9 +90,9 @@ Elevation.addInitHook(function() {
97
90
  scaleX : 'distance',
98
91
  scaleY : 'altitude',
99
92
  color : color.area || theme,
100
- strokeColor : stroke || '#000',
93
+ strokeColor : this.options.detached ? color.stroke : '#000',
101
94
  strokeOpacity: "1",
102
- fillOpacity : alpha,
95
+ fillOpacity : this.options.detached ? (color.alpha || '0.8') : 1,
103
96
  preferCanvas : this.options.preferCanvas,
104
97
  });
105
98
 
@@ -107,14 +100,20 @@ Elevation.addInitHook(function() {
107
100
 
108
101
  }
109
102
 
103
+ this._registerTooltip({
104
+ name: 'y',
105
+ chart: (item) => L._("y: ") + d3.format("." + opts.decimalsY + "f")(item[opts.yAttr]) + " " + altitude.label,
106
+ marker: (item) => d3.format("." + opts.decimalsY + "f")(item[opts.yAttr]) + " " + altitude.label,
107
+ });
108
+
110
109
  this._registerSummary({
111
110
  "maxele" : {
112
111
  label: "Max Elevation: ",
113
- value: (track) => (track.elevation_max || 0).toFixed(2) + '&nbsp;' + this._yLabel
112
+ value: (track) => (track.elevation_max || 0).toFixed(2) + '&nbsp;' + altitude.label
114
113
  },
115
114
  "minele" : {
116
115
  label: "Min Elevation: ",
117
- value: (track) => (track.elevation_min || 0).toFixed(2) + '&nbsp;' + this._yLabel
116
+ value: (track) => (track.elevation_min || 0).toFixed(2) + '&nbsp;' + altitude.label
118
117
  },
119
118
  });
120
119
 
package/src/chart.js CHANGED
@@ -14,13 +14,13 @@ export var Chart = L.Class.extend({
14
14
 
15
15
  // cache registered components
16
16
  this._props = {
17
- scales : {},
18
- paths : {},
19
- areas : {},
20
- grids : {},
21
- axes : {},
22
- legendItems: {},
23
- focusLabels: {},
17
+ scales : {},
18
+ paths : {},
19
+ areas : {},
20
+ grids : {},
21
+ axes : {},
22
+ legendItems : {},
23
+ tooltipItems: {},
24
24
  };
25
25
 
26
26
  this._scales = {};
@@ -31,14 +31,6 @@ export var Chart = L.Class.extend({
31
31
  this._brushEnabled = opts.dragging;
32
32
  this._zoomEnabled = opts.zooming;
33
33
 
34
- if (opts.imperial) {
35
- this._xLabel = "mi";
36
- this._yLabel = "ft";
37
- } else {
38
- this._xLabel = opts.xLabel;
39
- this._yLabel = opts.yLabel;
40
- }
41
-
42
34
  let chart = this._chart = D3.Chart(opts);
43
35
 
44
36
  // SVG Container
@@ -50,22 +42,17 @@ export var Chart = L.Class.extend({
50
42
  this._point = chart.pane('point');
51
43
  this._axis = chart.pane('axis');
52
44
  this._legend = chart.pane('legend');
45
+ this._tooltip = chart.pane('tooltip');
46
+ this._ruler = chart.pane('ruler');
53
47
 
54
48
  // Scales
55
49
  this._initScale();
56
50
 
57
51
  // Helpers
58
- this._clipPath = chart.get('clipPath');
59
- this._canvas = chart.get('canvas');
52
+ this._mask = chart.get('mask');
60
53
  this._context = chart.get('context');
61
- this._dragG = chart.get('dragG');
62
- this._focusG = chart.get('focusG');
63
54
  this._brush = chart.get('brush');
64
55
 
65
- // Tooltip
66
- this._focusline = this._focusG.select('.mouse-focus-line');
67
- this._focuslabel = this._focusG.select('.mouse-focus-label');
68
-
69
56
  this._zoom = d3.zoom();
70
57
  this._drag = d3.drag();
71
58
 
@@ -74,6 +61,13 @@ export var Chart = L.Class.extend({
74
61
 
75
62
  // svg.on('resize', (e)=>console.log(e.detail));
76
63
 
64
+ // Handle multi-track segments (BETA)
65
+ this._maskGaps = [];
66
+ control.on('eletrack_added', (e) => {
67
+ this._maskGaps.push(e.index);
68
+ control.once('eledata_updated', (e) => this._maskGaps.push(e.index));
69
+ });
70
+
77
71
  },
78
72
 
79
73
  update: function(props) {
@@ -101,62 +95,33 @@ export var Chart = L.Class.extend({
101
95
  this._area.selectAll('path').attr("d", "M0 0");
102
96
  this._context.clearRect(0, 0, this._width(), this._height());
103
97
 
104
- if (this._path) {
98
+ // if (this._path) {
105
99
  // this._x.domain([0, 1]);
106
100
  // this._y.domain([0, 1]);
107
- }
101
+ // }
108
102
  },
109
103
 
110
104
  _drawPath: function(name) {
111
- let opts = this.options;
112
- let ctx = this._context;
113
-
114
105
  let path = this._paths[name];
115
106
  let area = this._props.areas[name];
116
- let node = path.node();
117
- let scaleX = this._scales[area.scaleX];
118
- let scaleY = this._scales[area.scaleY];
119
-
120
- area = L.extend({}, area, {
121
- width : this._width(),
122
- height : this._height(),
123
- scaleX : scaleX,
124
- scaleY : scaleY
125
- });
126
-
127
- if (!scaleY || !scaleY) {
128
- return console.warn('Unable to render path:' + name);
129
- }
130
107
 
131
- path.datum(this._data).attr("d", D3.Area(area));
108
+ path.datum(this._data).attr("d",
109
+ D3.Area(
110
+ L.extend({}, area, {
111
+ width : this._width(),
112
+ height : this._height(),
113
+ scaleX : this._scales[area.scaleX],
114
+ scaleY : this._scales[area.scaleY]
115
+ })
116
+ )
117
+ );
132
118
 
133
119
  if (path.classed('leaflet-hidden')) return;
134
120
 
135
- if (opts.preferCanvas) {
136
- path.classed('canvas-path', true);
137
-
138
- ctx.beginPath();
139
- ctx.moveTo(0, 0);
140
- let p = new Path2D(path.attr('d'));
141
-
142
- ctx.strokeStyle = path.attr('stroke');
143
- ctx.fillStyle = path.attr('fill');
144
- ctx.lineWidth = 1.25;
145
- ctx.globalCompositeOperation = 'source-over';
146
-
147
- // stroke opacity
148
- ctx.globalAlpha = path.attr('stroke-opacity') || 0.3;
149
- ctx.stroke(p);
150
-
151
- // fill opacity
152
- ctx.globalAlpha = path.attr('fill-opacity') || 0.45;
153
- ctx.fill(p);
154
-
155
- ctx.globalAlpha = 1;
156
-
157
- ctx.closePath();
121
+ if (this.options.preferCanvas) {
122
+ _.drawCanvas(this._context, path);
158
123
  } else {
159
- this._area.append(() => node);
124
+ this._area.append(() => path.node());
160
125
  }
161
126
  },
162
127
 
@@ -220,6 +185,10 @@ export var Chart = L.Class.extend({
220
185
  if (e.sourceEvent && e.sourceEvent.type == "mousedown") svg.style('cursor', 'grabbing');
221
186
  if (e.transform.k == 1 && e.transform.x == 0) {
222
187
  this._container.classed('zoomed', true);
188
+ // Apply d3-zoom (bind <clipPath> mask)
189
+ if (this._mask) {
190
+ this._point.attr('mask', 'url(#' + this._mask.attr('id') + ')');
191
+ }
223
192
  }
224
193
  this.zooming = true;
225
194
  };
@@ -227,6 +196,10 @@ export var Chart = L.Class.extend({
227
196
  const onEnd = (e) => {
228
197
  if (e.transform.k ==1 && e.transform.x == 0){
229
198
  this._container.classed('zoomed', false);
199
+ // Apply d3-zoom (bind <clipPath> mask)
200
+ if (this._mask) {
201
+ this._point.attr('mask', null);
202
+ }
230
203
  }
231
204
  this.zooming = false;
232
205
  svg.style('cursor', '');
@@ -307,8 +280,8 @@ export var Chart = L.Class.extend({
307
280
 
308
281
  const label = (e, d) => {
309
282
  let yMax = this._height();
310
- let y = this._dragG.data()[0].y;
311
- if (y >= yMax || y <= 0) this._dragG.select(".horizontal-drag-label").text('');
283
+ let y = this._ruler.data()[0].y;
284
+ if (y >= yMax || y <= 0) this._ruler.select(".horizontal-drag-label").text('');
312
285
  this._hideDiagramIndicator();
313
286
  };
314
287
 
@@ -317,15 +290,15 @@ export var Chart = L.Class.extend({
317
290
  let yCoord = d3.pointers(e, this._area.node())[0][1];
318
291
  let y = yCoord > 0 ? (yCoord < yMax ? yCoord : yMax) : 0;
319
292
  let z = this._y.invert(y);
320
- let data = L.extend(this._dragG.data()[0], { y: y });
293
+ let data = L.extend(this._ruler.data()[0], { y: y });
321
294
 
322
- this._dragG
295
+ this._ruler
323
296
  .data([data])
324
297
  .attr("transform", d => "translate(" + d.x + "," + d.y + ")")
325
298
  .classed('active', y < yMax);
326
299
 
327
300
  this._container.select(".horizontal-drag-label")
328
- .text(formatNum(z) + " " + this._yLabel);
301
+ .text(formatNum(z) + " " + (this.options.imperial ? 'ft' : 'm'));
329
302
 
330
303
  this.fire('ruler_filter', { coords: yCoord < yMax && yCoord > 0 ? this._findCoordsForY(yCoord) : [] });
331
304
  }
@@ -334,7 +307,7 @@ export var Chart = L.Class.extend({
334
307
  .on("start end", label)
335
308
  .on("drag", position);
336
309
 
337
- this._dragG.call(drag);
310
+ this._ruler.call(drag);
338
311
 
339
312
  },
340
313
 
@@ -427,7 +400,7 @@ export var Chart = L.Class.extend({
427
400
  },
428
401
 
429
402
  /**
430
- * Add a waypoint of interest over the chart
403
+ * Add a point of interest over the chart
431
404
  */
432
405
  _registerCheckPoint: function(point) {
433
406
  if (!this._data.length) return;
@@ -442,58 +415,19 @@ export var Chart = L.Class.extend({
442
415
  x = this._x(point.dist);
443
416
  item = this._data[this._findIndexForXCoord(x)]
444
417
  y = this._y(item.z);
445
- } else
446
-
447
- if (isNaN(x) || isNaN(y)) return;
448
-
449
- if (!point.item || !point.item.property('isConnected')) {
450
- point.position = point.position || "bottom";
451
-
452
- point.item = this._point.append('g');
453
-
454
- point.item.append("svg:line")
455
- .attr("y1", 0)
456
- .attr("x1", 0)
457
- .attr("style","stroke: rgb(51, 51, 51); stroke-width: 0.5; stroke-dasharray: 2, 2;");
458
-
459
- point.item
460
- .append("svg:circle")
461
- .attr("class", " height-focus circle-lower")
462
- .attr("r", 3);
463
-
464
- if (point.label) {
465
- point.item.append("svg:text")
466
- .attr("dx", "4px")
467
- .attr("dy", "-4px");
468
- }
469
- }
470
-
471
- point.item
472
- .datum({
473
- pos: point.position,
474
- x: x,
475
- y: y
476
- })
477
- .attr("class", d => "point " + d.pos)
478
- .attr("transform", d => "translate(" + d.x + "," + d.y + ")");
479
-
480
- point.item.select('line')
481
- .datum({
482
- y2: ({'top': -y, 'bottom': this._height() - y})[point.position],
483
- x2: ({'left': -x, 'right': this._width() - x})[point.position] || 0
484
- })
485
- .attr("y2", d => d.y2)
486
- .attr("x2", d => d.x2)
487
-
488
- if (point.label) {
489
- point.item.select('text')
490
- .text(point.label);
491
- }
492
-
418
+ }
419
+
420
+ this._point.call(D3.CheckPoint({
421
+ point: point,
422
+ width: this._width(),
423
+ height: this._height(),
424
+ x: x,
425
+ y: y,
426
+ }));
493
427
  },
494
428
 
495
- _registerFocusLabel: function(props) {
496
- this._props.focusLabels[props.name] = props;
429
+ _registerTooltip: function(props) {
430
+ this._props.tooltipItems[props.name] = props;
497
431
  },
498
432
 
499
433
  _updateArea: function() {
@@ -578,11 +512,29 @@ export var Chart = L.Class.extend({
578
512
  [this._width() - margin.right, Infinity]
579
513
  ]);
580
514
 
515
+ // Apply svg mask on multi-track segments (BETA)
516
+ this._mask.selectAll(".gap").remove()
517
+ this._maskGaps.forEach((d, i) => {
518
+ if(i >= this._maskGaps.length - 2) return;
519
+ let d1 = this._data[this._maskGaps[i]];
520
+ let d2 = this._data[this._maskGaps[i + 1]];
521
+ let x1 = this._x(this._data[this._findIndexForLatLng(d1.latlng)].dist);
522
+ let x2 = this._x(this._data[this._findIndexForLatLng(d2.latlng)].dist);
523
+ this._mask
524
+ .append("rect")
525
+ .attr("x", x1)
526
+ .attr("y", 0)
527
+ .attr("width", x2 - x1 )
528
+ .attr("height", this._height())
529
+ .attr('class', 'gap')
530
+ .attr('fill-opacity', '0.8')
531
+ .attr("fill", 'black'); // black = hide
532
+ });
581
533
  },
582
534
 
583
535
  _updateLegend: function () {
584
536
 
585
- if (this.options.legend == false) return;
537
+ if (this.options.legend === false) return;
586
538
 
587
539
  let legends = this._props.legendItems;
588
540
  let legend;
@@ -654,8 +606,8 @@ export var Chart = L.Class.extend({
654
606
  }
655
607
 
656
608
  // Apply d3-zoom (bind <clipPath> mask)
657
- if (this._clipPath) {
658
- path.attr('clip-path', 'url(#' + this._clipPath.attr('id') + ')');
609
+ if (this._mask) {
610
+ path.attr('mask', 'url(#' + this._mask.attr('id') + ')');
659
611
  }
660
612
 
661
613
  });
@@ -664,16 +616,10 @@ export var Chart = L.Class.extend({
664
616
  _updateScale: function() {
665
617
  if (this.zooming) return { x: this._x, y: this._y };
666
618
 
667
- let opts = this.options;
668
-
669
- let scales = this._scales;
670
- let d = this._domains;
671
- let r = this._ranges;
672
-
673
- for (let i in scales) {
674
- scales[i]
675
- .domain(d[i](this._data))
676
- .range(r[i](this._width(), this._height()))
619
+ for (let i in this._scales) {
620
+ this._scales[i]
621
+ .domain(this._domains[i](this._data))
622
+ .range(this._ranges[i](this._width(), this._height()))
677
623
  }
678
624
 
679
625
  return { x: this._x, y: this._y };
@@ -772,52 +718,19 @@ export var Chart = L.Class.extend({
772
718
  * Display distance and altitude level ("focus-rect").
773
719
  */
774
720
  _showDiagramIndicator: function(item, xCoordinate) {
775
- let opts = this.options;
776
- let yCoordinate = this._y(item[opts.yAttr]);
777
-
778
- this._focusG.attr("display", null);
779
-
780
- this._focusline.call(
781
- D3.MouseFocusLine({
782
- xCoord: xCoordinate,
783
- height: this._height()
784
- })
785
- );
786
- this._focuslabel.call(
787
- D3.MouseFocusLabel({
721
+ this._tooltip
722
+ .attr("display", null)
723
+ .call(D3.Tooltip({
788
724
  xCoord: xCoordinate,
789
- yCoord: yCoordinate,
725
+ yCoord: this._y(item[this.options.yAttr]),
790
726
  height: this._height(),
791
727
  width : this._width(),
792
- labelX: d3.format("."+ opts.decimalsX +"f")(item[opts.xAttr]) + " " + this._xLabel,
793
- labelY: d3.format("."+ opts.decimalsY +"f")(item[opts.yAttr]) + " " + this._yLabel
794
- })
795
- );
796
-
797
- // this._focuslabel.selectAll('tspan').data(Object.values(this._focuslabels));
798
-
799
- let labels = this._props.focusLabels;
800
- let tooltip = this._focuslabel.select('text');
801
- let label;
802
-
803
- for (var i in labels) {
804
- label = tooltip.select(".mouse-focus-label-" + labels[i].name);
805
-
806
- if (!label.size()) {
807
- label = tooltip.append("svg:tspan", ".mouse-focus-label-x")
808
- .attr("class", "mouse-focus-label-" + labels[i].name)
809
- .attr("dy", "1.5em");
810
- }
811
-
812
- label.text(typeof labels[i].value !== "function" ? labels[i].value : labels[i].value(item) );
813
-
814
- this._focuslabel.select('.mouse-focus-label-x')
815
- .attr("dy", "1.5em");
816
- }
817
-
728
+ labels: this._props.tooltipItems,
729
+ item: item
730
+ }));
818
731
  },
819
732
 
820
733
  _hideDiagramIndicator: function() {
821
- this._focusG.attr("display", 'none');
734
+ this._tooltip.attr("display", 'none');
822
735
  },
823
736
  });