@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/src/marker.js CHANGED
@@ -7,39 +7,28 @@ export var Marker = L.Class.extend({
7
7
  initialize: function(options) {
8
8
  this.options = options;
9
9
 
10
- if (this.options.imperial) {
11
- this._xLabel = "mi";
12
- this._yLabel = "ft";
13
- } else {
14
- this._xLabel = this.options.xLabel;
15
- this._yLabel = this.options.yLabel;
10
+ switch(this.options.marker) {
11
+ case 'elevation-line':
12
+ // this._container = d3.create("g").attr("class", "height-focus-group");
13
+ this._container = d3.select(map.getPane('elevationPane')).select("svg > g")
14
+ .attr("class", "height-focus-group");
15
+ break;
16
+ case 'position-marker':
17
+ // this._marker = L.circleMarker([0, 0], { pane: 'overlayPane', radius: 6, fillColor: '#fff', fillOpacity:1, color: '#000', weight:1, interactive: false });
18
+ this._marker = L.marker([0, 0], { icon: this.options.markerIcon, zIndexOffset: 1000000, interactive: false });
19
+ break;
16
20
  }
17
21
 
18
- if (this.options.marker == 'elevation-line') {
19
- // this._container = d3.create("g").attr("class", "height-focus-group");
20
- this._focusline = d3.create('svg:line');
21
- this._focusmarker = d3.create("svg:circle");
22
- this._focuslabel = d3.create("svg:text");
23
- } else if (this.options.marker == 'position-marker') {
24
- // this._marker = L.circleMarker([0, 0], { pane: 'overlayPane', radius: 6, fillColor: '#fff', fillOpacity:1, color: '#000', weight:1, interactive: false });
25
- this._marker = L.marker([0, 0], { icon: this.options.markerIcon, zIndexOffset: 1000000, interactive: false });
26
- }
27
-
28
- this._focuslabels = {};
22
+ this._labels = {};
29
23
 
30
24
  return this;
31
25
  },
32
26
 
33
27
  addTo: function(map) {
34
28
  this._map = map;
35
- if (this.options.marker == 'elevation-line') {
36
- let g = this._container = d3.select(map.getPane('elevationPane')).select("svg > g")
37
- .attr("class", "height-focus-group");
38
- g.append(() => this._focusline.node());
39
- g.append(() => this._focusmarker.node());
40
- g.append(() => this._focuslabel.node());
41
- } else if (this.options.marker == 'position-marker') {
42
- this._marker.addTo(map, { pane: 'overlayPane' });
29
+ switch(this.options.marker) {
30
+ case 'elevation-line': this._container.call(D3.PositionMarker({})); break;
31
+ case 'position-marker': this._marker.addTo(map, { pane: 'overlayPane' }); break;
43
32
  }
44
33
  return this;
45
34
  },
@@ -56,75 +45,27 @@ export var Marker = L.Class.extend({
56
45
  if (props.options) this.options = props.options;
57
46
  if (!this._map) this.addTo(props.map);
58
47
 
59
- let opts = this.options;
60
- let map = this._map;
61
-
62
48
  this._latlng = props.item.latlng;
63
49
 
64
- let pos = map.latLngToLayerPoint(this._latlng);
65
-
66
- if (map._rotate) {
67
- pos = map.rotatedPointToMapPanePoint(pos);
68
- }
69
-
70
- let point = L.extend({}, props.item, pos);
71
-
72
- if (this.options.marker == 'elevation-line') {
73
-
74
- let normalizedAlt = this._height() / props.maxElevation * point.z;
75
- let normalizedY = point.y - normalizedAlt;
76
-
77
- this._container.classed("leaflet-hidden", false);
78
-
79
- this._focusmarker
80
- .call(
81
- D3.HeightFocusMarker({
82
- theme : opts.theme,
83
- xCoord: point.x,
84
- yCoord: point.y,
85
- }));
86
-
87
- this._focusline
88
- .call(
89
- D3.HeightFocusLine({
90
- theme : opts.theme,
91
- xCoord: point.x,
92
- yCoord: point.y,
93
- length: normalizedY
94
- })
95
- );
96
- this._focuslabel
97
- .call(
98
- D3.HeightFocusLabel({
99
- theme : opts.theme,
100
- xCoord: point.x,
101
- yCoord: normalizedY,
102
- label : d3.format("." + opts.decimalsY + "f")(point[opts.yAttr]) + " " + this._yLabel
103
- })
104
- );
105
-
106
- let labels = this._focuslabels;
107
- let tooltip = this._focuslabel;
108
- let label;
109
-
110
- for (var i in labels) {
111
- label = tooltip.select(".height-focus-" + labels[i].name);
112
-
113
- if (!label.size()) {
114
- label = tooltip.append("svg:tspan")
115
- .attr("class", "height-focus-" + labels[i].name)
116
- .attr("dy", "1.5em");
117
- }
118
-
119
- label.text(typeof labels[i].value !== "function" ? labels[i].value : labels[i].value(props.item) );
120
-
121
- this._focuslabel.select('.height-focus-y')
122
- .attr("dy", "-1.5em");
123
- }
124
-
125
- } else if (this.options.marker == 'position-marker') {
126
- _.removeClass(this._marker.getElement(), 'leaflet-hidden');
127
- this._marker.setLatLng(this._latlng);
50
+ switch(this.options.marker) {
51
+ case 'elevation-line':
52
+ let point = this._map.latLngToLayerPoint(this._latlng);
53
+ point = L.extend({}, props.item, this._map._rotate ? this._map.rotatedPointToMapPanePoint(point) : point);
54
+
55
+ this._container.classed("leaflet-hidden", false);
56
+ this._container.call(D3.PositionMarker({
57
+ theme : this.options.theme,
58
+ xCoord: point.x,
59
+ yCoord: point.y,
60
+ length: point.y - (this._height() / props.maxElevation * point.z), // normalized Y
61
+ labels: this._labels,
62
+ item: point
63
+ }));
64
+ break;
65
+ case 'position-marker':
66
+ _.removeClass(this._marker.getElement(), 'leaflet-hidden');
67
+ this._marker.setLatLng(this._latlng);
68
+ break;
128
69
  }
129
70
  },
130
71
 
@@ -133,10 +74,9 @@ export var Marker = L.Class.extend({
133
74
  */
134
75
  remove: function() {
135
76
  this._props = null;
136
- if (this.options.marker == 'elevation-line') {
137
- if (this._container) this._container.classed("leaflet-hidden", true);
138
- } else if (this.options.marker == 'position-marker') {
139
- _.addClass(this._marker.getElement(), 'leaflet-hidden');
77
+ switch(this.options.marker) {
78
+ case 'elevation-line': this._container && this._container.classed("leaflet-hidden", true); break;
79
+ case 'position-marker': _.addClass(this._marker.getElement(), 'leaflet-hidden'); break;
140
80
  }
141
81
  },
142
82
 
@@ -152,8 +92,8 @@ export var Marker = L.Class.extend({
152
92
  return opts.height - opts.margins.top - opts.margins.bottom;
153
93
  },
154
94
 
155
- _registerFocusLabel: function(props) {
156
- this._focuslabels[props.name] = props;
95
+ _registerTooltip: function(props) {
96
+ this._labels[props.name] = props;
157
97
  }
158
98
 
159
99
  });
package/src/options.js CHANGED
@@ -9,7 +9,6 @@ export var Options = {
9
9
  collapsed: false,
10
10
  detached: true,
11
11
  distance: true,
12
- distanceFactor: 1,
13
12
  distanceMarkers: false,
14
13
  decimalsX: 2,
15
14
  decimalsY: 0,
@@ -19,15 +18,10 @@ export var Options = {
19
18
  followMarker: true,
20
19
  forceAxisBounds: false,
21
20
  height: 200,
22
- heightFactor: 1,
23
21
  imperial: false,
24
22
  interpolation: "curveLinear",
25
23
  lazyLoadJS: true,
26
24
  legend: true,
27
- loadData: {
28
- defer: false,
29
- lazy: false,
30
- },
31
25
  margins: {
32
26
  top: 30,
33
27
  right: 30,
@@ -41,7 +35,6 @@ export var Options = {
41
35
  iconSize: [32, 32],
42
36
  iconAnchor: [16, 16],
43
37
  }),
44
- placeholder: false,
45
38
  position: "topright",
46
39
  polyline: {
47
40
  className: 'elevation-polyline',
@@ -60,7 +53,6 @@ export var Options = {
60
53
  ruler: true,
61
54
  skipNullZCoords: false,
62
55
  theme: "lightblue-theme",
63
- responsive: true,
64
56
  summary: 'inline',
65
57
  slope: false,
66
58
  speed: false,
@@ -68,6 +60,7 @@ export var Options = {
68
60
  time: false,
69
61
  timeFactor: 3600,
70
62
  timeFormat: false,
63
+ timestamps: false,
71
64
  sDeltaMax: undefined,
72
65
  sInterpolation: "curveStepAfter",
73
66
  sRange: undefined,
package/src/slope.js CHANGED
@@ -74,39 +74,30 @@ Elevation.addInitHook(function() {
74
74
  }
75
75
 
76
76
  // Range of acceptable slope values.
77
- if (this.options.sRange) {
78
- let range = this.options.sRange;
79
- if (slope < range[0]) slope = range[0];
80
- else if (slope > range[1]) slope = range[1];
81
- }
82
-
83
- slope = L.Util.formatNum(slope, 2);
84
-
85
- if (slope > sMax) sMax = slope;
86
- if (slope < sMin) sMin = slope;
87
-
88
- data[i].slope = slope;
77
+ slope = _.clamp(slope, this.options.sRange);
89
78
 
90
79
  track.ascent = tAsc;
91
80
  track.descent = tDes;
92
- track.slope_max = sMax;
93
- track.slope_min = sMin;
81
+ track.slope_max = slope > sMax ? slope : sMax;
82
+ track.slope_min = slope < sMin ? slope : sMin;
83
+
84
+ data[i].slope = L.Util.formatNum(slope, 2);
94
85
  });
95
86
 
96
- this._registerFocusLabel({
87
+ this._registerTooltip({
97
88
  name: 'slope',
98
- chart: (item) => item.slope + slope.label,
89
+ chart: (item) => L._("m: ") + item.slope + slope.label,
99
90
  marker: (item) => Math.round(item.slope) + slope.label
100
91
  });
101
92
 
102
93
  this._registerSummary({
103
94
  "ascent" : {
104
95
  label: "Total Ascent: ",
105
- value: (track) => Math.round(track.ascent || 0) + '&nbsp;' + this._yLabel
96
+ value: (track) => Math.round(track.ascent || 0) + '&nbsp;' + (this.options.imperial ? 'ft' : 'm')
106
97
  },
107
98
  "descent" : {
108
99
  label: "Total Descent: ",
109
- value: (track) => Math.round(track.descent || 0) + '&nbsp;' + this._yLabel
100
+ value: (track) => Math.round(track.descent || 0) + '&nbsp;' + (this.options.imperial ? 'ft' : 'm')
110
101
  },
111
102
  "minslope": {
112
103
  label: "Min Slope: ",
package/src/speed.js CHANGED
@@ -10,8 +10,8 @@ Elevation.addInitHook(function() {
10
10
  let opts = this.options;
11
11
  let speed = {};
12
12
 
13
- speed.label = opts.speedLabel || L._(this.options.imperial ? 'mph' : 'km/h');
14
- this._speedFactor = opts.speedFactor || 1;
13
+ speed.label = opts.speedLabel || L._(opts.imperial ? 'mph' : 'km/h');
14
+ opts.speedFactor = opts.speedFactor || 1;
15
15
 
16
16
  if (this.options.speed && this.options.speed != "summary") {
17
17
 
@@ -60,7 +60,7 @@ Elevation.addInitHook(function() {
60
60
 
61
61
  if (deltaT > 0) {
62
62
  let delta = (data[i].dist - data[i > 0 ? i - 1 : i].dist) * 1000;
63
- speed = Math.abs((delta / deltaT) * this._timeFactor) * this._speedFactor;
63
+ speed = Math.abs((delta / deltaT) * opts.timeFactor) * opts.speedFactor;
64
64
  }
65
65
 
66
66
  // Try to smooth "crazy" speed values.
@@ -73,31 +73,21 @@ Elevation.addInitHook(function() {
73
73
  }
74
74
 
75
75
  // Range of acceptable speed values.
76
- if (this.options.speedRange) {
77
- let range = this.options.speedRange;
78
- if (speed < range[0]) speed = range[0];
79
- else if (speed > range[1]) speed = range[1];
80
- }
81
-
82
- speed = L.Util.formatNum(speed, 2);
83
-
84
- sMax = speed > sMax ? speed : sMax;
85
- sMin = speed < sMin ? speed : sMin;
86
- sAvg = (speed + sAvg) / 2.0;
76
+ speed = _.clamp(speed, this.options.speedRange);
87
77
 
88
- data[i].speed = speed;
78
+ track.speed_max = speed > sMax ? speed : sMax;
79
+ track.speed_min = speed < sMin ? speed : sMin;
80
+ track.speed_avg = (speed + sAvg) / 2.0;
89
81
 
90
- track.speed_max = sMax;
91
- track.speed_min = sMin;
92
- track.speed_avg = sAvg;
82
+ data[i].speed = L.Util.formatNum(speed, 2);
93
83
  });
94
84
 
95
85
  if (this.options.speed) {
96
86
 
97
- this._registerFocusLabel({
87
+ this._registerTooltip({
98
88
  name: 'speed',
99
- chart: (item) => item.speed + " " + speed.label,
100
- marker: (item) => Math.round(item.speed) + " " + speed.label
89
+ chart: (item) => L._('v: ') + item.speed + " " + speed.label,
90
+ marker: (item) => Math.round(item.speed) + " " + speed.label,
101
91
  });
102
92
 
103
93
  this._registerSummary({
package/src/time.js CHANGED
@@ -8,8 +8,8 @@ Elevation.addInitHook(function() {
8
8
  let opts = this.options;
9
9
  let time = {};
10
10
 
11
- time.label = opts.timeLabel || 't';
12
- this._timeFactor = opts.timeFactor;
11
+ time.label = opts.timeLabel || 't';
12
+ opts.timeFactor = opts.timeFactor || 3600;
13
13
 
14
14
  /**
15
15
  * Common AVG speeds:
@@ -75,7 +75,7 @@ Elevation.addInitHook(function() {
75
75
  e.point.meta.time = new Date(
76
76
  (
77
77
  i > 0
78
- ? data[i - 1].time.getTime() + ((( delta / speed) * this._timeFactor) * 1000)
78
+ ? data[i - 1].time.getTime() + ((( delta / speed) * opts.timeFactor) * 1000)
79
79
  : Date.now()
80
80
  )
81
81
  );
@@ -94,26 +94,31 @@ Elevation.addInitHook(function() {
94
94
  let deltaT = Math.abs(currT - prevT);
95
95
  let duration = (this.track_info.time || 0) + deltaT;
96
96
 
97
+ this.track_info.time = duration;
98
+
97
99
  data[i].time = time;
98
100
  data[i].duration = duration;
99
-
100
- this.track_info.time = duration;
101
101
  });
102
102
 
103
- if (this.options.time) {
104
103
 
105
- this._registerFocusLabel({
106
- name: 'time',
107
- chart: (item) => this.options.timeFormat(item.time)
104
+ if (this.options.timestamps) {
105
+ this._registerTooltip({
106
+ name: 'date',
107
+ chart: (item) => L._("t: ") + this.options.timeFormat(item.time)
108
108
  });
109
+ }
109
110
 
111
+ if (this.options.time) {
112
+ this._registerTooltip({
113
+ name: 'time',
114
+ chart: (item) => L._("T: ") + _.formatTime(item.duration || 0)
115
+ });
110
116
  this._registerSummary({
111
117
  "tottime" : {
112
118
  label: "Total Time: ",
113
119
  value: (track) => _.formatTime(track.time || 0)
114
120
  }
115
121
  });
116
-
117
122
  }
118
123
 
119
124
  });
package/src/utils.js CHANGED
@@ -22,29 +22,6 @@ export function deepMerge(target, ...sources) {
22
22
  return deepMerge(target, ...sources);
23
23
  }
24
24
 
25
- /**
26
- * Wait for document load before execute function.
27
- */
28
- export function deferFunc(f) {
29
- if (document.readyState !== 'complete') window.addEventListener("load", f, { once: true });
30
- else f();
31
- }
32
- /**
33
- * Debounce function to limit events are being fired.
34
- */
35
- export function debounce(func, wait, context, immediate) {
36
- var timeout;
37
- return function() {
38
- var args = arguments;
39
- clearTimeout(timeout);
40
- timeout = setTimeout(function() {
41
- timeout = null;
42
- if (!immediate) func.apply(context, args);
43
- }, wait);
44
- if (immediate && !timeout) func.apply(context, args);
45
- };
46
- }
47
-
48
25
  const SEC = 1000;
49
26
  const MIN = SEC * 60;
50
27
  const HOUR = MIN * 60;
@@ -169,13 +146,6 @@ export function GeoJSONLoader(data, control) {
169
146
  return layer;
170
147
  }
171
148
 
172
- /**
173
- * Check DOM element visibility.
174
- */
175
- export function isDomVisible(elem) {
176
- return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
177
- }
178
-
179
149
  /**
180
150
  * Check object type.
181
151
  */
@@ -183,47 +153,6 @@ export function isObject(item) {
183
153
  return (item && typeof item === 'object' && !Array.isArray(item));
184
154
  }
185
155
 
186
- /**
187
- * Check DOM element viewport visibility.
188
- */
189
- export function isVisible(elem) {
190
- if (!elem) return false;
191
-
192
- let styles = window.getComputedStyle(elem);
193
-
194
- function isVisibleByStyles(elem, styles) {
195
- return styles.visibility !== 'hidden' && styles.display !== 'none';
196
- }
197
-
198
- function isAboveOtherElements(elem, styles) {
199
- let boundingRect = elem.getBoundingClientRect();
200
- let left = boundingRect.left + 1;
201
- let right = boundingRect.right - 1;
202
- let top = boundingRect.top + 1;
203
- let bottom = boundingRect.bottom - 1;
204
- let above = true;
205
-
206
- let pointerEvents = elem.style.pointerEvents;
207
-
208
- if (styles['pointer-events'] == 'none') elem.style.pointerEvents = 'auto';
209
-
210
- if (document.elementFromPoint(left, top) !== elem) above = false;
211
- if (document.elementFromPoint(right, top) !== elem) above = false;
212
-
213
- // Only for completely visible elements
214
- // if (document.elementFromPoint(left, bottom) !== elem) above = false;
215
- // if (document.elementFromPoint(right, bottom) !== elem) above = false;
216
-
217
- elem.style.pointerEvents = pointerEvents;
218
-
219
- return above;
220
- }
221
-
222
- if (!isVisibleByStyles(elem, styles)) return false;
223
- if (!isAboveOtherElements(elem, styles)) return false;
224
- return true;
225
- }
226
-
227
156
  /**
228
157
  * Check JSON object type.
229
158
  */
@@ -258,6 +187,17 @@ export function isXMLDoc(doc, lazy) {
258
187
  }
259
188
  }
260
189
 
190
+ /**
191
+ * Generate download data event.
192
+ */
193
+ export function saveFile(dataURI, fileName) {
194
+ let a = create('a', '', { href: dataURI, target: '_new', download: fileName || "", style: "display:none;" });
195
+ let b = document.body;
196
+ b.appendChild(a);
197
+ a.click();
198
+ b.removeChild(a);
199
+ }
200
+
261
201
  /**
262
202
  * Async JS script download.
263
203
  */
@@ -276,39 +216,71 @@ export function lazyLoader(url, skip, loader) {
276
216
  });
277
217
  }
278
218
 
219
+ export function lazyLoad(src, control) {
220
+ if (!control.options.lazyLoadJS) return Promise.resolve();
221
+
222
+ const Elevation = L.Control.Elevation;
223
+ let loader = '';
224
+ let condition = false;
225
+
226
+ switch(src) {
227
+ case control.__D3:
228
+ loader = '_d3LazyLoader';
229
+ condition = typeof d3 !== 'object';
230
+ break;
231
+ case control.__TOGEOJSON:
232
+ loader = '_togeojsonLazyLoader';
233
+ condition = typeof toGeoJSON !== 'object';
234
+ break;
235
+ case control.__LGEOMUTIL:
236
+ loader = '_geomutilLazyLoader';
237
+ condition = typeof L.GeometryUtil !== 'object';
238
+ case control.__LALMOSTOVER:
239
+ loader = '_almostoverLazyLoader';
240
+ condition = typeof L.Handler.AlmostOver !== 'function ';
241
+ break;
242
+ case control.__LDISTANCEM:
243
+ loader = '_distanceMarkersLazyLoader';
244
+ condition = typeof L.DistanceMarkers !== 'function';
245
+ break;
246
+ }
247
+ return Elevation[loader] = lazyLoader(src, condition, Elevation[loader]);
248
+ }
249
+
279
250
  /**
280
- * Generate download data event.
251
+ * Convert SVG Path into Path2D and then update canvas
281
252
  */
282
- export function saveFile(dataURI, fileName) {
283
- let a = create('a', '', { href: dataURI, target: '_new', download: fileName || "", style: "display:none;" });
284
- let b = document.body;
285
- b.appendChild(a);
286
- a.click();
287
- b.removeChild(a);
253
+ export function drawCanvas(ctx, path) {
254
+ path.classed('canvas-path', true);
255
+
256
+ ctx.beginPath();
257
+ ctx.moveTo(0, 0);
258
+ let p = new Path2D(path.attr('d'));
259
+
260
+ ctx.strokeStyle = path.attr('stroke');
261
+ ctx.fillStyle = path.attr('fill');
262
+ ctx.lineWidth = 1.25;
263
+ ctx.globalCompositeOperation = 'source-over';
264
+
265
+ // stroke opacity
266
+ ctx.globalAlpha = path.attr('stroke-opacity') || 0.3;
267
+ ctx.stroke(p);
268
+
269
+ // fill opacity
270
+ ctx.globalAlpha = path.attr('fill-opacity') || 0.45;
271
+ ctx.fill(p);
272
+
273
+ ctx.globalAlpha = 1;
274
+
275
+ ctx.closePath();
288
276
  }
289
277
 
290
278
  /**
291
- * Wait for element visible before execute function.
279
+ * Limit a number between min / max values
292
280
  */
293
- export function waitHolder(elem) {
294
- return new Promise((resolve, reject) => {
295
- let ticking = false;
296
- let scrollFn = () => {
297
- if (!ticking) {
298
- L.Util.requestAnimFrame(() => {
299
- if (isVisible(elem)) {
300
- window.removeEventListener('scroll', scrollFn);
301
- resolve();
302
- }
303
- ticking = false;
304
- });
305
- ticking = true;
306
- }
307
- };
308
- window.addEventListener('scroll', scrollFn);
309
- if (elem) elem.addEventListener('mouseenter', scrollFn, { once: true });
310
- scrollFn();
311
- });
281
+ export function clamp(val, range) {
282
+ if (range) return val < range[0] ? range[0] : val > range[1] ? range[1] : val;
283
+ return val;
312
284
  }
313
285
 
314
286
  /**
@@ -401,10 +373,16 @@ export const off = L.DomEvent.off;
401
373
  export const hasClass = L.DomUtil.hasClass;
402
374
 
403
375
 
376
+ /**
377
+ * Generate a random string
378
+ */
404
379
  export function randomId() {
405
380
  return Math.random().toString(36).substr(2, 9);
406
381
  }
407
382
 
383
+ /**
384
+ * Execute a function foreach element in object
385
+ */
408
386
  export function each(obj, fn) {
409
387
  for (let i in obj) fn(obj[i], i);
410
- }
388
+ }