Calcola la distanza in km dai punti più vicini (indicati in lat / long) usando ArcGIS DEsktop e / o R?


10

Ho due set di dati punti in ArcGIS, entrambi indicati nelle coordinate lat / lon WGS84 e i punti sono sparsi in tutto il mondo. Vorrei trovare il punto più vicino nel set di dati A per ciascun punto nel set di dati B e ottenere la distanza tra loro in chilometri.

Questo sembra un uso perfetto dello strumento Vicino, ma ciò mi dà risultati nel sistema di coordinate dei punti di input: cioè, gradi decimali. So di poter riproiettare i dati, ma ritengo ( da questa domanda ) che sia difficile (se non impossibile) trovare una proiezione che fornisca distanze precise in tutto il mondo.

Le risposte a questa domanda suggeriscono di usare la formula di Haversine per calcolare le distanze usando direttamente le coordinate di latitudine-longitudine. C'è un modo per farlo e ottenere un risultato in km usando ArcGIS? In caso contrario, qual è il modo migliore per affrontarlo?

Risposte:


6

Sebbene questa non sia una soluzione ArcGIS, il tuo problema può essere risolto in R esportando i tuoi punti da Arc e usando la spDists funzione dal sppacchetto. La funzione trova le distanze tra un punto (i) di riferimento e una matrice di punti, in chilometri se impostati longlat=T.

Ecco un esempio veloce e sporco:

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)

Grazie - questa sembra la soluzione più realistica. Guardando i documenti sembra che posso farlo solo tra un punto di riferimento e una serie di altri punti, quindi dovrei farlo in un ciclo per esaminare tutti i miei punti. Conosci un modo più efficiente per farlo in R?
Robintw,

Nessun ciclo necessario, puoi assegnare alla funzione due serie di punti e restituirà una matrice con le distanze tra ciascuna combinazione di punti. Risposta modificata per includere il codice di esempio.
Allen,


2

È necessario un calcolo della distanza che funzioni con Lat / Long. Vincenty è quello che vorrei usare (precisione 0,5 mm ). Ci ho già giocato prima e non è difficile da usare.

Il codice è un po 'lungo, ma funziona. Dati due punti in WGS, restituirà una distanza in metri.

Puoi usarlo come uno script Python in ArcGIS o avvolgerlo in un altro script che scorre semplicemente sui due Shapefile Point e crea una matrice di distanza per te. Oppure, è probabilmente più semplice alimentare i risultati di GENERATE_NEAR_TABLE con la ricerca delle 2-3 caratteristiche più vicine (per evitare complicazioni della curvatura terrestre).

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

Ho fatto esperienze simili con piccoli set di dati usando lo strumento Point Distance. In questo modo, non è possibile trovare automaticamente i punti più vicini nel set di dati A, ma almeno ottenere un output di tabella con risultati km o m utili. Nel passaggio successivo è possibile selezionare la distanza più breve da ciascun punto del set di dati B fuori dal tavolo.

Ma questo approccio dipende dalla quantità di punti nei set di dati. Potrebbe non funzionare correttamente con set di dati di grandi dimensioni.


Grazie per il suggerimento Tuttavia, non riesco a vedere come questo mi aiuterà. Secondo i documenti ( help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//… ) "la distanza è nell'unità lineare del sistema di coordinate delle funzioni di input", che come le mie caratteristiche di input sono in lat / lon sicuramente mi darà risultati in gradi decimali? (Non ho una macchina con ArcGIS su di essa per testare)
robintw

In questo caso, probabilmente utilizzerei una soluzione "veloce e sporca" aggiungendo i campi X e Y nel tuo datatable e fare clic su Calcola geometria scegliendo X e Y in metro. Se non è possibile selezionare questa opzione, cambia il sistema di coordinate del tuo MXD. Prima stavo lavorando a un progetto, in cui il mio cliente voleva valori long / lat, X / Y e Gauss-Krueger R / H tutti in ogni file Shape. Per evitare calcoli complicati, cambiare semplicemente le proiezioni e calcolare la geometria è stato il modo più semplice in cui ha funzionato.
basto

0

Se hai bisogno di misurazioni geodetiche affidabili e di alta precisione, usa GeographicLib , che è scritto in modo nativo in diversi linguaggi di programmazione, tra cui C ++, Java, MATLAB, Python, ecc.

Vedi CFF Karney (2013) "Algorithms for geodesics" per un riferimento letterario. Si noti che questi algoritmi sono più robusti e precisi dell'algoritmo di Vincenty, ad esempio vicino agli antipodi.

Per calcolare la distanza in metri tra due punti, ottenere l' s12attributo della distanza dalla soluzione geodetica inversa . Ad esempio, con il pacchetto geographiclib per Python

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}

O crea una funzione di convenienza, che converte anche da metri a chilometri:

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

Ora per trovare il punto più vicino tra gli elenchi Ae B, ciascuno con 100 punti:

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))

22.481 km tra (84.57916462672875, 158.67545706102192) e (84.70326937581333, 156.9784597422855)

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.