简体   繁体   English

计算R中多边形和点之间的距离

[英]Calculating the distance between polygon and point in R

I have a, not necessarily convex, polygon without intersections and a point outside this polygon. 我有一个不一定是凸的多边形,没有交叉点,也有一个点在这个多边形之外。 I'm wondering how calculate the Euclidian distance most efficiently in a 2-dimensional space. 我想知道如何在二维空间中最有效地计算欧几里德距离。 Is there a standard method in R ? R有标准方法吗?

My first idea was to calculate the minimum distance of all the lines of the polygon (extended infinitely so they are line, not line pieces) and then calculate the distance from the point to each individual line using the start of the line piece and Pythagoras. 我的第一个想法是计算多边形的所有线的最小距离(无限延伸,因此它们是线,而不是线条),然后使用线条和毕达哥拉斯的起点计算从点到每条单独线的距离。

Do you know about a package that implements an efficient algorithm? 你知道一个实现高效算法的包吗?

You could use the rgeos package and the gDistance method. 您可以使用rgeos包gDistance方法。 This will require you to prepare your geometries, creating spgeom objects from the data you have (I assume it is a data.frame or something similar). 这将要求您准备几何,从您拥有的数据创建spgeom对象(我假设它是data.frame或类似的东西)。 The rgeos documentation is very detailed (see the PDF manual of the package from the CRAN page), this is one relevant example from the gDistance documentation: rgeos文档非常详细(请参阅CRAN页面中的软件包的PDF手册),这是gDistance文档中的一个相关示例:

pt1 = readWKT("POINT(0.5 0.5)")
pt2 = readWKT("POINT(2 2)")
p1 = readWKT("POLYGON((0 0,1 0,1 1,0 1,0 0))")
p2 = readWKT("POLYGON((2 0,3 1,4 0,2 0))")
gDistance(pt1,pt2)
gDistance(p1,pt1)
gDistance(p1,pt2)
gDistance(p1,p2)

readWKT is included in rgeos as well. readWKT也包含在rgeos中。

Rgeos is based on the GEOS library, one of the de facto standards in geometric computing. Rgeos基于GEOS库,是几何计算中事实上的标准之一。 If you don't feel like reinventing the wheel, this is a good way to go. 如果您不想重新发明轮子,这是一个很好的方法。

I decided to return and write up a theoretical solution, just for posterity. 我决定回归并撰写理论解决方案,仅供后人使用。 This isn't the most concise example, but it is fully transparent for those who want to know how to go about solving a problem like this by hand. 这不是最简洁的例子,但对于那些想要知道如何手动解决这类问题的人来说,它是完全透明的。

The theoretical algorithm 理论算法

First, our assumptions. 首先,我们的假设。

  1. We assume the polygon's vertices specify the points of a polygon in a rotational order going clockwise or counter-clockwise and the lines of the polygon cannot intersect. 我们假设多边形的顶点以顺时针或逆时针的旋转顺序指定多边形的点,并且多边形的线不能相交。 This means we have a normal geometric polygon, and not some strangely-defined vector graphic shape. 这意味着我们有一个普通的几何多边形,而不是一些奇怪定义的矢量图形。
  2. We assume this is a set of Cartesian coordinates, using 'x' and 'y' values that represent location on a 2-dimensional plane. 我们假设这是一组笛卡尔坐标,使用'x'和'y'值表示二维平面上的位置。
  3. We assume the point must be outside the internal area of the polygon. 我们假设该点必须在多边形的内部区域之外。
  4. Finally, we assume that the distance desired is the minimum distance between the point and all of the infinite number of points on the perimeter of the polygon. 最后,我们假设所需的距离是该点与多边形周边上所有无限多个点之间的最小距离。

Now before coding, we should write out in basic terms what we want to do. 在编码之前,我们应该用基本的术语写出我们想要做的事情。 We can assume that the shortest distance between the polygon and the point outside the polygon will always be one of two things: a vertex of the polygon or a point on a line between two vertices. 我们可以假设多边形和多边形外部点之间的最短距离将始终是两个事物之一:多边形的顶点或两个顶点之间的线上的点。 With this in mind, we do the following steps: 考虑到这一点,我们执行以下步骤:

  1. Calculate the distances between all vertices and the target point. 计算所有顶点和目标点之间的距离。
  2. Find the two vertices closest to the target point. 找到最接近目标点的两个顶点。
  3. If either: (a) the two closest vertices are not adjacent or (b) the inside angles of either of the two vertices is greater or equal to 90 degrees, then the closest vertex is the closest point. 如果:(a)两个最接近的顶点不相邻或(b)两个顶点中的任一个的内角大于或等于90度,则最近的顶点是最近的点。 Calculate the distance between the closest point and the target point. 计算最近点和目标点之间的距离。
  4. Otherwise, calculate the height of the triangle formed between the two points. 否则,计算两点之间形成的三角形的高度。

We're basically just looking to see if a vertex is closest to the point or if a point on a line is closest to the point. 我们基本上只是想看一个顶点是否最接近该点,或者一条线上的点是否最接近该点。 We have to use a few trig functions to make this work. 我们必须使用一些trig函数来完成这项工作。

The code 编码

To make this work properly, we want to avoid any 'for' loops and want to only use vectorized functions when looking at the entire list of polygon vertices. 为了使其正常工作,我们希望避免任何“for”循环,并且在查看整个多边形顶点列表时只想使用矢量化函数。 Luckily, this is pretty easy in R. We accept a data frame with 'x' and 'y' columns for our polygon's vertices, and we accept a vector with one 'x' and 'y' value for the point's location. 幸运的是,在R中这很容易。我们接受一个带有'x'和'y'列的数据框用于我们的多边形顶点,我们接受一个带有一个'x'和'y'值的矢量用于该点的位置。

get_Point_Dist_from_Polygon <- function(.polygon, .point){

    # Calculate all vertex distances from the target point.
    vertex_Distance <- sqrt((.point[1] - .polygon$x)^2 + (.point[2] - .polygon$y)^2)

    # Select two closest vertices.
    min_1_Index <- which.min(vertex_Distance)
    min_2_Index <- which.min(vertex_Distance[-min_1_Index])

    # Calculate lengths of triangle sides made of
    # the target point and two closest points.
    a <- vertex_Distance[min_1_Index]
    b <- vertex_Distance[min_2_Index]
    c <- sqrt(diff(.polygon$x[c(min_1_Index, min_2_Index)])^2 + diff(.polygon$y[c(min_1_Index, min_2_Index)])^2)

    if(abs(min_1_Index - min_2_Index) != 1 |
        acos((b^2 + c^2 - a^2)/(2*b*c)) >= pi/2 | 
        acos((a^2 + c^2 - b^2)/(2*a*c)) >= pi/2
        ){
        # Step 3 of algorithm.
        return(vertex_Distance[min_1_Index])
    } else {
        # Step 4 of algorithm.
        # Here we are using the law of cosines.
        return(sqrt((a+b-c) * (a-b+c) * (-a+b+c) * (a+b+c)) / (2 * c))
    }
}

Demo 演示

polygon <- read.table(text="
x,  y
0,  1
1,  0.8
2,  1.3
3,  1.4
2.5,0.3
1.5,0.5
0.5,0.1", header=TRUE, sep=",")

point <- c(3.2, 4.1)

get_Point_Dist_from_Polygon(polygon, point)
# 2.707397

Otherwise: 除此以外:

p2poly <- function(pt, poly){
    # Closing the polygon
    if(!identical(poly[1,],poly[nrow(poly),])){poly<-rbind(poly,poly[1,])}
    # A simple distance function
    dis <- function(x0,x1,y0,y1){sqrt((x0-x1)^2 +(y0-y1)^2)}
    d <- c()   # Your distance vector
    for(i in 1:(nrow(poly)-1)){
        ba <- c((pt[1]-poly[i,1]),(pt[2]-poly[i,2])) #Vector BA
        bc <- c((poly[i+1,1]-poly[i,1]),(poly[i+1,2]-poly[i,2])) #Vector BC
        dbc <- dis(poly[i+1,1],poly[i,1],poly[i+1,2],poly[i,2]) #Distance BC
        dp <- (ba[1]*bc[1]+ba[2]*bc[2])/dbc          #Projection of A on BC
        if(dp<=0){ #If projection is outside of BC on B side
            d[i] <- dis(pt[1],poly[i,1],pt[2],poly[i,2])
            }else if(dp>=dbc){ #If projection is outside of BC on C side
                d[i] <- dis(poly[i+1,1],pt[1],poly[i+1,2],pt[2])
                }else{ #If projection is inside of BC
                    d[i] <- sqrt(abs((ba[1]^2 +ba[2]^2)-dp^2))
                    }
        }
    min(d)
    }

Example: 例:

pt <- c(3,2)
triangle <- matrix(c(1,3,2,3,4,2),byrow=T, nrow=3)
p2poly(pt,triangle)
[1] 0.3162278

I used distm() function in geosphere package to calculate the distence when points and apexes are presented in coordinate system. 我用distm()函数在geosphere包时被呈现点和顶点坐标系来计算distence。 Also, you can easily make some alternation by substitude dis <- function(x0,x1,y0,y1){sqrt((x0-x1)^2 +(y0-y1)^2)} for distm() . 此外,您可以通过dis <- function(x0,x1,y0,y1){sqrt((x0-x1)^2 +(y0-y1)^2)}轻松地进行一些替换,用于distm()

algo.p2poly <- function(pt, poly){
  if(!identical(poly[1,],poly[nrow(poly),])){poly<-rbind(poly,poly[1,])}
  library(geosphere)
  n <- nrow(poly) - 1
  pa <- distm(pt, poly[1:n, ])
  pb <- distm(pt, poly[2:(n+1), ])
  ab <- diag(distm(poly[1:n, ], poly[2:(n+1), ]))
  p <- (pa + pb + ab) / 2
  d <- 2 * sqrt(p * (p - pa) * (p - pb) * (p - ab)) / ab
  cosa <- (pa^2 + ab^2 - pb^2) / (2 * pa * ab)
  cosb <- (pb^2 + ab^2 - pa^2) / (2 * pb * ab)
  d[which(cosa <= 0)] <- pa[which(cosa <= 0)]
  d[which(cosb <= 0)] <- pb[which(cosb <= 0)]
  return(min(d))
}

Example: 例:

poly <- matrix(c(114.33508, 114.33616,
                 114.33551, 114.33824,
                 114.34629, 114.35053,
                 114.35592, 114.35951, 
                 114.36275, 114.35340, 
                 114.35391, 114.34715,
                 114.34385, 114.34349,
                 114.33896, 114.33917,
                 30.48271, 30.47791,
                 30.47567, 30.47356, 
                 30.46876, 30.46851,
                 30.46882, 30.46770, 
                 30.47219, 30.47356,
                 30.47499, 30.47673,
                 30.47405, 30.47723, 
                 30.47872, 30.48320),
               byrow = F, nrow = 16)
pt1 <- c(114.33508, 30.48271)
pt2 <- c(114.6351, 30.98271)
algo.p2poly(pt1, poly)
algo.p2poly(pt2, poly)

Outcome: 结果:

> algo.p2poly(pt1, poly)
[1] 0
> algo.p2poly(pt2, poly)
[1] 62399.81

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM