Calcular la distancia en km a los puntos más cercanos (en lat / long) usando ArcGIS DEsktop y / o R?

10

Tengo dos conjuntos de datos de puntos en ArcGIS, los cuales se dan en coordenadas WGS84 lat / lon y los puntos se extienden por todo el mundo. Me gustaría encontrar el punto más cercano en el conjunto de datos A a cada punto en el conjunto de datos B, y obtener la distancia entre ellos en kilómetros.

Esto parece un uso perfecto de la herramienta Near, pero eso me da resultados en el sistema de coordenadas de los puntos de entrada: es decir, grados decimales. Sé que podría volver a proyectar los datos, pero deduzco ( de esta pregunta ) que es difícil (si no imposible) encontrar una proyección que proporcione distancias precisas en todo el mundo.

Las respuestas a esa pregunta sugieren usar la fórmula de Haversine para calcular distancias usando las coordenadas de latitud y longitud directamente. ¿Hay alguna manera de hacer esto y obtener un resultado en km usando ArcGIS? Si no, ¿cuál es la mejor manera de abordar esto?

robintw
fuente

Respuestas:

6

Aunque esta no es una solución de ArcGIS, su problema puede resolverse en R exportando sus puntos desde Arc y utilizando la spDists función del sppaquete. La función encuentra las distancias entre un punto (s) de referencia y una matriz de puntos, en kilómetros si establece longlat=T.

Aquí hay un ejemplo rápido y sucio:

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)
Allen
fuente
Gracias, esta parece ser la solución más realista. Al mirar los documentos, parece que solo puedo hacer esto entre un punto de referencia y un conjunto de otros puntos, por lo que tendría que hacerlo en un bucle para recorrer todos mis puntos. ¿Conoces una forma más eficiente de hacer esto en R?
robintw
No se necesitan bucles, puede darle a la función dos conjuntos de puntos y devolverá una matriz con distancias entre cada combinación de puntos. Respuesta editada para incluir código de ejemplo.
Allen
2

Necesita un cálculo de distancia que funcione con Lat / Long. Vincenty es el que usaría (precisión de 0,5 mm). He jugado con él antes, y no es demasiado difícil de usar.

El código es un poco largo, pero funciona. Dados dos puntos en WGS, devolverá una distancia en metros.

Puede usar esto como un script de Python en ArcGIS, o envolverlo alrededor de otro script que simplemente itera sobre los dos archivos de forma de puntos y crea una matriz de distancia para usted. O, probablemente sea más fácil alimentar los resultados de GENERATE_NEAR_TABLE al encontrar las 2-3 características más cercanas (para evitar complicaciones de la curvatura de la tierra).

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
Michalis Avraam
fuente
1

Hice experiencias similares con pequeños conjuntos de datos usando la herramienta Distancia de puntos. Al hacerlo, no puede encontrar automáticamente los puntos más cercanos en su conjunto de datos A, pero al menos obtener una salida de tabla con resultados útiles de km o m. En el siguiente paso, puede seleccionar la distancia más corta a cada punto del conjunto de datos B fuera de la tabla.

Pero este enfoque dependerá de la cantidad de puntos en sus conjuntos de datos. Es posible que no funcione correctamente con grandes conjuntos de datos.

basto
fuente
Gracias por la sugerencia. Sin embargo, no puedo ver cómo eso me ayudará. De acuerdo con los documentos ( help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//… ) "la distancia está en la unidad lineal del sistema de coordenadas de características de entrada", que como mis características de entrada son en lat / lon seguramente me dará resultados en grados decimales? (No tengo una máquina con ArcGIS aquí para probar)
robintw
En este caso, probablemente usaría una solución "rápida y sucia" agregando campos X e Y en su tabla de datos y haciendo clic en Calcular geometría eligiendo X e Y en el medidor. Si no es posible elegir esta opción, cambie el sistema de coordenadas de su MXD. Estaba trabajando en un proyecto antes, donde mi cliente quería valores R / H largos / lat, X / Y y Gauss-Krueger, todos en cada archivo Shape. Para evitar cálculos complicados, simplemente cambiar las proyecciones y calcular la geometría fue la forma más fácil de resolverlo.
basto
0

Si necesita mediciones geodésicas robustas y de alta precisión, use GeographicLib , que está escrito de forma nativa en varios lenguajes de programación, incluidos C ++, Java, MATLAB, Python, etc.

Ver CFF Karney (2013) "Algorithms for geodesics" para una referencia literaria. Tenga en cuenta que estos algoritmos son más robustos y precisos que el algoritmo de Vincenty, por ejemplo, cerca de las antípodas.

Para calcular la distancia en metros entre dos puntos, obtenga el s12atributo de distancia de la solución geodésica inversa . Por ejemplo, con el GeographicLib paquete para 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 realice una función de conveniencia, que también convierte de metros a kilómetros:

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

Ahora para encontrar el punto más cercano entre las listas Ay B, cada una con 100 puntos:

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 entre (84.57916462672875, 158.67545706102192) y (84.70326937581333, 156.9784597422855)

Mike T
fuente