@raruto/leaflet-elevation 1.7.6 → 1.8.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/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,58 +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
-
97
- this._focuslabel
98
- .call(
99
- D3.HeightFocusLabel({
100
- theme : opts.theme,
101
- xCoord: point.x,
102
- yCoord: normalizedY,
103
- labels: this._focuslabels,
104
- item: point
105
- })
106
- );
107
-
108
- } else if (this.options.marker == 'position-marker') {
109
- _.removeClass(this._marker.getElement(), 'leaflet-hidden');
110
- 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;
111
69
  }
112
70
  },
113
71
 
@@ -116,10 +74,9 @@ export var Marker = L.Class.extend({
116
74
  */
117
75
  remove: function() {
118
76
  this._props = null;
119
- if (this.options.marker == 'elevation-line') {
120
- if (this._container) this._container.classed("leaflet-hidden", true);
121
- } else if (this.options.marker == 'position-marker') {
122
- _.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;
123
80
  }
124
81
  },
125
82
 
@@ -135,8 +92,8 @@ export var Marker = L.Class.extend({
135
92
  return opts.height - opts.margins.top - opts.margins.bottom;
136
93
  },
137
94
 
138
- _registerFocusLabel: function(props) {
139
- this._focuslabels[props.name] = props;
95
+ _registerTooltip: function(props) {
96
+ this._labels[props.name] = props;
140
97
  }
141
98
 
142
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,
package/src/slope.js CHANGED
@@ -74,26 +74,17 @@ 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
89
  chart: (item) => L._("m: ") + item.slope + slope.label,
99
90
  marker: (item) => Math.round(item.slope) + slope.label
@@ -102,11 +93,11 @@ Elevation.addInitHook(function() {
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,28 +73,18 @@ 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
89
  chart: (item) => L._('v: ') + item.speed + " " + speed.label,
100
90
  marker: (item) => Math.round(item.speed) + " " + speed.label,
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,22 +94,22 @@ 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
103
 
104
104
  if (this.options.timestamps) {
105
- this._registerFocusLabel({
105
+ this._registerTooltip({
106
106
  name: 'date',
107
107
  chart: (item) => L._("t: ") + this.options.timeFormat(item.time)
108
108
  });
109
109
  }
110
110
 
111
111
  if (this.options.time) {
112
- this._registerFocusLabel({
112
+ this._registerTooltip({
113
113
  name: 'time',
114
114
  chart: (item) => L._("T: ") + _.formatTime(item.duration || 0)
115
115
  });
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
+ }