1

I have an Object of class 'SpatialPolygonsDataFrame' with the structure shown below.

What's the secret to getting to the @area slot? Ultimately, I'm trying to filter this file so that I'm only left with polygons >= 64m^2. The resultant shapefile will be imported to OCAD (orienteering mapping software), in an attempt to semi-automatically map a class of vegetation.

I thought I had some understanding of S4 classes, but clearly I have a gap.

among many similar failed attempts:

> xfile@polygons$Polygons@Polygons$Polygon
Error: trying to get slot "Polygons" from an object of a basic class ("NULL") with no slots
> xfile@polygons$Polygons@Polygons
Error: trying to get slot "Polygons" from an object of a basic class ("NULL") with no slots
> xfile@polygons@Polygons
Error: trying to get slot "Polygons" from an object of a basic class ("list") with no slots
> xfile@polygons$Polygons
NULL
> xfile@polygons$'Polygons'@Polygons$'Polygon'@area
Error: trying to get slot "Polygons" from an object of a basic class ("NULL") with no slots
> xfile@polygons$'Polygons'@Polygons
Error: trying to get slot "Polygons" from an object of a basic class ("NULL") with no slots
> xfile@polygons$'Polygons'
NULL
> xfile@polygons$area
NULL
> xfile$polygons
NULL
> xfile@polygons
( the whole file )
Formal class 'SpatialPolygonsDataFrame' [package "sp"] with 5 slots
  ..@ data       :'data.frame': 1 obs. of  1 variable:
  .. ..$ layer: num 1
  ..@ polygons   :List of 1
  .. ..$ :Formal class 'Polygons' [package "sp"] with 5 slots
  .. .. .. ..@ Polygons :List of 191
  .. .. .. .. ..$ :Formal class 'Polygon' [package "sp"] with 5 slots
  .. .. .. .. .. .. ..@ labpt  : num [1:2] 513021 4319805
  .. .. .. .. .. .. ..@ area   : num 9.64
  .. .. .. .. .. .. ..@ hole   : logi FALSE
  .. .. .. .. .. .. ..@ ringDir: int 1
  .. .. .. .. .. .. ..@ coords : num [1:19, 1:2] 513018 513025 513025 513024 513024 ...
  .. .. .. .. ..$ :Formal class 'Polygon' [package "sp"] with 5 slots
  .. .. .. .. .. .. ..@ labpt  : num [1:2] 513053 4319805
  .. .. .. .. .. .. ..@ area   : num 1.85
  .. .. .. .. .. .. ..@ hole   : logi FALSE
  .. .. .. .. .. .. ..@ ringDir: int 1
  .. .. .. .. .. .. ..@ coords : num [1:5, 1:2] 513051 513054 513054 513051 513051 ...

......

.. .. .. .. .. [list output truncated]
  .. .. .. ..@ plotOrder: int [1:191] 18 118 101 168 110 148 61 135 166 90 ...
  .. .. .. ..@ labpt    : num [1:2] 513181 4319787
  .. .. .. ..@ ID       : chr "0"
  .. .. .. ..@ area     : num 38206
  ..@ plotOrder  : int 1
  ..@ bbox       : num [1:2, 1:2] 512793 4319369 513380 4319807
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:2] "x" "y"
  .. .. ..$ : chr [1:2] "min" "max"
  ..@ proj4string:Formal class 'CRS' [package "sp"] with 1 slot
  .. .. ..@ projargs: chr "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs"

3
  • Have you tried reading the shapefile with the package sf by using x <-st_read("myshapefile.shp")? Perhaps it might be easier that way.
    – saQuist
    Feb 3, 2021 at 9:25
  • @saQuist - Yes, I'd fiddled with that but did not see anything relating to 'area' in the resulting x, str(s), or attributes(x). I'd actually tried this with the subject file with the same no 'area' info results. My workaround was to write it to disk with st_write() and read it back with shapefile() and voilà, I could then see (but not maneuver to) 'area.' Any hints on how to navigate the shapefile() result as shown above, or how to locate the (hidden?) area slot/location in the result of st_read()? Thanks.
    – G. Moore
    Feb 3, 2021 at 12:53
  • You can use gArea from the rgeos package to return area for each polygon on a sp class object. Mar 24, 2021 at 2:09

2 Answers 2

1

Ref Hong Ooi's answer in https://stackoverflow.com/questions/42059378/cant-use-operator-on-s4-objects-stored-in-a-list-in-r, I've worked out that the area value for individual Polygons in this object can be accessed via:

> xfile@polygons[[1]]@Polygons[[1]]@area
[1] 9.642906
> xfile@polygons[[1]]@Polygons[[2]]@area
[1] 1.854405

Now I just need to figure out how to filter on area. I think I see a lapply() fcn in my future.

Instead of lapply(), I found my way to purrr::keep():

("mba" = "minimum blob area")

smthd.filt@polygons <- smthd@polygons %>% purrr::keep(~min(.@area) >= mba)

0

Rather than working through the headache of accessing slots using sapply, I would recommend just using rgeos::gArea (or it's sf analog sf::st_area) and filter using the vector. In R ordering is your friend. In this case, the resulting vector from rgeos::gArea will match the SpatialPolygonsDataFrame so, you can just pull indices from the vector to operate on the polygons.

Add packages (note; adding sf for the nc polygon data). Need to reproject the polygons so they are in a planar coordinate space. We are wrapping st_read in as to coerce to a sp class object. This is a good thing to know when working with sp objects as, st_read is much faster than rgdal:readOGR and allow for queries.

library(sf)
library(sp)
library(rgeos)

nc = as(st_read(system.file("shape/nc.shp", 
        package="sf")), "Spatial")
  nc <- spTransform(nc, CRS("ESRI:102003"))

We can now get the area of each polygon and subset based on the area. We use which to return an index of our query [area < 1270326706] which is the mean of the polygon areas. Using a standard bracket index (operating on rows) and a minus sign, we can remove the polygons from our which index.

( a <- gArea(nc, byid=TRUE) )
nc.sub <- nc[-which(a < 1270326706),]

plot(nc)
  plot(nc.sub, col="blue", add=TRUE)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.