I'm trying to view the latitudinal and longitudinal distributions of different shapes in R. I'm trying to find the area of an sf object across bands of latitude, basically flattening the shape sideways to see where most of its mass is distributed. How would I go about this?
I thought about using st_cast()
to reproject the multipolygon into multipoints, then making a density curve of those points across latitude, but that only makes points around the perimeter of the shape, not the inside. Maybe I can find the distance between multipoints at the same latitudes, then plot those distances as a pseudo-histogram?
I think maybe it will be easier if I convert to raster and count the number of cells in each latitudinal row? Any thoughts appreciated.
Code to get a small shapefile below to start with:
# try with the country of brazil
brazil <- rnaturalearth::ne_countries(continent = "South America",
returnclass = "sf") %>%
filter(name == "Brazil")
brazil %>%
ggplot() +
geom_sf() +
theme_minimal()
Desired output: density plot or histogram showing latitude on the Y-axis and area of the country on the x axis.
Thanks!
I think this will do the trick:
Put a horizontal line going through each of the vertex points of the polygon, extending past the bounds of the polygon.
Crop those lines with the polygon.
Get the line lengths, and sort by latitude.
Interpolate linearly with latitude.
I think this will work because the length of cross-section changes linearly except at vertex points, so you only need to find the cross-section length and vertex points and then linear interpolation gets you the rest. Can all be done with sf
functions, sf_intersection
and sf_length
might be all you need except for geometry constructors to make the lines.
12 hours later, here's this:
flatten_EW <- function(polygon){
## get all the vertex coords
bp = st_coordinates(polygon)
## get the limits in longitude over the polygon
xr = range(bp[,"X"])
## build a data frame of big lines across the polygon
xxyy = data.frame(t(xr), bp[,"Y"], bp[,"Y"])
xylines = st_sfc(apply(xxyy, 1, function(v){st_linestring(matrix(v,2,2))}, simplify=FALSE), crs=st_crs(polygon))
## make into a spatial data frame
xylines = st_as_sf(data.frame(y=bp[,"Y"], geometry=xylines))
## crop to the polygon
xylines = st_intersection(xylines, st_geometry(polygon))
## order by increasing latitude
xylines = xylines[order(xylines$y),]
## return data frame with line length
data.frame(y = xylines$y, L = st_length(xylines))
}
Which you use like this:
flBr = flatten_EW(brazil)
plot(flBr$y, flBr$L, type="l")
to produce this:
Interpreting this as an area is tricky - the line lengths are in metres and the latitude is in degrees. And Brazil is large enough that the ellipsoidal earth shape will probably have an effect. For a planar polygon in cartesian coordinates this will be a shape that has the same area. I think.