使用ArcGIS DEsktop和/或R计算到最近点的距离(以公里为单位)(以纬度/经度给出)?


10

我在ArcGIS中有两个点数据集,这两个数据集都是在WGS84纬度/经度坐标中给出的,并且点分布在整个世界上。我想找到数据集A中与数据集B中每个点最近的点,并以公里为单位获取它们之间的距离。

这似乎是对Near工具的完美使用,但这使我得到了输入点坐标系中的结果:即十进制度。我知道我可以重新投影数据,但是我(从这个问题)收集到(很难(如果不是不可能的话))很难找到一个可以给出全世界准确距离的投影。

该问题的答案建议使用Haversine公式直接使用纬度-经度坐标来计算距离。有没有办法做到这一点,并使用ArcGIS获得以km为单位的结果?如果没有,什么是解决此问题的最佳方法?

Answers:


6

尽管这不是ArcGIS解决方案,但是可以通过从Arc导出点并使用包中的spDists 函数在R中解决您的问题sp。如果设置,该函数将查找参考点与点矩阵之间的距离(以公里为单位)longlat=T

这是一个快速而肮脏的示例:

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)

谢谢-这似乎是最现实的解决方案。查看文档,看来我只能在参考点和一组其他点之间执行此操作,因此我必须循环执行才能遍历所有点。您知道在R中执行此操作的更有效方法吗?
robintw 2012年

无需循环,您可以为函数提供两组点,它将返回一个矩阵,其中每个点组合之间都有距离。编辑答案以包含示例代码。
艾伦·2012年


2

您需要适用于纬度/经度的距离计算。Vincenty是我要使用的那个(精度为0.5mm)。我以前玩过它,并且使用起来并不难。

代码有点长,但是可以用。给定WGS中的两个点,它将返回以米为单位的距离。

您可以将其用作ArcGIS中的Python脚本,也可以将其包装在另一个简单地迭代两个Point Shapefile并为您构建距离矩阵的脚本周围。或者,通过找到2-3个最接近的要素(以避免地球曲率的复杂性)来馈送GENERATE_NEAR_TABLE的结果可能更容易。

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

我使用“点距离”工具对小型数据集进行了类似的体验。这样做无法自动在数据集A中找到最接近的点,但至少会获得具有有用的km或m结果的表格输出。在下一步中,您可以从表格中选择到数据集B每个点的最短距离。

但是这种方法将取决于数据集中的点数。它可能不适用于大型数据集。


谢谢你的建议。但是,我看不到这对我有什么帮助。根据文档(help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//…),“距离是输入要素坐标系的线性单位。”,这就是我的输入要素纬度/经度一定会给我十进制的结果吗?(我这里没有要测试的装有ArcGIS的计算机)
robintw 2012年

在这种情况下,我可能会通过在数据表中添加X和Y字段,然后单击“计算几何”,选择以米为单位的X和Y,来使用“快速而肮脏的”解决方案。如果无法选择此选项,请更改MXD的坐标系。我之前在做一个项目,我的客户想要每个Shape文件中的long / lat,X / Y和Gauss-Krueger R / H值。为了避免复杂的计算,最简单的方法就是简单地更改投影并计算几何形状。
basto 2012年

0

如果您需要高精度和健壮的测地线测量,请使用GeographicLib,它是用几种编程语言(包括C ++,Java,MATLAB,Python等)原生编写的。

有关文学参考,请参见CFF Karney(2013)“大地测量学算法”。请注意,这些算法比Vincenty的算法更健壮和准确,例如在对映体附近。

要计算两点之间的距离(以米为单位),请s12反大地测量解中获取距离属性。例如,使用适用于Python 的geolib库软件包

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}

或设置便捷功能,该功能也可以将米转换为公里:

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

现在要查找列表A和之间的最接近点B,每个点都有100个点:

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)和(84.70326937581333,156.9784597422855)之间22.481公里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.