From 2723c6550662c451bf47944314da1ec897ce7118 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 23 Jun 2014 16:23:04 -0400 Subject: [PATCH] refactor dLat/dLon <-> meters calcs and use slightly better WGS84 constants --- js/id/geo.js | 33 ++++++++++++++++++++++++++++++--- js/id/geo/extent.js | 4 ++-- js/id/ui/scale.js | 19 ++----------------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/js/id/geo.js b/js/id/geo.js index 8978828b0..b46f586b0 100644 --- a/js/id/geo.js +++ b/js/id/geo.js @@ -21,11 +21,38 @@ iD.geo.euclideanDistance = function(a, b) { var x = a[0] - b[0], y = a[1] - b[1]; return Math.sqrt((x * x) + (y * y)); }; + +// using WGS84 polar radius (6356752.314245179 m) +// const = 2 * PI * r / 360 +iD.geo.latToMeters = function(dLat) { + return dLat * 110946.257617; +}; + +// using WGS84 equatorial radius (6378137.0 m) +// const = 2 * PI * r / 360 +iD.geo.lonToMeters = function(dLon, atLat) { + return Math.abs(atLat) >= 90 ? 0 : + dLon * 111319.490793 * Math.abs(Math.cos(atLat * (Math.PI/180))); +}; + +// using WGS84 polar radius (6356752.314245179 m) +// const = 2 * PI * r / 360 +iD.geo.metersToLat = function(m) { + return m / 110946.257617; +}; + +// using WGS84 equatorial radius (6378137.0 m) +// const = 2 * PI * r / 360 +iD.geo.metersToLon = function(m, atLat) { + return Math.abs(atLat) >= 90 ? 0 : + m / 111319.490793 / Math.abs(Math.cos(atLat * (Math.PI/180))); +}; + // Equirectangular approximation of spherical distances on Earth iD.geo.sphericalDistance = function(a, b) { - var x = Math.cos(a[1]*Math.PI/180) * (a[0] - b[0]), - y = a[1] - b[1]; - return 6.3710E6 * Math.sqrt((x * x) + (y * y)) * Math.PI/180; + var x = iD.geo.lonToMeters(a[0] - b[0], (a[1] + b[1]) / 2), + y = iD.geo.latToMeters(a[1] - b[1]); + return Math.sqrt((x * x) + (y * y)); }; iD.geo.edgeEqual = function(a, b) { diff --git a/js/id/geo/extent.js b/js/id/geo/extent.js index 9fd659f5e..04c8c7434 100644 --- a/js/id/geo/extent.js +++ b/js/id/geo/extent.js @@ -58,8 +58,8 @@ _.extend(iD.geo.Extent.prototype, { }, padByMeters: function(meters) { - var dLat = meters / 111132.954, - dLon = meters / 111132.954 / Math.abs(Math.cos(this.center()[1] * (Math.PI / 180))); + var dLat = iD.geo.metersToLat(meters), + dLon = iD.geo.metersToLon(meters, this.center()[1]); return iD.geo.Extent( [this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]); diff --git a/js/id/ui/scale.js b/js/id/ui/scale.js index 3824cfddc..6a18bec74 100644 --- a/js/id/ui/scale.js +++ b/js/id/ui/scale.js @@ -4,25 +4,10 @@ iD.ui.Scale = function(context) { maxLength = 180, tickHeight = 8; - // http://stackoverflow.com/a/27943/7620 - // Haversine distance formula - function distance(loc1, loc2) { - var R = 6371009; // Mean radius of the earth in m - var dLat = (loc2[1] - loc1[1]) * (Math.PI / 180); - var dLon = (loc2[0] - loc1[0]) * (Math.PI / 180); - var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(loc1[1] * (Math.PI / 180)) * - Math.cos(loc2[1] * (Math.PI / 180)) * - Math.sin(dLon / 2) * Math.sin(dLon / 2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - var d = R * c; // Distance in m - return d; - } - function scaleDefs(loc1, loc2) { var lat = (loc2[1] + loc1[1]) / 2, conversion = (imperial ? 3.28084 : 1), - dist = distance(loc1, loc2) * conversion, + dist = iD.geo.lonToMeters(loc2[0] - loc1[0], lat) * conversion, scale = { dist: 0, px: 0, text: '' }, buckets, i, val, dLon; @@ -41,7 +26,7 @@ iD.ui.Scale = function(context) { } } - dLon = scale.dist / (111132.954 * conversion) / Math.abs(Math.cos( lat * (Math.PI / 180))); + dLon = iD.geo.metersToLon(scale.dist / conversion, lat); scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]); if (imperial) {