[Pkg-javascript-commits] [leaflet-geometryutil] 01/06: New upstream version 0.4.0
Dominik George
natureshadow-guest at moszumanska.debian.org
Sat Jan 21 18:35:30 UTC 2017
This is an automated email from the git hooks/post-receive script.
natureshadow-guest pushed a commit to branch master
in repository leaflet-geometryutil.
commit b0e2584907f31038a01d3b80a064ad30c9efb3cc
Author: Dominik George <nik at naturalnet.de>
Date: Sat Jan 21 13:39:39 2017 +0100
New upstream version 0.4.0
---
.gitignore | 2 +
.gitmodules | 3 +
.travis.yml | 4 +
LICENSE | 27 +++
Makefile | 19 ++
README.md | 94 ++++++++
dist/leaflet.geometryutil.js | 557 +++++++++++++++++++++++++++++++++++++++++++
package.json | 19 ++
test/index.html | 26 ++
test/test.geometryutil.js | 542 +++++++++++++++++++++++++++++++++++++++++
10 files changed, 1293 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..788346b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/node_modules/
+.install.stamp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4a0723a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "docs"]
+ path = docs
+ url = https://github.com/makinacorpus/Leaflet.GeometryUtil.git
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..18ae2d8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - "0.11"
+ - "0.10"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..090a981
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Makina Corpus
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of ODE nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..af6b802
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+INSTALL_STAMP=.install.stamp
+
+all: install
+install: $(INSTALL_STAMP)
+
+$(INSTALL_STAMP):
+ npm install
+ touch $(INSTALL_STAMP)
+
+test: install
+ @./node_modules/mocha-phantomjs/bin/mocha-phantomjs test/index.html
+
+docs: install
+ @./node_modules/jsdoc/jsdoc -d ./docs/ dist/ README.md
+
+clean:
+ rm -rf node_modules/ $(INSTALL_STAMP)
+
+.PHONY: test docs
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..41b1f70
--- /dev/null
+++ b/README.md
@@ -0,0 +1,94 @@
+Leaflet.GeometryUtil
+====================
+
+[![Build Status](https://travis-ci.org/makinacorpus/Leaflet.GeometryUtil.png?branch=master)](https://travis-ci.org/makinacorpus/Leaflet.GeometryUtil)
+
+* Tested with stable Leaflet 0.7.0
+
+Usage
+-----
+
+Check out [online documentation](http://makinacorpus.github.io/Leaflet.GeometryUtil/).
+
+
+Development
+-----------
+
+### Running tests in command-line
+
+* Install [nodejs](http://nodejs.org) and [phantomjs](http://phantomjs.org)
+
+```
+ sudo apt-get install nodejs phantomjs
+```
+
+* Ready !
+
+```
+ make test
+```
+
+Changelog
+---------
+### master ###
+
+* Nothing changed yet.
+
+### 0.4.0 ###
+
+* Same version as v0.3.3, new release as v0.4.0 to keep numbering coherent as a new feature has been added
+
+### 0.3.3 ###
+
+* Add bearing and destination functions (thanks @doublestranded)
+
+### 0.3.2 ###
+
+* Use a soft dependency for Leaflet (thanks Erik Escoffier)
+
+### 0.3.1 ###
+
+* Make sure interpolateOnLine() always returns a L.LatLng object (thanks Justin Manley)
+
+### 0.3.0 ###
+
+* Added UMD style initialization (thanks @PerLiedman)
+* Added readable distance (thanks @Mylen)
+* Fix side effects on latlngs with `closest()` (thanks @AndrewIngram)
+
+### 0.2.0 ###
+
+* Locate point on line
+* Rotate point around center
+* Fixed bug if closest point was on last segment
+
+### 0.1.0 ###
+
+* Line subpart extraction
+* Line lengths
+* Angle and slope computation
+* Line reverse
+* Line interpolation
+
+### 0.0.1 ###
+
+* Initial working version
+
+
+License
+-------
+
+* BSD New
+
+
+Authors
+-------
+
+* [Benjamin Becquet](https://github.com/bbecquet)
+* [Mathieu Leplatre](https://github.com/leplatrem)
+* [Simon Thépot](https://github.com/djcoin)
+* [Nhinze](https://github.com/nhinze)
+* [Frédéric Bonifas](https://github.com/fredericbonifas)
+* [Alexander Melard](https://github.com/mylen)
+
+[![Makina Corpus](http://depot.makina-corpus.org/public/logo.gif)](http://makinacorpus.com)
diff --git a/dist/leaflet.geometryutil.js b/dist/leaflet.geometryutil.js
new file mode 100644
index 0000000..d139188
--- /dev/null
+++ b/dist/leaflet.geometryutil.js
@@ -0,0 +1,557 @@
+// Packaging/modules magic dance.
+(function (factory) {
+ var L;
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['leaflet'], factory);
+ } else if (typeof module !== 'undefined') {
+ // Node/CommonJS
+ L = require('leaflet');
+ module.exports = factory(L);
+ } else {
+ // Browser globals
+ if (typeof window.L === 'undefined')
+ throw 'Leaflet must be loaded first';
+ factory(window.L);
+ }
+}(function (L) {
+"use strict";
+
+/**
+ * @fileOverview Leaflet Geometry utilities for distances and linear referencing.
+ * @name L.GeometryUtil
+ */
+
+L.GeometryUtil = L.extend(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);
+ },
+
+ /**
+ Shortcut function for converting distance to readable distance.
+ @param {Number} distance
+ @param {String} unit ('metric' or 'imperial')
+ @returns {Number} in yard or miles
+ */
+ readableDistance: function (distance, unit) {
+ var isMetric = (unit !== 'imperial'),
+ distanceStr;
+ if (isMetric) {
+ // show metres when distance is < 1km, then show km
+ if (distance > 1000) {
+ distanceStr = (distance / 1000).toFixed(2) + ' km';
+ }
+ else {
+ distanceStr = Math.ceil(distance) + ' m';
+ }
+ }
+ else {
+ distance *= 1.09361;
+ if (distance > 1760) {
+ distanceStr = (distance / 1760).toFixed(2) + ' miles';
+ }
+ else {
+ distanceStr = Math.ceil(distance) + ' yd';
+ }
+ }
+ return distanceStr;
+ },
+
+ /**
+ 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().slice(0),
+ 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] instanceof L.LatLng ? latLngs[0] : L.latLng(latLngs[0]),
+ predecessor: -1
+ };
+ }
+ if (ratio == 1) {
+ return {
+ latLng: latLngs[latLngs.length -1] instanceof L.LatLng ? latLngs[latLngs.length -1] : L.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);
+ },
+
+ /**
+ Returns the bearing in degrees clockwise from north (0 degrees)
+ from the first L.LatLng to the second, at the first LatLng
+ @param {L.LatLng} latlng1: origin point of the bearing
+ @param {L.LatLng} latlng2: destination point of the bearing
+ @returns {float} degrees clockwise from north.
+ */
+ bearing: function(latlng1, latlng2) {
+ var rad = Math.PI / 180,
+ lat1 = latlng1.lat * rad,
+ lat2 = latlng2.lat * rad,
+ lon1 = latlng1.lng * rad,
+ lon2 = latlng2.lng * rad,
+ y = Math.sin(lon2 - lon1) * Math.cos(lat2),
+ x = Math.cos(lat1) * Math.sin(lat2) -
+ Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
+
+ var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
+ return bearing >= 180 ? bearing-360 : bearing;
+ },
+
+ /**
+ Returns the point that is a distance and heading away from
+ the given origin point.
+ @param {L.LatLng} latlng: origin point
+ @param {float}: heading in degrees, clockwise from 0 degrees north.
+ @param {float}: distance in meters
+ @returns {L.latLng} the destination point.
+ Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
+ for a great reference and examples.
+ */
+ destination: function(latlng, heading, distance) {
+ heading = (heading + 360) % 360;
+ var rad = Math.PI / 180,
+ radInv = 180 / Math.PI,
+ R = 6378137, // approximation of Earth's radius
+ lon1 = latlng.lng * rad,
+ lat1 = latlng.lat * rad,
+ rheading = heading * rad,
+ sinLat1 = Math.sin(lat1),
+ cosLat1 = Math.cos(lat1),
+ cosDistR = Math.cos(distance / R),
+ sinDistR = Math.sin(distance / R),
+ lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 *
+ sinDistR * Math.cos(rheading)),
+ lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR *
+ cosLat1, cosDistR - sinLat1 * Math.sin(lat2));
+ lon2 = lon2 * radInv;
+ lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2;
+ return L.latLng([lat2 * radInv, lon2]);
+ }
+});
+
+return L.GeometryUtil;
+
+}));
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8e6179e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "leaflet-geometryutil"
+ , "version": "0.4.0"
+ , "description": "Leaflet utility functions on geometries"
+ , "keywords": ["Leaflet", "GIS"]
+ , "main": "dist/leaflet.geometryutil.js"
+ , "scripts": {
+ "test": "make test"
+ }
+ , "dependencies": {
+ "leaflet": "^0.7.0"
+ }
+ , "devDependencies": {
+ "mocha": "1.9.0",
+ "chai": "1.6.0",
+ "mocha-phantomjs": "2.0.1",
+ "jsdoc": "https://github.com/jsdoc3/jsdoc/tarball/v3.1.1"
+ }
+}
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..9f19a01
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Mocha Tests</title>
+ <link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
+</head>
+<body>
+ <div id="map" style="display: none; height: 300px"></div>
+ <div id="mocha"></div>
+ <script src="../node_modules/chai/chai.js"></script>
+ <script src="../node_modules/mocha/mocha.js"></script>
+ <script src="../node_modules/leaflet/build/deps.js"></script>
+ <script src="../node_modules/leaflet/debug/leaflet-include.js"></script>
+ <script src="../dist/leaflet.geometryutil.js"></script>
+
+ <script>
+ var map = L.map('map').fitWorld();
+ </script>
+
+ <script>mocha.setup('bdd')</script>
+ <script src="test.geometryutil.js"></script>
+ <script>
+ (window.mochaPhantomJS || window.mocha).run();
+ </script>
+</body>
+</html>
diff --git a/test/test.geometryutil.js b/test/test.geometryutil.js
new file mode 100644
index 0000000..43dbb14
--- /dev/null
+++ b/test/test.geometryutil.js
@@ -0,0 +1,542 @@
+var assert = chai.assert;
+
+
+assert.almostequal = function (a, b, n) {
+ n = n || 12;
+ return assert.equal(Math.round(a * Math.pow(10, n)) / Math.pow(10, n),
+ Math.round(b * Math.pow(10, n)) / Math.pow(10, n));
+};
+// use Leaflet equality functions for Point and LatLng
+assert.pointEqual = function (a, b) {
+ return a.equals(b);
+};
+assert.latLngEqual = function (a, b, n) {
+ n = n || 2;
+ return assert.almostequal(a.lat, b.lat, 2) && assert.almostequal(a.lng, b.lng, n);
+};
+
+describe('Distance to segment', function() {
+ it('It should be 0 if point on segment', function(done) {
+ assert.equal(0, L.GeometryUtil.distanceSegment(map, L.latLng([10, 5]), L.latLng([10, 0]), L.latLng([10, 10])));
+ done();
+ });
+
+ it('It should not fail if segment has no length', function(done) {
+ assert.equal(1, L.GeometryUtil.distanceSegment(map, L.latLng([0, 1]), L.latLng([0, 0]), L.latLng([0, 0])));
+ done();
+ });
+
+ it('It should be the shortest distance', function(done) {
+ assert.equal(1, L.GeometryUtil.distanceSegment(map, L.latLng([0, 1]), L.latLng([0, 0]), L.latLng([10, 0])));
+ done();
+ });
+});
+
+
+describe('Length of line', function() {
+ it('It should be 0 for empty line', function(done) {
+ assert.equal(0, L.GeometryUtil.length([]));
+ done();
+ });
+
+ it('It should return length in meters', function(done) {
+ assert.equal(111319.49079327357, L.GeometryUtil.length(L.polyline([[0, 0], [1, 0]])));
+ done();
+ });
+});
+
+
+describe('Readable distances', function() {
+ it('It should be meters by default', function(done) {
+ assert.equal("0 m", L.GeometryUtil.readableDistance(0));
+ done();
+ });
+
+ it('It should be 0 yd if imperial', function(done) {
+ assert.equal("0 yd", L.GeometryUtil.readableDistance(0, 'imperial'));
+ done();
+ });
+
+ it('It should be kilometers if superior to 1000', function(done) {
+ assert.equal("1.01 km", L.GeometryUtil.readableDistance(1010));
+ done();
+ });
+
+ it('It should be miles if superior to 1760', function(done) {
+ assert.equal("1.24 miles", L.GeometryUtil.readableDistance(2000, 'imperial'));
+ done();
+ });
+});
+
+
+describe('Accumulated length of line', function() {
+ it('It should be empty for empty line', function(done) {
+ assert.deepEqual([], L.GeometryUtil.accumulatedLengths([]));
+ done();
+ });
+
+ it('It should return 0 and length in meters for a segment', function(done) {
+ assert.deepEqual([0, 111319.49079327357], L.GeometryUtil.accumulatedLengths(L.polyline([[0, 0], [1, 0]])));
+ done();
+ });
+
+ it('It should return accumulated lengths', function(done) {
+ assert.deepEqual([0, 55659.74539663678, 111319.49079327357], L.GeometryUtil.accumulatedLengths(L.polyline([[0, 0], [0.5, 0], [1, 0]])));
+ done();
+ });
+});
+
+
+describe('Closest on segment', function() {
+ it('It should be same point if point on segment', function(done) {
+ var ll = L.latLng([0, 0]),
+ closest = L.GeometryUtil.closestOnSegment(map, ll, L.latLng([0, 0]), L.latLng([10, 10]));
+ assert.equal(ll.toString(), closest.toString());
+ done();
+ });
+
+ it('It should be exactly on path', function(done) {
+ var ll = L.latLng([-1, 1]),
+ closest = L.GeometryUtil.closestOnSegment(map, ll, L.latLng([-10, -10]), L.latLng([10, 10]));
+ // TODO: should not be almost equal
+ assert.almostequal(0, closest.lat, 2);
+ assert.almostequal(0, closest.lng, 2);
+ done();
+ });
+});
+
+
+describe('Closest on path with precision', function() {
+ it('It should have distance at 0 if on path', function(done) {
+ var ll = L.latLng([0, 0]),
+ closest = L.GeometryUtil.closest(map, [[-30, -50], [-10, -10], [10, 10], [30, 50]], ll);
+ assert.equal(0, closest.distance);
+ assert.equal(ll.toString(), closest.toString());
+ done();
+ });
+
+ it('It should return same point if on path', function(done) {
+ var line = L.polyline([[0,0], [1, 1], [2, 2]]);
+ closest = L.GeometryUtil.closest(map, line, [1.7, 1.7]);
+ assert.almostequal(closest.lat, 1.7, 2);
+ assert.almostequal(closest.lng, 1.7, 2);
+ done();
+ });
+
+ it('It should be exactly on path', function(done) {
+ var ll = L.latLng([1, -1]),
+ closest = L.GeometryUtil.closest(map, [[-10, -10], [10, 10]], ll);
+ assert.equal(Math.sqrt(2), closest.distance);
+ // TODO: should not be almost equal
+ assert.almostequal(closest.lat, 0, 2);
+ assert.almostequal(closest.lng, 0, 2);
+ done();
+ });
+
+ it('It should not depend on zoom', function(done) {
+ // Test with plain value
+ var ll = L.latLng([5, 10]),
+ line = L.polyline([[-50, -10], [30, 40]]).addTo(map),
+ closest = L.GeometryUtil.closest(map, line, ll);
+ assert.isTrue(closest.distance > 0);
+ /*
+ SELECT ST_AsText(
+ ST_ClosestPoint(
+ ST_MakeLine('SRID=4326;POINT(-10 -50)'::geometry, 'SRID=4326;POINT(40 30)'::geometry),
+ 'SRID=4326;POINT(10 5)'::geometry))
+ Gives:
+ "POINT(20.3370786516854 -1.46067415730337)"
+ TODO: find out what's going on with Longitudes :)
+ */
+ assert.equal('LatLng(-1.46743, 21.57294)', closest.toString());
+
+ // Change zoom and check that closest did not change.
+ assert.equal(0, map.getZoom());
+ L.Util.setOptions(map, {maxZoom: 18});
+
+ map.on('moveend', function () {
+ assert.notEqual(0, map.getZoom());
+
+ closest = L.GeometryUtil.closest(map, line, ll);
+ assert.equal('LatLng(-1.46743, 21.57294)', closest.toString());
+ // Restore zoom
+ map.off('moveend');
+ map._resetView(map.getCenter(), 0);
+ done();
+ });
+
+ map._resetView(map.getCenter(), 17);
+ });
+
+ it('It should work with last segment of polygon', function(done) {
+ var polygon = L.polygon([[0, 0], [10, 10], [0, 10]]),
+ ll = [-1, 5],
+ closest = L.GeometryUtil.closest(map, polygon, ll);
+ assert.almostequal(closest.lat, 0, 2);
+ assert.almostequal(closest.lng, 5, 2);
+ done();
+ });
+});
+
+
+describe('Closest among layers', function() {
+ it('It should return null if list is empty', function(done) {
+ var ll = L.latLng([0, 0]),
+ closest = L.GeometryUtil.closestLayer(map, [], ll);
+ assert.equal(null, closest);
+ done();
+ });
+
+ it('It should return an object with layer, latlng and distance', function(done) {
+ var ll = L.latLng([0, 0]),
+ layers = [L.marker([2, 2])],
+ closest = L.GeometryUtil.closestLayer(map, layers, ll);
+ assert.deepEqual(closest,
+ {layer: layers[0], latlng: layers[0].getLatLng(), distance: Math.sqrt(2)});
+ done();
+ });
+});
+
+
+describe('Closest snap', function() {
+ var square, diagonal, d, w, layers;
+
+ beforeEach(function() {
+ // Snapping distance
+ d = L.GeometryUtil.distance(map, L.latLng([0, 0]), L.latLng([0, 10]));
+ w = 3 * d;
+ square = L.rectangle([[-w, -w], [w, w]]);
+ diagonal = L.polyline([[-w, -w], [0, 0], [w, w]]);
+ layers = [square, diagonal];
+ });
+
+ it('It should snap even if over layer', function(done) {
+ var snap = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([0, 0]));
+ assert.equal(snap.distance, 0);
+ assert.equal(snap.layer, diagonal);
+ done();
+ });
+
+ it('It should not snap if tolerance exceeded', function(done) {
+ var snap = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([-w-d, w+d]), d);
+ assert.equal(null, snap);
+ done();
+ });
+
+ it('It should snap to corners by default', function(done) {
+ var snap = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([-w-d, w+d]));
+ assert.isTrue(snap.distance > d);
+ assert.equal(snap.layer, square);
+ done();
+ });
+
+ it('It should not snap to corners if vertices disabled', function(done) {
+ var corner = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([w-d, -w-d]));
+ assert.equal(corner.layer, square);
+ assert.almostequal(corner.latlng.lat, w);
+ assert.almostequal(corner.latlng.lng, -w);
+
+ var snap = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([w-d, -w-d]), Infinity, false);
+ assert.almostequal(snap.latlng.lat, w-d);
+ assert.almostequal(snap.latlng.lng, -w);
+ done();
+ });
+
+ it('It should not snap to corners if distance to vertice exceeds tolerance', function(done) {
+ var corner = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([w-d-d/2, -w-d]));
+ assert.equal(corner.layer, square);
+ assert.almostequal(corner.latlng.lat, w);
+ assert.almostequal(corner.latlng.lng, -w);
+
+ var snap = L.GeometryUtil.closestLayerSnap(map, layers, L.latLng([w-d-d/2, -w-d]), d);
+ assert.almostequal(snap.latlng.lat, w-d-d/2);
+ assert.almostequal(snap.latlng.lng, -w);
+ done();
+ });
+});
+
+describe('Interpolate on point segment', function() {
+ var p1 = L.point(0, 2),
+ p2 = L.point(0, 6);
+ it('It should be the first point if offset is 0', function(done) {
+ assert.pointEqual(p1, L.GeometryUtil.interpolateOnPointSegment(p1, p2, 0));
+ done();
+ });
+
+ it('It should be the last point if offset is 1', function(done) {
+ assert.pointEqual(p2, L.GeometryUtil.interpolateOnPointSegment(p1, p2, 1));
+ done();
+ });
+
+ it('It should return the correct interpolations', function(done) {
+ assert.pointEqual(L.point(0, 4), L.GeometryUtil.interpolateOnPointSegment(p1, p2, 0.5));
+ assert.pointEqual(L.point(0, 5), L.GeometryUtil.interpolateOnPointSegment(p1, p2, 0.75));
+ done();
+ });
+});
+
+describe('Interpolate on line', function() {
+ var llA = L.latLng(1, 2),
+ llB = L.latLng(3, 4),
+ llC = L.latLng(5, 6);
+
+ it('It should be null if the line has less than 2 vertices', function(done) {
+ assert.equal(null, L.GeometryUtil.interpolateOnLine(map, [], 0.5));
+ assert.equal(null, L.GeometryUtil.interpolateOnLine(map, [llA], 0.5));
+ done();
+ });
+
+ it('It should be the first vertex if offset is 0', function(done) {
+ var interp = L.GeometryUtil.interpolateOnLine(map, [llA, llB], 0);
+ assert.latLngEqual(interp.latLng, llA);
+ assert.equal(interp.predecessor, -1);
+ done();
+ });
+
+ it('It should be the last vertex if offset is 1', function(done) {
+ var interp = L.GeometryUtil.interpolateOnLine(map, [llA, llB, llC], 1);
+ assert.latLngEqual(interp.latLng, llC);
+ assert.equal(interp.predecessor, 1);
+ done();
+ });
+
+ it('It should not fail if line has no length', function(done) {
+ var interp = L.GeometryUtil.interpolateOnLine(map, [llA, llA, llA], 0.5);
+ assert.latLngEqual(interp.latLng, llA);
+ done();
+ });
+
+ it('It should return the correct interpolations', function(done) {
+ var interp1 = L.GeometryUtil.interpolateOnLine(map, [llA, llB, llC], 0.5);
+ assert.latLngEqual(interp1.latLng, llB);
+ var interp2 = L.GeometryUtil.interpolateOnLine(map, [llA, llB, llC], 0.75);
+ assert.latLngEqual(interp2.latLng, L.latLng([4, 5]));
+ done();
+ });
+
+ it('It should work the same with instances of L.PolyLine and arrays of L.LatLng', function(done) {
+ var lls = [llA, llB, llC];
+ var withArray = L.GeometryUtil.interpolateOnLine(map, lls, 0.75);
+ var withPolyLine = L.GeometryUtil.interpolateOnLine(map, L.polyline(lls), 0.75);
+ assert.deepEqual(withArray, withPolyLine);
+ done();
+ });
+
+ it('Should always return a LatLng object.', function() {
+ var interp1 = L.GeometryUtil.interpolateOnLine(map, [llA, llB, llC], 0);
+ var interp2 = L.GeometryUtil.interpolateOnLine(map, [llA, llB, llC], 1);
+
+ assert.isDefined(interp1.latLng.lat);
+ assert.isDefined(interp1.latLng.lng);
+ assert.isDefined(interp2.latLng.lat);
+ assert.isDefined(interp2.latLng.lng);
+ });
+});
+
+
+describe('Locate on line', function() {
+ var line = L.polyline([[0,0], [1, 1], [2, 2]]);
+
+ it('It should return 0 if start', function(done) {
+ assert.equal(0, L.GeometryUtil.locateOnLine(map, line, L.latLng([0, 0])));
+ done();
+ });
+
+ it('It should return 1 if end', function(done) {
+ assert.equal(1, L.GeometryUtil.locateOnLine(map, line, L.latLng([2, 2])));
+ done();
+ });
+
+ it('It should return ratio of point', function(done) {
+ assert.almostequal(0.5, L.GeometryUtil.locateOnLine(map, line, L.latLng([1, 1])), 4);
+ assert.almostequal(0.25, L.GeometryUtil.locateOnLine(map, line, L.latLng([0.5, 0.5])), 4);
+ assert.almostequal(0.85, L.GeometryUtil.locateOnLine(map, line, L.latLng([1.7, 1.7])), 4);
+ done();
+ });
+});
+
+
+describe('Reverse line', function() {
+ var line = L.polyline([[0,0], [1, 1]]);
+
+ it('It should invert coordinates', function(done) {
+ assert.latLngEqual(line.getLatLngs()[0], L.GeometryUtil.reverse(line).getLatLngs()[1]);
+ done();
+ });
+
+ it('It should not affect original', function(done) {
+ var start = line.getLatLngs()[0];
+ L.GeometryUtil.reverse(line);
+ assert.latLngEqual(start, line.getLatLngs()[0]);
+ done();
+ });
+});
+
+
+describe('Extract line', function() {
+ var line = L.polyline([[0,0], [1, 1], [2, 2], [3, 3]]);
+
+ it('It should return all coordinates from 0 to 1', function(done) {
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 0, 1), line.getLatLngs());
+ done();
+ });
+
+ it('It should return inverted coordinates from 1 to 0', function(done) {
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 1, 0), L.GeometryUtil.reverse(line).getLatLngs());
+ done();
+ });
+
+ it('It should return one coordinate if start equals end', function(done) {
+ assert.latLngEqual(L.latLng(0.7501691078194406, 0.7501524538236026),
+ L.GeometryUtil.extract(map, line, 0.25, 0.25)[0]);
+ done();
+ });
+
+ it('It should return extra coordinate if middle of segment', function(done) {
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 0, 0.2),
+ [L.latLng([0, 0]), L.latLng([0.600141459027052, 0.6001219630588661])]);
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 0, 0.6),
+ [L.latLng([0, 0]), L.latLng([1, 1]), L.latLng([1.800282914111311, 1.8002439493392906])]);
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 0.6, 1.0),
+ [L.latLng([1.800282914111311, 1.8002439493392906]), L.latLng([2, 2]), L.latLng([3, 3])]);
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 0.2, 0.8),
+ [L.latLng([0.600141459027052, 0.6001219630588661]), L.latLng([1, 1]), L.latLng([2, 2]), L.latLng([2.40024267258436, 2.4001524293923637])]);
+
+ // Should work symetrically
+ assert.deepEqual(L.GeometryUtil.extract(map, line, 1.0, 0.6),
+ [L.latLng([3, 3]), L.latLng([2, 2]), L.latLng([1.800282914111311, 1.8002439493392906])]);
+ done();
+ });
+});
+
+
+describe('Line order', function() {
+ var lineA = L.polyline([[0, 0], [1, 1]]),
+ lineB = L.polyline([[1, 1], [2, 2]]);
+
+ it('It should detect if line is before', function(done) {
+ assert.isTrue(L.GeometryUtil.isBefore(lineA, lineB));
+ assert.isFalse(L.GeometryUtil.isBefore(lineB, lineA));
+ done();
+ });
+
+ it('It should detect if line is after', function(done) {
+ assert.isTrue(L.GeometryUtil.isAfter(lineB, lineA));
+ assert.isFalse(L.GeometryUtil.isAfter(lineA, lineB));
+ done();
+ });
+
+ it('It should detect if line starts at extremity', function(done) {
+ var lineC = L.polyline([[0, 0], [1, 1]]);
+ assert.isTrue(L.GeometryUtil.startsAtExtremity(lineA, lineC));
+ assert.isTrue(L.GeometryUtil.startsAtExtremity(lineB, lineC));
+ assert.isFalse(L.GeometryUtil.startsAtExtremity(lineC, lineB));
+ done();
+ });
+});
+
+describe('Compute angle', function() {
+ it('It should return angle', function(done) {
+ var p1 = L.point(0, 0),
+ p2 = L.point(6, 6);
+ assert.equal(L.GeometryUtil.computeAngle(p1, p2), 45);
+ done();
+ });
+});
+
+describe('Compute slope', function() {
+ it('It should return A and B', function(done) {
+ var p1 = L.point(0, 2),
+ p2 = L.point(5, 7);
+ assert.deepEqual(L.GeometryUtil.computeSlope(p1, p2), {a: 1, b: 2})
+ done();
+ });
+});
+
+describe('Point rotation', function() {
+ it('It should return the same point if angle is 0', function(done) {
+ var llPoint = L.latLng([3, 3]),
+ llCenter = L.latLng([2, 2]),
+ rotated = L.GeometryUtil.rotatePoint(map, llPoint, 0, llCenter);
+ assert.latLngEqual(llPoint, rotated);
+ done();
+ });
+
+ it('It should return the same point if center and point are the same', function(done) {
+ var llPoint = L.latLng([1, 1]),
+ llCenter = L.latLng([1, 1]),
+ rotated = L.GeometryUtil.rotatePoint(map, llPoint, 90, llCenter);
+ assert.latLngEqual(llPoint, rotated);
+ done();
+ });
+
+ it('It should return a rotated point', function(done) {
+ var llPoint = L.latLng([1, 1]),
+ llCenter = L.latLng([2, 2]),
+ rotated = L.GeometryUtil.rotatePoint(map, llPoint, 90, llCenter);
+ assert.latLngEqual(rotated, L.latLng([3, 1]));
+ done();
+ });
+});
+
+describe('Compute Bearing', function() {
+
+ it('It should be degrees clockwise from north, 0 degrees.', function(done) {
+ var latlng1 = L.latLng([0.0, 0.0]),
+ latlng2 = L.latLng([90.0, 0.0]);
+ assert.equal(0.0, L.GeometryUtil.bearing(latlng1,latlng2));
+ done();
+ });
+
+ it('Same point, should be zero.', function(done) {
+ var latlng1 = L.latLng([0.0, 0.0]),
+ latlng2 = L.latLng([0.0, 0.0]);
+ assert.equal(0, L.GeometryUtil.bearing(latlng1,latlng2));
+ done();
+ });
+
+ it('Crossing Prime Meridian.', function(done) {
+ var latlng1 = L.latLng([10.0, -10.0]),
+ latlng2 = L.latLng([-10.0, 10.0]);
+ assert.equal(134.5614514132577, L.GeometryUtil.bearing(latlng1,latlng2));
+ done();
+ });
+
+ it('Negative value for bearing greater than / equal to 180', function(done) {
+ var latlng1 = L.latLng([33.0, -120.0]),
+ latlng2 = L.latLng([34.0, -122.0]);
+ assert.equal(-58.503883697887375, L.GeometryUtil.bearing(latlng1,latlng2));
+ done();
+ });
+
+});
+
+describe('Destination', function() {
+
+ it('It should be [90.0,0.0]', function(done) {
+ var latlng1 = L.latLng([0.0, 0.0]),
+ heading = 0.0;
+ dist = 6378137 * Math.PI / 2.0; // 1/4 Earth's circumference.
+ result = L.latLng([90.0,0.0]);
+ assert.latLngEqual(result, L.GeometryUtil.destination(latlng1, heading, dist));
+ done();
+ });
+
+ it('Crossing the International Date Line', function(done) {
+ var latlng1 = L.latLng([0.0, -175.0]),
+ heading = -90.0;
+ dist = 6378137 * Math.PI / 8.0;
+ result = L.latLng([0.0, 162.5]);
+ assert.latLngEqual(result, L.GeometryUtil.destination(latlng1, heading, dist));
+ done();
+ });
+
+ it('Crossing the Prime Meridian', function(done) {
+ var latlng1 = L.latLng([10.0, -10.0]),
+ heading = 134.5614514132577;
+ dist = 3140555.3283872544;
+ result = L.latLng([-10, 10.0]);
+ assert.latLngEqual(result, L.GeometryUtil.destination(latlng1, heading, dist));
+ done();
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/leaflet-geometryutil.git
More information about the Pkg-javascript-commits
mailing list