From e3c1df179d08447804b3ac480c4b7b1c96cd21a7 Mon Sep 17 00:00:00 2001 From: robinlovelace Date: Wed, 4 Sep 2024 19:28:38 +0100 Subject: [PATCH] Add minimal R package (#67) --- r/.gitignore | 1 + r/DESCRIPTION | 23 +++++++++++ r/NAMESPACE | 2 + r/R/setup.R | 90 +++++++++++++++++++++++++++++++++++++++++ r/README.md | 65 +++++++++++++++++++++++++++++ r/README.qmd | 84 ++++++++++++++++++++++++++++++++++++++ r/man/make_elevation.Rd | 24 +++++++++++ r/man/make_zones.Rd | 17 ++++++++ 8 files changed, 306 insertions(+) create mode 100644 r/.gitignore create mode 100644 r/DESCRIPTION create mode 100644 r/NAMESPACE create mode 100644 r/R/setup.R create mode 100644 r/README.md create mode 100644 r/README.qmd create mode 100644 r/man/make_elevation.Rd create mode 100644 r/man/make_zones.Rd diff --git a/r/.gitignore b/r/.gitignore new file mode 100644 index 0000000..075b254 --- /dev/null +++ b/r/.gitignore @@ -0,0 +1 @@ +/.quarto/ diff --git a/r/DESCRIPTION b/r/DESCRIPTION new file mode 100644 index 0000000..abaeea4 --- /dev/null +++ b/r/DESCRIPTION @@ -0,0 +1,23 @@ +Package: od2net +Title: What the Package Does (One Line, Title Case) +Version: 0.0.0.9000 +Authors@R: + person("First", "Last", , "first.last@example.com", role = c("aut", "cre"), + comment = c(ORCID = "YOUR-ORCID-ID")) +Description: What the package does (one paragraph). +License: MIT + file LICENSE +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.2 +Imports: + dplyr, + osmextract, + R.utils, + readr, + sf +Suggests: + simodels (>= 0.1.0) +Depends: + R (>= 2.10) +LazyData: true +URL: https://acteng.github.io/od2net/ diff --git a/r/NAMESPACE b/r/NAMESPACE new file mode 100644 index 0000000..6ae9268 --- /dev/null +++ b/r/NAMESPACE @@ -0,0 +1,2 @@ +# Generated by roxygen2: do not edit by hand + diff --git a/r/R/setup.R b/r/R/setup.R new file mode 100644 index 0000000..c54e644 --- /dev/null +++ b/r/R/setup.R @@ -0,0 +1,90 @@ +# Aim: generate input data for od2net with R + +#' Generate a 'zones.geojson' file +#' +#' This function requires a zones file, e.g. +#' "https://raw.githubusercontent.com/nptscot/npt/main/data-raw/zones_edinburgh.geojson" +#' or a file on your computer. +#' It will generate a file in the input/ folder +#' +#' @param file Location or URL of zones file +make_zones = function(file) { + zones = sf::read_sf(file)[1] + names(zones)[1] = "name" + sf::write_sf(zones, "input/zones.geojson", delete_dsn = TRUE) +} + +getbbox_from_zones = function() { + zones = sf::st_read("input/zones.geojson") + bbox = sf::st_bbox(zones) + paste0(bbox, collapse = ",") +} + +make_osm = function(force_download = FALSE) { + zones = sf::read_sf("input/zones.geojson") + zones_union = sf::st_union(zones) + osmextract_match = osmextract::oe_match(place = zones_union) + osmextract::oe_download(file_url = osmextract_match$url, download_directory = "input", force_download = force_download) + input_pbf = list.files(path = "input", pattern = basename(osmextract_match$url), full.names = TRUE) + bb = getbbox_from_zones() + msg = paste0("osmium extract -b ", bb, " ", input_pbf, " -o input/input.osm.pbf --overwrite") + system(msg) +} + +#' Get elevation data +#' +#' This function downloads elevation data from a source such as +#' https://play.abstreet.org/dev/data/input/shared/elevation/UK-dem-50m-4326.tif.gz +#' or https://assets.od2net.org/input/LisboaIST_10m_4326.tif +#' +#' @param url Full URL of the elevation dataset if available +#' @param file File name if hosted on a known site +#' @param base_url Base URL associated with the 'file' argument +#' +make_elevation = function( + url = NULL, + file = "UK-dem-50m-4326.tif.gz", + base_url = "https://play.abstreet.org/dev/data/input/shared/elevation/" + ) { + if (is.null(url)) { + url = paste0(base_url, file) + } + is_gzip = grepl(pattern = "gz", url) + # Download the file + if (!file.exists("input/elevation.tif") && is_gzip) { + download.file( + url = url, + destfile = "input/elevation.tif.gz" + ) + R.utils::gunzip("input/elevation.tif.gz", destname = "input/elevation.tif") + } else { + download.file( + url = url, + destfile = "input/elevation.tif" + ) + } +} + +make_origins = function() { + buildings = sf::read_sf("input/input.osm.pbf", query = "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL") + use_sf = sf::sf_use_s2(FALSE) + centroids = sf::st_centroid(buildings) + sf::sf_use_s2(use_sf) + sf::write_sf(centroids, "input/buildings.geojson", delete_dsn = TRUE) +} + +make_od = function() { + od = readr::read_csv("https://raw.githubusercontent.com/nptscot/npt/main/data-raw/od_subset.csv") + od = od |> + dplyr::transmute(from = geo_code1, to = geo_code2, count = bicycle) + readr::write_csv(od, "input/od.csv") +} + +main = function() { + dir.create("input", showWarnings = FALSE) + make_zones() + make_osm() + # make_elevation() + make_origins() + make_od() +} diff --git a/r/README.md b/r/README.md new file mode 100644 index 0000000..e480e4d --- /dev/null +++ b/r/README.md @@ -0,0 +1,65 @@ +# Preparing OD data for network generation with od2net + + +This R package provides functions to prepare OD data for network +generation with the `od2net` tool, as illustrated in the example below. + +``` r +source("R/setup.R") +dir.create("input", showWarnings = FALSE) +make_zones("https://github.com/acteng/netgen/raw/main/input/zones_york.geojson") +make_osm() +make_origins() +make_elevation() +destinations = destinations_york # Provided in the R package +names(destinations)[1] = "name" +destinations = destinations[1] +class(destinations$name) = "character" +sf::write_sf(destinations, "https://github.com/acteng/netgen/raw/main/input/destinations.geojson", delete_dsn = TRUE) +# Save the OD dataset: +od = od_geo |> + sf::st_drop_geometry() |> + transmute(from = O, to = as.character(D), count = round(trips_modelled)) +readr::write_csv(od, "input/od.csv", quote = "all") +``` + +Then create a config.json file, e.g. with the following content: + +``` json +{ + "requests": { + "description": "Test data for SchoolRoutes project.", + "pattern": { + "ZoneToPoint": { + "zones_path": "zones.geojson", + "destinations_path": "destinations.geojson", + "csv_path": "od.csv", + "origin_zone_centroid_fallback": false + } + }, + "origins_path": "buildings.geojson", + "destinations_path": "destinations.geojson" + }, + "cost": "Distance", + "uptake": "Identity", + "lts": "BikeOttawa", + "elevation_geotiff": "elevation.tif" +} +``` + +Then run the following code to generate the network: + +Run the tool with Docker as follows: + +``` bash +# On Linux: +sudo docker run -v $(pwd):/app ghcr.io/urban-analytics-technology-platform/od2net:main /app/config.json +# or in Windows: +sudo docker run -v ${pwd}:/app ghcr.io/urban-analytics-technology-platform/od2net:main /app/config.json +``` + +After that you should see the following in the output folder: + +``` r +fs::dir_tree("output") +``` diff --git a/r/README.qmd b/r/README.qmd new file mode 100644 index 0000000..85da2a1 --- /dev/null +++ b/r/README.qmd @@ -0,0 +1,84 @@ +--- +title: Preparing OD data for network generation with od2net +#| eval: false +#| echo: false +format: gfm +execute: + message: false + warning: false +--- + +This R package provides functions to prepare OD data for network generation with the `od2net` tool, as illustrated in the example below. + + +```{r} +#| eval: false +source("R/setup.R") +dir.create("input", showWarnings = FALSE) +make_zones("https://github.com/acteng/netgen/raw/main/input/zones_york.geojson") +make_osm() +make_origins() +make_elevation() +destinations = destinations_york # Provided in the R package +names(destinations)[1] = "name" +destinations = destinations[1] +class(destinations$name) = "character" +sf::write_sf(destinations, "https://github.com/acteng/netgen/raw/main/input/destinations.geojson", delete_dsn = TRUE) +# Save the OD dataset: +od = od_geo |> + sf::st_drop_geometry() |> + transmute(from = O, to = as.character(D), count = round(trips_modelled)) +readr::write_csv(od, "input/od.csv", quote = "all") +``` + +Then create a config.json file, e.g. with the following content: + +```json +{ + "requests": { + "description": "Test data for SchoolRoutes project.", + "pattern": { + "ZoneToPoint": { + "zones_path": "zones.geojson", + "destinations_path": "destinations.geojson", + "csv_path": "od.csv", + "origin_zone_centroid_fallback": false + } + }, + "origins_path": "buildings.geojson", + "destinations_path": "destinations.geojson" + }, + "cost": "Distance", + "uptake": "Identity", + "lts": "BikeOttawa", + "elevation_geotiff": "elevation.tif" +} +``` + +Then run the following code to generate the network: + +Run the tool with Docker as follows: + + +```{bash} +#| eval: false +# On Linux: +sudo docker run -v $(pwd):/app ghcr.io/urban-analytics-technology-platform/od2net:main /app/config.json +# or in Windows: +sudo docker run -v ${pwd}:/app ghcr.io/urban-analytics-technology-platform/od2net:main /app/config.json +``` + +```{r} +#| eval: false +#| echo: false +system("docker run -v $(pwd):/app ghcr.io/urban-analytics-technology-platform/od2net:main /app/config.json") +``` + +After that you should see the following in the output folder: + +```{r} +#| eval: false +fs::dir_tree("output") +``` + + diff --git a/r/man/make_elevation.Rd b/r/man/make_elevation.Rd new file mode 100644 index 0000000..af111f8 --- /dev/null +++ b/r/man/make_elevation.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/setup.R +\name{make_elevation} +\alias{make_elevation} +\title{Get elevation data} +\usage{ +make_elevation( + url = NULL, + file = "UK-dem-50m-4326.tif.gz", + base_url = "https://play.abstreet.org/dev/data/input/shared/elevation/" +) +} +\arguments{ +\item{url}{Full URL of the elevation dataset if available} + +\item{file}{File name if hosted on a known site} + +\item{base_url}{Base URL associated with the 'file' argument} +} +\description{ +This function downloads elevation data from a source such as +https://play.abstreet.org/dev/data/input/shared/elevation/UK-dem-50m-4326.tif.gz +or https://assets.od2net.org/input/LisboaIST_10m_4326.tif +} diff --git a/r/man/make_zones.Rd b/r/man/make_zones.Rd new file mode 100644 index 0000000..bfcedee --- /dev/null +++ b/r/man/make_zones.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/setup.R +\name{make_zones} +\alias{make_zones} +\title{Generate a 'zones.geojson' file} +\usage{ +make_zones(file) +} +\arguments{ +\item{file}{Location or URL of zones file} +} +\description{ +This function requires a zones file, e.g. +"https://raw.githubusercontent.com/nptscot/npt/main/data-raw/zones_edinburgh.geojson" +or a file on your computer. +It will generate a file in the input/ folder +}