简体   繁体   中英

Verify vector coordinates in polygon matrix in R

From a given matrix and vector in R . Which is the short code way to verify if a point is in an area? (Target: get True or False )

What I tried at the moment:

library(sf)
library(spatialEco)
# Lat long provided

Area <-  SpatialPolygons(list(Polygons(list(Polygon(do.call(rbind,list(
c(45.406164, 28.429255),
  c(44.182204, 27.395851),
  c(43.699651, 29.055894),
  c(45.259422,30.474260))))),'1')))

point=data.frame(t(c(44.590467, 28.057815)))
names(point)=c("lat", "long")
coordinates(point)=~lat+long

point.in.poly(point, Area)

My expectation is to get a feature( True/False ) to confirm if it is inside or not.

By doing the same with a point that i know that it is not inside the polygon I see that the only that changes at into the output of point.in.poly is poly.ids giving NA . Is this correct output to accomplish what I want to check?

point2=data.frame(t(c(-44.590467, -28.057815)))
point.in.poly(point2, Area)

And of course, is there any shorter code way?

In my opinion there are two approaches, depdendently on how robust soution you require. If you do not need a high accuracy solution you can easily write the function yourself using ray tracing ( ray-line intersection and point in polygon ).

What I mean by "not accurate solution" is that you agree to the fact that points that lie exactly on the line or in the polygon nodes etc. will be very, very, very rarely improperly classified. In x64 computers this issue is neglectible unless you do build a high accuracy triangulation algorithm or something similar (which you probably wouldn't do in R).

In such a case it is really simple thing to do, but as mentioned before such function can sometimes fail due to floating point arithmetic precision flaws. More on this issue can be found in computational geometry books and in docs of CGAL .

The basic function could look like:

section.ray.intersection <- function(p,d,a,b)
# this function tests if a ray r=[p,d] intersects the segment [a,b]
# p - point c(x,y)
# d - ray direction c(x,y)
# a - one of the nodes of the section [a,b] (not ordered with respect to computational geometry standards)
# b - remaining node of the section [a,b]
{
   v1 = p-a
   v2 = b-a
   v3 = c(-d[2],d[1])

   t1 = (v2[1]*v1[2] - v2[2]*v1[1])/(v2 %*% v3)
   t2 = (v1 %*% v3)/(v2 %*% v3) 

   return(t1 >=0. & (t2 >= 0. & t2 <= 1.0)) 
}

in.polygon <- function(px,py,Ax,Ay)
# basic function testing if the point is in the polygon
# px - x coordinate of the point
# py - y coordinate of the point
# Ax - a vector of x coordinates of the polygon
# Ay - a vector of y coordinates of the polygon    
{
    n <- length(Ax) # gent the number of nodes of the polygon
    if (n != length(Ay)) return(NA) # can be ommitted if Ax and Ay are always valid

    d = runif(2) # get the random vector of the direction
    is = 0 # number of intersections
    for (i in 2:n) # go trough all edges
        is <- is + section.ray.intersection(c(px,py),d,c(Ax[i-1],Ay[i-1]),c(Ax[i],Ay[i])) # and count TRUEs

    return( !(is %% 2==0)) # if the number of intersections is odd the point is inside
}

And the sample usage:

# libs
library(ggplot2)
library(purrr)
library(dplyr)

# polygon
Ax <- c(0,1,2,0.25,0)
Ay <- c(0,0,1,1.00,0)
# points
N = 1000 # number of points 
p <- data.frame(x = rnorm(N,0.5,.4), y= rnorm(N,0.5,.4)) # data frame od point coordinates

# apply the function to points - this is a "for loop" equivalent
p$is.inside <- apply(p, 1, function(X){return(in.polygon(X[1],X[2],Ax,Ay))})

# make a plot
ggplot() + geom_path(data=data.frame(x=Ax,y=Ay),aes(x,y)) + geom_point(data=p,aes(x,y,col = is.inside))

在此处输入图像描述

However if you need a bit more accurate solution you can make multiple tests and then use some voting methodology. For example assume that during a single test a risk of improper classification is about p=0.001 (which in reality is much lower) then the probability that in all three tests (with random direction vectors) it will be classified improperly would be only ppp = 0.000000001. A modified version of the function could look like:

in.polygon.robust <- function(px,py,Ax,Ay)
{
    n <- length(Ax)
    if (n != length(Ay)) return(NA) # can be ommitted if Ax and Ay are always valid

    d1 <- runif(2) # direction 1
    d2 <- runif(2) # direction 2
    d3 <- runif(2) # direction 3
    is1 <- 0 # number of intersections in direction 1
    is2 <- 0 # number of intersections in direction 2
    is3 <- 0 # number of intersections in direction 3
    for (i in 2:n) # go trough all edges
    {
        P <- c(px,py)
        Ap1 <- c(Ax[i-1],Ay[i-1])
        Ap2 <- c(Ax[i],Ay[i])
        # count intersections
        is1 <- is1 + section.ray.intersection(P,d1,Ap1,Ap2)
        is2 <- is2 + section.ray.intersection(P,d2,Ap1,Ap2)   
        is3 <- is3 + section.ray.intersection(P,d3,Ap1,Ap2)
    }
    r <- (is1 %% 2==0) + (is2 %% 2==0) + (is3 %% 2==0) # sum the even outcomes
    return( !(r>=2)) # return voting outcome
}

However if that still is insufficient, you have an option to use for example CGAL via RCpp or ( R bindings ) or if the license does not suit you, then you have to write your own robust algorithm, which is a really hard thing to do.

Other thing is efficiency issue. If you wish to apply that test to lots of polygons (especially described with large amounts of edges) than you have to use some specific data structures in order to make everyting acceptably efficient. More details on that can be found in this book or in this book . And write that all in C or C++.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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