Me gustaría generar coordenadas dentro de un radio dado de coordenadas dadas. Escribí un pequeño script, que la mayoría de las veces genera valores válidos:
const coordinate = [Math.random() * 180 - 90, Math.random() * 360 - 180]; // [lat, long] const angleRadians = Math.PI / 4; // "random" angle in ° radians const totalDistance = 100; // max distance in km // First calculate the new latitude const kmPerDegreeLatitude = 111; // in km/° const distanceLatitude = totalDistance / kmPerDegreeLatitude; // in ° const offsetLatitude = Math.sin(angleRadians) * distanceLatitude; const newLatitude = coordinate[0] + offsetLatitude; // This looks wrong to me, but reduces the likelihood of errors const remainingDistance = Math.sqrt( Math.pow(totalDistance, 2) - Math.pow(offsetLatitude * kmPerDegreeLatitude, 2) ); // Now calculate the longitude const kmPerDegreeLongitude = Math.abs(Math.cos(degreesToRadians(newLatitude))) * 111; // in km/° const distanceLongitude = remainingDistance / kmPerDegreeLongitude; // in ° const offsetLongitude = Math.cos(angleRadians) * distanceLongitude; const newLongitude = coordinate[1] + offsetLongitude; // Done const newCoordinate: [latitude: number, longitude: number] = [ newLatitude, newLongitude, ];Pero cuando uso el siguiente código para verificar la distancia, a veces termino por encima de la distancia permitida:
function haversine( latitude1: number, longitude1: number, latitude2: number, longitude2: number, ) { const EQUATORIAL_EARTH_RADIUS = 6378.137; const distanceLatitude = degreesToRadians(latitude2 - latitude1); const distanceLongitude = degreesToRadians(longitude2 - longitude1); const a = Math.sin(distanceLatitude / 2) * Math.sin(distanceLatitude / 2) + Math.cos(degreesToRadians(latitude1)) * Math.cos(degreesToRadians(latitude2)) * Math.sin(distanceLongitude / 2) * Math.sin(distanceLongitude / 2); const distance = EQUATORIAL_EARTH_RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return distance; }¿Alguna idea, dónde me equivoqué? Supongo que está en la generación de coordenadas, pero podría estar en el paso de verificación. ¿O tal vez hay formas más fáciles de generar las coordenadas cercanas?
Tenga en cuenta que no puedo usar las bibliotecas existentes para eso, porque las coordenadas generadas deberían ser reproducibles, pero omití el código relacionado por simplicidad.
Bueno, traté de complicar demasiado las cosas y la imprecisión flotante en JS se acumuló a veces hasta un 20% debido a las llamadas apiladas.
Simplifiqué el cálculo ahora para asumir que vivimos en una esfera perfecta:
const angleRadians = randomAngle(); // in ° radians const errorCorrection = 0.995; // avoid float issues const distanceInKm = randomDinstance(100) * errorCorrection; // in km const distanceInDegree = distanceInKm / kmPerDegree; // in ° const newCoordinate: [latitude: number, longitude: number] = [ coordinate[0] + Math.sin(angleRadians) * distanceInDegree, coordinate[1] + Math.cos(angleRadians) * distanceInDegree, ]; // Box latitude [-90°, 90°] newCoordinate[0] = newCoordinate[0] % 180; if (newCoordinate[0] < -90 || newCoordinate[0] > 90) { newCoordinate[0] = Math.sign(newCoordinate[0]) * 180 - newCoordinate[0]; newCoordinate[1] += 180; } // Box longitude [-180°, 180°] newCoordinate[1] = (((newCoordinate[1] % 360) + 540) % 360) - 180;Asumiendo que usted puede aproximar localmente la superficie de la tierra como un plano, entonces la siguiente función le da la latitud y longitud de un punto P1, dada la latitud y longitud de un punto P0, la distancia lineal |P1-P0| y el ángulo formado por P1-P0 con el paralelo local. El radio de tierra local R también es necesario como entrada.
function incrementCoordinates (long0, lat0, dist, angle, R) { // Calculate the distance component along the parallel dist_x = dist * Math.cos(angle / 180 * Math.PI) // Calculate the distance component along the meridian dist_y = dist * Math.sin(angle / 180 * Math.PI) // Calculate the new longitude long1 = long0 + dist_x / R / Math.PI * 180 // Calculate the new latitude lat1 = lat0 + dist_y / R / Math.PI * 180 return [long1, lat1] }