bigmap-plugin 1.0.3 → 1.0.4
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/LICENSE +21 -21
- package/README.md +1392 -1392
- package/dist/bigmap-plugin.js +124 -119
- package/dist/bigmap-plugin.umd.cjs +34 -34
- package/dist/index.d.ts +22 -11
- package/dist/libs/bigemap/bigemap.css +838 -1
- package/dist/libs/bigemap/bm.geometryutil.js +754 -1
- package/dist/libs/bigemap/bmgl-header.js +110 -2
- package/dist/libs/bigemap/circleWave.js +284 -34
- package/dist/libs/bigemap/moveMarker.js +74 -1
- package/dist/libs/bigemap/widgets.css +1 -1
- package/package.json +66 -66
- package/dist/libs/bigemap/bigemap-gl.js +0 -1
|
@@ -1 +1,754 @@
|
|
|
1
|
-
|
|
1
|
+
// Packaging/modules magic dance.
|
|
2
|
+
(function (factory) {
|
|
3
|
+
factory(window.BM)
|
|
4
|
+
}(function (BM) {
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
BM.Polyline._flat = BM.LineUtil.isFlat || BM.Polyline._flat || function (latlngs) {
|
|
8
|
+
// true if it's a flat array of latlngs; false if nested
|
|
9
|
+
return !BM.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @fileOverview Geometry utilities for distances and linear referencing.
|
|
14
|
+
* @name BM.GeometryUtil
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
BM.GeometryUtil = BM.extend(BM.GeometryUtil || {}, {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
Shortcut function for planar distance between two {BM.LatLng} at current zoom.
|
|
21
|
+
|
|
22
|
+
@tutorial distance-length
|
|
23
|
+
|
|
24
|
+
@param {BM.Map} map map to be used for this method
|
|
25
|
+
@param {BM.LatLng} latlngA geographical point A
|
|
26
|
+
@param {BM.LatLng} latlngB geographical point B
|
|
27
|
+
@returns {Number} planar distance
|
|
28
|
+
*/
|
|
29
|
+
distance: function (map, latlngA, latlngB) {
|
|
30
|
+
return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB));
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
Shortcut function for planar distance between a {BM.LatLng} and a segment (A-B).
|
|
35
|
+
@param {BM.Map} map map to be used for this method
|
|
36
|
+
@param {BM.LatLng} latlng - The position to search
|
|
37
|
+
@param {BM.LatLng} latlngA geographical point A of the segment
|
|
38
|
+
@param {BM.LatLng} latlngB geographical point B of the segment
|
|
39
|
+
@returns {Number} planar distance
|
|
40
|
+
*/
|
|
41
|
+
distanceSegment: function (map, latlng, latlngA, latlngB) {
|
|
42
|
+
var p = map.latLngToLayerPoint(latlng),
|
|
43
|
+
p1 = map.latLngToLayerPoint(latlngA),
|
|
44
|
+
p2 = map.latLngToLayerPoint(latlngB);
|
|
45
|
+
return BM.LineUtil.pointToSegmentDistance(p, p1, p2);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
Shortcut function for converting distance to readable distance.
|
|
50
|
+
@param {Number} distance distance to be converted
|
|
51
|
+
@param {String} unit 'metric' or 'imperial'
|
|
52
|
+
@returns {String} in yard or miles
|
|
53
|
+
*/
|
|
54
|
+
readableDistance: function (distance, unit) {
|
|
55
|
+
var isMetric = (unit !== 'imperial'),
|
|
56
|
+
distanceStr;
|
|
57
|
+
if (isMetric) {
|
|
58
|
+
// show metres when distance is < 1km, then show km
|
|
59
|
+
if (distance > 1000) {
|
|
60
|
+
distanceStr = (distance / 1000).toFixed(2) + ' km';
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
distanceStr = Math.ceil(distance) + ' m';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
distance *= 1.09361;
|
|
68
|
+
if (distance > 1760) {
|
|
69
|
+
distanceStr = (distance / 1760).toFixed(2) + ' miles';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
distanceStr = Math.ceil(distance) + ' yd';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return distanceStr;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
Returns true if the latlng belongs to segment A-B
|
|
80
|
+
@param {BM.LatLng} latlng - The position to search
|
|
81
|
+
@param {BM.LatLng} latlngA geographical point A of the segment
|
|
82
|
+
@param {BM.LatLng} latlngB geographical point B of the segment
|
|
83
|
+
@param {?Number} [tolerance=0.2] tolerance to accept if latlng belongs really
|
|
84
|
+
@returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
belongsSegment: function(latlng, latlngA, latlngB, tolerance) {
|
|
87
|
+
tolerance = tolerance === undefined ? 0.2 : tolerance;
|
|
88
|
+
var hypotenuse = latlngA.distanceTo(latlngB),
|
|
89
|
+
delta = latlngA.distanceTo(latlng) + latlng.distanceTo(latlngB) - hypotenuse;
|
|
90
|
+
return delta/hypotenuse < tolerance;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Returns total length of line
|
|
95
|
+
* @tutorial distance-length
|
|
96
|
+
*
|
|
97
|
+
* @param {BM.Polyline|Array<BM.Point>|Array<BM.LatLng>} coords Set of coordinates
|
|
98
|
+
* @returns {Number} Total length (pixels for Point, meters for LatLng)
|
|
99
|
+
*/
|
|
100
|
+
length: function (coords) {
|
|
101
|
+
var accumulated = BM.GeometryUtil.accumulatedLengths(coords);
|
|
102
|
+
return accumulated.length > 0 ? accumulated[accumulated.length-1] : 0;
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns a list of accumulated length along a line.
|
|
107
|
+
* @param {BM.Polyline|Array<BM.Point>|Array<BM.LatLng>} coords Set of coordinates
|
|
108
|
+
* @returns {Array<Number>} Array of accumulated lengths (pixels for Point, meters for LatLng)
|
|
109
|
+
*/
|
|
110
|
+
accumulatedLengths: function (coords) {
|
|
111
|
+
if (typeof coords.getLatLngs == 'function') {
|
|
112
|
+
coords = coords.getLatLngs();
|
|
113
|
+
}
|
|
114
|
+
if (coords.length === 0)
|
|
115
|
+
return [];
|
|
116
|
+
var total = 0,
|
|
117
|
+
lengths = [0];
|
|
118
|
+
for (var i = 0, n = coords.length - 1; i< n; i++) {
|
|
119
|
+
total += coords[i].distanceTo(coords[i+1]);
|
|
120
|
+
lengths.push(total);
|
|
121
|
+
}
|
|
122
|
+
return lengths;
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
Returns the closest point of a {BM.LatLng} on the segment (A-B)
|
|
127
|
+
|
|
128
|
+
@tutorial closest
|
|
129
|
+
|
|
130
|
+
@param {BM.Map} map map to be used for this method
|
|
131
|
+
@param {BM.LatLng} latlng - The position to search
|
|
132
|
+
@param {BM.LatLng} latlngA geographical point A of the segment
|
|
133
|
+
@param {BM.LatLng} latlngB geographical point B of the segment
|
|
134
|
+
@returns {BM.LatLng} Closest geographical point
|
|
135
|
+
*/
|
|
136
|
+
closestOnSegment: function (map, latlng, latlngA, latlngB) {
|
|
137
|
+
var maxzoom = map.getMaxZoom();
|
|
138
|
+
if (maxzoom === Infinity)
|
|
139
|
+
maxzoom = map.getZoom();
|
|
140
|
+
var p = map.project(latlng, maxzoom),
|
|
141
|
+
p1 = map.project(latlngA, maxzoom),
|
|
142
|
+
p2 = map.project(latlngB, maxzoom),
|
|
143
|
+
closest = BM.LineUtil.closestPointOnSegment(p, p1, p2);
|
|
144
|
+
return map.unproject(closest, maxzoom);
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
Returns the closest latlng on layer.
|
|
149
|
+
|
|
150
|
+
Accept nested arrays
|
|
151
|
+
|
|
152
|
+
@tutorial closest
|
|
153
|
+
|
|
154
|
+
@param {BM.Map} map map to be used for this method
|
|
155
|
+
@param {Array<BM.LatLng>|Array<Array<BM.LatLng>>|BM.PolyLine|BM.Polygon} layer - Layer that contains the result
|
|
156
|
+
@param {BM.LatLng} latlng - The position to search
|
|
157
|
+
@param {?boolean} [vertices=false] - Whether to restrict to path vertices.
|
|
158
|
+
@returns {BM.LatLng} Closest geographical point or null if layer param is incorrect
|
|
159
|
+
*/
|
|
160
|
+
closest: function (map, layer, latlng, vertices) {
|
|
161
|
+
|
|
162
|
+
var latlngs,
|
|
163
|
+
mindist = Infinity,
|
|
164
|
+
result = null,
|
|
165
|
+
i, n, distance, subResult;
|
|
166
|
+
|
|
167
|
+
if (layer instanceof Array) {
|
|
168
|
+
// if layer is Array<Array<T>>
|
|
169
|
+
if (layer[0] instanceof Array && typeof layer[0][0] !== 'number') {
|
|
170
|
+
// if we have nested arrays, we calc the closest for each array
|
|
171
|
+
// recursive
|
|
172
|
+
for (i = 0; i < layer.length; i++) {
|
|
173
|
+
subResult = BM.GeometryUtil.closest(map, layer[i], latlng, vertices);
|
|
174
|
+
if (subResult.distance < mindist) {
|
|
175
|
+
mindist = subResult.distance;
|
|
176
|
+
result = subResult;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
} else if (layer[0] instanceof BM.LatLng
|
|
181
|
+
|| typeof layer[0][0] === 'number'
|
|
182
|
+
|| typeof layer[0].lat === 'number') { // we could have a latlng as [x,y] with x & y numbers or {lat, lng}
|
|
183
|
+
layer = BM.polyline(layer);
|
|
184
|
+
} else {
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// if we don't have here a Polyline, that means layer is incorrect
|
|
190
|
+
// see https://github.com/makinacorpus/.GeometryUtil/issues/23
|
|
191
|
+
if (! ( layer instanceof BM.Polyline ) )
|
|
192
|
+
return result;
|
|
193
|
+
|
|
194
|
+
// deep copy of latlngs
|
|
195
|
+
latlngs = JSON.parse(JSON.stringify(layer.getLatLngs().slice(0)));
|
|
196
|
+
|
|
197
|
+
// add the last segment for BM.Polygon
|
|
198
|
+
if (layer instanceof BM.Polygon) {
|
|
199
|
+
// add the last segment for each child that is a nested array
|
|
200
|
+
var addLastSegment = function(latlngs) {
|
|
201
|
+
if (BM.Polyline._flat(latlngs)) {
|
|
202
|
+
latlngs.push(latlngs[0]);
|
|
203
|
+
} else {
|
|
204
|
+
for (var i = 0; i < latlngs.length; i++) {
|
|
205
|
+
addLastSegment(latlngs[i]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
addLastSegment(latlngs);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// we have a multi polygon / multi polyline / polygon with holes
|
|
213
|
+
// use recursive to explore and return the good result
|
|
214
|
+
if ( ! BM.Polyline._flat(latlngs) ) {
|
|
215
|
+
for (i = 0; i < latlngs.length; i++) {
|
|
216
|
+
// if we are at the lower level, and if we have a BM.Polygon, we add the last segment
|
|
217
|
+
subResult = BM.GeometryUtil.closest(map, latlngs[i], latlng, vertices);
|
|
218
|
+
if (subResult.distance < mindist) {
|
|
219
|
+
mindist = subResult.distance;
|
|
220
|
+
result = subResult;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
|
|
225
|
+
} else {
|
|
226
|
+
|
|
227
|
+
// Lookup vertices
|
|
228
|
+
if (vertices) {
|
|
229
|
+
for(i = 0, n = latlngs.length; i < n; i++) {
|
|
230
|
+
var ll = latlngs[i];
|
|
231
|
+
distance = BM.GeometryUtil.distance(map, latlng, ll);
|
|
232
|
+
if (distance < mindist) {
|
|
233
|
+
mindist = distance;
|
|
234
|
+
result = ll;
|
|
235
|
+
result.distance = distance;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Keep the closest point of all segments
|
|
242
|
+
for (i = 0, n = latlngs.length; i < n-1; i++) {
|
|
243
|
+
var latlngA = latlngs[i],
|
|
244
|
+
latlngB = latlngs[i+1];
|
|
245
|
+
distance = BM.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB);
|
|
246
|
+
if (distance <= mindist) {
|
|
247
|
+
mindist = distance;
|
|
248
|
+
result = BM.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
|
|
249
|
+
result.distance = distance;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
Returns the closest layer to latlng among a list of layers.
|
|
259
|
+
|
|
260
|
+
@tutorial closest
|
|
261
|
+
|
|
262
|
+
@param {BM.Map} map map to be used for this method
|
|
263
|
+
@param {Array<BM.ILayer>} layers Set of layers
|
|
264
|
+
@param {BM.LatLng} latlng - The position to search
|
|
265
|
+
@returns {object} ``{layer, latlng, distance}`` or ``null`` if list is empty;
|
|
266
|
+
*/
|
|
267
|
+
closestLayer: function (map, layers, latlng) {
|
|
268
|
+
var mindist = Infinity,
|
|
269
|
+
result = null,
|
|
270
|
+
ll = null,
|
|
271
|
+
distance = Infinity;
|
|
272
|
+
|
|
273
|
+
for (var i = 0, n = layers.length; i < n; i++) {
|
|
274
|
+
var layer = layers[i];
|
|
275
|
+
if (layer instanceof BM.LayerGroup) {
|
|
276
|
+
// recursive
|
|
277
|
+
var subResult = BM.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
|
|
278
|
+
if (subResult.distance < mindist) {
|
|
279
|
+
mindist = subResult.distance;
|
|
280
|
+
result = subResult;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
// Single dimension, snap on points, else snap on closest
|
|
284
|
+
if (typeof layer.getLatLng == 'function') {
|
|
285
|
+
ll = layer.getLatLng();
|
|
286
|
+
distance = BM.GeometryUtil.distance(map, latlng, ll);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
ll = BM.GeometryUtil.closest(map, layer, latlng);
|
|
290
|
+
if (ll) distance = ll.distance; // Can return null if layer has no points.
|
|
291
|
+
}
|
|
292
|
+
if (distance < mindist) {
|
|
293
|
+
mindist = distance;
|
|
294
|
+
result = {layer: layer, latlng: ll, distance: distance};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
Returns the n closest layers to latlng among a list of input layers.
|
|
303
|
+
|
|
304
|
+
@param {BM.Map} map - map to be used for this method
|
|
305
|
+
@param {Array<BM.ILayer>} layers - Set of layers
|
|
306
|
+
@param {BM.LatLng} latlng - The position to search
|
|
307
|
+
@param {?Number} [n=layers.length] - the expected number of output layers.
|
|
308
|
+
@returns {Array<object>} an array of objects ``{layer, latlng, distance}`` or ``null`` if the input is invalid (empty list or negative n)
|
|
309
|
+
*/
|
|
310
|
+
nClosestLayers: function (map, layers, latlng, n) {
|
|
311
|
+
n = typeof n === 'number' ? n : layers.length;
|
|
312
|
+
|
|
313
|
+
if (n < 1 || layers.length < 1) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
var results = [];
|
|
318
|
+
var distance, ll;
|
|
319
|
+
|
|
320
|
+
for (var i = 0, m = layers.length; i < m; i++) {
|
|
321
|
+
var layer = layers[i];
|
|
322
|
+
if (layer instanceof BM.LayerGroup) {
|
|
323
|
+
// recursive
|
|
324
|
+
var subResult = BM.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
|
|
325
|
+
results.push(subResult);
|
|
326
|
+
} else {
|
|
327
|
+
// Single dimension, snap on points, else snap on closest
|
|
328
|
+
if (typeof layer.getLatLng == 'function') {
|
|
329
|
+
ll = layer.getLatLng();
|
|
330
|
+
distance = BM.GeometryUtil.distance(map, latlng, ll);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
ll = BM.GeometryUtil.closest(map, layer, latlng);
|
|
334
|
+
if (ll) distance = ll.distance; // Can return null if layer has no points.
|
|
335
|
+
}
|
|
336
|
+
results.push({layer: layer, latlng: ll, distance: distance});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
results.sort(function(a, b) {
|
|
341
|
+
return a.distance - b.distance;
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
if (results.length > n) {
|
|
345
|
+
return results.slice(0, n);
|
|
346
|
+
} else {
|
|
347
|
+
return results;
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Returns all layers within a radius of the given position, in an ascending order of distance.
|
|
353
|
+
@param {BM.Map} map map to be used for this method
|
|
354
|
+
@param {Array<ILayer>} layers - A list of layers.
|
|
355
|
+
@param {BM.LatLng} latlng - The position to search
|
|
356
|
+
@param {?Number} [radius=Infinity] - Search radius in pixels
|
|
357
|
+
@return {object[]} an array of objects including layer within the radius, closest latlng, and distance
|
|
358
|
+
*/
|
|
359
|
+
layersWithin: function(map, layers, latlng, radius) {
|
|
360
|
+
radius = typeof radius == 'number' ? radius : Infinity;
|
|
361
|
+
|
|
362
|
+
var results = [];
|
|
363
|
+
var ll = null;
|
|
364
|
+
var distance = 0;
|
|
365
|
+
|
|
366
|
+
for (var i = 0, n = layers.length; i < n; i++) {
|
|
367
|
+
var layer = layers[i];
|
|
368
|
+
|
|
369
|
+
if (typeof layer.getLatLng == 'function') {
|
|
370
|
+
ll = layer.getLatLng();
|
|
371
|
+
distance = BM.GeometryUtil.distance(map, latlng, ll);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
ll = BM.GeometryUtil.closest(map, layer, latlng);
|
|
375
|
+
if (ll) distance = ll.distance; // Can return null if layer has no points.
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (ll && distance < radius) {
|
|
379
|
+
results.push({layer: layer, latlng: ll, distance: distance});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
var sortedResults = results.sort(function(a, b) {
|
|
384
|
+
return a.distance - b.distance;
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return sortedResults;
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
Returns the closest position from specified {LatLng} among specified layers,
|
|
392
|
+
with a maximum tolerance in pixels, providing snapping behaviour.
|
|
393
|
+
|
|
394
|
+
@tutorial closest
|
|
395
|
+
|
|
396
|
+
@param {BM.Map} map map to be used for this method
|
|
397
|
+
@param {Array<ILayer>} layers - A list of layers to snap on.
|
|
398
|
+
@param {BM.LatLng} latlng - The position to snap
|
|
399
|
+
@param {?Number} [tolerance=Infinity] - Maximum number of pixels.
|
|
400
|
+
@param {?boolean} [withVertices=true] - Snap to layers vertices or segment points (not only vertex)
|
|
401
|
+
@returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded.
|
|
402
|
+
*/
|
|
403
|
+
closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
|
|
404
|
+
tolerance = typeof tolerance == 'number' ? tolerance : Infinity;
|
|
405
|
+
withVertices = typeof withVertices == 'boolean' ? withVertices : true;
|
|
406
|
+
|
|
407
|
+
var result = BM.GeometryUtil.closestLayer(map, layers, latlng);
|
|
408
|
+
if (!result || result.distance > tolerance)
|
|
409
|
+
return null;
|
|
410
|
+
|
|
411
|
+
// If snapped layer is linear, try to snap on vertices (extremities and middle points)
|
|
412
|
+
if (withVertices && typeof result.layer.getLatLngs == 'function') {
|
|
413
|
+
var closest = BM.GeometryUtil.closest(map, result.layer, result.latlng, true);
|
|
414
|
+
if (closest.distance < tolerance) {
|
|
415
|
+
result.latlng = closest;
|
|
416
|
+
result.distance = BM.GeometryUtil.distance(map, closest, latlng);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return result;
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
Returns the Point located on a segment at the specified ratio of the segment length.
|
|
424
|
+
@param {BM.Point} pA coordinates of point A
|
|
425
|
+
@param {BM.Point} pB coordinates of point B
|
|
426
|
+
@param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive.
|
|
427
|
+
@returns {BM.Point} the interpolated point.
|
|
428
|
+
*/
|
|
429
|
+
interpolateOnPointSegment: function (pA, pB, ratio) {
|
|
430
|
+
return BM.point(
|
|
431
|
+
(pA.x * (1 - ratio)) + (ratio * pB.x),
|
|
432
|
+
(pA.y * (1 - ratio)) + (ratio * pB.y)
|
|
433
|
+
);
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
Returns the coordinate of the point located on a line at the specified ratio of the line length.
|
|
438
|
+
@param {BM.Map} map map to be used for this method
|
|
439
|
+
@param {Array<BM.LatLng>|BM.PolyLine} latlngs Set of geographical points
|
|
440
|
+
@param {Number} ratio the length ratio, expressed as a decimal between 0 and 1, inclusive
|
|
441
|
+
@returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline
|
|
442
|
+
(-1 if the interpolated point is the first vertex)
|
|
443
|
+
*/
|
|
444
|
+
interpolateOnLine: function (map, latLngs, ratio) {
|
|
445
|
+
latLngs = (latLngs instanceof BM.Polyline) ? latLngs.getLatLngs() : latLngs;
|
|
446
|
+
var n = latLngs.length;
|
|
447
|
+
if (n < 2) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ensure the ratio is between 0 and 1;
|
|
452
|
+
ratio = Math.max(Math.min(ratio, 1), 0);
|
|
453
|
+
|
|
454
|
+
if (ratio === 0) {
|
|
455
|
+
return {
|
|
456
|
+
latLng: latLngs[0] instanceof BM.LatLng ? latLngs[0] : BM.latLng(latLngs[0]),
|
|
457
|
+
predecessor: -1
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (ratio == 1) {
|
|
461
|
+
return {
|
|
462
|
+
latLng: latLngs[latLngs.length -1] instanceof BM.LatLng ? latLngs[latLngs.length -1] : BM.latLng(latLngs[latLngs.length -1]),
|
|
463
|
+
predecessor: latLngs.length - 2
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// project the LatLngs as Points,
|
|
468
|
+
// and compute total planar length of the line at max precision
|
|
469
|
+
var maxzoom = map.getMaxZoom();
|
|
470
|
+
if (maxzoom === Infinity)
|
|
471
|
+
maxzoom = map.getZoom();
|
|
472
|
+
var pts = [];
|
|
473
|
+
var lineLength = 0;
|
|
474
|
+
for(var i = 0; i < n; i++) {
|
|
475
|
+
pts[i] = map.project(latLngs[i], maxzoom);
|
|
476
|
+
if(i > 0)
|
|
477
|
+
lineLength += pts[i-1].distanceTo(pts[i]);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
var ratioDist = lineLength * ratio;
|
|
481
|
+
|
|
482
|
+
// follow the line segments [ab], adding lengths,
|
|
483
|
+
// until we find the segment where the points should lie on
|
|
484
|
+
var cumulativeDistanceToA = 0, cumulativeDistanceToB = 0;
|
|
485
|
+
for (var i = 0; cumulativeDistanceToB < ratioDist; i++) {
|
|
486
|
+
var pointA = pts[i], pointB = pts[i+1];
|
|
487
|
+
|
|
488
|
+
cumulativeDistanceToA = cumulativeDistanceToB;
|
|
489
|
+
cumulativeDistanceToB += pointA.distanceTo(pointB);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (pointA == undefined && pointB == undefined) { // Happens when line has no length
|
|
493
|
+
var pointA = pts[0], pointB = pts[1], i = 1;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// compute the ratio relative to the segment [ab]
|
|
497
|
+
var segmentRatio = ((cumulativeDistanceToB - cumulativeDistanceToA) !== 0) ? ((ratioDist - cumulativeDistanceToA) / (cumulativeDistanceToB - cumulativeDistanceToA)) : 0;
|
|
498
|
+
var interpolatedPoint = BM.GeometryUtil.interpolateOnPointSegment(pointA, pointB, segmentRatio);
|
|
499
|
+
return {
|
|
500
|
+
latLng: map.unproject(interpolatedPoint, maxzoom),
|
|
501
|
+
predecessor: i-1
|
|
502
|
+
};
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
Returns a float between 0 and 1 representing the location of the
|
|
507
|
+
closest point on polyline to the given latlng, as a fraction of total line length.
|
|
508
|
+
(opposite of BM.GeometryUtil.interpolateOnLine())
|
|
509
|
+
@param {BM.Map} map map to be used for this method
|
|
510
|
+
@param {BM.PolyLine} polyline Polyline on which the latlng will be search
|
|
511
|
+
@param {BM.LatLng} latlng The position to search
|
|
512
|
+
@returns {Number} Float between 0 and 1
|
|
513
|
+
*/
|
|
514
|
+
locateOnLine: function (map, polyline, latlng) {
|
|
515
|
+
var latlngs = polyline.getLatLngs();
|
|
516
|
+
if (latlng.equals(latlngs[0]))
|
|
517
|
+
return 0.0;
|
|
518
|
+
if (latlng.equals(latlngs[latlngs.length-1]))
|
|
519
|
+
return 1.0;
|
|
520
|
+
|
|
521
|
+
var point = BM.GeometryUtil.closest(map, polyline, latlng, false),
|
|
522
|
+
lengths = BM.GeometryUtil.accumulatedLengths(latlngs),
|
|
523
|
+
total_length = lengths[lengths.length-1],
|
|
524
|
+
portion = 0,
|
|
525
|
+
found = false;
|
|
526
|
+
for (var i=0, n = latlngs.length-1; i < n; i++) {
|
|
527
|
+
var l1 = latlngs[i],
|
|
528
|
+
l2 = latlngs[i+1];
|
|
529
|
+
portion = lengths[i];
|
|
530
|
+
if (BM.GeometryUtil.belongsSegment(point, l1, l2, 0.0001)) {
|
|
531
|
+
portion += l1.distanceTo(point);
|
|
532
|
+
found = true;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (!found) {
|
|
537
|
+
throw "Could not interpolate " + latlng.toString() + " within " + polyline.toString();
|
|
538
|
+
}
|
|
539
|
+
return portion / total_length;
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
Returns a clone with reversed coordinates.
|
|
544
|
+
@param {BM.PolyLine} polyline polyline to reverse
|
|
545
|
+
@returns {BM.PolyLine} polyline reversed
|
|
546
|
+
*/
|
|
547
|
+
reverse: function (polyline) {
|
|
548
|
+
return BM.polyline(polyline.getLatLngs().slice(0).reverse());
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
Returns a sub-part of the polyline, from start to end.
|
|
553
|
+
If start is superior to end, returns extraction from inverted line.
|
|
554
|
+
@param {BM.Map} map map to be used for this method
|
|
555
|
+
@param {BM.PolyLine} polyline Polyline on which will be extracted the sub-part
|
|
556
|
+
@param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive
|
|
557
|
+
@param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive
|
|
558
|
+
@returns {Array<BM.LatLng>} new polyline
|
|
559
|
+
*/
|
|
560
|
+
extract: function (map, polyline, start, end) {
|
|
561
|
+
if (start > end) {
|
|
562
|
+
return BM.GeometryUtil.extract(map, BM.GeometryUtil.reverse(polyline), 1.0-start, 1.0-end);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Bound start and end to [0-1]
|
|
566
|
+
start = Math.max(Math.min(start, 1), 0);
|
|
567
|
+
end = Math.max(Math.min(end, 1), 0);
|
|
568
|
+
|
|
569
|
+
var latlngs = polyline.getLatLngs(),
|
|
570
|
+
startpoint = BM.GeometryUtil.interpolateOnLine(map, polyline, start),
|
|
571
|
+
endpoint = BM.GeometryUtil.interpolateOnLine(map, polyline, end);
|
|
572
|
+
// Return single point if start == end
|
|
573
|
+
if (start == end) {
|
|
574
|
+
var point = BM.GeometryUtil.interpolateOnLine(map, polyline, end);
|
|
575
|
+
return [point.latLng];
|
|
576
|
+
}
|
|
577
|
+
// Array.slice() works indexes at 0
|
|
578
|
+
if (startpoint.predecessor == -1)
|
|
579
|
+
startpoint.predecessor = 0;
|
|
580
|
+
if (endpoint.predecessor == -1)
|
|
581
|
+
endpoint.predecessor = 0;
|
|
582
|
+
var result = latlngs.slice(startpoint.predecessor+1, endpoint.predecessor+1);
|
|
583
|
+
result.unshift(startpoint.latLng);
|
|
584
|
+
result.push(endpoint.latLng);
|
|
585
|
+
return result;
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
Returns true if first polyline ends where other second starts.
|
|
590
|
+
@param {BM.PolyLine} polyline First polyline
|
|
591
|
+
@param {BM.PolyLine} other Second polyline
|
|
592
|
+
@returns {bool}
|
|
593
|
+
*/
|
|
594
|
+
isBefore: function (polyline, other) {
|
|
595
|
+
if (!other) return false;
|
|
596
|
+
var lla = polyline.getLatLngs(),
|
|
597
|
+
llb = other.getLatLngs();
|
|
598
|
+
return (lla[lla.length-1]).equals(llb[0]);
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
Returns true if first polyline starts where second ends.
|
|
603
|
+
@param {BM.PolyLine} polyline First polyline
|
|
604
|
+
@param {BM.PolyLine} other Second polyline
|
|
605
|
+
@returns {bool}
|
|
606
|
+
*/
|
|
607
|
+
isAfter: function (polyline, other) {
|
|
608
|
+
if (!other) return false;
|
|
609
|
+
var lla = polyline.getLatLngs(),
|
|
610
|
+
llb = other.getLatLngs();
|
|
611
|
+
return (lla[0]).equals(llb[llb.length-1]);
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
Returns true if first polyline starts where second ends or start.
|
|
616
|
+
@param {BM.PolyLine} polyline First polyline
|
|
617
|
+
@param {BM.PolyLine} other Second polyline
|
|
618
|
+
@returns {bool}
|
|
619
|
+
*/
|
|
620
|
+
startsAtExtremity: function (polyline, other) {
|
|
621
|
+
if (!other) return false;
|
|
622
|
+
var lla = polyline.getLatLngs(),
|
|
623
|
+
llb = other.getLatLngs(),
|
|
624
|
+
start = lla[0];
|
|
625
|
+
return start.equals(llb[0]) || start.equals(llb[llb.length-1]);
|
|
626
|
+
},
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
Returns horizontal angle in degres between two points.
|
|
630
|
+
@param {BM.Point} a Coordinates of point A
|
|
631
|
+
@param {BM.Point} b Coordinates of point B
|
|
632
|
+
@returns {Number} horizontal angle
|
|
633
|
+
*/
|
|
634
|
+
computeAngle: function(a, b) {
|
|
635
|
+
return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI);
|
|
636
|
+
},
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
Returns slope (Ax+B) between two points.
|
|
640
|
+
@param {BM.Point} a Coordinates of point A
|
|
641
|
+
@param {BM.Point} b Coordinates of point B
|
|
642
|
+
@returns {Object} with ``a`` and ``b`` properties.
|
|
643
|
+
*/
|
|
644
|
+
computeSlope: function(a, b) {
|
|
645
|
+
var s = (b.y - a.y) / (b.x - a.x),
|
|
646
|
+
o = a.y - (s * a.x);
|
|
647
|
+
return {'a': s, 'b': o};
|
|
648
|
+
},
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
Returns LatLng of rotated point around specified LatLng center.
|
|
652
|
+
@param {BM.LatLng} latlngPoint: point to rotate
|
|
653
|
+
@param {double} angleDeg: angle to rotate in degrees
|
|
654
|
+
@param {BM.LatLng} latlngCenter: center of rotation
|
|
655
|
+
@returns {BM.LatLng} rotated point
|
|
656
|
+
*/
|
|
657
|
+
rotatePoint: function(map, latlngPoint, angleDeg, latlngCenter) {
|
|
658
|
+
var maxzoom = map.getMaxZoom();
|
|
659
|
+
if (maxzoom === Infinity)
|
|
660
|
+
maxzoom = map.getZoom();
|
|
661
|
+
var angleRad = angleDeg*Math.PI/180,
|
|
662
|
+
pPoint = map.project(latlngPoint, maxzoom),
|
|
663
|
+
pCenter = map.project(latlngCenter, maxzoom),
|
|
664
|
+
x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x,
|
|
665
|
+
y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y;
|
|
666
|
+
return map.unproject(new BM.Point(x2,y2), maxzoom);
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
Returns the bearing in degrees clockwise from north (0 degrees)
|
|
671
|
+
from the first BM.LatLng to the second, at the first LatLng
|
|
672
|
+
@param {BM.LatLng} latlng1: origin point of the bearing
|
|
673
|
+
@param {BM.LatLng} latlng2: destination point of the bearing
|
|
674
|
+
@returns {float} degrees clockwise from north.
|
|
675
|
+
*/
|
|
676
|
+
bearing: function(latlng1, latlng2) {
|
|
677
|
+
var rad = Math.PI / 180,
|
|
678
|
+
lat1 = latlng1.lat * rad,
|
|
679
|
+
lat2 = latlng2.lat * rad,
|
|
680
|
+
lon1 = latlng1.lng * rad,
|
|
681
|
+
lon2 = latlng2.lng * rad,
|
|
682
|
+
y = Math.sin(lon2 - lon1) * Math.cos(lat2),
|
|
683
|
+
x = Math.cos(lat1) * Math.sin(lat2) -
|
|
684
|
+
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
|
|
685
|
+
|
|
686
|
+
var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
|
|
687
|
+
return bearing >= 180 ? bearing-360 : bearing;
|
|
688
|
+
},
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
Returns the point that is a distance and heading away from
|
|
692
|
+
the given origin point.
|
|
693
|
+
@param {BM.LatLng} latlng: origin point
|
|
694
|
+
@param {float} heading: heading in degrees, clockwise from 0 degrees north.
|
|
695
|
+
@param {float} distance: distance in meters
|
|
696
|
+
@returns {BM.latLng} the destination point.
|
|
697
|
+
Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
|
|
698
|
+
for a great reference and examples.
|
|
699
|
+
*/
|
|
700
|
+
destination: function(latlng, heading, distance) {
|
|
701
|
+
heading = (heading + 360) % 360;
|
|
702
|
+
var rad = Math.PI / 180,
|
|
703
|
+
radInv = 180 / Math.PI,
|
|
704
|
+
R = 6378137, // approximation of Earth's radius
|
|
705
|
+
lon1 = latlng.lng * rad,
|
|
706
|
+
lat1 = latlng.lat * rad,
|
|
707
|
+
rheading = heading * rad,
|
|
708
|
+
sinLat1 = Math.sin(lat1),
|
|
709
|
+
cosLat1 = Math.cos(lat1),
|
|
710
|
+
cosDistR = Math.cos(distance / R),
|
|
711
|
+
sinDistR = Math.sin(distance / R),
|
|
712
|
+
lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 *
|
|
713
|
+
sinDistR * Math.cos(rheading)),
|
|
714
|
+
lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR *
|
|
715
|
+
cosLat1, cosDistR - sinLat1 * Math.sin(lat2));
|
|
716
|
+
lon2 = lon2 * radInv;
|
|
717
|
+
lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2;
|
|
718
|
+
return BM.latLng([lat2 * radInv, lon2]);
|
|
719
|
+
},
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
Returns the the angle of the given segment and the Equator in degrees,
|
|
723
|
+
clockwise from 0 degrees north.
|
|
724
|
+
@param {BM.Map} map: map to be used for this method
|
|
725
|
+
@param {BM.LatLng} latlngA: geographical point A of the segment
|
|
726
|
+
@param {BM.LatLng} latlngB: geographical point B of the segment
|
|
727
|
+
@returns {Float} the angle in degrees.
|
|
728
|
+
*/
|
|
729
|
+
angle: function(map, latlngA, latlngB) {
|
|
730
|
+
var pointA = map.latLngToContainerPoint(latlngA),
|
|
731
|
+
pointB = map.latLngToContainerPoint(latlngB),
|
|
732
|
+
angleDeg = Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI + 90;
|
|
733
|
+
angleDeg += angleDeg < 0 ? 360 : 0;
|
|
734
|
+
return angleDeg;
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
Returns a point snaps on the segment and heading away from the given origin point a distance.
|
|
739
|
+
@param {BM.Map} map: map to be used for this method
|
|
740
|
+
@param {BM.LatLng} latlngA: geographical point A of the segment
|
|
741
|
+
@param {BM.LatLng} latlngB: geographical point B of the segment
|
|
742
|
+
@param {float} distance: distance in meters
|
|
743
|
+
@returns {BM.latLng} the destination point.
|
|
744
|
+
*/
|
|
745
|
+
destinationOnSegment: function(map, latlngA, latlngB, distance) {
|
|
746
|
+
var angleDeg = BM.GeometryUtil.angle(map, latlngA, latlngB),
|
|
747
|
+
latlng = BM.GeometryUtil.destination(latlngA, angleDeg, distance);
|
|
748
|
+
return BM.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
return BM.GeometryUtil;
|
|
753
|
+
|
|
754
|
+
}));
|