123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- var L = L || exports;
- (function () {
- "use strict";
- /**
- * @fileOverview Leaflet Geometry utilities for distances and linear referencing.
- * @name L.GeometryUtil
- */
- L.GeometryUtil = {
- /**
- Shortcut function for planar distance between two {L.LatLng} at current zoom.
- @param {L.Map} map
- @param {L.LatLng} latlngA
- @param {L.LatLng} latlngB
- @returns {Number} in pixels
- */
- distance: function (map, latlngA, latlngB) {
- return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB));
- },
- /**
- Shortcut function for planar distance between a {L.LatLng} and a segment (A-B).
- @param {L.Map} map
- @param {L.LatLng} latlng
- @param {L.LatLng} latlngA
- @param {L.LatLng} latlngB
- @returns {Number} in pixels
- */
- distanceSegment: function (map, latlng, latlngA, latlngB) {
- var p = map.latLngToLayerPoint(latlng),
- p1 = map.latLngToLayerPoint(latlngA),
- p2 = map.latLngToLayerPoint(latlngB);
- return L.LineUtil.pointToSegmentDistance(p, p1, p2);
- },
- /**
- Returns true if the latlng belongs to segment.
- param {L.LatLng} latlng
- @param {L.LatLng} latlngA
- @param {L.LatLng} latlngB
- @param {?Number} [tolerance=0.2]
- @returns {boolean}
- */
- belongsSegment: function(latlng, latlngA, latlngB, tolerance) {
- tolerance = tolerance === undefined ? 0.2 : tolerance;
- var hypotenuse = latlngA.distanceTo(latlngB),
- delta = latlngA.distanceTo(latlng) + latlng.distanceTo(latlngB) - hypotenuse;
- return delta/hypotenuse < tolerance;
- },
- /**
- * Returns total length of line
- * @param {L.Polyline|Array<L.Point>|Array<L.LatLng>}
- * @returns {Number} in meters
- */
- length: function (coords) {
- var accumulated = L.GeometryUtil.accumulatedLengths(coords);
- return accumulated.length > 0 ? accumulated[accumulated.length-1] : 0;
- },
- /**
- * Returns a list of accumulated length along a line.
- * @param {L.Polyline|Array<L.Point>|Array<L.LatLng>}
- * @returns {Number} in meters
- */
- accumulatedLengths: function (coords) {
- if (typeof coords.getLatLngs == 'function') {
- coords = coords.getLatLngs();
- }
- if (coords.length === 0)
- return [];
- var total = 0,
- lengths = [0];
- for (var i = 0, n = coords.length - 1; i< n; i++) {
- total += coords[i].distanceTo(coords[i+1]);
- lengths.push(total);
- }
- return lengths;
- },
- /**
- Returns the closest point of a {L.LatLng} on the segment (A-B)
- @param {L.Map} map
- @param {L.LatLng} latlng
- @param {L.LatLng} latlngA
- @param {L.LatLng} latlngB
- @returns {L.LatLng}
- */
- closestOnSegment: function (map, latlng, latlngA, latlngB) {
- var maxzoom = map.getMaxZoom();
- if (maxzoom === Infinity)
- maxzoom = map.getZoom();
- var p = map.project(latlng, maxzoom),
- p1 = map.project(latlngA, maxzoom),
- p2 = map.project(latlngB, maxzoom),
- closest = L.LineUtil.closestPointOnSegment(p, p1, p2);
- return map.unproject(closest, maxzoom);
- },
- /**
- Returns the closest latlng on layer.
- @param {L.Map} map
- @param {Array<L.LatLng>|L.PolyLine} layer - Layer that contains the result.
- @param {L.LatLng} latlng
- @param {?boolean} [vertices=false] - Whether to restrict to path vertices.
- @returns {L.LatLng}
- */
- closest: function (map, layer, latlng, vertices) {
- if (typeof layer.getLatLngs != 'function')
- layer = L.polyline(layer);
- var latlngs = layer.getLatLngs(),
- mindist = Infinity,
- result = null,
- i, n, distance;
- // Lookup vertices
- if (vertices) {
- for(i = 0, n = latlngs.length; i < n; i++) {
- var ll = latlngs[i];
- distance = L.GeometryUtil.distance(map, latlng, ll);
- if (distance < mindist) {
- mindist = distance;
- result = ll;
- result.distance = distance;
- }
- }
- return result;
- }
- if (layer instanceof L.Polygon) {
- latlngs.push(latlngs[0]);
- }
- // Keep the closest point of all segments
- for (i = 0, n = latlngs.length; i < n-1; i++) {
- var latlngA = latlngs[i],
- latlngB = latlngs[i+1];
- distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB);
- if (distance <= mindist) {
- mindist = distance;
- result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
- result.distance = distance;
- }
- }
- return result;
- },
- /**
- Returns the closest layer to latlng among a list of layers.
- @param {L.Map} map
- @param {Array<L.ILayer>} layers
- @param {L.LatLng} latlng
- @returns {object} with layer, latlng and distance or {null} if list is empty;
- */
- closestLayer: function (map, layers, latlng) {
- var mindist = Infinity,
- result = null,
- ll = null,
- distance = Infinity;
- for (var i = 0, n = layers.length; i < n; i++) {
- var layer = layers[i];
- // Single dimension, snap on points, else snap on closest
- if (typeof layer.getLatLng == 'function') {
- ll = layer.getLatLng();
- distance = L.GeometryUtil.distance(map, latlng, ll);
- }
- else {
- ll = L.GeometryUtil.closest(map, layer, latlng);
- if (ll) distance = ll.distance; // Can return null if layer has no points.
- }
- if (distance < mindist) {
- mindist = distance;
- result = {layer: layer, latlng: ll, distance: distance};
- }
- }
- return result;
- },
- /**
- Returns the closest position from specified {LatLng} among specified layers,
- with a maximum tolerance in pixels, providing snapping behaviour.
- @param {L.Map} map
- @param {Array<ILayer>} layers - A list of layers to snap on.
- @param {L.LatLng} latlng - The position to snap.
- @param {?Number} [tolerance=Infinity] - Maximum number of pixels.
- @param {?boolean} [withVertices=true] - Snap to layers vertices.
- @returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded.
- */
- closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
- tolerance = typeof tolerance == 'number' ? tolerance : Infinity;
- withVertices = typeof withVertices == 'boolean' ? withVertices : true;
- var result = L.GeometryUtil.closestLayer(map, layers, latlng);
- if (!result || result.distance > tolerance)
- return null;
- // If snapped layer is linear, try to snap on vertices (extremities and middle points)
- if (withVertices && typeof result.layer.getLatLngs == 'function') {
- var closest = L.GeometryUtil.closest(map, result.layer, result.latlng, true);
- if (closest.distance < tolerance) {
- result.latlng = closest;
- result.distance = L.GeometryUtil.distance(map, closest, latlng);
- }
- }
- return result;
- },
- /**
- Returns the Point located on a segment at the specified ratio of the segment length.
- @param {L.Point} pA
- @param {L.Point} pB
- @param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive.
- @returns {L.Point} the interpolated point.
- */
- interpolateOnPointSegment: function (pA, pB, ratio) {
- return L.point(
- (pA.x * (1 - ratio)) + (ratio * pB.x),
- (pA.y * (1 - ratio)) + (ratio * pB.y)
- );
- },
- /**
- Returns the coordinate of the point located on a line at the specified ratio of the line length.
- @param {L.Map} map
- @param {Array<L.LatLng>|L.PolyLine} latlngs
- @param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive
- @returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline
- (-1 if the interpolated point is the first vertex)
- */
- interpolateOnLine: function (map, latLngs, ratio) {
- latLngs = (latLngs instanceof L.Polyline) ? latLngs.getLatLngs() : latLngs;
- var n = latLngs.length;
- if (n < 2) {
- return null;
- }
- if (ratio === 0) {
- return {latLng: latLngs[0],
- predecessor: -1};
- }
- if (ratio == 1) {
- return {latLng: latLngs[latLngs.length -1],
- predecessor: latLngs.length-2};
- }
- // ensure the ratio is between 0 and 1;
- ratio = Math.max(Math.min(ratio, 1), 0);
- // project the LatLngs as Points,
- // and compute total planar length of the line at max precision
- var maxzoom = map.getMaxZoom();
- if (maxzoom === Infinity)
- maxzoom = map.getZoom();
- var pts = [];
- var lineLength = 0;
- for(var i = 0; i < n; i++) {
- pts[i] = map.project(latLngs[i], maxzoom);
- if(i > 0)
- lineLength += pts[i-1].distanceTo(pts[i]);
- }
- var ratioDist = lineLength * ratio;
- var a = pts[0],
- b = pts[1],
- distA = 0,
- distB = a.distanceTo(b);
- // follow the line segments [ab], adding lengths,
- // until we find the segment where the points should lie on
- var index = 1;
- for (; index < n && distB < ratioDist; index++) {
- a = b;
- distA = distB;
- b = pts[index];
- distB += a.distanceTo(b);
- }
- // compute the ratio relative to the segment [ab]
- var segmentRatio = ((distB - distA) !== 0) ? ((ratioDist - distA) / (distB - distA)) : 0;
- var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(a, b, segmentRatio);
- return {
- latLng: map.unproject(interpolatedPoint, maxzoom),
- predecessor: index-2
- };
- },
- /**
- Returns a float between 0 and 1 representing the location of the
- closest point on polyline to the given latlng, as a fraction of total 2d line length.
- (opposite of L.GeometryUtil.interpolateOnLine())
- @param {L.Map} map
- @param {L.PolyLine} polyline
- @param {L.LatLng} latlng
- @returns {Number}
- */
- locateOnLine: function (map, polyline, latlng) {
- var latlngs = polyline.getLatLngs();
- if (latlng.equals(latlngs[0]))
- return 0.0;
- if (latlng.equals(latlngs[latlngs.length-1]))
- return 1.0;
- var point = L.GeometryUtil.closest(map, polyline, latlng, false),
- lengths = L.GeometryUtil.accumulatedLengths(latlngs),
- total_length = lengths[lengths.length-1],
- portion = 0,
- found = false;
- for (var i=0, n = latlngs.length-1; i < n; i++) {
- var l1 = latlngs[i],
- l2 = latlngs[i+1];
- portion = lengths[i];
- if (L.GeometryUtil.belongsSegment(point, l1, l2)) {
- portion += l1.distanceTo(point);
- found = true;
- break;
- }
- }
- if (!found) {
- throw "Could not interpolate " + latlng.toString() + " within " + polyline.toString();
- }
- return portion / total_length;
- },
- /**
- Returns a clone with reversed coordinates.
- @param {L.PolyLine} polyline
- @returns {L.PolyLine}
- */
- reverse: function (polyline) {
- return L.polyline(polyline.getLatLngs().slice(0).reverse());
- },
- /**
- Returns a sub-part of the polyline, from start to end.
- If start is superior to end, returns extraction from inverted line.
- @param {L.Map} map
- @param {L.PolyLine} latlngs
- @param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive
- @param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive
- @returns {Array<L.LatLng>}
- */
- extract: function (map, polyline, start, end) {
- if (start > end) {
- return L.GeometryUtil.extract(map, L.GeometryUtil.reverse(polyline), 1.0-start, 1.0-end);
- }
- // Bound start and end to [0-1]
- start = Math.max(Math.min(start, 1), 0);
- end = Math.max(Math.min(end, 1), 0);
- var latlngs = polyline.getLatLngs(),
- startpoint = L.GeometryUtil.interpolateOnLine(map, polyline, start),
- endpoint = L.GeometryUtil.interpolateOnLine(map, polyline, end);
- // Return single point if start == end
- if (start == end) {
- var point = L.GeometryUtil.interpolateOnLine(map, polyline, end);
- return [point.latLng];
- }
- // Array.slice() works indexes at 0
- if (startpoint.predecessor == -1)
- startpoint.predecessor = 0;
- if (endpoint.predecessor == -1)
- endpoint.predecessor = 0;
- var result = latlngs.slice(startpoint.predecessor+1, endpoint.predecessor+1);
- result.unshift(startpoint.latLng);
- result.push(endpoint.latLng);
- return result;
- },
- /**
- Returns true if first polyline ends where other second starts.
- @param {L.PolyLine} polyline
- @param {L.PolyLine} other
- @returns {bool}
- */
- isBefore: function (polyline, other) {
- if (!other) return false;
- var lla = polyline.getLatLngs(),
- llb = other.getLatLngs();
- return (lla[lla.length-1]).equals(llb[0]);
- },
- /**
- Returns true if first polyline starts where second ends.
- @param {L.PolyLine} polyline
- @param {L.PolyLine} other
- @returns {bool}
- */
- isAfter: function (polyline, other) {
- if (!other) return false;
- var lla = polyline.getLatLngs(),
- llb = other.getLatLngs();
- return (lla[0]).equals(llb[llb.length-1]);
- },
- /**
- Returns true if first polyline starts where second ends or start.
- @param {L.PolyLine} polyline
- @param {L.PolyLine} other
- @returns {bool}
- */
- startsAtExtremity: function (polyline, other) {
- if (!other) return false;
- var lla = polyline.getLatLngs(),
- llb = other.getLatLngs(),
- start = lla[0];
- return start.equals(llb[0]) || start.equals(llb[llb.length-1]);
- },
- /**
- Returns horizontal angle in degres between two points.
- @param {L.Point} a
- @param {L.Point} b
- @returns {float}
- */
- computeAngle: function(a, b) {
- return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI);
- },
- /**
- Returns slope (Ax+B) between two points.
- @param {L.Point} a
- @param {L.Point} b
- @returns {Object} with ``a`` and ``b`` properties.
- */
- computeSlope: function(a, b) {
- var s = (b.y - a.y) / (b.x - a.x),
- o = a.y - (s * a.x);
- return {'a': s, 'b': o};
- },
-
- /**
- Returns LatLng of rotated point around specified LatLng center.
- @param {L.LatLng} latlngPoint: point to rotate
- @param {double} angleDeg: angle to rotate in degrees
- @param {L.LatLng} latlngCenter: center of rotation
- @returns {L.LatLng} rotated point
- */
- rotatePoint: function(map, latlngPoint, angleDeg, latlngCenter) {
- var maxzoom = map.getMaxZoom();
- if (maxzoom === Infinity)
- maxzoom = map.getZoom();
- var angleRad = angleDeg*Math.PI/180,
- pPoint = map.project(latlngPoint, maxzoom),
- pCenter = map.project(latlngCenter, maxzoom),
- x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x,
- y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y;
- return map.unproject(new L.Point(x2,y2), maxzoom);
- }
-
- };
- }());
|