Generate coordinates with Minimum/Maximum distance from given coordinates

Background:

I am trying to create a coordinates generator for Unit Testing my code. I would like to try to test cases for methods like:

``````// Check if within 10km.
// Should pass because it is 0 - 10km from location
location.isWithin(generateLocation(location, 10), 10) === true
``````
``````// Check if within 10km.
// Should fail because it is 20 - 25km from location
location.isWithin(generateLocation(location, 25, 20), 10) === false
``````

I have tried lots of Stackoverflow answers I never found one answer to this problem.

What I have tried:

``````function generateNearbyLocation(latitude, longitude, max, min = 0) {
// Convert max in kilometers to degrees
const radiusInDegrees = (max * 1000) / 111300;

const u = Math.random();
const v = Math.random();

const w = radiusInDegrees * Math.sqrt(u);

// random angle
const t = 2 * Math.PI * v;

const x = w * Math.cos(t);
const x1 = x / Math.cos(latitude);
const y1 = w * Math.sin(t);

// Not working attempt to add Minimum distance
// const newLatitude = latitude + (min * 1000 / 111300) + y1;
// const newLongitude = longitude + (min * 1000 / 111300) + x1;

const newLatitude = latitude + y1;
const newLongitude = longitude + x1;
const distance = getDistanceFromLatLonInKm(latitude, longitude, newLatitude, newLongitude);

return {
newLatitude,
newLongitude,
distance
};
}
``````

This works but is not accurate. Even with max of 10, it sometimes generate 11km coordinates.

Here's a playground that I made: https://jsbin.com/hakemubogo/edit?js,console

EDIT

I found a much more accurate formula from another answer but still can't create minimum distance.

``````function generateLocation(latitude, longitude, max, min = 0) {
const EARTH_RADIUS = 6371; // km
const DEGREE = EARTH_RADIUS * 2 * Math.PI / 360 * 1000; // 1° latitude in meters

const r = (max * 1000) * Math.random() ** 0.5;
const theta = Math.random() * 2 * Math.PI; // random * (360deg in radians)

const dy = r * Math.sin(theta);
const dx = r * Math.cos(theta);

const newLatitude = latitude + dy / DEGREE;
const newLongitude = longitude + dx / (DEGREE * Math.cos(deg2rad(latitude)));
const distance = getDistanceFromLatLonInKm(latitude, longitude, newLatitude, newLongitude);

return {
newLatitude,
newLongitude,
distance
};
}
``````

I was able to finally figure it out. According to this answer:

If you have a center (x,y) and you move on the x axis by 30 meters and on the y axis by another 30 meters your distance from the center won't be 30.

It turns out the variable `r` should be the one I'm adjusting and not the `newLatitude` and `newLongitude`.

`(max * 1000) * Math.random() ** 0.5` is only generating random `[0 - max]` value (in a non-uniform way).

Therefore, what I need to add is just add the minimum distance on variable `r` so it generates `[min-max]`

``````const maxKm = max * 1000;
const minKm = min * 1000;
const r = ((maxKm - minKm + 1) * Math.random() ** 0.5) + minKm;
``````

The only thing I added is the `** 0.5` part.

Full code:

``````// Final Code Here
function generateLocation(latitude, longitude, max, min = 0) {
if (min > max) {
throw new Error(`min(\${min}) cannot be greater than max(\${max})`);
}

// 1° latitude in meters
const DEGREE = EARTH_RADIUS * 2 * Math.PI / 360 * 1000;

// random distance within [min-max] in km in a non-uniform way
const maxKm = max * 1000;
const minKm = min * 1000;
const r = ((maxKm - minKm + 1) * Math.random() ** 0.5) + minKm;

// random angle
const theta = Math.random() * 2 * Math.PI;

const dy = r * Math.sin(theta);
const dx = r * Math.cos(theta);

let newLatitude = latitude + dy / DEGREE;
let newLongitude = longitude + dx / (DEGREE * Math.cos(deg2rad(latitude)));

const distance = getDistanceFromLatLonInKm(latitude, longitude, newLatitude, newLongitude);

return {
newLatitude,
newLongitude,
distance: Math.round(distance)
};
}

// See https://stackoverflow.com/a/27943/10975709
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
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 km

return d;
}