diff options
| author | Marak <[email protected]> | 2018-09-22 21:28:12 -0400 |
|---|---|---|
| committer | Marak <[email protected]> | 2018-09-22 21:29:31 -0400 |
| commit | facdb927e0c8e4aeef2d3f16c28808010fac14cf (patch) | |
| tree | f4dfff20b759abcedadd6d53bb3a2525c2b65252 | |
| parent | 9e13a69f2a1a0d6ba7f99dc45274c24ef57ad7ac (diff) | |
| parent | 58936caa649a7b1fbb296ed66b5e1c3af656ea9a (diff) | |
| download | faker-facdb927e0c8e4aeef2d3f16c28808010fac14cf.tar.xz faker-facdb927e0c8e4aeef2d3f16c28808010fac14cf.zip | |
Merged `addess.nearbyGPSCoordinate` method
| -rw-r--r-- | lib/address.js | 52 | ||||
| -rw-r--r-- | test/address.unit.js | 49 |
2 files changed, 101 insertions, 0 deletions
diff --git a/lib/address.js b/lib/address.js index 21e14ff6..e7cc053a 100644 --- a/lib/address.js +++ b/lib/address.js @@ -289,6 +289,58 @@ function Address (faker) { "sampleResults": ["Northwest", "Southeast", "SW", "NE"] }; + this.nearbyGPSCoordinate = function(coordinate, radius, isMetric) { + function randomFloat(min, max) { + return Math.random() * (max-min) + min; + } + function degreesToRadians(degrees) { + return degrees * (Math.PI/180.0); + } + function radiansToDegrees(radians) { + return radians * (180.0/Math.PI); + } + function kilometersToMiles(miles) { + return miles * 0.621371; + } + function coordinateWithOffset(coordinate, bearing, distance, isMetric) { + var R = 6378.137; // Radius of the Earth (http://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html) + var d = isMetric ? distance : kilometersToMiles(distance); // Distance in km + + var lat1 = degreesToRadians(coordinate[0]); //Current lat point converted to radians + var lon1 = degreesToRadians(coordinate[1]); //Current long point converted to radians + + var lat2 = Math.asin(Math.sin(lat1) * Math.cos(d/R) + + Math.cos(lat1) * Math.sin(d/R) * Math.cos(bearing)); + + var lon2 = lon1 + Math.atan2( + Math.sin(bearing) * Math.sin(d/R) * Math.cos(lat1), + Math.cos(d/R) - Math.sin(lat1) * Math.sin(lat2)); + + // Keep longitude in range [-180, 180] + if (lon2 > degreesToRadians(180)) { + lon2 = lon2 - degreesToRadians(360); + } else if (lon2 < degreesToRadians(-180)) { + lon2 = lon2 + degreesToRadians(360); + } + + return [radiansToDegrees(lat2), radiansToDegrees(lon2)]; + } + + // If there is no coordinate, the best we can do is return a random GPS coordinate. + if (coordinate === undefined) { + return [this.latitude(), this.longitude()] + } + radius = radius || 10.0; + isMetric = isMetric || false; + + // TODO: implement either a gaussian/uniform distribution of points in cicular region. + // Possibly include param to function that allows user to choose between distributions. + + // This approach will likely result in a higher density of points near the center. + var randomCoord = coordinateWithOffset(coordinate, degreesToRadians(Math.random() * 360.0), radius, isMetric); + return [randomCoord[0].toFixed(4), randomCoord[1].toFixed(4)]; + } + return this; } diff --git a/test/address.unit.js b/test/address.unit.js index 28775850..634cde9e 100644 --- a/test/address.unit.js +++ b/test/address.unit.js @@ -361,4 +361,53 @@ describe("address.js", function () { faker.address.cardinalDirection.restore(); }) }) + + describe("nearbyGPSCoordinate()", function () { + it("returns random gps coordinate within a distance of another one", function () { + function haversine(lat1, lon1, lat2, lon2, isMetric) { + function degreesToRadians(degrees) { + return degrees * (Math.PI/180.0); + } + function kilometersToMiles(miles) { + return miles * 0.621371; + } + var R = 6378.137; + var dLat = degreesToRadians(lat2-lat1); + var dLon = degreesToRadians(lon2-lon1); + var a = Math.sin(dLat/2) * Math.sin(dLat/2) + + Math.cos(degreesToRadians(lat1)) * Math.cos(degreesToRadians(lat2)) + * Math.sin(dLon/2) * Math.sin(dLon/2); + var distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return isMetric ? distance : kilometersToMiles(distance); + } + for (var i = 0; i < 10000; i++) { + var latFloat1 = parseFloat(faker.address.latitude()); + var lonFloat1 = parseFloat(faker.address.longitude()); + var radius = (Math.random() * 99) + 1; // range of [1, 100) + var isMetric = (Math.round(Math.random()) == 1); + + var coordinate = faker.address.nearbyGPSCoordinate([latFloat1, lonFloat1], radius, isMetric); + assert.ok(coordinate.length === 2); + assert.ok(typeof coordinate[0] === 'string'); + assert.ok(typeof coordinate[1] === 'string'); + + var latFloat2 = parseFloat(coordinate[0]); + assert.ok(latFloat2 >= -90.0); + assert.ok(latFloat2 <= 90.0); + + var lonFloat2 = parseFloat(coordinate[1]); + assert.ok(lonFloat2 >= -180.0); + assert.ok(lonFloat2 <= 180.0); + + // Due to floating point math, and constants that are not extremely precise, + // returned points will not be strictly within the given radius of the input + // coordinate. Using a error of 1.0 to compensate. + var error = 1.0; + var actualDistance = haversine(latFloat1, lonFloat1, latFloat2, lonFloat2, isMetric); + assert.ok(actualDistance <= (radius + error)); + } + }); + }); + }); |
