aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarak <[email protected]>2018-09-22 21:28:12 -0400
committerMarak <[email protected]>2018-09-22 21:29:31 -0400
commitfacdb927e0c8e4aeef2d3f16c28808010fac14cf (patch)
treef4dfff20b759abcedadd6d53bb3a2525c2b65252
parent9e13a69f2a1a0d6ba7f99dc45274c24ef57ad7ac (diff)
parent58936caa649a7b1fbb296ed66b5e1c3af656ea9a (diff)
downloadfaker-facdb927e0c8e4aeef2d3f16c28808010fac14cf.tar.xz
faker-facdb927e0c8e4aeef2d3f16c28808010fac14cf.zip
Merged `addess.nearbyGPSCoordinate` method
-rw-r--r--lib/address.js52
-rw-r--r--test/address.unit.js49
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));
+ }
+ });
+ });
+
});