From 27fff3282294a7d571b2375f5f75bdc6332dd44b Mon Sep 17 00:00:00 2001 From: Robin Lovelace Date: Tue, 10 Sep 2024 11:02:04 +0100 Subject: [PATCH] Add minimal R package (#67) (#68) * Add minimal R package (#67) * Add URL * Port make_zones() to README, style file * More tweaks to pkg * Document functions * Reformat * Re-document * Changes for all R pkg checks to pass * Document, ready for review * Add demo config --- r/.Rbuildignore | 8 ++ r/.devcontainer/devcontainer.json | 39 ++++++ r/.gitignore | 4 + r/DESCRIPTION | 23 ++++ r/LICENSE.md | 194 ++++++++++++++++++++++++++++++ r/NAMESPACE | 5 + r/R/setup.R | 66 ++++++++++ r/README.md | 153 +++++++++++++++++++++++ r/README.qmd | 148 +++++++++++++++++++++++ r/config.json | 19 +++ r/man/getbbox_from_zones.Rd | 17 +++ r/man/make_origins.Rd | 26 ++++ r/man/make_osm.Rd | 30 +++++ 13 files changed, 732 insertions(+) create mode 100644 r/.Rbuildignore create mode 100644 r/.devcontainer/devcontainer.json create mode 100644 r/.gitignore create mode 100644 r/DESCRIPTION create mode 100644 r/LICENSE.md 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/config.json create mode 100644 r/man/getbbox_from_zones.Rd create mode 100644 r/man/make_origins.Rd create mode 100644 r/man/make_osm.Rd diff --git a/r/.Rbuildignore b/r/.Rbuildignore new file mode 100644 index 0000000..687a27e --- /dev/null +++ b/r/.Rbuildignore @@ -0,0 +1,8 @@ +^README\.qmd$ +^input$ +^LICENSE\.md$ +^\.quarto$ +^\.devcontainer$ +^output$ +^intermediate$ +^config\.json$ diff --git a/r/.devcontainer/devcontainer.json b/r/.devcontainer/devcontainer.json new file mode 100644 index 0000000..fc3e717 --- /dev/null +++ b/r/.devcontainer/devcontainer.json @@ -0,0 +1,39 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "ghcr.io/geocompx/docker:rust", + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + // Including reditorsupport.r and the quarto extension. + "extensions": [ + "reditor-support.r", + "quarto.quarto", + "ms-python.python", + "ms-toolsai.jupyter" + ] + } + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" + + // These are now set in the Dockerfile: + // https://github.com/geocompx/docker/blob/master/rust/Dockerfile + // "postCreateCommand": [ + // "bash .devcontainer/install-additional-dependencies.sh" + // ] + +} diff --git a/r/.gitignore b/r/.gitignore new file mode 100644 index 0000000..638e7a2 --- /dev/null +++ b/r/.gitignore @@ -0,0 +1,4 @@ +/.quarto/ +input/ +output/ +intermediate/ diff --git a/r/DESCRIPTION b/r/DESCRIPTION new file mode 100644 index 0000000..eee7619 --- /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: Apache License (>= 2) +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.2 +Imports: + osmextract, + sf +Suggests: + simodels (>= 0.1.0), + readr, + dplyr, + R.utils, +Depends: + R (>= 2.10) +LazyData: true +URL: https://urban-analytics-technology-platform.github.io/od2net/r/, https://github.com/urban-analytics-technology-platform/od2net diff --git a/r/LICENSE.md b/r/LICENSE.md new file mode 100644 index 0000000..b62a9b5 --- /dev/null +++ b/r/LICENSE.md @@ -0,0 +1,194 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### Terms and Conditions for use, reproduction, and distribution + +#### 1. Definitions + +“License” shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +“Licensor” shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +“Legal Entity” shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, “control” means **(i)** the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the +outstanding shares, or **(iii)** beneficial ownership of such entity. + +“You” (or “Your”) shall mean an individual or Legal Entity exercising +permissions granted by this License. + +“Source” form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +“Object” form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +“Work” shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +“Derivative Works” shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +“Contribution” shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +“submitted” means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as “Not a Contribution.” + +“Contributor” shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +#### 2. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +#### 3. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +#### 4. Redistribution + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +* **(a)** You must give any other recipients of the Work or Derivative Works a copy of +this License; and +* **(b)** You must cause any modified files to carry prominent notices stating that You +changed the files; and +* **(c)** You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +#### 5. Submission of Contributions + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +#### 6. Trademarks + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +#### 7. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +#### 8. Limitation of Liability + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +#### 9. Accepting Warranty or Additional Liability + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +_END OF TERMS AND CONDITIONS_ + +### APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets `[]` replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same “printed page” as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/r/NAMESPACE b/r/NAMESPACE new file mode 100644 index 0000000..1730d8a --- /dev/null +++ b/r/NAMESPACE @@ -0,0 +1,5 @@ +# Generated by roxygen2: do not edit by hand + +export(getbbox_from_zones) +export(make_origins) +export(make_osm) diff --git a/r/R/setup.R b/r/R/setup.R new file mode 100644 index 0000000..7353769 --- /dev/null +++ b/r/R/setup.R @@ -0,0 +1,66 @@ +#' Get bounding box from zones +#' +#' This function reads a GeoJSON file containing zones and calculates the bounding box of the zones. +#' +#' @param zones_file Path to the GeoJSON file containing zones. Default is 'input/zones.geojson'. +#' +#' @return A character string representing the bounding box in the format "xmin, ymin, xmax, ymax". +#' @export +getbbox_from_zones = function(zones_file = "input/zones.geojson") { + zones = sf::st_read(zones_file) + bbox = sf::st_bbox(zones) + paste0(bbox, collapse = ",") +} + +#' make_osm Function +#' +#' This function is used to download and extract OpenStreetMap (OSM) data based on specified zones. +#' +#' @param force_download A logical value indicating whether to force the download of OSM data even if it already exists. Default is \code{FALSE}. +#' @param zones_file The file path or name of the zones file in GeoJSON format. Default is \code{"input/zones.geojson"}. +#' @param output_file The file path or name of the output OSM file in PBF format. Default is "input/input.osm.pbf". +#' +#' @return This function does not return any value. It downloads and extracts OSM data based on the specified zones. +#' +#' @examples +#' if (file.exists("input/zones.geojson")) { +#' make_osm(force_download = TRUE, zones_file = "input/zones.geojson") +#' } +#' @export +make_osm = function( + force_download = FALSE, + zones_file = "input/zones.geojson", + output_file = "input/input.osm.pbf" + ) { + zones = sf::read_sf(zones_file) + 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 ", output_file, " --overwrite") + system(msg) +} + +#' make_origins function +#' +#' This function reads an OpenStreetMap file, selects the multipolygons with a non-null building attribute, +#' calculates the centroids of the selected buildings, and writes the resulting centroids to a GeoJSON file. +#' +#' @param osm_file The file path or name of the OSM file in PBF format. Default is "input/input.osm.pbf". +#' @param query The SQL query to select the multipolygons with a non-null building attribute. Default is "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL". +#' @param output_file The file path or name of the output GeoJSON file containing the centroids of the selected buildings. Default is "input/buildings.geojson". +#' +#' @return None +#' @export +make_origins = function( + osm_file = "input/input.osm.pbf", + query = "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL", + output_file = "input/buildings.geojson" + ) { + buildings = sf::read_sf(osm_file, query = query) + use_sf = sf::sf_use_s2(FALSE) + centroids = sf::st_centroid(buildings) + sf::sf_use_s2(use_sf) + sf::write_sf(centroids, output_file, delete_dsn = TRUE) +} diff --git a/r/README.md b/r/README.md new file mode 100644 index 0000000..1bd6a25 --- /dev/null +++ b/r/README.md @@ -0,0 +1,153 @@ +# 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. + +## Example + +Imagine you want to generate a route network with values on the links +representing the number of pupils/parents on their way to school each +school day. The following code shows how to prepare the data for the +`od2net` tool. + +The following functions could be useful when you’re preparing the data, +and show the kind of data preparation that is required. + +
+ + +``` r +# 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) +} + +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") +} +#' 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" + ) + } +} +``` + +
+ +``` r +# Install pak if not already installed: +if (!requireNamespace("pak", quietly = TRUE)) { + install.packages("pak") +} +pak::pkg_install("robinlovelace/od2net/r@67-r-port") +dir.create("input", showWarnings = FALSE) +# Get some zones from a URL: +uz = "https://github.com/acteng/netgen/raw/main/input/zones_york.geojson" +make_zones(uz) +sf::write_sf(zones, "input/zones.geojson", delete_dsn = TRUE) +make_osm(zones_file = "input/zones.geojson", output_file = "input/input.osm.pbf") +make_origins( + osm_file = "input/input.osm.pbf", + query = "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL", + output_file = "input/buildings.geojson" +) +make_elevation() +destinations = simodels::destinations_york # Provided in the R package +names(destinations)[1] = "name" +destinations = destinations[1] +class(destinations$name) = "character" +sf::write_sf(destinations, "input/destinations.geojson", delete_dsn = TRUE) +od_geo = sf::read_sf("https://github.com/acteng/netgen/releases/download/v0.1.0/res_output.geojson") +# Save the OD dataset: +od = od_geo |> + sf::st_drop_geometry() |> + dplyr::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: + +``` r +readLines("config.json") +``` + +\[1\] “{” +\[2\] ” "requests": {” +\[3\] ” "description": "Test data for SchoolRoutes project.",” \[4\] ” +"pattern": {” +\[5\] ” "ZoneToPoint": {” +\[6\] ” "zones_path": "zones.geojson",” +\[7\] ” "destinations_path": "destinations.geojson",” +\[8\] ” "csv_path": "od.csv",” +\[9\] ” "origin_zone_centroid_fallback": false” +\[10\] ” }” +\[11\] ” },” +\[12\] ” "origins_path": "buildings.geojson",” +\[13\] ” "destinations_path": "destinations.geojson"” +\[14\] ” },” +\[15\] ” "cost": "Distance",” +\[16\] ” "uptake": "Identity",” +\[17\] ” "lts": "BikeOttawa",” +\[18\] ” "elevation_geotiff": "elevation.tif"” +\[19\] “}” + +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 something like 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..e0a80e4 --- /dev/null +++ b/r/README.qmd @@ -0,0 +1,148 @@ +--- +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. + +## Example + +Imagine you want to generate a route network with values on the links representing the number of pupils/parents on their way to school each school day. The following code shows how to prepare the data for the `od2net` tool. + +The following functions could be useful when you're preparing the data, and show the kind of data preparation that is required. + +
+ + + +```{r} +# 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) +} + +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") +} +#' 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" + ) + } +} +``` + +
+ + +```{r} +#| eval: false +# Install pak if not already installed: +if (!requireNamespace("pak", quietly = TRUE)) { + install.packages("pak") +} +pak::pkg_install("robinlovelace/od2net/r@67-r-port") +dir.create("input", showWarnings = FALSE) +# Get some zones from a URL: +uz = "https://github.com/acteng/netgen/raw/main/input/zones_york.geojson" +make_zones(uz) +sf::write_sf(zones, "input/zones.geojson", delete_dsn = TRUE) +make_osm(zones_file = "input/zones.geojson", output_file = "input/input.osm.pbf") +make_origins( + osm_file = "input/input.osm.pbf", + query = "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL", + output_file = "input/buildings.geojson" +) +make_elevation() +destinations = simodels::destinations_york # Provided in the R package +names(destinations)[1] = "name" +destinations = destinations[1] +class(destinations$name) = "character" +sf::write_sf(destinations, "input/destinations.geojson", delete_dsn = TRUE) +od_geo = sf::read_sf("https://github.com/acteng/netgen/releases/download/v0.1.0/res_output.geojson") +# Save the OD dataset: +od = od_geo |> + sf::st_drop_geometry() |> + dplyr::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: + +```{r} +#| output: asis +readLines("config.json") +``` + +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 something like the following in the output folder: + +```{r} +#| eval: false +fs::dir_tree("output") +``` + diff --git a/r/config.json b/r/config.json new file mode 100644 index 0000000..aafce3b --- /dev/null +++ b/r/config.json @@ -0,0 +1,19 @@ +{ + "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" +} \ No newline at end of file diff --git a/r/man/getbbox_from_zones.Rd b/r/man/getbbox_from_zones.Rd new file mode 100644 index 0000000..0e89a8e --- /dev/null +++ b/r/man/getbbox_from_zones.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/setup.R +\name{getbbox_from_zones} +\alias{getbbox_from_zones} +\title{Get bounding box from zones} +\usage{ +getbbox_from_zones(zones_file = "input/zones.geojson") +} +\arguments{ +\item{zones_file}{Path to the GeoJSON file containing zones. Default is 'input/zones.geojson'.} +} +\value{ +A character string representing the bounding box in the format "xmin, ymin, xmax, ymax". +} +\description{ +This function reads a GeoJSON file containing zones and calculates the bounding box of the zones. +} diff --git a/r/man/make_origins.Rd b/r/man/make_origins.Rd new file mode 100644 index 0000000..3eabb2a --- /dev/null +++ b/r/man/make_origins.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/setup.R +\name{make_origins} +\alias{make_origins} +\title{make_origins function} +\usage{ +make_origins( + osm_file = "input/input.osm.pbf", + query = "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL", + output_file = "input/buildings.geojson" +) +} +\arguments{ +\item{osm_file}{The file path or name of the OSM file in PBF format. Default is "input/input.osm.pbf".} + +\item{query}{The SQL query to select the multipolygons with a non-null building attribute. Default is "SELECT osm_id FROM multipolygons WHERE building IS NOT NULL".} + +\item{output_file}{The file path or name of the output GeoJSON file containing the centroids of the selected buildings. Default is "input/buildings.geojson".} +} +\value{ +None +} +\description{ +This function reads an OpenStreetMap file, selects the multipolygons with a non-null building attribute, +calculates the centroids of the selected buildings, and writes the resulting centroids to a GeoJSON file. +} diff --git a/r/man/make_osm.Rd b/r/man/make_osm.Rd new file mode 100644 index 0000000..10a7af9 --- /dev/null +++ b/r/man/make_osm.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/setup.R +\name{make_osm} +\alias{make_osm} +\title{make_osm Function} +\usage{ +make_osm( + force_download = FALSE, + zones_file = "input/zones.geojson", + output_file = "input/input.osm.pbf" +) +} +\arguments{ +\item{force_download}{A logical value indicating whether to force the download of OSM data even if it already exists. Default is \code{FALSE}.} + +\item{zones_file}{The file path or name of the zones file in GeoJSON format. Default is \code{"input/zones.geojson"}.} + +\item{output_file}{The file path or name of the output OSM file in PBF format. Default is "input/input.osm.pbf".} +} +\value{ +This function does not return any value. It downloads and extracts OSM data based on the specified zones. +} +\description{ +This function is used to download and extract OpenStreetMap (OSM) data based on specified zones. +} +\examples{ +if (file.exists("input/zones.geojson")) { + make_osm(force_download = TRUE, zones_file = "input/zones.geojson") +} +}