--- title: "Rendering Ocean" author: "Richard Beare" date: "`r Sys.Date()`" output: html_document: toc: true toc_float: true #number_sections: true theme: flatly vignette: > %\VignetteIndexEntry{Maps with Ocean} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- # 1 Introduction It may often be desirable to separately colour two or more portions of a map that are separated by a line object. This is not directly possible because only polygons can be filled with colour, not lines. The `osm_line2poly()` function comes to the rescue here by converting a line to a polygon surrounding a given plotting region. The classic example where this arises is with coastlines. These are always represented in OpenStreetMap as line objects, preventing any ability to simply colour land and ocean separately. This vignette illustrates the general principles of the `osm_line2poly()` function, along with several ancillary issues such as plotting coastal islands. Although this functionality has been primarily developed with coastlines in mind, the `osm_line2poly()` function has been designed in a sufficiently general manner to be readily adaptable to any other cases where such line-to-polygon conversion may be desirable. This vignette explores the example of the coastline around Greater Melbourne, Australia, first demonstrating how to extract coastline and convert it to land and sea polygons, and then demonstrating how these areas may be delineated on a plot. # 2 Data Extraction and Conversion We use [`osmdata`](https://cran.r-project.org/package=osmdata) to extract the coastline within the bounding box of Greater Melbourne. ```{r, echo = FALSE} map_dpi <- 72 # dpi res for all maps fetch_osm <- FALSE ``` ```{r GMFuncs, message=FALSE, eval = fetch_osm} library (osmplotr) library (osmdata) library (magrittr) bbox <- osmdata::getbb ("greater melbourne, australia") coast <- opq (bbox = bbox) %>% add_osm_feature (key = "natural", value = "coastline") %>% osmdata_sf (quiet = FALSE) ``` This coastline object consists of several types of structure ```{r, eval = FALSE} coast ``` ```{r, echo = FALSE} message (paste0 ( "Object of class 'osmdata' with:\n", " $bbox : -38.49937,144.44405,-37.40175,146.1925\n", " $overpass_call : The call submitted to the overpass API\n", " $timestamp : [ Thurs 5 Oct 2017 10:23:18 ]\n", " $osm_points : 'sf' Simple Features Collection with 13635 points\n", " $osm_lines : 'sf' Simple Features Collection with 73 linestrings\n", " $osm_polygons : 'sf' Simple Features Collection with 12 polygons\n", " $osm_multilines : 'sf' Simple Features Collection with 0 multilinestrings\n", " $osm_multipolygons : 'sf' Simple Features Collection with 0 multipolygons" )) ``` Because OpenStreetMap represents coastline as line objects, all coastline data is contained within the `$osm_lines` object. The `osm_line2poly()` function can then convert these lines to polygons which can be used to plot filled areas. ```{r, eval = fetch_osm} coast_poly <- osm_line2poly (coast$osm_lines, bbox) names (coast_poly) ``` ```{r, echo = FALSE} c ("sea", "land", "islands") ``` Note that reflecting its envisioned primary usage, the function always returns objects named `"sea"`, `"land"`, and `"islands"`. For usages other than coastline, these names will of course reflect other kinds of object. The `"islands"` item contains any polygons which are separate to those originally queried. Each item of this list is an `sf::data.frame` object: ```{r, eval = FALSE} class (coast_poly$sea) ``` ```{r, echo = FALSE} c ("sf", "data.frame") ``` # 3 Plotting The `list` items returned by `osm_line2poly()` may then be used to provide a map background which distinguishes ocean from land. Here we first colour the entire map using the background colour for the ocean, and overlay the land and island polygons on top of that. ```{r, eval = fetch_osm} map <- osm_basemap (bbox = bbox, bg = "cadetblue2") %>% add_osm_objects (coast_poly$land, col = "lightyellow1") %>% add_osm_objects (coast_poly$islands, col = "orange") %>% add_osm_objects (coast$osm_polygons, col = "purple", border = "black") %>% add_osm_objects (coast$osm_lines, col = "black") %>% print_osm_map () ``` ```{r, echo=FALSE, eval = fetch_osm} print_osm_map (map, filename = "melb_a1.png", width = 600, units = "px", dpi = map_dpi ) ``` ![](melb_a1.png) The gaudy colours differentiate the source of polygons. Purple islands were returned by the original osm query, while the orange ones were constructed from fragments by `osm_line2poly`. # Further Demonstrations The `osm_line2poly()` function works by identifying lines which extend at at least two points beyond a given bounding box. For coastline, OpenStreetMap is designed so that land always lies to the left side in the direction of the line, enabling water and land to be systematically distinguished. The following test cases demonstrate the reliability of this distinction. ```{r, echo=FALSE} getCoast <- function (bbox) { qry <- opq (bbox) qry <- add_osm_feature (qry, key = "natural", value = "coastline") return (osmdata_sf (qry)) } testPlot <- function (coast, bbox) { if (!dev.cur ()) dev.off () map <- osm_basemap (bbox = bbox) map <- add_osm_objects (map, coast$osm_lines) print_osm_map (map) } testPlotPoly <- function (coast, bbox, fname) { ## trouble doing this check properly on Travis if (nrow (coast$osm_lines) > 0) { coastp <- osm_line2poly (coast$osm_lines, bbox = bbox) map <- osm_basemap (bbox = bbox) map <- add_osm_objects (map, coastp$sea, col = "cadetblue2") map <- add_osm_objects (map, coastp$land, col = "sienna2") print_osm_map (map, filename = fname, width = 200, units = "px", dpi = map_dpi ) } else { warning ("osm query probably failed - not plotting") invisible (NULL) } } ``` ```{r, eval = fetch_osm} test_plot <- function (bbox) { dat <- opq (bbox) %>% add_osm_feature (key = "natural", value = "coastline") %>% osmdata_sf (quiet = FALSE) coast <- osm_line2poly (dat$osm_lines, bbox) osm_basemap (bbox = bbox) %>% add_osm_objects (coast$sea, col = "cadetblue2") %>% add_osm_objects (coast$land, col = "sienna2") } ``` ```{r, eval = fetch_osm, echo = FALSE} test_plot <- function (bbox, filename, map_dpi) { dat <- opq (bbox) %>% add_osm_feature (key = "natural", value = "coastline") %>% osmdata_sf (quiet = FALSE) coast <- osm_line2poly (dat$osm_lines, bbox) osm_basemap (bbox = bbox) %>% add_osm_objects (coast$sea, col = "cadetblue2") %>% add_osm_objects (coast$land, col = "sienna2") %>% print_osm_map ( file = filename, width = 200, units = "px", dpi = map_dpi ) } ``` Fetch the test data. A variable name with `WE` indicates that the coast enters the bounding box on the western side and exits on the east. The land is on the left when following that path. ```{r, eval = fetch_osm} bbWE <- get_bbox (c (142.116906, -38.352713, 142.205162, -38.409661)) coastWE <- getCoast (bbWE) bbEW <- get_bbox (c (144.603127, -38.104003, 144.685557, -38.135596)) coastEW <- getCoast (bbEW) bbNS <- get_bbox (c (143.807998, -39.770986, 143.906494, -39.918643)) coastNS <- getCoast (bbNS) bbSN <- get_bbox (c (144.073544, -39.854586, 144.149318, -39.960047)) coastSN <- getCoast (bbSN) bbWW <- get_bbox (c (144.904865, -37.858295, 144.923679, -37.874367)) coastWW <- getCoast (bbWW) bbEE <- get_bbox (c (144.643383, -38.294671, 144.692197, -38.336022)) coastEE <- getCoast (bbEE) bbNN <- get_bbox (c (145.856321, -38.831642, 146.050920, -38.914031)) coastNN <- getCoast (bbNN) bbSS <- get_bbox (c (146.363768, -38.770345, 146.486389, -38.837287)) coastSS <- getCoast (bbSS) bbEN <- get_bbox (c (144.738212, -38.337690, 144.758053, -38.346966)) coastEN <- getCoast (bbEN) bbEWWS <- get_bbox (c (144.693077, -38.307526, 144.729113, -38.343997)) coastEWWS <- getCoast (bbEWWS) bbWS <- get_bbox (c (143.164906, -38.704885, 143.2075563, -38.7462058)) coastWS <- getCoast (bbWS) ``` ```{r, eval = fetch_osm} testPlotPoly (coastWE, bbWE, "testWE.png") ``` ![](testWE.png) ```{r, eval = fetch_osm} testPlotPoly (coastEW, bbEW, "testEW.png") ``` ![](testEW.png) ```{r, eval = fetch_osm} testPlotPoly (coastNS, bbNS, "testNS.png") ``` ![](testNS.png) ```{r, eval = fetch_osm} testPlotPoly (coastSN, bbSN, "testSN.png") ``` ![](testSN.png) ```{r, eval = fetch_osm} testPlotPoly (coastWW, bbWW, "testWW.png") ``` ![](testWW.png) ```{r, eval = fetch_osm} testPlotPoly (coastEE, bbEE, "testEE.png") ``` ![](testEE.png) ```{r, eval = fetch_osm} testPlotPoly (coastNN, bbNN, "testNN.png") ``` ![](testNN.png) ```{r, eval = fetch_osm} testPlotPoly (coastSS, bbSS, "testSS.png") ``` ![](testSS.png) ```{r, eval = fetch_osm} testPlotPoly (coastEN, bbEN, "testEN.png") ``` ![](testEN.png) ```{r, eval = fetch_osm} testPlotPoly (coastEWWS, bbEWWS, "testEWWS.png") ``` ![](testEWWS.png) ```{r, eval = fetch_osm} testPlotPoly (coastWS, bbWS, "testWS.png") ``` ![](testWS.png)