简体   繁体   中英

Bounding box for each polygon in SpatialPolygonsDataFrame R

How can I get the bbox for each polygon in polys ?

pp <- cbind(coordinates(polys),as.data.frame(polys))  

gives me lonlat only but I would like to get lat1lat2 and lon1lon2 for each polygon.

polys=new("SpatialPolygonsDataFrame"
        , data = structure(list(NAMRB_EN = structure(c(6L, 45L, 2L, 41L, 31L, 
    3L, 40L, 14L, 42L, 7L, 26L, 12L, 38L, 25L, 36L, 9L, 39L, 27L, 
    32L, 19L, 43L, 21L, 15L, 22L, 20L, 9L, 17L, 11L, 33L, 44L, 37L, 
    13L, 8L, 5L, 18L, 30L, 16L, 10L, 1L, 29L, 34L, 23L, 24L, 28L, 
    4L, 35L), .Label = c("Albany", "Arctic Ocean Seaboard", "Arnaud", 
    "Atlantic Ocean Seaboard", "Attawapiskat", "Back", "Baleine", 
    "Broadback", "Churchill", "Columbia", "Eastmain", "Feuilles", 
    "Fraser", "George", "Grande Baleine", "Harricanaw", "Hayes", 
    "Hudson Bay Seaboard", "Koksoak", "La Grande", "Little Mecatina", 
    "Mackenzie", "Mississippi", "Moose", "Naskaupi", "Nass", "Natashquan", 
    "Nelson", "Nottaway", "Pacific Ocean Seaboard", "Povungnituk", 
    "Romaine", "Rupert", "Saint John", "Saint Lawrence", "Seal", 
    "Severn", "Skeena", "St.-Augustin", "Stikine", "Taku", "Thelon", 
    "Wannock", "Winisk", "Yukon"), class = "factor"), NAODA_EN = structure(c(1L, 
    5L, 1L, 5L, 4L, 4L, 5L, 4L, 4L, 4L, 5L, 4L, 5L, 2L, 4L, 2L, 2L, 
    2L, 2L, 4L, 5L, 2L, 4L, 1L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 4L, 
    4L, 4L, 5L, 4L, 5L, 4L, 4L, 2L, 3L, 4L, 4L, 2L, 2L), .Label = c("Arctic Ocean", 
    "Atlantic Ocean", "Gulf of Mexico", "Hudson Bay", "Pacific Ocean"
    ), class = "factor"), NAMRB_FR = structure(c(4L, 45L, 19L, 41L, 
    31L, 2L, 40L, 12L, 42L, 5L, 27L, 10L, 38L, 26L, 36L, 7L, 39L, 
    28L, 32L, 16L, 43L, 18L, 13L, 23L, 17L, 7L, 15L, 9L, 33L, 44L, 
    37L, 11L, 6L, 3L, 22L, 21L, 14L, 8L, 1L, 30L, 34L, 24L, 25L, 
    29L, 20L, 35L), .Label = c("Albany", "Arnaud", "Attawapiskat", 
    "Back", "Baleine", "Broadback", "Churchill", "Columbia", "Eastmain", 
    "Feuilles", "Fraser", "George", "Grande Baleine", "Harricanaw", 
    "Hayes", "Koksoak", "La Grande", "Little Mecatina", "Littoral de l'océan Arctique", 
    "Littoral de l'océan Atlantique", "Littoral de l'océan Pacifique", 
    "Littoral de la Baie d'Hudson", "Mackenzie", "Mississippi", "Moose", 
    "Naskaupi", "Nass", "Natashquan", "Nelson", "Nottaway", "Povungnituk", 
    "Romaine", "Rupert", "Saint-Jean", "Saint-Laurent", "Seal", "Severn", 
    "Skeena", "St.-Augustin", "Stikine", "Taku", "Thelon", "Wannock", 
    "Winisk", "Yukon"), class = "factor"), NAODA_FR = structure(c(3L, 
    5L, 3L, 5L, 1L, 1L, 5L, 1L, 1L, 1L, 5L, 1L, 5L, 4L, 1L, 4L, 4L, 
    4L, 4L, 1L, 5L, 4L, 1L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 5L, 1L, 
    1L, 1L, 5L, 1L, 5L, 1L, 1L, 4L, 2L, 1L, 1L, 4L, 4L), .Label = c("Baie d'Hudson", 
    "Golfe de Mexique", "Océan Arctique", "Océan Atlantique", "Océan Pacifique"
    ), class = "factor")), .Names = c("NAMRB_EN", "NAODA_EN", "NAMRB_FR", 
    "NAODA_FR"), row.names = 0:45, class = "data.frame")
        , polygons = list(<S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>, 
        <S4 object of class structure("Polygons", package = "sp")>)
        , plotOrder = c(3L, 24L, 35L, 46L, 44L, 2L, 42L, 38L, 45L, 36L, 26L, 9L, 32L, 
    1L, 20L, 39L, 27L, 31L, 43L, 25L, 16L, 7L, 30L, 40L, 6L, 15L, 
    34L, 13L, 12L, 41L, 28L, 8L, 23L, 29L, 5L, 10L, 37L, 11L, 14L, 
    33L, 4L, 22L, 18L, 19L, 17L, 21L)
        , bbox = structure(c(-152.812332679775, 40.3769750107632, -52.6362915039062, 
    83.1106262207029), .Dim = c(2L, 2L), .Dimnames = list(c("x", 
    "y"), c("min", "max")))
        , proj4string = new("CRS"
        , projargs = "+proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0"
    )
    )

Spatial polygon data frame has a few slots. @data is the data frame, @polygons is the polygons. You can first try str(polys@polygons) to see what you get. If it is a list of polygons, then lapply it with sp::bbox function

require(sp)
lapply(polys@polygons, bbox)

Previous answer is fine, unfortunately it was much too slow for me on large datasets (millions of polygons).

Using Rcpp allows to speed it up a lot (>20 times), the function hereafter is extracted from the spatDataManagement package, you can put it in a cpp file and run sourceCpp on it to get the function or you can install spatDataManagement and use the function directly. In addition it nicely puts everything in a data.frame with adequate column names:

> head(GetBBoxes(phillyCensusTracts))
       minX     minY      maxX     maxY
1 -74.97678 40.07721 -74.95576 40.09781
2 -75.00539 40.09937 -74.96408 40.12390
3 -75.14641 39.92889 -75.13040 39.96294
4 -75.00942 40.05475 -74.99043 40.06916
5 -75.03647 40.05078 -75.02171 40.06731
6 -74.98463 40.07198 -74.97151 40.09381

Code:

#include <Rcpp.h>
#include <string>

using namespace Rcpp;

//' Get bounding box for each polygon/polyline
//' @description Gets the bounding box (range of x and y) for each item of a SpatialLines/Polygons (DataFrame or not) object.
//'    It is equivalent to applying the \code{sp::bbox} function to all polygons with lapply but it is 
//'    simpler to use and much faster (even on a toy example of a few hundred polygons but designed for datasets with millions, then easily gets > 20x faster). 
//'    It is an important function to speed up other more complex comparisons between sp objects such as over().
//' @param x A SpatialPolygons[DataFrame] or SpatialLines[DataFrame]
//' @return A matrix of same number of columns than x and 4 columns : xmin, ymin, xmax, ymax
//' @export
//' @examples
//' data(phillyCensusTract)
//' ## simple use
//' system.time(bboxes <- GetBBoxes(phillyCensusTracts))
//' #=> 0.002 seconds
//' ## same using bbox
//' system.time(bboxesRef <- matrix(unlist(lapply(phillyCensusTracts@polygons,bbox)),ncol=4,byrow=TRUE))
//' #=> 0.021 seconds
// [[Rcpp::export]]
SEXP GetBBoxes( SEXP x ){
    // determines object type and adapts the search of coordinates
    S4 obj(x) ;
    std::string nameList; 
    std::string nameSubList;
    if(Rf_inherits(x, "SpatialLines") || Rf_inherits(x, "SpatialLinesDataFrame")){
        nameList = "lines";
        nameSubList = "Lines";
    }else if(Rf_inherits(x, "SpatialPolygons") || Rf_inherits(x, "SpatialPolygonsDataFrame")){
        nameList = "polygons";
        nameSubList = "Polygons";
    }else{
        ::Rf_error("In GetBBoxes, class must be Spatial[Polygons|Lines][DataFrame]");
    }
    List a =  obj.slot(nameList);

    // count items
    int nPol = a.length();
    NumericMatrix bboxes(nPol,4);

    // get the range
    for(int iPol = 0;iPol < nPol;iPol++){
        S4 pol = a(iPol);
        List b = pol.slot(nameSubList);

        double minX = std::numeric_limits<double>::infinity();
        double maxX = -std::numeric_limits<double>::infinity();
        double minY = std::numeric_limits<double>::infinity();
        double maxY = -std::numeric_limits<double>::infinity();

        for(int iSP = 0; iSP < b.length(); iSP++){
            S4 subPol = b(iSP);
            NumericMatrix coords = subPol.slot("coords");
            // X
            NumericVector rangeX = range(coords(_,0));
            if(rangeX(0)<minX) minX = rangeX(0);
            if(rangeX(1)>maxX) maxX = rangeX(1);
            // Y
            NumericVector rangeY = range(coords(_,1));
            if(rangeY(0)<minY) minY = rangeY(0);
            if(rangeY(1)>maxY) maxY = rangeY(1);
        }

        bboxes(iPol,0) = minX;
        bboxes(iPol,1) = minY;
        bboxes(iPol,2) = maxX;
        bboxes(iPol,3) = maxY;
    }
    Rcpp::DataFrame BBoxes = Rcpp::DataFrame::create(Rcpp::Named("minX")=bboxes(_,0),
            Rcpp::Named("minY")=bboxes(_,1),
            Rcpp::Named("maxX")=bboxes(_,2),
            Rcpp::Named("maxY")=bboxes(_,3));

    return BBoxes;
}

To return plottable bounding boxes (in the form of a SpatialPolygonsDataFrame) instead of a list of matrices that lapply(polys@polygons, bbox) returns:

spatial_bboxes <- function(polygons) {
  individual_bb <- function(polygon, projection) {
    polygon <- sp::SpatialPolygons(list(polygon), proj4string = projection)
    spatial_bbox <- as(raster::extent(polygon), "SpatialPolygons")
    spatial_bbox <- spatial_bbox@polygons[[1]]
    spatial_bbox@ID <- polygon@polygons[[1]]@ID
    return(spatial_bbox)
  }
  polys <- lapply(polygons@polygons, individual_bb, polygons@proj4string)
  spatial_polys <- sp::SpatialPolygons(polys, proj4string = polygons@proj4string)
  spatial_polys_df <- sp::SpatialPolygonsDataFrame(spatial_polys, polygons@data)
  return(spatial_polys_df)
}

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