Calcolo della latitudine / longitudine X miglia dal punto?


46

Voglio trovare un punto di latitudine e longitudine dato un rilevamento, una distanza e una latitudine e una longitudine di partenza.

Questo sembra essere l'opposto di questa domanda ( distanza tra punti lat / long ).

Ho già esaminato la formula di haversine e penso che la sua approssimazione del mondo sia probabilmente abbastanza vicina.

Sto assumendo che devo risolvere la formula haversine per il mio lat / long sconosciuto, è corretto? Ci sono dei buoni siti web che parlano di questo genere di cose? Sembra che sarebbe comune, ma il mio googling ha solo sollevato domande simili a quella sopra.

Quello che sto davvero cercando è solo una formula per questo. Vorrei dargli un lat / lng iniziale, un rilevamento e una distanza (miglia o chilometri) e vorrei uscirne una coppia lat / lng che rappresenta dove si sarebbe finito se avessero viaggiato quella via.


Sei alla ricerca di uno strumento che funzioni come Esri's pe.dll o di una formula?
Kirk Kuykendall,

Mi dispiace non ero specifico ... Sto cercando una formula. Aggiornerò la mia domanda per essere più specifici.
Jason Whitehorn,

Un sacco di campioni matematici elaborati sono qui <a href=" movable-type.co.uk/scripts/latlong.html"> Calcola distanza, rilevamento e altro tra i punti di latitudine / longitudine </a> che include la soluzione a "Destinazione punto dato distanza e rilevamento dal punto iniziale ".
Stephen Quan,

1
Strettamente correlato: gis.stackexchange.com/questions/2951/… .
whuber

ecco la pagina che collega ai calcoli lat / long [calcoli lat / long] ( movable-type.co.uk/scripts/latlong.html ) anche questa pagina calcoli Lat / long c'è un codice + calcolatrice
Sopra gaseem

Risposte:


21

Sarei curioso di come i risultati di questa formula si confrontano con pe.dll di Esri .

( citazione ).

Un punto {lat, lon} è una distanza d sulla tc radiale dal punto 1 se:

 lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 IF (cos(lat)=0)
    lon=lon1      // endpoint a pole
 ELSE
    lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
 ENDIF

Questo algoritmo è limitato a distanze tali che dlon <pi / 2, cioè quelle che si estendono per circa un quarto della circonferenza terrestre in longitudine. Un algoritmo completamente generale, ma più complicato è necessario se sono consentite distanze maggiori:

 lat =asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 dlon=atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(lat))
 lon=mod( lon1-dlon +pi,2*pi )-pi

Ecco una pagina HTML per i test .


Grazie per la rapida risposta. Lasciami digerire alcune di queste informazioni e tornerò con te. In superficie, tuttavia, sembra perfetto.
Jason Whitehorn,

1
Ho provato il caso diretto usando pe.dll (in realtà libpe.so su solaris) dopo aver recuperato la distanza e l'azimut in avanti dalla pagina html per 32N, 117W a 40N, 82W. Usando 32N, 117W, d = 3255.056515890041, azi = 64.24498012065699, ho ottenuto 40N, 82W (in realtà 82.00000000064).
mkennedy,

3
Eccezionale! Grazie mille per il link all'articolo di Aviation Formulary di Ed Williams, non l'avevo mai visto prima, ma finora ha dimostrato di essere un'ottima lettura. Solo una nota per chiunque guardi questo in futuro, gli input e gli output di questa formula sono TUTTI in radianti, anche la distanza.
Jason Whitehorn,

1
Qual è l'unità di distanza in questa formula?
Karthik Murugan,

1
@KarthikMurugan L' introduzione di Ed dice che le unità di distanza sono in radianti lungo un grande cerchio.
Kirk Kuykendall,

18

Se ti trovassi su un piano, il punto che si trova a r metri di distanza in direzione di un grado a est di nord viene spostato da r * cos (a) nella direzione nord e r * sin (a) nella direzione est. (Queste affermazioni definiscono più o meno il seno e il coseno.)

Anche se non sei su un piano - stai lavorando sulla superficie di un ellissoide curvo che modella la superficie terrestre - qualsiasi distanza inferiore a poche centinaia di chilometri copre una parte così piccola della superficie che per la maggior parte degli scopi pratici può essere considerato piatto. L'unica complicazione rimasta è che un grado di longitudine non copre la stessa distanza di un grado di latitudine. In un modello terrestre sferico, un grado di longitudine è solo cos (latitudine) fintanto che un grado di latitudine. (In un modello ellissoidale, questa è ancora un'approssimazione eccellente, buona a circa 2,5 cifre significative.)

Infine, un grado di latitudine è di circa 10 ^ 7/90 = 111.111 metri. Ora abbiamo tutte le informazioni necessarie per convertire i contatori in gradi:

Lo spostamento verso nord è r * cos (a) / 111111 gradi;

Lo spostamento verso est è r * sin (a) / cos (latitudine) / 111111 gradi.

Ad esempio, ad una latitudine di -0.31399 gradi e una direzione di a = 30 gradi ad est del nord, possiamo calcolare

cos(a) = cos(30 degrees) = cos(pi/6 radians) = Sqrt(3)/2 = 0.866025.
sin(a) = sin(30 degrees) = sin(pi/6 radians) = 1/2 = 0.5.
cos(latitude) = cos(-0.31399 degrees) = cos(-0.00548016 radian) = 0.999984984.
r = 100 meters.
east displacement = 100 * 0.5 / 0.999984984 / 111111 = 0.000450007 degree.
north displacement = 100 * 0.866025 / 111111 = 0.000779423 degree.

Quindi, a partire da (-78.4437, -0.31399), la nuova posizione è a (-78.4437 + 0.00045, -0.31399 + 0.0007794) = (-78.4432, -0.313211).

Una risposta più accurata, nel moderno sistema di riferimento ITRF00, è (-78.4433, -0.313207): si trova a 0,43 metri dalla risposta approssimativa, indicando in questo caso gli errori di approssimazione dello 0,43%. Per ottenere una maggiore precisione, è necessario utilizzare formule di distanza ellissoidali (che sono molto più complicate) o una proiezione conforme ad alta fedeltà con divergenza zero (in modo che il rilevamento sia corretto).


2
+1 per comprendere correttamente il contesto matematico (cioè il suo piano locale)
Frank Conry,

4

Se hai bisogno di una soluzione JavaScript, considera questi functionse questo violino :

var gis = {
  /**
  * All coordinates expected EPSG:4326
  * @param {Array} start Expected [lon, lat]
  * @param {Array} end Expected [lon, lat]
  * @return {number} Distance - meter.
  */
  calculateDistance: function(start, end) {
    var lat1 = parseFloat(start[1]),
        lon1 = parseFloat(start[0]),
        lat2 = parseFloat(end[1]),
        lon2 = parseFloat(end[0]);

    return gis.sphericalCosinus(lat1, lon1, lat2, lon2);
  },

  /**
  * All coordinates expected EPSG:4326
  * @param {number} lat1 Start Latitude
  * @param {number} lon1 Start Longitude
  * @param {number} lat2 End Latitude
  * @param {number} lon2 End Longitude
  * @return {number} Distance - meters.
  */
  sphericalCosinus: function(lat1, lon1, lat2, lon2) {
    var radius = 6371e3; // meters
    var dLon = gis.toRad(lon2 - lon1),
        lat1 = gis.toRad(lat1),
        lat2 = gis.toRad(lat2),
        distance = Math.acos(Math.sin(lat1) * Math.sin(lat2) +
            Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLon)) * radius;

    return distance;
  },

  /**
  * @param {Array} coord Expected [lon, lat] EPSG:4326
  * @param {number} bearing Bearing in degrees
  * @param {number} distance Distance in meters
  * @return {Array} Lon-lat coordinate.
  */
  createCoord: function(coord, bearing, distance) {
    /** http://www.movable-type.co.uk/scripts/latlong.html
    * φ is latitude, λ is longitude, 
    * θ is the bearing (clockwise from north), 
    * δ is the angular distance d/R; 
    * d being the distance travelled, R the earth’s radius*
    **/
    var 
      radius = 6371e3, // meters
      δ = Number(distance) / radius, // angular distance in radians
      θ = gis.toRad(Number(bearing));
      φ1 = gis.toRad(coord[1]),
      λ1 = gis.toRad(coord[0]);

    var φ2 = Math.asin(Math.sin(φ1)*Math.cos(δ) + 
      Math.cos(φ1)*Math.sin(δ)*Math.cos(θ));

    var λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(δ)*Math.cos(φ1),
      Math.cos(δ)-Math.sin(φ1)*Math.sin(φ2));
    // normalise to -180..+180°
    λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; 

    return [gis.toDeg(λ2), gis.toDeg(φ2)];
  },
  /**
   * All coordinates expected EPSG:4326
   * @param {Array} start Expected [lon, lat]
   * @param {Array} end Expected [lon, lat]
   * @return {number} Bearing in degrees.
   */
  getBearing: function(start, end){
    var
      startLat = gis.toRad(start[1]),
      startLong = gis.toRad(start[0]),
      endLat = gis.toRad(end[1]),
      endLong = gis.toRad(end[0]),
      dLong = endLong - startLong;

    var dPhi = Math.log(Math.tan(endLat/2.0 + Math.PI/4.0) / 
      Math.tan(startLat/2.0 + Math.PI/4.0));

    if (Math.abs(dLong) > Math.PI) {
      dLong = (dLong > 0.0) ? -(2.0 * Math.PI - dLong) : (2.0 * Math.PI + dLong);
    }

    return (gis.toDeg(Math.atan2(dLong, dPhi)) + 360.0) % 360.0;
  },
  toDeg: function(n) { return n * 180 / Math.PI; },
  toRad: function(n) { return n * Math.PI / 180; }
};

Quindi, se vuoi calcolare una nuova coordinata, può essere così:

var start = [15, 38.70250];
var end = [21.54967, 38.70250];
var total_distance = gis.calculateDistance(start, end); // meters
var percent = 10;
// this can be also meters
var distance = (percent / 100) * total_distance;
var bearing = gis.getBearing(start, end);
var new_coord = gis.createCoord(icon_coord, bearing, distance);

2

Ho funzionato in ObjectiveC. La chiave qui è sapere che sono necessari punti lat / lng in radianti e che è necessario riconvertirli in lat / lng dopo aver applicato l'equazione. Inoltre, sappi che distanza e tc sono in radianti.

Ecco l'equazione originale:

 lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
 IF (cos(lat)=0)
    lon=lon1      // endpoint a pole
 ELSE
    lon=mod(lon1-asin(sin(tc)*sin(d)/cos(lat))+pi,2*pi)-pi
 ENDIF

Qui è implementato in ObjC dove il radiante è un radiante misurato in senso antiorario da N (ad es. PI / 2 è W, PI è S, 2 PI / 3 è E) e la distanza è in chilometri.

+ (CLLocationCoordinate2D)displaceLatLng:(CLLocationCoordinate2D)coordinate2D withRadian:(double)radian
                            withDistance:(CGFloat)distance {
  double lat1Radians = coordinate2D.latitude * (M_PI / 180);
  double lon1Radians = coordinate2D.longitude * (M_PI / 180);
  double distanceRadians = distance / 6371;
  double lat = asin(sin(lat1Radians)*cos(distanceRadians)+cos(lat1Radians)*sin(distanceRadians)
      *cos(radian));
  double lon;
  if (cos(lat) == 0) {
    lon = lon1Radians;
  } else {
    lon = fmodf((float) (lon1Radians - asin(sin(radian) * sin(distanceRadians) / cos(lat)) + M_PI),
        (float) (2 * M_PI)) - M_PI;
  }
  double newLat = lat * (180 / M_PI);
  double newLon = lon * (180 / M_PI);
  return CLLocationCoordinate2DMake(newLat, newLon);
}

Sto cercando una soluzione in cui voglio ottenere 4 lat, long dal punto in cui mi trovo a 50 miglia a nord, 50 miglia a ovest, 50 miglia a est e così via ... Nella risposta sopra puoi spiegare come posso ottenere Questo ?
Rahul Vyas,

1

Se sei interessato a una migliore precisione, c'è Vincenty . (Il collegamento è al modulo "diretto", che è esattamente quello che stai cercando).

Ci sono alcune implementazioni esistenti, se cerchi il codice.

Inoltre, una domanda: non stai assumendo che il viaggiatore mantenga lo stesso orientamento per l'intero viaggio, vero? In tal caso, questi metodi non rispondono alla domanda giusta. (Faresti meglio a proiettare verso il mercatore, disegnare una linea retta, quindi annullare la proiezione del risultato.)


Ottima domanda, nonostante le parole nella mia domanda che potrebbero aver indicato che stavo calcolando una destinazione per un viaggiatore, non lo sono. Buon punto però. Questo principalmente per poter calcolare un'area di delimitazione (su un piccolo ordine, diciamo 50 miglia) ... Spero che abbia senso.
Jason Whitehorn,

gis.stackexchange.com/questions/3264/… aveva la stessa domanda (costruire aree di delimitazione da un punto e distanza)
Dan S.

0

Ecco una soluzione Python:

    def displace(self, theta, distance):
    """
    Displace a LatLng theta degrees counterclockwise and some
    meters in that direction.
    Notes:
        http://www.movable-type.co.uk/scripts/latlong.html
        0 DEGREES IS THE VERTICAL Y AXIS! IMPORTANT!
    Args:
        theta:    A number in degrees.
        distance: A number in meters.
    Returns:
        A new LatLng.
    """
    theta = np.float32(theta)

    delta = np.divide(np.float32(distance), np.float32(E_RADIUS))

    def to_radians(theta):
        return np.divide(np.dot(theta, np.pi), np.float32(180.0))

    def to_degrees(theta):
        return np.divide(np.dot(theta, np.float32(180.0)), np.pi)

    theta = to_radians(theta)
    lat1 = to_radians(self.lat)
    lng1 = to_radians(self.lng)

    lat2 = np.arcsin( np.sin(lat1) * np.cos(delta) +
                      np.cos(lat1) * np.sin(delta) * np.cos(theta) )

    lng2 = lng1 + np.arctan2( np.sin(theta) * np.sin(delta) * np.cos(lat1),
                              np.cos(delta) - np.sin(lat1) * np.sin(lat2))

    lng2 = (lng2 + 3 * np.pi) % (2 * np.pi) - np.pi

    return LatLng(to_degrees(lat2), to_degrees(lng2))

-2

Uso l'approccio descritto di seguito per determinare la coordinata successiva in base al rilevamento e alla distanza dalla coordinata precedente. Ho problemi di precisione con altri approcci che ho letto da Internet.

Lo uso per determinare l'area di terra, che è un poligono, e tracciare quel poligono in Google Earth. Un titolo di terra ha cuscinetti e distanze scritti in questo modo: "NorthOrSouth x gradi y minuti EastOrWest, z metri al punto n;".

Quindi, partendo dalle coordinate indicate del punto di riferimento, per prima cosa calcolo la distanza per un grado di latitudine e un grado di longitudine usando la formula haversine perché questo varia a seconda della posizione. Quindi determino la coordinata successiva dalla formula del seno e del coseno della trigonometria.

Di seguito è riportato il javascript:

var mapCenter = new google.maps.LatLng(referencePointLatitude, referencePointLongitude); //the ref point lat and lon must be given, usually a land mark (BLLM)
var latDiv = latDiv(mapCenter); //distance per one degree latitude in this location
var lngDiv = lngDiv(mapCenter); //distance per one degree longitude in this location
var LatLng2 = NextCoord(PrevCoord,NorthOrSouth,x,y,EastOrWest,z); //next coordinate given the bearing and distance from previous coordinate
var Lat2 = LatLng2.lat(); //next coord latitude in degrees
var Lng2 = LatLng2.lng(); //next coord longitude in degrees
var polygon=[p1,p2,...,pn-1,pn,p1]; //p1,p2,etc. are the coordinates of points of the polygon, i.e. the land Title. Be sure to close the polygon to the point of beginning p1
var area = Area(polygon); //area of the polygon in sq.m.
function NextCoord(PrevCoord,NorthOrSouth,x,y,EastOrWest,z) {
  var angle = ( x + ( y / 60 ) ) * Math.PI / 180;
  var a = 1;
  var b = 1;
  if (NorthOrSouth == 'South') { a = -1; }
  if (EastOrWest == 'West') { b = -1; }
  var nextLat = PrevCoord.lat() +  ( a * z * Math.cos(angle) / latDiv );
  var nextLng = PrevCoord.lng() +  ( b * z * Math.sin(angle) / lngDiv );
  var nextCoord = new google.maps.LatLng(nextLat, nextLng);
  return nextCoord;
}
function latDiv(mapCenter) {
  var p1 = new google.maps.LatLng(mapCenter.lat()-0.5, mapCenter.lng());
  var p2 = new google.maps.LatLng(mapCenter.lat()+0.5, mapCenter.lng());
  return dist(p1,p2);
}
function lngDiv(mapCenter) {
  var p3 = new google.maps.LatLng(mapCenter.lat(), mapCenter.lng()-0.5);
  var p4 = new google.maps.LatLng(mapCenter.lat(), mapCenter.lng()+0.5);
  return dist(p3,p4);
}
function dist(pt1, pt2) {
    var dLat  = ( pt2.lat() - pt1.lat() ) * Math.PI / 180;
    var dLng = ( pt2.lng() - pt1.lng() ) * Math.PI / 180;
    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +                 
            Math.cos(rad(pt1.lat())) * Math.cos(rad(pt2.lat())) *
            Math.sin(dLng/2) * Math.sin(dLng/2);
    var R = 6372800; //earth's radius
    var distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return distance;
}
function Area(polygon) {
  var xPts=[];
  for (i=1; i&lt;polygon.length; i++) {
    xPts[i-1] = ( polygon[i].lat() - polygon[0].lat() ) * latDiv;
  }
  var yPts=[];
  for (i=1; i&lt;polygon.length; i++) {
    yPts[i-1] = ( polygon[i].lng() - polygon[0].lng() ) * lngDiv;
  }
  var area = 0;
  j = polygon.length-2;
  for (i=0; i&lt;polygon.length-1; i++) {
    area = area +  ( xPts[j] + xPts[i] ) * ( yPts[j] - yPts[i] );
    j = i;
  }
  area = Math.abs(area/2);
  return area;
}

1
La formula cartesiana per l'area poligonale che si tenta di applicare qui non è applicabile alle distanze e agli angoli calcolati su una superficie curva (come uno sferoide). Questa formula fa un ulteriore errore usando latitudine e longitudine come se fossero coordinate cartesiane. Le uniche circostanze in cui il suo uso potrebbe essere preso in considerazione sarebbero esattamente quelle (per poligoni molto piccoli) in cui la formula haversine non è comunque necessaria. Nel complesso sembra che questo codice funzioni troppo duramente per nessun guadagno.
whuber
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.