diff --git a/dist/leaflet.geometryutil.js b/dist/leaflet.geometryutil.js index b431b6f..93df08f 100644 --- a/dist/leaflet.geometryutil.js +++ b/dist/leaflet.geometryutil.js @@ -497,6 +497,56 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { 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. + @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. + */ + 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]); } }); diff --git a/test/test.geometryutil.js b/test/test.geometryutil.js index 538eb28..43dbb14 100644 --- a/test/test.geometryutil.js +++ b/test/test.geometryutil.js @@ -478,3 +478,65 @@ describe('Point rotation', function() { 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(); + }); +});