Menghitung Lintang / Bujur X mil dari titik?

46

Saya ingin menemukan titik lintang dan bujur yang diberi bantalan, jarak, dan garis lintang dan bujur yang mulai.

Ini tampaknya kebalikan dari pertanyaan ini ( Jarak antara titik lat / panjang ).

Saya telah melihat formula haversine dan berpikir bahwa perkiraan dunia mungkin cukup dekat.

Saya berasumsi bahwa saya perlu menyelesaikan rumus haversine untuk lat / long saya yang tidak diketahui, apakah ini benar? Adakah situs web bagus yang membicarakan hal semacam ini? Sepertinya itu akan umum, tetapi googling saya hanya memunculkan pertanyaan yang mirip dengan yang di atas.

Apa yang sebenarnya saya cari hanyalah formula untuk ini. Saya ingin memberikannya lat awal / lng, bantalan, dan jarak (mil atau kilometer) dan saya ingin keluar dari pasangan lat / lng yang mewakili di mana seseorang akan berakhir jika mereka bepergian bersama rute itu.

Jason Whitehorn
sumber
Apakah Anda mencari alat yang melakukan ini (seperti Esri's pe.dll) atau formula?
Kirk Kuykendall
Maaf saya tidak spesifik ... Saya mencari formula. Saya akan memperbarui pertanyaan saya menjadi lebih spesifik.
Jason Whitehorn
Banyak sampel matematika yang berhasil ada di sini <a href=" movable-type.co.uk/scripts/latlong.html"> Hitung jarak, bantalan, dan lainnya di antara titik Lintang / Bujur </a> yang mencakup solusi untuk "Tujuan" titik yang diberikan jarak dan bantalan dari titik awal ".
Stephen Quan
1
Terkait erat: gis.stackexchange.com/questions/2951/… .
whuber
inilah halaman yang menautkan ke perhitungan lat / long [perhitungan Lat / long] ( movable-type.co.uk/scripts/latlong.html ) juga halaman ini perhitungan Lat / long ada kode + kalkulator
Abo gaseem

Jawaban:

21

Saya ingin tahu bagaimana hasil dari formula ini dibandingkan dengan Esri's pe.dll .

( kutipan ).

Titik {lat, lon} adalah jarak d pada radial tc dari titik 1 jika:

 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

Algoritma ini terbatas pada jarak sedemikian rupa sehingga dlon <pi / 2, yaitu yang memperpanjang sekitar kurang dari seperempat keliling bumi dalam bujur. Algoritma yang sepenuhnya umum, tetapi lebih rumit diperlukan jika jarak yang lebih jauh diizinkan:

 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

Inilah halaman html untuk pengujian .

Kirk Kuykendall
sumber
Terima kasih atas balasan cepatnya. Biarkan saya mencerna beberapa informasi ini, dan saya akan kembali dengan Anda. Namun di permukaan, ini terlihat tepat.
Jason Whitehorn
1
Saya mencoba kasus langsung menggunakan pe.dll (sebenarnya libpe.so di solaris) setelah mengambil jarak dan forward azimuth dari halaman html untuk 32N, 117W ke 40N, 82W. Menggunakan 32N, 117W, d = 3255.056515890041, azi = 64.24498012065699, saya mendapat 40N, 82W (sebenarnya 82.00000000064).
mkennedy
3
Luar biasa! Terima kasih banyak atas tautannya ke artikel Formulasi Aviation oleh Ed Williams, saya belum pernah melihat ini sebelumnya, tetapi sejauh ini terbukti sebagai bacaan yang bagus. Sebagai catatan bagi siapa pun yang melihat ini di masa depan, input dan output dari rumus ini adalah SEMUA dalam radian, bahkan jaraknya.
Jason Whitehorn
1
Berapa satuan jarak dalam rumus ini?
Karthik Murugan
1
@KarthikMurugan Ed intro mengatakan unit jarak jauh adalah dalam radian sepanjang lingkaran besar.
Kirk Kuykendall
18

Jika Anda berada di pesawat, maka titik yang r meter dengan arah dari suatu derajat timur utara digantikan oleh r * cos (a) dalam arah utara dan r * sin (a) dalam arah timur. (Pernyataan ini kurang lebih mendefinisikan sinus dan cosinus.)

Meskipun Anda tidak berada di dalam pesawat - Anda sedang bekerja di permukaan ellipsoid yang melengkung yang memodelkan permukaan bumi - jarak apa pun yang kurang dari beberapa ratus kilometer mencakup bagian kecil dari permukaan sehingga untuk tujuan praktis dapat dilakukan dianggap datar. Satu-satunya komplikasi yang tersisa adalah bahwa satu derajat garis bujur tidak mencakup jarak yang sama dengan tingkat garis lintang. Dalam model Bumi berbentuk bola, satu derajat garis bujur hanya cos (garis lintang) sepanjang satu derajat garis lintang. (Dalam model ellipsoidal, ini masih merupakan perkiraan yang sangat baik, bagus hingga sekitar 2,5 angka signifikan.)

Akhirnya, satu derajat garis lintang adalah sekitar 10 ^ 7/90 = 111.111 meter. Kami sekarang memiliki semua informasi yang diperlukan untuk mengonversi meter ke derajat:

Perpindahan ke utara adalah r * cos (a) / 111111 derajat;

Perpindahan ke timur adalah r * sin (a) / cos (lintang) / 111111 derajat.

Sebagai contoh, pada garis lintang -0.31399 derajat dan arah a = 30 derajat timur utara, kita dapat menghitung

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.

Di mana, mulai dari (-78.4437, -0.31399), lokasi baru berada di (-78.4437 + 0.00045, -0.31399 + 0.0007794) = (-78.4432, -0.313211).

Jawaban yang lebih akurat, dalam sistem referensi ITRF00 modern, adalah (-78.4433, -0.313207): ini adalah 0,43 meter dari perkiraan jawaban, menunjukkan perkiraan keliru sebesar 0,43% dalam kasus ini. Untuk mencapai akurasi yang lebih tinggi, Anda harus menggunakan formula jarak ellipsoidal (yang jauh lebih rumit) atau proyeksi konformitas kesetiaan tinggi dengan divergensi nol (sehingga bantalannya benar).

whuber
sumber
2
+1 untuk memahami konteks matematis dengan benar (yaitu bidangnya secara lokal)
Frank Conry
4

Jika Anda memerlukan solusi JavaScript, pertimbangkan ini functionsdan biola ini :

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; }
};

Jadi, jika Anda ingin menghitung koordinat baru, bisa seperti ini:

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);
Jonatas Walker
sumber
2

Saya mendapatkan ini berfungsi di ObjectiveC. Kuncinya di sini adalah mengetahui bahwa Anda memerlukan titik lat / lng di radian dan Anda perlu mengubahnya kembali ke lat / lng setelah menerapkan persamaan. Juga, ketahuilah bahwa jarak dan tc ada dalam radian.

Inilah persamaan aslinya:

 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

Ini diimplementasikan dalam ObjC di mana radian adalah radian yang diukur berlawanan arah jarum jam dari N (misalnya PI / 2 adalah W, PI adalah S, 2 PI / 3 adalah E) dan jarak adalah dalam kilometer.

+ (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);
}
BigCheesy
sumber
Saya mencari solusi di mana saya ingin mendapatkan 4 lat, rindu dari titik di mana saya berdiri hingga 50 mil utara, 50 mil barat, 50 mil timur dan seterusnya ... Dalam jawaban di atas dapatkah Anda menjelaskan bagaimana saya dapat mencapai ini?
Rahul Vyas
1

Jika Anda tertarik pada akurasi yang lebih baik, ada Vincenty . (Tautan ke bentuk 'langsung', yang persis seperti yang Anda cari).

Ada beberapa implementasi yang ada, jika Anda mencari kode.

Juga, sebuah pertanyaan: Anda tidak berasumsi bahwa pelancong mempertahankan hal yang sama untuk seluruh perjalanan, bukan? Jika demikian, maka metode ini tidak menjawab pertanyaan yang tepat. (Anda akan lebih baik memproyeksikan ke mercator, menggambar garis lurus, lalu tidak memproyeksikan hasilnya.)

Dan S.
sumber
Pertanyaan yang sangat bagus, meskipun kata-kata dalam pertanyaan saya yang mungkin mengindikasikan bahwa saya menghitung tujuan untuk seorang musafir, saya tidak. Poin yang bagus. Ini terutama agar saya bisa menghitung area yang membatasi (dengan pesanan kecil, katakan 50 mil) ... Saya harap itu masuk akal.
Jason Whitehorn
gis.stackexchange.com/questions/3264/… memiliki pertanyaan yang sama (membangun area pembatas dari titik & jarak)
Dan S.
0

Berikut ini adalah solusi 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))
Liam Horne
sumber
-2

Saya menggunakan pendekatan yang dijelaskan di bawah ini dalam menentukan koordinat berikutnya mengingat arah dan jarak dari koordinat sebelumnya. Saya memiliki masalah pada akurasi dengan pendekatan lain yang saya baca dari internet.

Saya menggunakan ini dalam menentukan luas tanah, yang merupakan poligon, dan plot poligon itu di google earth. Judul tanah memiliki bantalan dan jarak yang ditulis dengan cara ini: "NorthOrSouth x derajat y menit EastOrWest, z meter ke titik n;".

Jadi, mulai dari koordinat titik referensi yang diberikan, pertama-tama saya menghitung jarak per satu derajat lintang dan satu derajat bujur menggunakan rumus haversine karena ini bervariasi tergantung pada lokasi. Lalu saya menentukan koordinat berikutnya dari rumus trigonometri sinus dan kosinus.

Di bawah ini adalah 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;
}
Dante Valenzuela
sumber
1
Formula Cartesian untuk area poligon yang Anda coba terapkan di sini tidak berlaku untuk jarak dan sudut yang dihitung pada permukaan melengkung (seperti spheroid). Rumus ini membuat kesalahan tambahan dengan menggunakan lintang dan bujur seolah-olah mereka adalah koordinat Kartesius. Satu-satunya keadaan di mana penggunaannya dapat dipertimbangkan adalah persis (untuk poligon yang sangat kecil) di mana formula haversine tidak perlu. Secara keseluruhan tampaknya kode ini bekerja terlalu keras tanpa hasil.
whuber