检查点是否在由多个多边形/孔组成的空间对象中

时间:2014-02-23 17:08:17

标签: r polygon geospatial spatial point-in-polygon

我有一个SpatialPolygonsDataFrame,包含11589个“polygons”类对象。这些对象中的10699个正好由1个多边形组成,但其余对象由多个多边形(2到22)组成。

如果一个对象由多个多边形组成,则可能有三种情况:

  1. 有时,这些额外的多边形描述了“多边形”类对象中第一个多边形描述的地理区域中的“洞”。
  2. 有时,这些额外的多边形描述了额外的地理区域,即该区域的形状非常复杂,并通过将多个部分放在一起来描述。
  3. 有时,它可能是两者的混合,1)和2)。
  4. Stackoverflow帮助我正确地绘制了这样一个空间对象(Plot spatial area defined by multiple polygons)。

    但是,我仍然无法回答如何确定一个点(由经度/纬度定义)是否在多边形中。

    以下是我的代码。 我尝试在point.in.polygon包中应用函数sp,但发现它无法处理由多个多边形/孔组成的对象。

    # Load packages
    # ---------------------------------------------------------------------------
    library(maptools)
    library(rgdal)
    library(rgeos)
    library(ggplot2)
    library(sp) 
    
    
    # Get data
    # ---------------------------------------------------------------------------
    # Download shape information from the internet
    URL <- "http://www.geodatenzentrum.de/auftrag1/archiv/vektor/vg250_ebenen/2012/vg250_2012-01-01.utm32s.shape.ebenen.zip"
    td <- tempdir()
    setwd(td)
    temp <- tempfile(fileext = ".zip")
    download.file(URL, temp)
    unzip(temp)
    
    # Get shape file
    shp <- file.path(tempdir(),"vg250_0101.utm32s.shape.ebenen/vg250_ebenen/vg250_gem.shp")
    
    # Read in shape file
    map <- readShapeSpatial(shp, proj4string = CRS("+init=epsg:25832"))
    
    # Transform the geocoding from UTM to Longitude/Latitude
    map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))
    
    
    # Pick an geographic area which consists of multiple polygons
    # ---------------------------------------------------------------------------
    # Output a frequency table of areas with N polygons 
    nPolys <- sapply(map@polygons, function(x)length(x@Polygons))
    
    # Get geographic area with the most polygons
    polygon.with.max.polygons <- which(nPolys==max(nPolys))
    
    # Get shape for the geographic area with the most polygons
    Poly.coords <- map[which(nPolys==max(nPolys)),]
    
    
    # Plot
    # ---------------------------------------------------------------------------
    # Plot region without Google maps (ggplot2) 
    plot(Poly.coords,  col="lightgreen")
    
    
    # Find if a point is in a polygon 
    # ---------------------------------------------------------------------------
    # Define points 
    points_of_interest <- data.frame(long=c(10.5,10.51,10.15,10.4), 
                         lat =c(51.85,51.72,51.81,51.7),
                         id  =c("A","B","C","D"), stringsAsFactors=F)
    
    # Plot points
    points(points_of_interest$long, points_of_interest$lat, pch=19)
    

    enter image description here

2 个答案:

答案 0 :(得分:19)

您只需使用gContains(...)包中的rgeos即可完成此操作。

gContains(sp1,sp2)

根据sp1是否包含在sp1中返回逻辑。唯一的细微差别是sp2必须是一个SpatialPoints对象,它必须与sp1具有相同的投影。要做到这一点,你会做这样的事情:

point <- data.frame(lon=10.2, lat=51.7)
sp2   <- SpatialPoints(point,proj4string=CRS(proj4string(sp1)))
gContains(sp1,sp2)

以下是基于您上一个问题的答案的工作示例。

library(rgdal)   # for readOGR(...)
library(rgeos)   # for gContains(...)
library(ggplot2)

setwd("< directory with all your files >")
map <- readOGR(dsn=".", layer="vg250_gem", p4s="+init=epsg:25832")
map <- spTransform(map, CRS("+proj=longlat +datum=WGS84"))

nPolys <- sapply(map@polygons, function(x)length(x@Polygons))
region <- map[which(nPolys==max(nPolys)),]

region.df <- fortify(region)
points <- data.frame(long=c(10.5,10.51,10.15,10.4), 
                     lat =c(51.85,51.72,51.81,51.7),
                     id  =c("A","B","C","D"), stringsAsFactors=F)

ggplot(region.df, aes(x=long,y=lat,group=group))+
  geom_polygon(fill="lightgreen")+
  geom_path(colour="grey50")+
  geom_point(data=points,aes(x=long,y=lat,group=NULL, color=id), size=4)+
  coord_fixed()

这里,A点在主多边形中,B点在湖(洞)中,C点在岛上,D点完全在该区域之外。因此,此代码使用gContains(...)

检查所有点
sapply(1:4,function(i)
  list(id=points[i,]$id,
       gContains(region,SpatialPoints(points[i,1:2],proj4string=CRS(proj4string(region))))))
#    [,1] [,2]  [,3] [,4] 
# id "A"  "B"   "C"  "D"  
#    TRUE FALSE TRUE FALSE

答案 1 :(得分:5)

既然你可以使用“多边形点”程序,这显然还没有合适的设计来处理R中的多边形情况(实际上我觉得有点奇怪),你需要循环通过多个多边形中的每一个。现在的诀窍是,如果你在奇数个多边形内,你就在多边形内部。如果你在偶数个多边形内,那么你实际上是在形状之外。

使用光线交叉的多边形测试点应该能够处理这个,只需要确保将所有顶点传递给原始的point.in.polygon测试,但我不确定R使用哪个机制所以我只能给你上面的偶数/奇数建议。

我也找到了这段代码,不确定它是否会有所帮助:

require(sp)
require(rgdal)
require(maps)

# read in bear data, and turn it into a SpatialPointsDataFrame
bears <- read.csv("bear-sightings.csv")
coordinates(bears) <- c("longitude", "latitude")

# read in National Parks polygons
parks <- readOGR(".", "10m_us_parks_area")

# tell R that bear coordinates are in the same lat/lon reference system
# as the parks data -- BUT ONLY BECAUSE WE KNOW THIS IS THE CASE!
proj4string(bears) <- proj4string(parks)

# combine is.na() with over() to do the containment test; note that we
# need to "demote" parks to a SpatialPolygons object first
inside.park <- !is.na(over(bears, as(parks, "SpatialPolygons")))

# what fraction of sightings were inside a park?
mean(inside.park)
## [1] 0.1720648

# use 'over' again, this time with parks as a SpatialPolygonsDataFrame
# object, to determine which park (if any) contains each sighting, and
# store the park name as an attribute of the bears data
bears$park <- over(bears, parks)$Unit_Name

# draw a map big enough to encompass all points (but don't actually plot
# the points yet), then add in park boundaries superimposed upon a map
# of the United States
plot(coordinates(bears), type="n")
map("world", region="usa", add=TRUE)
plot(parks, border="green", add=TRUE)
legend("topright", cex=0.85,
    c("Bear in park", "Bear not in park", "Park boundary"),
    pch=c(16, 1, NA), lty=c(NA, NA, 1),
    col=c("red", "grey", "green"), bty="n")
title(expression(paste(italic("Ursus arctos"),
    " sightings with respect to national parks")))

# now plot bear points with separate colors inside and outside of parks
points(bears[!inside.park, ], pch=1, col="gray")
points(bears[inside.park, ], pch=16, col="red")

# write the augmented bears dataset to CSV
write.csv(bears, "bears-by-park.csv", row.names=FALSE)

# ...or create a shapefile from the points
writeOGR(bears, ".", "bears-by-park", driver="ESRI Shapefile")