ArcGIS DEsktop ve / veya R kullanarak km'ye en yakın noktalara (enlem / boylam olarak verilen) mesafeyi hesapla?


10

ArcGIS'te her ikisi de WGS84 enlem / boylam koordinatlarında verilen iki nokta veri setim var ve puanlar tüm dünyaya yayılıyor. Dataset A'da Dataset B'deki her noktaya en yakın noktayı bulmak ve aralarındaki mesafeyi kilometre olarak almak istiyorum.

Bu, Yakın aracının mükemmel bir kullanımı gibi görünüyor, ancak bu bana giriş noktalarının koordinat sisteminde sonuçlar veriyor: yani ondalık dereceler. Verileri yeniden yansıtabileceğimi biliyorum, ancak ( bu sorudan ) tüm dünyada doğru mesafeler verecek bir projeksiyon bulmanın zor olduğunu (imkansız değilse) topladım.

Bu sorunun cevabı, doğrudan enlem-boylam koordinatlarını kullanarak mesafeleri hesaplamak için Haversine formülünü kullanmanızı önerir. ArcGIS kullanarak bunu yapmanın ve km'de sonuç almanın bir yolu var mı? Değilse, buna yaklaşmanın en iyi yolu nedir?

Yanıtlar:


6

Bu bir ArcGIS çözümü olmasa da, sorununuzu puanlarınızı Arc'dan dışa aktararak spDists ve sppaketteki işlevi kullanarak R'de çözebilirsiniz . İşlev, ayarladıysanız kilometre olarak bir referans noktası (noktaları) ile nokta matrisi arasındaki mesafeleri bulur longlat=T.

İşte hızlı ve kirli bir örnek:

library(sp)
## Sim up two sets of 100 points, we'll call them set a and set b:
a <- SpatialPoints(coords = data.frame(x = rnorm(100, -87.5), y = rnorm(100, 30)), proj4string=CRS("+proj=longlat +datum=WGS84"))
b <- SpatialPoints(coords = data.frame(x = rnorm(100, -88.5), y = rnorm(100, 30.5)), proj4string=CRS("+proj=longlat +datum=WGS84"))

## Find the distance from each point in a to each point in b, store
##    the results in a matrix.
results <- spDists(a, b, longlat=T)

Teşekkürler - bu en gerçekçi çözüm gibi görünüyor. Dokümanlara baktığımda bunu sadece bir referans noktası ve diğer noktalar arasında yapabileceğim gibi görünüyor, bu yüzden tüm noktalarımdan geçmek için bir döngüde yapmam gerekecekti. Bunu R'de yapmanın daha etkili bir yolunu biliyor musunuz?
robintw

Döngü gerekmez, fonksiyona iki nokta seti verebilirsiniz ve her nokta kombinasyonu arasındaki mesafeleri içeren bir matris döndürür. Yanıt, örnek kodu içerecek şekilde düzenlendi.
allen


2

Enlem / Boylam ile çalışan bir mesafe hesaplamasına ihtiyacınız vardır. Kullanacağım Vincenty (0.5mm doğruluk). Daha önce onunla oynadım ve kullanmak çok zor değil.

Kod biraz uzun, ama çalışıyor. WGS'de iki puan verildiğinde, metre cinsinden bir mesafe geri dönecektir.

Bunu ArcGIS'de bir Python betiği olarak kullanabilir veya iki Nokta Shapefiles'i üzerinde yinelenen ve sizin için bir mesafe matrisi oluşturan başka bir betiğin etrafına sarabilirsiniz. Veya, GENERATE_NEAR_TABLE sonuçlarını en yakın 2-3 özelliği bularak beslemek daha kolaydır (dünyanın eğriliğinin komplikasyonlarından kaçınmak için).

import math

ellipsoids = {
    #name        major(m)   minor(m)            flattening factor
    'WGS-84':   (6378137,   6356752.3142451793, 298.25722356300003),
    'GRS-80':   (6378137,   6356752.3141403561, 298.25722210100002),
    'GRS-67':   (6378160,   6356774.5160907144, 298.24716742700002),

}

def distanceVincenty(lat1, long1, lat2, long2, ellipsoid='WGS-84'):
    """Computes the Vicenty distance (in meters) between two points
    on the earth. Coordinates need to be in decimal degrees.
    """
    # Check if we got numbers
    # Removed to save space
    # Check if we know about the ellipsoid
    # Removed to save space
    major, minor, ffactor = ellipsoids[ellipsoid]
    # Convert degrees to radians
    x1 = math.radians(lat1)
    y1 = math.radians(long1)
    x2 = math.radians(lat2)
    y2 = math.radians(long2)
    # Define our flattening f
    f = 1 / ffactor
    # Find delta X
    deltaX = y2 - y1
    # Calculate U1 and U2
    U1 = math.atan((1 - f) * math.tan(x1))
    U2 = math.atan((1 - f) * math.tan(x2))
    # Calculate the sin and cos of U1 and U2
    sinU1 = math.sin(U1)
    cosU1 = math.cos(U1)
    sinU2 = math.sin(U2)
    cosU2 = math.cos(U2)
    # Set initial value of L
    L = deltaX
    # Set Lambda equal to L
    lmbda = L
    # Iteration limit - when to stop if no convergence
    iterLimit = 100
    while abs(lmbda) > 10e-12 and iterLimit >= 0:
        # Calculate sine and cosine of lmbda
        sin_lmbda = math.sin(lmbda)
        cos_lmbda = math.cos(lmbda)
        # Calculate the sine of sigma
        sin_sigma = math.sqrt(
                (cosU2 * sin_lmbda) ** 2 + 
                (cosU1 * sinU2 - 
                 sinU1 * cosU2 * cos_lmbda) ** 2
        )
        if sin_sigma == 0.0:
            # Concident points - distance is 0
            return 0.0
        # Calculate the cosine of sigma
        cos_sigma = (
                    sinU1 * sinU2 + 
                    cosU1 * cosU2 * cos_lmbda
        )
        # Calculate sigma
        sigma = math.atan2(sin_sigma, cos_sigma)
        # Calculate the sine of alpha
        sin_alpha = (cosU1 * cosU2 * math.sin(lmbda)) / (sin_sigma)
        # Calculate the square cosine of alpha
        cos_alpha_sq = 1 - sin_alpha ** 2
        # Calculate the cosine of 2 sigma
        cos_2sigma = cos_sigma - ((2 * sinU1 * sinU2) / cos_alpha_sq)
        # Identify C
        C = (f / 16.0) * cos_alpha_sq * (4.0 + f * (4.0 - 3 * cos_alpha_sq))
        # Recalculate lmbda now
        lmbda = L + ((1.0 - C) * f * sin_alpha * (sigma + C * sin_sigma * (cos_2sigma + C * cos_sigma * (-1.0 + 2 * cos_2sigma ** 2)))) 
        # If lambda is greater than pi, there is no solution
        if (abs(lmbda) > math.pi):
            raise ValueError("No solution can be found.")
        iterLimit -= 1
    if iterLimit == 0 and lmbda > 10e-12:
        raise ValueError("Solution could not converge.")
    # Since we converged, now we can calculate distance
    # Calculate u squared
    u_sq = cos_alpha_sq * ((major ** 2 - minor ** 2) / (minor ** 2))
    # Calculate A
    A = 1 + (u_sq / 16384.0) * (4096.0 + u_sq * (-768.0 + u_sq * (320.0 - 175.0 * u_sq)))
    # Calculate B
    B = (u_sq / 1024.0) * (256.0 + u_sq * (-128.0 + u_sq * (74.0 - 47.0 * u_sq)))
    # Calculate delta sigma
    deltaSigma = B * sin_sigma * (cos_2sigma + 0.25 * B * (cos_sigma * (-1.0 + 2.0 * cos_2sigma ** 2) - 1.0/6.0 * B * cos_2sigma * (-3.0 + 4.0 * sin_sigma ** 2) * (-3.0 + 4.0 * cos_2sigma ** 2)))
    # Calculate s, the distance
    s = minor * A * (sigma - deltaSigma)
    # Return the distance
    return s

1

Nokta Mesafesi aracını kullanarak küçük veri kümeleriyle benzer deneyimler yaşadım. Bunu yaptığınızda, Veri Kümesi A'nızdaki en yakın noktaları otomatik olarak bulamazsınız, ancak en azından faydalı km veya m sonuçları içeren bir tablo çıktısı alabilirsiniz. Bir sonraki adımda, Veri Kümesi B'nin her noktasına olan en kısa mesafeyi tablodan seçebilirsiniz.

Ancak bu yaklaşım, veri kümelerinizdeki noktaların miktarına bağlı olacaktır. Büyük veri kümeleriyle düzgün çalışmayabilir.


Önerin için teşekkürler. Ancak bunun bana nasıl yardımcı olacağını göremiyorum. Dokümanlara göre ( help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//… ) "uzaklık, girdi özellikleri koordinat sisteminin doğrusal birimindedir." lat / lon kesinlikle bana ondalık derece sonuçları verecektir? (Test etmek için üzerinde ArcGIS bulunan bir
makinem yok

Bu durumda, veri tablonuza X ve Y alanları ekleyerek ve "X ve Y'yi metre cinsinden seçerek Geometri Hesapla" yı tıklayarak "hızlı ve kirli" bir çözüm kullanırım. Bu seçeneği belirlemek mümkün değilse, MXD'nizin koordinat sistemini değiştirin. Daha önce bir proje üzerinde çalışıyordum ki müşterim her Shape dosyasında uzun / lat, X / Y ve Gauss-Krueger R / H değerleri istiyordu. Karmaşık hesaplamayı önlemek için, sadece projeksiyonları değiştirmek ve geometriyi hesaplamak en kolay yoluydu.
basto

0

Yüksek hassasiyetli ve sağlam jeodezik ölçümlere ihtiyacınız varsa , C ++, Java, MATLAB, Python vb. Gibi çeşitli programlama dillerinde yerel olarak yazılan GeographicLib'i kullanın .

Edebi referans için bakınız CFF Karney (2013) "Jeodezikler için Algoritmalar" . Bu algoritmaların, örneğin antipodların yakınında, Vincenty'nin algoritmasından daha sağlam ve doğru olduğunu unutmayın.

İki nokta arasındaki metre cinsinden mesafeyi hesaplamak için s12, ters jeodezik çözümden uzaklık niteliğini alın . Örneğin, Python için geographiclib paketi ile

from geographiclib.geodesic import Geodesic
g = Geodesic.WGS84.Inverse(-41.32, 174.81, 40.96, -5.50)
print(g)  # shows:
{'a12': 179.6197069334283,
 'azi1': 161.06766998615873,
 'azi2': 18.825195123248484,
 'lat1': -41.32,
 'lat2': 40.96,
 'lon1': 174.81,
 'lon2': -5.5,
 's12': 19959679.26735382}

Veya metreden kilometreye dönüşen bir rahatlık işlevi yapın:

dist_km = lambda a, b: Geodesic.WGS84.Inverse(a[0], a[1], b[0], b[1])['s12'] / 1000.0
a = (-41.32, 174.81)
b = (40.96, -5.50)
print(dist_km(a, b))  # 19959.6792674 km

Şimdi listeler arasındaki en yakın noktayı Ave Bher biri 100 puana sahip olanı bulmak için :

from random import uniform
from itertools import product
A = [(uniform(-90, 90), uniform(-180, 180)) for x in range(100)]
B = [(uniform(-90, 90), uniform(-180, 180)) for x in range(100)]
a_min = b_min = min_dist = None
for a, b in product(A, B):
    d = dist_km(a, b)
    if min_dist is None or d < min_dist:
        min_dist = d
        a_min = a
        b_min = b

print('%.3f km between %s and %s' % (min_dist, a_min, b_min))

(84.57916462672875, 158.67545706102192) ve (84.70326937581333, 156.9784597422855) arasında 22.481 km

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.