diff --git a/.github/workflows/R-CMD-check-CRAN.yaml b/.github/workflows/R-CMD-check-CRAN.yaml index 2f23af3c..f8b39f8a 100644 --- a/.github/workflows/R-CMD-check-CRAN.yaml +++ b/.github/workflows/R-CMD-check-CRAN.yaml @@ -1,25 +1,19 @@ -# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. -# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions on: push: paths: - r-package/** branches: - - main - master - - dev pull_request: paths: - r-package/** branches: - - main - master - - dev -name: R-CMD-check-as-CRAN +name: check_as_cran jobs: - R-CMD-check: + check_as_cran: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) @@ -28,10 +22,9 @@ jobs: fail-fast: false matrix: config: - - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} + - {os: ubuntu-22.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/jammy/latest"} env: - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true RSPM: ${{ matrix.config.rspm }} GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} @@ -39,52 +32,70 @@ jobs: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-r@v2 + id: install-r with: r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} - uses: r-lib/actions/setup-pandoc@v2 - - name: Query dependencies + - name: Install pak and query dependencies run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), "../.github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), "../.github/R-version") + install.packages("pak", repos = "https://r-lib.github.io/p/pak/dev/") + saveRDS(pak::pkg_deps("local::.", dependencies = TRUE), ".github/r-depends.rds") shell: Rscript {0} working-directory: r-package - - name: Cache R packages + - name: Restore R package cache uses: actions/cache@v2 with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- + path: | + ${{ env.R_LIBS_USER }}/* + !${{ env.R_LIBS_USER }}/pak + key: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1-${{ hashFiles('.github/r-depends.rds') }} + restore-keys: ${{ matrix.config.os }}-${{ steps.install-r.outputs.installed-r-version }}-1- - - name: Install system dependencies + - name: Install system dependencies (Linux) + if: runner.os == 'Linux' run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') + pak::local_system_requirements(execute = TRUE) + pak::pkg_system_requirements("rcmdcheck", execute = TRUE) + shell: Rscript {0} working-directory: r-package - name: Install dependencies run: | - remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") + pak::local_install_dev_deps(upgrade = TRUE) + pak::pkg_install("rcmdcheck") + shell: Rscript {0} + working-directory: r-package + + - name: Session info + run: | + options(width = 100) + pkgs <- installed.packages()[, "Package"] + sessioninfo::session_info(pkgs, include_base = TRUE) shell: Rscript {0} working-directory: r-package - name: Check env: - _R_CHECK_CRAN_INCOMING_REMOTE_: false + _R_CHECK_CRAN_INCOMING_: false NOT_CRAN: false - run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") + run: | + options(crayon.enabled = TRUE) + rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") shell: Rscript {0} working-directory: r-package + - name: Show testthat output + if: always() + run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + - name: Upload check results if: failure() uses: actions/upload-artifact@main with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results + name: ${{ matrix.config.os }}-r${{ matrix.config.r }}-results path: check diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 60ce7a42..effcad2e 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -5,14 +5,12 @@ on: paths: - r-package/** branches: - - main - master - dev pull_request: paths: - r-package/** branches: - - main - master - dev @@ -31,7 +29,6 @@ jobs: - {os: windows-latest, r: 'release'} - {os: windows-latest, r: 'oldrel'} - {os: macOS-latest, r: 'release'} - - {os: macOS-latest, r: 'oldrel'} - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} - {os: ubuntu-20.04, r: 'oldrel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} @@ -42,19 +39,22 @@ jobs: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true - - uses: r-lib/actions/setup-pandoc@v2 - name: Java setup - uses: actions/setup-java@v1 + uses: actions/setup-java@v2 with: + distribution: 'temurin' java-version: '21' - java-package: jdk - name: Query dependencies run: | @@ -74,36 +74,22 @@ jobs: - name: Install system dependencies (Linux) if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') + run: brew install gdal proj working-directory: r-package - - name: Install system dependencies (MacOS) - if: runner.os == 'macOS' - run: | - brew install pkg-config gdal proj geos + - name: Install macOS system dependencies + if: runner.os == 'macos' + run: brew install gdal proj working-directory: r-package - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") - shell: Rscript {0} - working-directory: r-package - - name: Check - env: - _R_CHECK_CRAN_INCOMING_REMOTE_: false - run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") - shell: Rscript {0} + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check working-directory: r-package - - name: Upload check results - if: failure() - uses: actions/upload-artifact@main + - uses: r-lib/actions/check-r-package@v2 with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results - path: check + upload-snapshots: true + diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 1548792b..eb47f58d 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -3,6 +3,7 @@ on: paths: - r-package/** - README.md + - .github/workflows/pkgdown.yaml branches: - master @@ -20,6 +21,12 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 + - name: Java setup + uses: actions/setup-java@v1 + with: + java-version: '21' + java-package: jdk + - name: Query dependencies run: | install.packages('remotes') diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 4ad144a0..7c192010 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: r-lib/actions/setup-r@v1 + - uses: r-lib/actions/setup-r@v2 - uses: r-lib/actions/setup-pandoc@v1 diff --git a/README.md b/README.md index 1b3155c5..51baa43c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # r5r: Rapid Realistic Routing with R5 in R logo [![CRAN/METACRAN Version](https://www.r-pkg.org/badges/version/r5r)](https://CRAN.R-project.org/package=r5r) -[![CRAN/METACRAN Total downloads](http://cranlogs.r-pkg.org/badges/grand-total/r5r?color=blue)](https://CRAN.R-project.org/package=r5r) +[![CRAN/METACRAN Total downloads](https://cranlogs.r-pkg.org/badges/grand-total/r5r?color=blue)](https://CRAN.R-project.org/package=r5r) [![R build status](https://github.com/ipeaGIT/r5r/workflows/R-CMD-check/badge.svg)](https://github.com/ipeaGIT/r5r/actions) [![Codecov test coverage](https://codecov.io/gh/ipeaGIT/r5r/branch/master/graph/badge.svg)](https://app.codecov.io/gh/ipeaGIT/r5r?branch=master) [![Lifecycle: maturing](https://lifecycle.r-lib.org/articles/figures/lifecycle-maturing.svg)](https://lifecycle.r-lib.org/articles/stages.html) @@ -27,29 +27,28 @@ This repository contains the `R` code (r-package folder) and the Java code You can install `r5r`: ```R -# From CRAN - install.packages("r5r") - library(r5r) +# from CRAN +install.packages("r5r") -# or use the development version with latest features - utils::remove.packages('r5r') - devtools::install_github("ipeaGIT/r5r", subdir = "r-package") - library(r5r) +# dev version with latest features +utils::remove.packages('r5r') +devtools::install_github("ipeaGIT/r5r", subdir = "r-package") ``` -Please bear in mind that you need to have *Java SE Development Kit 21* installed -on your computer to use `r5r`. No worries, you don't have to pay for it. The jdk -21 is freely available from the options below: -- [OpenJDK](https://jdk.java.net/java-se-ri/21) -- [Oracle](https://docs.oracle.com/en/java/javase/21/install/index.html) +Please bear in mind that you need to have *Java Development Kit (JDK) 21* installed on your computer to use `r5r`. No worries, you don't have to pay for it. There are numerous open-source JDK implementations, any of which should work with `r5r`. If you don't already have a preferred JDK, we recommend [Adoptium/Eclipse Temurin](https://adoptium.net/). Other open-source JDK implementations include [Amazon Corretto](https://aws.amazon.com/corretto/), and [Oracle OpenJDK](https://jdk.java.net/21/). You only need to install one JDK. -If you don't know what version of Java you have installed on your computer, you -can check it by running this on R console. +The easiest way to install JDK is using the new [{rJavaEnv}](https://www.ekotov.pro/rJavaEnv/) package in R: ```R -rJava::.jinit() -rJava::.jcall("java.lang.System", "S", "getProperty", "java.version") +# install.packages('rJavaEnv') + +# check version of Java currently installed (if any) +rJavaEnv::java_check_version_rjava() + +# install Java 21 +rJavaEnv::java_quick_install(version = 21) + ``` ## Usage @@ -146,11 +145,12 @@ and Open Street Map networks of Porto Alegre (Brazil). Three steps are required use `r5r`, as follows. ```R -# allocate RAM memory to Java +# allocate RAM memory to Java **before** loading the {r5r} library options(java.parameters = "-Xmx2G") -# 1) build transport network, pointing to the path where OSM and GTFS data are stored library(r5r) + +# 1) build transport network, pointing to the path where OSM and GTFS data are stored path <- system.file("extdata/poa", package = "r5r") r5r_core <- setup_r5(data_path = path, verbose = FALSE) diff --git a/java-r5rcore/.idea/artifacts/r5r_1_1_0_jar.xml b/java-r5rcore/.idea/artifacts/r5r_1_1_0_jar.xml index 116c0fea..98a709c6 100644 --- a/java-r5rcore/.idea/artifacts/r5r_1_1_0_jar.xml +++ b/java-r5rcore/.idea/artifacts/r5r_1_1_0_jar.xml @@ -2,7 +2,7 @@ $PROJECT_DIR$/../r-package/inst/jar - + \ No newline at end of file diff --git a/java-r5rcore/.idea/misc.xml b/java-r5rcore/.idea/misc.xml index eb4d409d..7e3040ba 100644 --- a/java-r5rcore/.idea/misc.xml +++ b/java-r5rcore/.idea/misc.xml @@ -1,7 +1,7 @@ - + diff --git a/java-r5rcore/.vscode/settings.json b/java-r5rcore/.vscode/settings.json new file mode 100644 index 00000000..c5f3f6b9 --- /dev/null +++ b/java-r5rcore/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/java-r5rcore/src/org/ipea/r5r/Fares/R5RTransferAllowance.java b/java-r5rcore/src/org/ipea/r5r/Fares/R5RTransferAllowance.java new file mode 100644 index 00000000..cd108dcd --- /dev/null +++ b/java-r5rcore/src/org/ipea/r5r/Fares/R5RTransferAllowance.java @@ -0,0 +1,51 @@ +package org.ipea.r5r.Fares; + +import com.conveyal.r5.analyst.fare.TransferAllowance; + +public class R5RTransferAllowance extends TransferAllowance { + public final int fareType; + + public R5RTransferAllowance(int fareType, int value, int number, int expirationTime){ + super(value, number, expirationTime); + this.fareType = fareType; + } + + public R5RTransferAllowance() { + super(); + this.fareType = -1; + } + + public R5RTransferAllowance tightenExpiration(int maxClockTime){ + // cap expiration time of transfer at max clock time of search, so that transfer slips that technically have more time + // remaining, but that time cannot be used within the constraints of this search, can be pruned. + return new R5RTransferAllowance(this.fareType, this.value, this.number, Math.min(this.expirationTime, maxClockTime)); + + } + + /** + * Is this transfer allowance as good as or better than another transfer allowance? This does not consider the fare + * paid so fare, and can be thought of as follows. If you are standing at a stop, and a perfectly trustworthy person + * comes up to you and offers you two tickets, one with this transfer allowance, and one with the other transfer + * allowance, is this one as good as or better than the other one for any trip that you might make? (Assume you have + * no moral scruples about obtaining a transfer slip from someone else who is probably not supposed to be giving + * them away). + * + * In the base class, this is true iff this transfer allowance has the same or higher value, and the same or later + * expiration time, the same or higher number of transfers remaining. In subclasses for transit systems that have + * different services, this may need to be overridden because not all transfer allowances are comparable. For example, + * in Greater Boston, transfers from local bus can be applied to local bus, subway, or express bus; transfers from + * subway can be applied to other subway services at the same station, local bus, or express bus, and transfers from + * express bus can be applied to only local bus or subway. So the values of those three types of transfers are not + * comparable. + */ + public boolean atLeastAsGoodForAllFutureRedemptions(R5RTransferAllowance other){ + // for empty transfer allowances, this is always true + if (other.fareType == -1) return true; + // if this transfer allowance is for a different fare type, it is not comparable + if (fareType != other.fareType) return false; + // otherwise, compare value, expiration time, and number of transfers remaining + return value >= other.value && expirationTime >= other.expirationTime && number >= other.number; + } + + +} diff --git a/java-r5rcore/src/org/ipea/r5r/Fares/RuleBasedInRoutingFareCalculator.java b/java-r5rcore/src/org/ipea/r5r/Fares/RuleBasedInRoutingFareCalculator.java index 729f0c84..3d24042c 100644 --- a/java-r5rcore/src/org/ipea/r5r/Fares/RuleBasedInRoutingFareCalculator.java +++ b/java-r5rcore/src/org/ipea/r5r/Fares/RuleBasedInRoutingFareCalculator.java @@ -2,7 +2,6 @@ import com.conveyal.r5.analyst.fare.FareBounds; import com.conveyal.r5.analyst.fare.InRoutingFareCalculator; -import com.conveyal.r5.analyst.fare.TransferAllowance; import com.conveyal.r5.profile.McRaptorSuboptimalPathProfileRouter; import com.conveyal.r5.transit.RouteInfo; import com.conveyal.r5.transit.TransitLayer; @@ -110,6 +109,7 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat int currentPatternIndex = -1; int currentBoardTime = -1; + int lastFareType = -1; // first leg of multimodal trip if (!patterns.isEmpty()) { @@ -119,6 +119,7 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat fareForState = getFullFareForRoute(currentPatternIndex); previousPatternIndex = currentPatternIndex; + lastFareType = faresPerRoute[currentPatternIndex].getTypeIndex(); } // subsequent legs @@ -129,6 +130,7 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat // get info on each leg FarePerRoute firstLegType = faresPerRoute[previousPatternIndex]; FarePerRoute secondLegType = faresPerRoute[currentPatternIndex]; + lastFareType = secondLegType.getTypeIndex(); // check if transfer is in same type with unlimited transfers if (firstLegType.getTypeIndex() == secondLegType.getTypeIndex()) { @@ -160,7 +162,7 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat // fares are limited by the maxFare parameter if (fareStructure.getFareCap() > 0) { - fareForState = Math.min(fareForState, Math.round(fareStructure.getIntegerFareCap())); + fareForState = Math.min(fareForState, fareStructure.getIntegerFareCap()); } // initialize transfer allowance @@ -171,20 +173,20 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat // if transfer allowances are inactive (for debugging purposes), just use and empty transfer allowance and // quit the function if (!ParetoItineraryPlanner.travelAllowanceActive) { - return new FareBounds(fareForState, new TransferAllowance()); + return new FareBounds(fareForState, new R5RTransferAllowance()); } // pattern is valid? if (currentPatternIndex == -1) { // no public transport patterns - return empty transfer allowance - return new FareBounds(fareForState, new TransferAllowance()); + return new FareBounds(fareForState, new R5RTransferAllowance()); } // remaining transfers int numberOfRemainingTransfers = fareStructure.getMaxDiscountedTransfers() - discountsApplied; if (numberOfRemainingTransfers <= 0) { // no remaining available transfers - return empty transfer allowance - return new FareBounds(fareForState, new TransferAllowance()); + return new FareBounds(fareForState, new R5RTransferAllowance()); } // get max benefit from possible transfers @@ -208,7 +210,7 @@ public FareBounds calculateFare(McRaptorSuboptimalPathProfileRouter.McRaptorStat int expirationTime = currentBoardTime + fareStructure.getTransferTimeAllowanceSeconds(); // build transfer allowance considering constraints above - TransferAllowance transferAllowance = new TransferAllowance(maxAllowanceValue, numberOfRemainingTransfers, expirationTime); + R5RTransferAllowance transferAllowance = new R5RTransferAllowance(lastFareType, maxAllowanceValue, numberOfRemainingTransfers, expirationTime); return new FareBounds(fareForState, transferAllowance); } diff --git a/java-r5rcore/src/org/ipea/r5r/Network/NetworkChecker.java b/java-r5rcore/src/org/ipea/r5r/Network/NetworkChecker.java index 57672b3a..baa69e7f 100644 --- a/java-r5rcore/src/org/ipea/r5r/Network/NetworkChecker.java +++ b/java-r5rcore/src/org/ipea/r5r/Network/NetworkChecker.java @@ -43,6 +43,7 @@ public static boolean checkR5NetworkVersion(String dataFolder) throws FileNotFou byte[] header = new byte[HEADER.length]; input.read(header, 0, header.length); if (!Arrays.equals(HEADER, header)) { + input.close(); throw new RuntimeException("Unrecognized file header. Is this an R5 Kryo network?"); } String formatVersion = kryo.readObject(input, String.class); diff --git a/java-r5rcore/src/org/ipea/r5r/Process/FaretoDebug.java b/java-r5rcore/src/org/ipea/r5r/Process/FaretoDebug.java new file mode 100644 index 00000000..5ca827d6 --- /dev/null +++ b/java-r5rcore/src/org/ipea/r5r/Process/FaretoDebug.java @@ -0,0 +1,57 @@ +package org.ipea.r5r.Process; + +import java.text.ParseException; +import java.util.concurrent.ForkJoinPool; + +import org.ipea.r5r.RDataFrame; +import org.ipea.r5r.RoutingProperties; +import org.ipea.r5r.R5.R5ParetoServer; + +import com.conveyal.r5.analyst.cluster.RegionalTask; +import com.conveyal.r5.transit.TransportNetwork; + +/** + * This outputs the Pareto itinerary planner results directly to JSON. + */ +public class FaretoDebug extends R5Process { + public FaretoDebug(ForkJoinPool threadPool, TransportNetwork transportNetwork, + RoutingProperties routingProperties) { + super(threadPool, transportNetwork, routingProperties); + } + + public R5ParetoServer.ParetoReturn pathResults = null; + + @Override + protected boolean isOneToOne() { + return true; + } + + @Override + protected RDataFrame runProcess(int index) throws ParseException { + RegionalTask request = buildRequest(index); + request.fromLat = fromLats[0]; + request.fromLon = fromLons[0]; + request.toLat = toLats[0]; + request.toLon = toLons[0]; + request.maxFare = 150_000_000; + + R5ParetoServer computer = new R5ParetoServer(request, transportNetwork); + pathResults = computer.handle(); + + // to match R5Process interface, return type must be dataframe. We work around this by returning an empty + // dataframe. Cannot return null because value is used in mergeDataFrame + return new RDataFrame(0); + } + + @Override + protected RDataFrame buildDataFrameStructure(String fromId, int nRows) { + // to avoid NPEs in mergeDataFrame, something must be returned + return new RDataFrame(nRows); + } + + @Override + protected void buildDestinationPointSet() { + // not needed in this class + } + +} diff --git a/java-r5rcore/src/org/ipea/r5r/Process/ParetoItineraryPlanner.java b/java-r5rcore/src/org/ipea/r5r/Process/ParetoItineraryPlanner.java index 57a1a394..ca7a7572 100644 --- a/java-r5rcore/src/org/ipea/r5r/Process/ParetoItineraryPlanner.java +++ b/java-r5rcore/src/org/ipea/r5r/Process/ParetoItineraryPlanner.java @@ -7,7 +7,6 @@ import org.ipea.r5r.RoutingProperties; import org.ipea.r5r.Utils.Utils; -import java.io.IOException; import java.text.ParseException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicInteger; diff --git a/java-r5rcore/src/org/ipea/r5r/Process/R5Process.java b/java-r5rcore/src/org/ipea/r5r/Process/R5Process.java index 58dbda9b..9b758e01 100644 --- a/java-r5rcore/src/org/ipea/r5r/Process/R5Process.java +++ b/java-r5rcore/src/org/ipea/r5r/Process/R5Process.java @@ -1,14 +1,12 @@ package org.ipea.r5r.Process; import com.conveyal.r5.analyst.FreeFormPointSet; -import com.conveyal.r5.analyst.cluster.PathResult; import com.conveyal.r5.analyst.cluster.RegionalTask; import com.conveyal.r5.analyst.scenario.Scenario; import com.conveyal.r5.api.util.LegMode; import com.conveyal.r5.api.util.TransitModes; import com.conveyal.r5.profile.StreetMode; import com.conveyal.r5.transit.TransportNetwork; -import org.ipea.r5r.Fares.RuleBasedInRoutingFareCalculator; import org.ipea.r5r.RDataFrame; import org.ipea.r5r.RoutingProperties; import org.ipea.r5r.Utils.Utils; @@ -149,7 +147,8 @@ public void setDestinations(String[] toIds, double[] toLats, double[] toLons, St this.nDestinations = toIds.length; // set maxDestinations in R5 for detailed path information retrieval - PathResult.maxDestinations = this.nDestinations; + // PathResult.maxDestinations does not exist in R5 anymore + //PathResult.maxDestinations = this.nDestinations; } public void setOrigins(String[] fromIds, double[] fromLats, double[] fromLons) { diff --git a/java-r5rcore/src/org/ipea/r5r/Process/TravelTimeMatrixComputer.java b/java-r5rcore/src/org/ipea/r5r/Process/TravelTimeMatrixComputer.java index 840c77d6..a170f000 100644 --- a/java-r5rcore/src/org/ipea/r5r/Process/TravelTimeMatrixComputer.java +++ b/java-r5rcore/src/org/ipea/r5r/Process/TravelTimeMatrixComputer.java @@ -1,5 +1,19 @@ package org.ipea.r5r.Process; +import static com.google.common.base.Preconditions.checkState; + +import java.lang.reflect.Field; +import java.text.ParseException; +import java.util.Collection; +import java.util.concurrent.ForkJoinPool; + +import org.ipea.r5r.RDataFrame; +import org.ipea.r5r.RoutingProperties; +import org.ipea.r5r.R5.R5TravelTimeComputer; +import org.ipea.r5r.Utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.conveyal.r5.OneOriginResult; import com.conveyal.r5.analyst.TravelTimeComputer; import com.conveyal.r5.analyst.cluster.PathResult; @@ -7,21 +21,9 @@ import com.conveyal.r5.analyst.cluster.TravelTimeResult; import com.conveyal.r5.transit.TransportNetwork; import com.conveyal.r5.transit.path.RouteSequence; +import com.conveyal.analysis.models.CsvResultOptions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; -import org.ipea.r5r.R5.R5TravelTimeComputer; -import org.ipea.r5r.RDataFrame; -import org.ipea.r5r.RoutingProperties; -import org.ipea.r5r.Utils.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Field; -import java.text.ParseException; -import java.util.*; -import java.util.concurrent.ForkJoinPool; - -import static com.google.common.base.Preconditions.checkState; public class TravelTimeMatrixComputer extends R5Process { @@ -175,7 +177,7 @@ private Multimap[] extractPathResults(PathResult paths, checkState(nIterations > 0, "A path was stored without any iterations"); // extract the route id's - String[] path = routeSequence.detailsWithGtfsIds(this.transportNetwork.transitLayer); + String[] path = routeSequence.detailsWithGtfsIds(this.transportNetwork.transitLayer, new CsvResultOptions()); for (PathResult.Iteration iteration : iterations) { PathBreakdown breakdown = new PathBreakdown(); diff --git a/java-r5rcore/src/org/ipea/r5r/R5/R5ParetoServer.java b/java-r5rcore/src/org/ipea/r5r/R5/R5ParetoServer.java index 00e44e20..66870bf3 100644 --- a/java-r5rcore/src/org/ipea/r5r/R5/R5ParetoServer.java +++ b/java-r5rcore/src/org/ipea/r5r/R5/R5ParetoServer.java @@ -2,6 +2,7 @@ import com.conveyal.r5.SoftwareVersion; import com.conveyal.r5.analyst.cluster.RegionalTask; +import com.conveyal.r5.analyst.fare.BostonInRoutingFareCalculator; import com.conveyal.r5.analyst.fare.TransferAllowance; import com.conveyal.r5.api.util.LegMode; import com.conveyal.r5.common.GeometryUtils; @@ -100,7 +101,11 @@ public R5ParetoServer.ParetoReturn handle () { ParetoReturn ret = null; try { - ret = new ParetoReturn(trips, totalTime); + ProfileRequest newReq = (ProfileRequest) profileRequest.clone(); + // don't serialize the whole R5R fare calculator. HACK fareto requires something here + // so we just return a BostonInRoutingFareCalculator. + newReq.inRoutingFareCalculator = new BostonInRoutingFareCalculator(); + ret = new ParetoReturn(trips, totalTime, newReq); } catch (NullPointerException e){ LOG.error("exception in building return"); e.printStackTrace(); @@ -139,10 +144,12 @@ public static final class ParetoReturn { /** save backend version in JSON output - useful for JSON that's being pushed to fareto-examples */ public SoftwareVersion backendVersion = SoftwareVersion.instance; public String generationTime = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME); + public ProfileRequest request; - public ParetoReturn(Collection trips, long computeTimeMillis) { + public ParetoReturn(Collection trips, long computeTimeMillis, ProfileRequest request) { this.trips = trips; this.computeTimeMillis = computeTimeMillis; + this.request = request; } } diff --git a/java-r5rcore/src/org/ipea/r5r/R5RCore.java b/java-r5rcore/src/org/ipea/r5r/R5RCore.java index 31fa00e8..c09ddadc 100644 --- a/java-r5rcore/src/org/ipea/r5r/R5RCore.java +++ b/java-r5rcore/src/org/ipea/r5r/R5RCore.java @@ -6,6 +6,8 @@ import com.conveyal.r5.analyst.cluster.PathResult; import com.conveyal.r5.analyst.decay.*; import com.conveyal.r5.transit.TransportNetwork; +import com.fasterxml.jackson.core.JsonProcessingException; + import org.ipea.r5r.Fares.FareStructure; import org.ipea.r5r.Fares.FareStructureBuilder; import org.ipea.r5r.Fares.RuleBasedInRoutingFareCalculator; @@ -440,6 +442,29 @@ public RDataFrame paretoFrontier(String[] fromIds, double[] fromLats, double[] f return paretoFrontierCalculator.run(); } + /** + * This functions returns the Pareto frontier as raw R5 JSON (for visualization with Fareto) + * @throws JsonProcessingException + */ + public String faretoJson(String fromId, double fromLat, double fromLon, + String toId, double toLat, double toLon, + String directModes, String transitModes, String accessModes, String egressModes, + String date, String departureTime, + int maxWalkTime, int maxBikeTime, int maxCarTime, int maxTripDuration) throws ExecutionException, InterruptedException, JsonProcessingException { + FaretoDebug calculator = new FaretoDebug(r5rThreadPool, transportNetwork, routingProperties); + calculator.setOrigins(new String[] {fromId}, new double[] {fromLat}, new double[] {fromLon}); + calculator.setDestinations(new String[] {toId}, new double[] {toLat}, new double[] {toLon}); + calculator.setModes(directModes, accessModes, transitModes, egressModes); + calculator.setDepartureDateTime(date, departureTime); + calculator.setTripDuration(maxWalkTime, maxBikeTime, maxCarTime, maxTripDuration); + + calculator.run(); + + // we use the conveyal objectmapper, because it is already configured to properly serialize pareto returns + // notably, it can handle GeoJson and names the properties the way fareto expects (camelCase rather than snake_case) + return com.conveyal.analysis.util.JsonUtil.objectMapper.writeValueAsString(calculator.pathResults); + } + // -------------------------------------- ACCESSIBILITY ---------------------------------------------- public RDataFrame accessibility(String[] fromIds, double[] fromLats, double[] fromLons, diff --git a/r-package/DESCRIPTION b/r-package/DESCRIPTION index 4b2f5052..b621c254 100644 --- a/r-package/DESCRIPTION +++ b/r-package/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: r5r Title: Rapid Realistic Routing with 'R5' -Version: 1.1.0999 +Version: 2.0.09999 Authors@R: c( person("Marcus", "Saraiva", , "marcus.saraiva@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-6218-2338")), @@ -21,8 +21,8 @@ Description: Rapid realistic routing on multimodal transport networks (walk, bike, public transport and car) using 'R5', the Rapid Realistic Routing on Real-world and Reimagined networks engine . The package allows users to generate - detailed routing analysis or calculate travel time matrices using - seamless parallel computing on top of the R5 Java machine. While R5 + detailed routing analysis or calculate travel time and monetary cost matrices + using seamless parallel computing on top of the R5 Java machine. While R5 is developed by Conveyal, the package r5r is independently developed by a team at the Institute for Applied Economic Research (Ipea) with contributions from collaborators. Apart from the documentation in this @@ -32,20 +32,20 @@ Description: Rapid realistic routing on multimodal transport networks independent update process. Hence, users should confirm the R5 version implied by the Conveyal user manual (see ) corresponds with the R5 version - that r5r depends on. + that r5r depends on. This version of r5r depends on R5 v7.1. License: MIT + file LICENSE -URL: https://github.com/ipeaGIT/r5r +URL: https://github.com/ipeaGIT/r5r, https://ipeagit.github.io/r5r/ BugReports: https://github.com/ipeaGIT/r5r/issues Depends: R (>= 3.6) Imports: checkmate, + cli, concaveman, - curl, data.table, - httr, jsonlite, rJava (>= 0.9-10), + rlang, sf (>= 1.0-12), sfheaders, utils, @@ -61,11 +61,12 @@ Suggests: knitr, parallel, patchwork, + rJavaEnv, rmarkdown, testthat VignetteBuilder: knitr Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 -SystemRequirements: Java JDK (>= 11.0) +RoxygenNote: 7.3.2 +SystemRequirements: Java JDK (>= 21.0) diff --git a/r-package/NAMESPACE b/r-package/NAMESPACE index 71e83e7c..d27d5bc2 100644 --- a/r-package/NAMESPACE +++ b/r-package/NAMESPACE @@ -7,6 +7,7 @@ export(expanded_travel_time_matrix) export(find_snap) export(isochrone) export(pareto_frontier) +export(r5r_cache) export(r5r_sitrep) export(read_fare_structure) export(setup_fare_structure) diff --git a/r-package/NEWS.md b/r-package/NEWS.md index 578950ae..fa4de3ed 100644 --- a/r-package/NEWS.md +++ b/r-package/NEWS.md @@ -1,4 +1,27 @@ -# r5r 1.1.0999 dev +# r5r 2.1.0 (dev) + +**Breaking changes** + +- r5r now uses the latest version of R5 V7.2. Closed [#401](https://github.com/ipeaGIT/r5r/issues/401) + +**Minor changes** + +- The `isochrone()` function has a new boolean parameter `polygon_output` that allows users to choose whether the output should be a polygon- or line-based isochrone. Closed [#382](https://github.com/ipeaGIT/r5r/issues/382) + + + +# r5r 2.0 + +**Breaking changes** + +- r5r uses now JDK 21 or higher (Breaking changes). Closed [#350](https://github.com/ipeaGIT/r5r/issues/350). +- r5r now uses the latest version of R5 V7.1. Closed [#350](https://github.com/ipeaGIT/r5r/issues/350) + +**Major changes** + +- r5r now stores R5 Jar file at user dir using `tools::R_user_dir()` +- New function `r5r_cache()` to manage the cache of the R5 Jar file. +- By using the JDK 21, this version of r5r also fixed an incompatibility with MAC ARM processors. Closed [#315](https://github.com/ipeaGIT/r5r/issues/315) **Minor changes** @@ -9,7 +32,7 @@ - Updated the vignette on time window to explain how this parameter behaves when used in the `detailed_itineraries()` function. **Bug Fixes** -- Fixed bug that prevented the using the `output_dir` parameter in the `detailed_itineraries(all_to_all = TRUE)` function. Closes [#327](https://github.com/ipeaGIT/r5r/issues/327) with a contribution ([PR #354](https://github.com/ipeaGIT/r5r/pull/354)) from Luyu Liu. +- Fixed bug that prevented the use the `output_dir` parameter in the `detailed_itineraries(all_to_all = TRUE)` function. Closes [#327](https://github.com/ipeaGIT/r5r/issues/327) with a contribution ([PR #354](https://github.com/ipeaGIT/r5r/pull/354)) from Luyu Liu. - Fixed bug that prevented `detailed_itineraries` from working with frequency-based GTFS feeds. It should ONLY work with frequency-based GTFS feeds. **New contributors to r5r** diff --git a/r-package/R/download_r5.R b/r-package/R/download_r5.R index 7292d943..3c81841b 100644 --- a/r-package/R/download_r5.R +++ b/r-package/R/download_r5.R @@ -2,8 +2,8 @@ #' #' Downloads `R5.jar` and saves it locally, inside the package directory. #' -#' @param version A string. The version of R5 to be downloaded. Defaults to the -#' latest version. +#' @param version A string. The version of R5 to be downloaded. When `NULL`, it +#' defaults to the latest version. #' @param quiet A logical. Whether to show informative messages when downloading #' the file. Defaults to `FALSE`. #' @param force_update A logical. Whether to overwrite a previously downloaded @@ -18,13 +18,17 @@ #' @examplesIf identical(tolower(Sys.getenv("NOT_CRAN")), "true") #' library(r5r) #' -#' download_r5(version = "7.0.0", temp_dir = TRUE) +#' download_r5(temp_dir = TRUE) #' @export -download_r5 <- function(version = "7.0.0", +download_r5 <- function(version = NULL, quiet = FALSE, force_update = FALSE, temp_dir = FALSE) { + # R5 version + if(is.null(version)) {version = r5r_env$r5_jar_version} + + # check inputs ---------------------------------------------------------- checkmate::assert_logical(quiet) @@ -40,50 +44,60 @@ download_r5 <- function(version = "7.0.0", options(timeout = max(600, getOption("timeout"))) - # download R5's jar ----------------------------------------------------- + # download R5 jar ----------------------------------------------------- + + if (!dir.exists(r5r_env$cache_dir)) {dir.create(r5r_env$cache_dir, recursive = TRUE)} file_url <- fileurl_from_metadata(version) filename <- basename(file_url) - destfile <- data.table::fifelse( + jar_file <- data.table::fifelse( temp_dir, file.path(tempdir(), filename), - file.path(system.file("jar", package = "r5r"), filename) + file.path( r5r_env$cache_dir , filename) ) + # check if the file exists and is not corrupted + if (file.exists(jar_file) && file.info(jar_file)$size < r5r_env$r5_jar_size && isFALSE(force_update)) { + stop(message("R5 Jar file is corrupted. To fix this problem, download it again with 'r5r::download_r5(force_update = TRUE)'")) + } + # check if the file exists, and returns its path if it does. otherwise, # download it from IPEA's server - if there's no internet connection "fail # gracefully" (i.e invisibly returns NULL and outputs a informative message)" - - if (file.exists(destfile) && (force_update == FALSE)) { - if (!quiet) message("Using cached R5 version from ", destfile) - return(destfile) + if (file.exists(jar_file) && (force_update == FALSE)) { + if (!quiet) message("Using cached R5 version from ", jar_file) + return(jar_file) } - if (isFALSE(check_connection(file_url))) { - # if (!quiet) - # message( - # "Problem connecting to the data server. ", - # "Please try again in a few minutes." - # ) - return(invisible(NULL)) - } - - # create dir - jar_dir <- system.file("jar", package = "r5r") - if (!dir.exists(jar_dir)) dir.create(jar_dir) - # download JAR - message("Downloading R5 jar file to ", destfile) - utils::download.file( - url = file_url, - destfile = destfile, - mode = "wb", - # method = "curl", - # extra = "--insecure", - quiet = quiet + message("Downloading R5 jar file to ", jar_file) + + try(silent = TRUE, + utils::download.file( + url = file_url, + destfile = jar_file, + mode = "wb", + # method = "curl", + # extra = "--insecure", + quiet = quiet + ) ) - return(destfile) + # try(silent = TRUE, + # httr::GET(url=file_url, + # if(isFALSE(quiet)){ httr::progress()}, + # httr::write_disk(jar_file, overwrite = TRUE), + # config = httr::config(ssl_verifypeer = FALSE)) + # ) + + +# Halt function if download failed (file must exist and be larger than 60 MB) +if (!file.exists(jar_file) | file.info(jar_file)$size < r5r_env$r5_jar_size) { + message('Internet connection not working properly.') + return(invisible(NULL)) + } + + return(jar_file) } diff --git a/r-package/R/isochrone.R b/r-package/R/isochrone.R index 69898919..c4bee059 100644 --- a/r-package/R/isochrone.R +++ b/r-package/R/isochrone.R @@ -1,20 +1,22 @@ #' Estimate isochrones from a given location #' #' @description Fast computation of isochrones from a given location. The -#' function estimates isochrones based on the travel times from the trip origin -#' to all nodes in the road network. -#' +#' function can return either polygon-based or line-based isochrones. +#' Polygon-based isochrones are generated as concave polygons based on the +#' travel times from the trip origin to all nodes in the transport network. +#' Meanwhile, line-based isochronesare based on travel times from each origin +#' to the centroids of all segments in the transport network. #' #' @template r5r_core #' @param origins Either a `POINT sf` object with WGS84 CRS, or a #' `data.frame` containing the columns `id`, `lon` and `lat`. #' @param cutoffs numeric vector. Number of minutes to define the time span of #' each Isochrone. Defaults to `c(0, 15, 30)`. -#' @param sample_size numeric. Sample size of nodes in the road network used to -#' estimate isochrones. Defaults to `0.8` (80% of all nodes in the +#' @param sample_size numeric. Sample size of nodes in the transport network used +#' to estimate isochrones. Defaults to `0.8` (80% of all nodes in the #' transport network). Value can range between `0.2` and `1`. Smaller #' values increase computation speed but return results with lower -#' precision. +#' precision. This parameter has no effect when `polygon_output = FALSE`. #' @param mode A character vector. The transport modes allowed for access, #' transfer and vehicle legs of the trips. Defaults to `WALK`. Please see #' details for other options. @@ -26,6 +28,12 @@ #' transport networks, please check the `calendar.txt` within your GTFS #' feeds for valid dates. Please see details for further information on #' how datetimes are parsed. +#' @param polygon_output A Logical. If `TRUE`, the function outputs +#' polygon-based isochrones (the default) based on travel times from each +#' origin to a sample of a random sample nodes in the transport network +#' (see parameter `sample_size`). If `FALSE`, the function outputs +#' line-based isochronesbased on travel times from each origin to the +#' centroids of all segments in the transport network. #' @param time_window An integer. The time window in minutes for which `r5r` #' will calculate multiple travel time matrices departing each minute. #' Defaults to 10 minutes. The function returns the result based on @@ -84,34 +92,60 @@ #' @family Isochrone #' #' @examplesIf identical(tolower(Sys.getenv("NOT_CRAN")), "true") -#' options(java.parameters = "-Xmx2G") -#' library(r5r) -#' -#' # build transport network -#' data_path <- system.file("extdata/poa", package = "r5r") -#' r5r_core <- setup_r5(data_path = data_path) -#' -#' # load origin/point of interest -#' points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) -#' origin_1 <- points[936,] -#' -#' departure_datetime <- as.POSIXct( -#' "13-05-2019 14:00:00", -#' format = "%d-%m-%Y %H:%M:%S" -#' ) -#' -#'# estimate isochrone from origin_1 -#'iso1 <- isochrone(r5r_core, -#' origin = origin_1, -#' mode = c("walk"), +#'options(java.parameters = "-Xmx2G") +#'library(r5r) +#'library(ggplot2) +#' +#'# build transport network +#'data_path <- system.file("extdata/poa", package = "r5r") +#'r5r_core <- setup_r5(data_path = data_path) +#' +#'# load origin/point of interest +#'points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) +#'origin_1 <- points[936,] +#' +#'departure_datetime <- as.POSIXct( +#' "13-05-2019 14:00:00", +#' format = "%d-%m-%Y %H:%M:%S" +#') +#' +#'# estimate polygon-based isochrone from origin_1 +#'iso_poly <- isochrone(r5r_core, +#' origins = origin_1, +#' mode = "walk", +#' polygon_output = TRUE, #' departure_datetime = departure_datetime, #' cutoffs = seq(0, 100, 10) #' ) -#'head(iso1) +#'head(iso_poly) +#' +#' +#'# estimate line-based isochrone from origin_1 +#'iso_lines <- isochrone(r5r_core, +#' origins = origin_1, +#' mode = "walk", +#' polygon_output = FALSE, +#' departure_datetime = departure_datetime, +#' cutoffs = seq(0, 100, 10) +#') +#'head(iso_lines) #' +#' +#'# plot colors #'colors <- c('#ffe0a5','#ffcb69','#ffa600','#ff7c43','#f95d6a', #' '#d45087','#a05195','#665191','#2f4b7c','#003f5c') -#'plot(iso1['isochrone'], col = colors) +#' +#'# polygons +#'ggplot() + +#' geom_sf(data=iso_poly, aes(fill=factor(isochrone))) + +#' scale_fill_manual(values = colors) + +#' theme_minimal() +#' +#'# lines +#'ggplot() + +#' geom_sf(data=iso_lines, aes(color=factor(isochrone))) + +#' scale_color_manual(values = colors) + +#' theme_minimal() #' #'stop_r5(r5r_core) #' @@ -119,10 +153,11 @@ isochrone <- function(r5r_core, origins, mode = "transit", - mode_egress = "WALK", + mode_egress = "walk", cutoffs = c(0, 15, 30), sample_size = 0.8, departure_datetime = Sys.time(), + polygon_output = TRUE, time_window = 10L, max_walk_time = Inf, max_bike_time = Inf, @@ -142,11 +177,11 @@ isochrone <- function(r5r_core, # check cutoffs checkmate::assert_numeric(cutoffs, lower = 0) + checkmate::assert_logical(polygon_output) # check sample_size checkmate::assert_numeric(sample_size, lower = 0.2, upper = 1, max.len = 1) - # max cutoff is used as max_trip_duration max_trip_duration = as.integer(max(cutoffs)) @@ -156,9 +191,12 @@ isochrone <- function(r5r_core, # IF no destinations input ------------------------------------------------------------ + + ## whether polygon- or line-based isochrones + if (isTRUE(polygon_output)) { + # use all network nodes as destination points - network <- r5r::street_network_to_sf(r5r_core) - destinations = network$vertices + destinations = r5r::street_network_to_sf(r5r_core)$vertices # sample size: proportion of nodes to be considered index_sample <- sample(1:nrow(destinations), @@ -166,10 +204,19 @@ isochrone <- function(r5r_core, replace = FALSE) destinations <- destinations[index_sample,] on.exit(rm(.Random.seed, envir=globalenv())) + } + if(isFALSE(polygon_output)){ + network_e <- r5r::street_network_to_sf(r5r_core)$edges - names(destinations)[1] <- 'id' - destinations$id <- as.character(destinations$id) + sf::st_agr(network_e) = "constant" + + destinations <- sf::st_centroid(network_e) + } + + # rename id col + names(destinations)[1] <- 'id' + destinations$id <- as.character(destinations$id) # estimate travel time matrix @@ -197,10 +244,14 @@ isochrone <- function(r5r_core, # aggregate travel-times - ttm[, isochrone := cut(x=travel_time_p50, breaks=cutoffs)] + # ttm[, isochrone_interval := cut(x=travel_time_p50, breaks=cutoffs)] + ttm[, isochrone := cut(x=travel_time_p50, breaks=cutoffs, labels=F)] + ttm[, isochrone := cutoffs[cutoffs>0][isochrone]] + - # fun to get isochrones for each origin - prep_iso <- function(orig){ # orig = '89a901280b7ffff' + ### fun to get isochrones for each origin + # polygon-based isochrones + prep_iso_poly <- function(orig){ # orig = '89a90128107ffff' temp_ttm <- subset(ttm, from_id == orig) @@ -215,27 +266,50 @@ isochrone <- function(r5r_core, get_poly <- function(cut){ # cut = 30 temp <- subset(dest, travel_time_p50 <= cut) + + if(nrow(temp)<=4){stop(paste0("Your origin point ", orig," is probably located in an area where the road density is too low to create proper isochrone polygons and/or the time cutoff is too short. In this case, we strongly recommend setting `polygon_output = FALSE` or setting longer cutoffs."))} + temp_iso <- concaveman::concaveman(temp) temp_iso$isochrone <- cut return(temp_iso) } - iso_list <- lapply(X=cutoffs[cutoffs>0], FUN=get_poly) iso <- data.table::rbindlist(iso_list) - iso$id <- orig - iso <- sf::st_sf(iso) - iso <- iso[ order(-iso$isochrone), ] + iso[, id := orig] + iso <- iso[ order(-isochrone), ] data.table::setcolorder(iso, c('id', 'isochrone')) + # iso <- sf::st_as_sf(iso) # plot(iso) return(iso) } + + # line-based isochrones + prep_iso_lines <- function(orig){ # orig = '89a90128107ffff' + + temp_ttm <- subset(ttm, from_id == orig) + + # join ttm results to destinations + temp_iso <- subset(network_e, edge_index %in% temp_ttm$to_id) + data.table::setDT(temp_iso)[, edge_index := as.character(edge_index)] + temp_iso[temp_ttm, on=c('edge_index' ='to_id'), c('travel_time_p50', 'isochrone') := list(i.travel_time_p50, i.isochrone)] + # temp_iso <- sf::st_as_sf(temp_iso) + + temp_iso <- temp_iso[order(-isochrone, -travel_time_p50)] + data.table::setcolorder(temp_iso, c('edge_index', 'osm_id', 'isochrone', 'travel_time_p50')) + # plot(temp_iso) + return(temp_iso) + } + + # get the isocrhone from each origin + prep_iso <- ifelse(isTRUE(polygon_output), prep_iso_poly, prep_iso_lines) iso_list <- lapply(X = unique(origins$id), FUN = prep_iso) # put output together iso <- data.table::rbindlist(iso_list) iso <- sf::st_sf(iso) + iso <- subset(iso, isochrone < Inf) # remove data.table from class class(iso) <- c("sf", "data.frame") diff --git a/r-package/R/onLoad.R b/r-package/R/onLoad.R new file mode 100644 index 00000000..5df0cee6 --- /dev/null +++ b/r-package/R/onLoad.R @@ -0,0 +1,33 @@ +# initial message about ram memory +.onAttach <- function(lib, pkg) { + packageStartupMessage( + "Please make sure you have already allocated ", + "some memory to Java by running:\n", + " options(java.parameters = '-Xmx2G').\n", + "You should replace '2G' by the amount of memory you'll require. ", + "Currently, Java memory is set to ", getOption("java.parameters") + ) + } + +# package global variables +r5r_env <- new.env(parent = emptyenv()) + +.onLoad <- function(lib, pkg) { + + # JAR version + r5r_env$r5_jar_version <- "7.2.0" + r5r_env$r5_jar_size <- 63602630 + + # create dir to store R5 Jar + cache_d <- paste0('r5r/r5_jar_v', r5r_env$r5_jar_version) + r5r_env$cache_dir <- tools::R_user_dir(cache_d, which = 'cache') + if (!dir.exists(r5r_env$cache_dir)) dir.create(r5r_env$cache_dir, recursive = TRUE) + # gsub("\\\\", "/", r5r_env$cache_dir) + + ## delete any JAR files from old releases + dir_above <- dirname(r5r_env$cache_dir) + all_cache <- list.files(dir_above, pattern = 'r5',full.names = TRUE) + old_cache <- all_cache[!grepl(r5r_env$r5_jar_version, all_cache)] + if(length(old_cache)>0){ unlink(old_cache, recursive = TRUE) } + +} diff --git a/r-package/R/r5r.R b/r-package/R/r5r.R index 4e479a7d..b3302fcb 100644 --- a/r-package/R/r5r.R +++ b/r-package/R/r5r.R @@ -79,17 +79,8 @@ if (getRversion() >= "2.15.1") { 'travel_time_p50', 'id', 'i.travel_time_p50', - 'i.isochrone' + 'i.isochrone', + 'edge_index' ) ) } - -.onAttach <- function(lib, pkg) { - packageStartupMessage( - "Please make sure you have already allocated ", - "some memory to Java by running:\n", - " options(java.parameters = '-Xmx2G').\n", - "You should replace '2G' by the amount of memory you'll require. ", - "Currently, Java memory is set to ", getOption("java.parameters") - ) -} diff --git a/r-package/R/r5r_cache.R b/r-package/R/r5r_cache.R new file mode 100644 index 00000000..2e1f5a8e --- /dev/null +++ b/r-package/R/r5r_cache.R @@ -0,0 +1,73 @@ +#' Manage cached files from the r5r package +#' +#' @param list_files Logical. Whether to print a message with the address of r5r +#' JAR files cached locally. Defaults to `TRUE`. +#' @param delete_file String. The file name (basename) of a JAR file cached +#' locally that should be deleted. Defaults to `NULL`, so that no +#' file is deleted. If `delete_file = "all"`, then all cached files are +#' deleted. +#' +#' @return A message indicating which file exist and/or which ones have been +#' deleted from local cache directory. +#' @export +#' @family Cache data +#' @examplesIf identical(tolower(Sys.getenv("NOT_CRAN")), "true") +#' # download r5 JAR +#' r5r::download_r5() +#' +#' # list all files cached +#' r5r_cache(list_files = TRUE) +#' +#' # delete r5 JAR +#' r5r_cache(delete_file = 'r5-v7.0') +#' +r5r_cache <- function(list_files = TRUE, + delete_file = NULL){ + + # check inputs + checkmate::assert_logical(list_files) + checkmate::assert_character(delete_file, null.ok = TRUE) + + # find / create local dir + if (!dir.exists(r5r_env$cache_dir)) { dir.create(r5r_env$cache_dir, recursive=TRUE) } + + # list cached files + files <- list.files(dirname(r5r_env$cache_dir), full.names = TRUE, recursive = TRUE) + + # if wants to delete file + # delete_file = "r5-v7.0-all.jar" + if (!is.null(delete_file)) { + + # IF file does not exist, print message + if (!any(grepl(delete_file, files)) & delete_file != "all") { + message(paste0("The file '", delete_file, "' is not cached.")) + } + + # IF file exists, delete file + if (any(grepl(delete_file, files))) { + f <- files[grepl(delete_file, files)] + unlink(f, recursive = TRUE, force = TRUE) + message(paste0("The file '", delete_file, "' has been removed.")) + } + + # Delete ALL file + if (delete_file=='all') { + + # delete any files from censobr, current and old data releases + dir_above <- dirname(r5r_env$cache_dir) + unlink(dir_above, recursive = TRUE, force = TRUE) + message(paste0("All files have been removed.")) + + } + } + + # list cached files + files <- list.files(r5r_env$cache_dir, full.names = TRUE) + + # print file names + if(isTRUE(list_files)){ + message('Files currently chached:') + message(paste0(files, collapse = '\n')) + } +} + diff --git a/r-package/R/r5r_sitrep.R b/r-package/R/r5r_sitrep.R index 2b2c8d8c..39c0556b 100644 --- a/r-package/R/r5r_sitrep.R +++ b/r-package/R/r5r_sitrep.R @@ -19,13 +19,13 @@ r5r_sitrep <- function() { r5r_package_version <- utils::packageVersion("r5r") - jar_dir <- system.file("jar", package = "r5r") + jar_dir <- r5r_env$cache_dir jar_dir_files <- list.files(jar_dir) jar_dir_files_full_names <- list.files(jar_dir, full.names = TRUE) r5r_jar <- jar_dir_files[grepl("r5r_\\d_\\d_\\d.*\\.jar", jar_dir_files)] - r5r_jar_version <- sub("\\.jar", "", sub("r5r_", "", r5r_jar)) - r5r_jar_version <- gsub("_", "\\.", r5r_jar_version) + + r5r_jar_version <- r5r_env$r5_jar_version r5r_jar_path <- jar_dir_files_full_names[ grepl("\\/r5r_\\d_\\d_\\d*\\.jar", jar_dir_files_full_names) ] diff --git a/r-package/R/setup_r5.R b/r-package/R/setup_r5.R index ca1ba5cf..eeee9936 100644 --- a/r-package/R/setup_r5.R +++ b/r-package/R/setup_r5.R @@ -56,9 +56,6 @@ setup_r5 <- function(data_path, elevation = "TOBLER", overwrite = FALSE) { - # R5 version - version = "7.0.0" - # check inputs ------------------------------------------------------------ checkmate::assert_directory_exists(data_path) @@ -109,22 +106,21 @@ setup_r5 <- function(data_path, } # check if the most recent JAR release is stored already. - - fileurl <- fileurl_from_metadata(version) + fileurl <- fileurl_from_metadata( r5r_env$r5_jar_version ) filename <- basename(fileurl) jar_file <- data.table::fifelse( temp_dir, file.path(tempdir(), filename), - file.path(system.file("jar", package = "r5r"), filename) + file.path( r5r_env$cache_dir, filename) ) - # If there isn't a JAR already, download it - if (checkmate::test_file_exists(jar_file)) { + # If there isn't a JAR already larger than 60MB, download it + if (checkmate::test_file_exists(jar_file) && file.info(jar_file)$size > r5r_env$r5_jar_size) { if (!verbose) message("Using cached R5 version from ", jar_file) } else { - check <- download_r5(version = version, temp_dir = temp_dir, quiet = !verbose) - if (is.null(check)) { return(invisible(NULL)) } + check <- download_r5(temp_dir = temp_dir, quiet = !verbose) + if (is.null(check)) { return(invisible(NULL)) } } # r5r jar @@ -148,6 +144,10 @@ setup_r5 <- function(data_path, message("\nUsing cached network.dat from ", dat_file) } else { + # check if the user has permission to write to the data directory. if not, + # R5 won't be able to create the required files and will fail with a + # not-that-enlightening error + error_if_no_write_permission(data_path) # stop r5 in case it is already running suppressMessages( r5r::stop_r5() ) @@ -182,3 +182,25 @@ setup_r5 <- function(data_path, return(r5r_core) } + +error_if_no_write_permission <- function(data_path) { + write_permission <- file.access(data_path, mode = 2) + + normalized_path <- normalizePath(data_path) + + if (write_permission == -1) { + cli::cli_abort( + c( + "Permission to write to {.path {normalized_path}} denied.", + i = paste0( + "{.pkg r5r} needs write privilege to create the network files. ", + "Please make sure you have this privilege in the provided directory." + ) + ), + class = "dir_permission_denied", + call = rlang::caller_env() + ) + } + + return(invisible(TRUE)) +} diff --git a/r-package/R/utils.R b/r-package/R/utils.R index 73d14558..397da212 100644 --- a/r-package/R/utils.R +++ b/r-package/R/utils.R @@ -2,14 +2,18 @@ #' #' Returns the most recent JAR file url from metadata, depending on the version. #' -#' @param version A string, the version of R5's to get the filename of. +#' @param version A string. The version of R5 to be downloaded. When `NULL`, it +#' defaults to the latest version. #' -#' @return The a url a string. +#' @return A url a string. #' #' @family support functions #' #' @keywords internal -fileurl_from_metadata <- function(version) { +fileurl_from_metadata <- function(version = NULL) { + + # R5 version + if(is.null(version)) {version = r5r_env$r5_jar_version} checkmate::assert_string(version) @@ -36,55 +40,3 @@ fileurl_from_metadata <- function(version) { } - -#' Check internet connection with Ipea server -#' -#' @description -#' Checks if there is internet connection to Ipea server to download r5r data. -#' -#' @param file_url A string with the file_url address of an geobr dataset -#' -#' @return Logical. `TRUE` if url is working, `FALSE` if not. -#' @family support functions -#' -#' @keywords internal -check_connection <- function(file_url = 'https://www.ipea.gov.br/geobr/metadata/metadata_gpkg.csv'){ - - # file_url <- 'http://google.com/' # ok - # file_url <- 'http://www.google.com:81/' # timeout - # file_url <- 'http://httpbin.org/status/300' # error - - # check if user has internet connection - if (!curl::has_internet()) { message("\nNo internet connection.") - return(FALSE) - } - - # message - msg <- "Problem connecting to data server. Please try it again in a few minutes." - - # test server connection - x <- try(silent = TRUE, - httr::GET(file_url, # timeout(5), - config = httr::config(ssl_verifypeer = FALSE))) - # link offline - if (class(x)[1]=="try-error") { - message( msg ) - return(FALSE) - } - - # link working fine - else if ( identical(httr::status_code(x), 200L)) { - return(TRUE) - } - - # link not working or timeout - else if (! identical(httr::status_code(x), 200L)) { - message(msg ) - return(FALSE) - - } else if (httr::http_error(x) == TRUE) { - message(msg) - return(FALSE) - } - -} diff --git a/r-package/cran-comments.md b/r-package/cran-comments.md index 18b13b3d..7b6e3e35 100644 --- a/r-package/cran-comments.md +++ b/r-package/cran-comments.md @@ -1,22 +1,33 @@ ## R CMD check results -── R CMD check results ───────────────────────────────────────────── r5r 1.1.0 ──── -Duration: 24m 8.8s +── R CMD check results ──────────────────────────────────────── r5r 2.0 ──── +Duration: 29m 21.6s 0 errors ✔ | 0 warnings ✔ | 0 notes ✔ -* This is a minor update with bug fixes +**Breaking changes** + +- r5r uses now JDK 21 or higher (Breaking changes). Closed [#350](https://github.com/ipeaGIT/r5r/issues/350). +- r5r now uses the latest version of R5 V7.1. Closed [#350](https://github.com/ipeaGIT/r5r/issues/350) **Major changes** -- New `isochrone()`function. Closes [#123](https://github.com/ipeaGIT/r5r/issues/123), and addresses requrests in issues [#164](https://github.com/ipeaGIT/r5r/issues/164) and [#328](https://github.com/ipeaGIT/r5r/issues/328). -- New vignette about calculating / visualizing isochrones with `r5r`. -- New vignette with responses to frequently asked questions (FAQ) from `r5r` users. +- r5r now stores R5 Jar file at user dir using `tools::R_user_dir()` +- New function `r5r_cache()` to manage the cache of the R5 Jar file. +- By using the JDK 21, this version of r5r also fixed an incompatibility with MAC ARM processors. Closed [#315](https://github.com/ipeaGIT/r5r/issues/315) **Minor changes** -- The default value of `time_window` is not set to `10` minutes in all functions to avoid weird results reported upstream in R5. Closes [#342](https://github.com/ipeaGIT/r5r/issues/342). -- Removed any mention to `percentiles` parameter in the `expanded_travel_time_matrix()` because this function does not expose this parameter to users. Closes [#343](https://github.com/ipeaGIT/r5r/issues/343). -- Updated vignette on calculating / visualizing accessibility with `r5r`. +- In the `accessibility()` function, the value of `max_trip_duration` is now capped by the max value passed to the `cutoffs` parameter. Closes [#342](https://github.com/ipeaGIT/r5r/issues/348). +- Updated documentation of parameter `max_walk_time` to make it clear that in walk-only trips, whenever `max_walk_time` differs from `max_trip_duration`, the lowest value is considered. Closes [#353](https://github.com/ipeaGIT/r5r/issues/353) +- Updated documentation of parameter `max_bike_time` to make it clear that in bicycle-only trips, whenever `max_bike_time` differs from `max_trip_duration`, the lowest value is considered. Closes [#353](https://github.com/ipeaGIT/r5r/issues/353) +- Improved documentation of parameter `suboptimal_minutes` in the `detailed_itineraries()` function. +- Updated the vignette on time window to explain how this parameter behaves when used in the `detailed_itineraries()` function. + +**Bug Fixes** +- Fixed bug that prevented the use the `output_dir` parameter in the `detailed_itineraries(all_to_all = TRUE)` function. Closes [#327](https://github.com/ipeaGIT/r5r/issues/327) with a contribution ([PR #354](https://github.com/ipeaGIT/r5r/pull/354)) from Luyu Liu. +- Fixed bug that prevented `detailed_itineraries` from working with frequency-based GTFS feeds. It should ONLY work with frequency-based GTFS feeds. +**New contributors to r5r** +- [Luyu Liu](https://github.com/luyuliu) diff --git a/r-package/inst/extdata/metadata_r5r.csv b/r-package/inst/extdata/metadata_r5r.csv index 9b883d0f..819ade84 100644 --- a/r-package/inst/extdata/metadata_r5r.csv +++ b/r-package/inst/extdata/metadata_r5r.csv @@ -24,3 +24,5 @@ version;release_date;download_path 6.8.0;20230109;https://github.com/conveyal/r5/releases/download/v6.8/r5-v6.8-all.jar 6.9.0;20230305;https://github.com/conveyal/r5/releases/download/v6.9/r5-v6.9-all.jar 7.0.0;20231203;https://github.com/conveyal/r5/releases/download/v7.0/r5-v7.0-all.jar +7.1.0;20240104;https://github.com/conveyal/r5/releases/download/v7.1/r5-v7.1-all.jar +7.2.0;20241020;https://github.com/conveyal/r5/releases/download/v7.2/r5-v7.2-all.jar diff --git a/r-package/inst/jar/r5r.jar b/r-package/inst/jar/r5r.jar index 2883afa4..430b4554 100644 Binary files a/r-package/inst/jar/r5r.jar and b/r-package/inst/jar/r5r.jar differ diff --git a/r-package/man/accessibility.Rd b/r-package/man/accessibility.Rd index 32b6aeff..234bbdf7 100644 --- a/r-package/man/accessibility.Rd +++ b/r-package/man/accessibility.Rd @@ -162,7 +162,9 @@ information.} perform per time window minute when calculating travel time matrices and when estimating accessibility. Defaults to 5. This would mean 300 draws in a 60-minute time window, for example. This parameter only affects the -results when the GTFS feeds contain a \code{frequencies.txt} table.} +results when the GTFS feeds contain a \code{frequencies.txt} table. If the GTFS +feed does not have a frequency table, r5r still allows for multiple runs +over the set \code{time_window} but in a deterministic way.} \item{n_threads}{An integer. The number of threads to use when running the router in parallel. Defaults to use all available threads (Inf).} diff --git a/r-package/man/check_connection.Rd b/r-package/man/check_connection.Rd deleted file mode 100644 index 43a917b5..00000000 --- a/r-package/man/check_connection.Rd +++ /dev/null @@ -1,26 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R -\name{check_connection} -\alias{check_connection} -\title{Check internet connection with Ipea server} -\usage{ -check_connection( - file_url = "https://www.ipea.gov.br/geobr/metadata/metadata_gpkg.csv" -) -} -\arguments{ -\item{file_url}{A string with the file_url address of an geobr dataset} -} -\value{ -Logical. \code{TRUE} if url is working, \code{FALSE} if not. -} -\description{ -Checks if there is internet connection to Ipea server to download r5r data. -} -\seealso{ -Other support functions: -\code{\link{fileurl_from_metadata}()}, -\code{\link{stop_r5}()} -} -\concept{support functions} -\keyword{internal} diff --git a/r-package/man/download_r5.Rd b/r-package/man/download_r5.Rd index 9c2ae8d9..2a610b4d 100644 --- a/r-package/man/download_r5.Rd +++ b/r-package/man/download_r5.Rd @@ -5,15 +5,15 @@ \title{Download \code{R5.jar}} \usage{ download_r5( - version = "7.0.0", + version = NULL, quiet = FALSE, force_update = FALSE, temp_dir = FALSE ) } \arguments{ -\item{version}{A string. The version of R5 to be downloaded. Defaults to the -latest version.} +\item{version}{A string. The version of R5 to be downloaded. When \code{NULL}, it +defaults to the latest version.} \item{quiet}{A logical. Whether to show informative messages when downloading the file. Defaults to \code{FALSE}.} @@ -34,7 +34,7 @@ Downloads \code{R5.jar} and saves it locally, inside the package directory. \dontshow{if (identical(tolower(Sys.getenv("NOT_CRAN")), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(r5r) -download_r5(version = "7.0.0", temp_dir = TRUE) +download_r5(temp_dir = TRUE) \dontshow{\}) # examplesIf} } \seealso{ diff --git a/r-package/man/expanded_travel_time_matrix.Rd b/r-package/man/expanded_travel_time_matrix.Rd index b9937106..d37d6a3e 100644 --- a/r-package/man/expanded_travel_time_matrix.Rd +++ b/r-package/man/expanded_travel_time_matrix.Rd @@ -115,7 +115,9 @@ information.} perform per time window minute when calculating travel time matrices and when estimating accessibility. Defaults to 5. This would mean 300 draws in a 60-minute time window, for example. This parameter only affects the -results when the GTFS feeds contain a \code{frequencies.txt} table.} +results when the GTFS feeds contain a \code{frequencies.txt} table. If the GTFS +feed does not have a frequency table, r5r still allows for multiple runs +over the set \code{time_window} but in a deterministic way.} \item{n_threads}{An integer. The number of threads to use when running the router in parallel. Defaults to use all available threads (Inf).} diff --git a/r-package/man/fileurl_from_metadata.Rd b/r-package/man/fileurl_from_metadata.Rd index 61b8bdf6..0c5dc676 100644 --- a/r-package/man/fileurl_from_metadata.Rd +++ b/r-package/man/fileurl_from_metadata.Rd @@ -4,20 +4,20 @@ \alias{fileurl_from_metadata} \title{Get most recent JAR file url from metadata} \usage{ -fileurl_from_metadata(version) +fileurl_from_metadata(version = NULL) } \arguments{ -\item{version}{A string, the version of R5's to get the filename of.} +\item{version}{A string. The version of R5 to be downloaded. When \code{NULL}, it +defaults to the latest version.} } \value{ -The a url a string. +A url a string. } \description{ Returns the most recent JAR file url from metadata, depending on the version. } \seealso{ Other support functions: -\code{\link{check_connection}()}, \code{\link{stop_r5}()} } \concept{support functions} diff --git a/r-package/man/isochrone.Rd b/r-package/man/isochrone.Rd index 9b503795..7a0e173f 100644 --- a/r-package/man/isochrone.Rd +++ b/r-package/man/isochrone.Rd @@ -8,10 +8,11 @@ isochrone( r5r_core, origins, mode = "transit", - mode_egress = "WALK", + mode_egress = "walk", cutoffs = c(0, 15, 30), sample_size = 0.8, departure_datetime = Sys.time(), + polygon_output = TRUE, time_window = 10L, max_walk_time = Inf, max_bike_time = Inf, @@ -45,11 +46,11 @@ from the last public transport. It can be either \code{WALK}, \code{BICYCLE} or \item{cutoffs}{numeric vector. Number of minutes to define the time span of each Isochrone. Defaults to \code{c(0, 15, 30)}.} -\item{sample_size}{numeric. Sample size of nodes in the road network used to -estimate isochrones. Defaults to \code{0.8} (80\% of all nodes in the +\item{sample_size}{numeric. Sample size of nodes in the transport network used +to estimate isochrones. Defaults to \code{0.8} (80\% of all nodes in the transport network). Value can range between \code{0.2} and \code{1}. Smaller values increase computation speed but return results with lower -precision.} +precision. This parameter has no effect when \code{polygon_output = FALSE}.} \item{departure_datetime}{A POSIXct object. Please note that the departure time only influences public transport legs. When working with public @@ -57,6 +58,13 @@ transport networks, please check the \code{calendar.txt} within your GTFS feeds for valid dates. Please see details for further information on how datetimes are parsed.} +\item{polygon_output}{A Logical. If \code{TRUE}, the function outputs +polygon-based isochrones (the default) based on travel times from each +origin to a sample of a random sample nodes in the transport network +(see parameter \code{sample_size}). If \code{FALSE}, the function outputs +line-based isochronesbased on travel times from each origin to the +centroids of all segments in the transport network.} + \item{time_window}{An integer. The time window in minutes for which \code{r5r} will calculate multiple travel time matrices departing each minute. Defaults to 10 minutes. The function returns the result based on @@ -107,7 +115,9 @@ details for more information.} perform per time window minute when calculating travel time matrices and when estimating accessibility. Defaults to 5. This would mean 300 draws in a 60-minute time window, for example. This parameter only affects the -results when the GTFS feeds contain a \code{frequencies.txt} table.} +results when the GTFS feeds contain a \code{frequencies.txt} table. If the GTFS +feed does not have a frequency table, r5r still allows for multiple runs +over the set \code{time_window} but in a deterministic way.} \item{n_threads}{An integer. The number of threads to use when running the router in parallel. Defaults to use all available threads (\code{Inf}).} @@ -130,8 +140,11 @@ A \verb{POLYGON "sf" "data.frame"} for each isochrone of each origin. } \description{ Fast computation of isochrones from a given location. The -function estimates isochrones based on the travel times from the trip origin -to all nodes in the road network. +function can return either polygon-based or line-based isochrones. +Polygon-based isochrones are generated as concave polygons based on the +travel times from the trip origin to all nodes in the transport network. +Meanwhile, line-based isochronesare based on travel times from each origin +to the centroids of all segments in the transport network. } \section{Transport modes}{ @@ -220,6 +233,7 @@ transit routing. Transportation Science, 49(3), 591-604. \dontshow{if (identical(tolower(Sys.getenv("NOT_CRAN")), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} options(java.parameters = "-Xmx2G") library(r5r) +library(ggplot2) # build transport network data_path <- system.file("extdata/poa", package = "r5r") @@ -230,22 +244,47 @@ points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) origin_1 <- points[936,] departure_datetime <- as.POSIXct( - "13-05-2019 14:00:00", - format = "\%d-\%m-\%Y \%H:\%M:\%S" +"13-05-2019 14:00:00", +format = "\%d-\%m-\%Y \%H:\%M:\%S" ) -# estimate isochrone from origin_1 -iso1 <- isochrone(r5r_core, - origin = origin_1, - mode = c("walk"), +# estimate polygon-based isochrone from origin_1 +iso_poly <- isochrone(r5r_core, + origins = origin_1, + mode = "walk", + polygon_output = TRUE, departure_datetime = departure_datetime, cutoffs = seq(0, 100, 10) ) -head(iso1) +head(iso_poly) + + +# estimate line-based isochrone from origin_1 +iso_lines <- isochrone(r5r_core, + origins = origin_1, + mode = "walk", + polygon_output = FALSE, + departure_datetime = departure_datetime, + cutoffs = seq(0, 100, 10) +) +head(iso_lines) + +# plot colors colors <- c('#ffe0a5','#ffcb69','#ffa600','#ff7c43','#f95d6a', '#d45087','#a05195','#665191','#2f4b7c','#003f5c') -plot(iso1['isochrone'], col = colors) + +# polygons +ggplot() + + geom_sf(data=iso_poly, aes(fill=factor(isochrone))) + + scale_fill_manual(values = colors) + + theme_minimal() + +# lines +ggplot() + + geom_sf(data=iso_lines, aes(color=factor(isochrone))) + + scale_color_manual(values = colors) + + theme_minimal() stop_r5(r5r_core) \dontshow{\}) # examplesIf} diff --git a/r-package/man/r5r.Rd b/r-package/man/r5r.Rd index 4b6fe2d1..c6d5ccd3 100644 --- a/r-package/man/r5r.Rd +++ b/r-package/man/r5r.Rd @@ -3,7 +3,6 @@ \docType{package} \name{r5r} \alias{r5r} -\alias{_PACKAGE} \alias{r5r-package} \title{r5r: Rapid Realistic Routing with 'R5'} \description{ @@ -32,6 +31,7 @@ Please check the vignettes on the \href{https://ipeagit.github.io/r5r/}{website} Useful links: \itemize{ \item \url{https://github.com/ipeaGIT/r5r} + \item \url{https://ipeagit.github.io/r5r/} \item Report bugs at \url{https://github.com/ipeaGIT/r5r/issues} } diff --git a/r-package/man/r5r_cache.Rd b/r-package/man/r5r_cache.Rd new file mode 100644 index 00000000..3a4bc8d0 --- /dev/null +++ b/r-package/man/r5r_cache.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/r5r_cache.R +\name{r5r_cache} +\alias{r5r_cache} +\title{Manage cached files from the r5r package} +\usage{ +r5r_cache(list_files = TRUE, delete_file = NULL) +} +\arguments{ +\item{list_files}{Logical. Whether to print a message with the address of r5r +JAR files cached locally. Defaults to \code{TRUE}.} + +\item{delete_file}{String. The file name (basename) of a JAR file cached +locally that should be deleted. Defaults to \code{NULL}, so that no +file is deleted. If \code{delete_file = "all"}, then all cached files are +deleted.} +} +\value{ +A message indicating which file exist and/or which ones have been +deleted from local cache directory. +} +\description{ +Manage cached files from the r5r package +} +\examples{ +\dontshow{if (identical(tolower(Sys.getenv("NOT_CRAN")), "true")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +# download r5 JAR +r5r::download_r5() + +# list all files cached +r5r_cache(list_files = TRUE) + +# delete r5 JAR +r5r_cache(delete_file = 'r5-v7.0') +\dontshow{\}) # examplesIf} +} +\concept{Cache data} diff --git a/r-package/man/roxygen/templates/draws_per_minute.R b/r-package/man/roxygen/templates/draws_per_minute.R index cdbe1461..13a49cdd 100644 --- a/r-package/man/roxygen/templates/draws_per_minute.R +++ b/r-package/man/roxygen/templates/draws_per_minute.R @@ -2,4 +2,6 @@ #' perform per time window minute when calculating travel time matrices and #' when estimating accessibility. Defaults to 5. This would mean 300 draws in #' a 60-minute time window, for example. This parameter only affects the -#' results when the GTFS feeds contain a `frequencies.txt` table. +#' results when the GTFS feeds contain a `frequencies.txt` table. If the GTFS +#' feed does not have a frequency table, r5r still allows for multiple runs +#' over the set `time_window` but in a deterministic way. diff --git a/r-package/man/stop_r5.Rd b/r-package/man/stop_r5.Rd index c7948437..cc4f4583 100644 --- a/r-package/man/stop_r5.Rd +++ b/r-package/man/stop_r5.Rd @@ -29,7 +29,6 @@ stop_r5(r5r_core) } \seealso{ Other support functions: -\code{\link{check_connection}()}, \code{\link{fileurl_from_metadata}()} } \concept{support functions} diff --git a/r-package/man/travel_time_matrix.Rd b/r-package/man/travel_time_matrix.Rd index 14da7ea6..abe035e5 100644 --- a/r-package/man/travel_time_matrix.Rd +++ b/r-package/man/travel_time_matrix.Rd @@ -131,7 +131,9 @@ information.} perform per time window minute when calculating travel time matrices and when estimating accessibility. Defaults to 5. This would mean 300 draws in a 60-minute time window, for example. This parameter only affects the -results when the GTFS feeds contain a \code{frequencies.txt} table.} +results when the GTFS feeds contain a \code{frequencies.txt} table. If the GTFS +feed does not have a frequency table, r5r still allows for multiple runs +over the set \code{time_window} but in a deterministic way.} \item{n_threads}{An integer. The number of threads to use when running the router in parallel. Defaults to use all available threads (Inf).} diff --git a/r-package/pkgdown/_pkgdown.yml b/r-package/pkgdown/_pkgdown.yml index 4974da35..c337fe49 100644 --- a/r-package/pkgdown/_pkgdown.yml +++ b/r-package/pkgdown/_pkgdown.yml @@ -9,12 +9,9 @@ repo: source: https://github.com/ipeaGIT/r5r/tree/master/r-package/ reference: - - title: "Setup" + - title: "Setup network" - contents: - - download_r5 - setup_r5 - - stop_r5 - - r5r_sitrep - title: "Accessibility" - contents: - accessibility @@ -22,18 +19,25 @@ reference: - contents: - travel_time_matrix - expanded_travel_time_matrix - - pareto_frontier - detailed_itineraries + - pareto_frontier - title: "Isochrone" - contents: - isochrone - - title: "Network" + - title: "Extract Network spatial data" - contents: - street_network_to_sf - transit_network_to_sf - - find_snap - title: "Fare structure" - contents: - setup_fare_structure - read_fare_structure - write_fare_structure + - title: "Support functions" + - contents: + - find_snap + - r5r_sitrep + - r5r_cache + - download_r5 + - stop_r5 + diff --git a/r-package/tests/tests_marcus/test_R5_7_2.R b/r-package/tests/tests_marcus/test_R5_7_2.R new file mode 100644 index 00000000..27ffe830 --- /dev/null +++ b/r-package/tests/tests_marcus/test_R5_7_2.R @@ -0,0 +1,28 @@ +# increase Java memory +options(java.parameters = "-Xmx2G") +library(r5r) + +# build transport network +data_path <- system.file("extdata/poa", package = "r5r") +r5r::download_r5(version = '7.2.0', force_update = TRUE) +r5r_core <- setup_r5(data_path, overwrite = TRUE) + +# load origin/destination points +points <- read.csv(file.path(data_path, "poa_points_of_interest.csv")) + +departure_datetime <- as.POSIXct( + "13-05-2019 14:00:00", + format = "%d-%m-%Y %H:%M:%S" +) + +ettm <- expanded_travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + time_window = 20, + departure_datetime = departure_datetime, + max_trip_duration = 60 +) + +head(ettm) diff --git a/r-package/tests/tests_marcus/test_ttm2.R b/r-package/tests/tests_marcus/test_ttm2.R new file mode 100644 index 00000000..b988d0a6 --- /dev/null +++ b/r-package/tests/tests_marcus/test_ttm2.R @@ -0,0 +1,52 @@ +options(java.parameters = '-Xmx16384m') +# options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx16384m")) + +devtools::load_all(".") +# library(ggplot2) +library(data.table) +library(tidyverse) + +# build transport network +data_path <- system.file("extdata/poa", package = "r5r") +r5r_core <- setup_r5(data_path = data_path, verbose = FALSE, overwrite = FALSE) + +# load origin/destination points + +departure_datetime <- as.POSIXct("13-05-2019 14:00:00", format = "%d-%m-%Y %H:%M:%S") +points <- fread(file.path(data_path, "poa_hexgrid.csv")) +fares <- read_fare_structure(file.path(data_path, "fares/fares_poa.zip")) + + +dir.create(here::here("csv")) + +# r5r_core$setCsvOutput(here::here("csv")) + +tictoc::tic() +normal_ttm <- travel_time_matrix(r5r_core, origins = points, #[id == "89a9012a3cfffff",], + destinations = points, #[id == "89a901284a3ffff",], + mode = c("WALK", "TRANSIT"), + departure_datetime = departure_datetime, + max_trip_duration = 60, + max_walk_time = 15, + time_window = 30, + percentiles = c(1, 25, 50, 75, 99), + verbose = FALSE, + progress = TRUE, + fare_structure = fares, + max_fare = 8) +tictoc::toc() + +write_csv(normal_ttm, here::here('csv', 'ttm_original.csv')) +# fixed equal - 658.054 sec elapsed +# fixed diff - 703.872 sec elapsed +# original - 702.352 sec elapsed + + +ttm_or <- read_csv(here::here('csv', 'ttm_original.csv')) +ttm_fx <- read_csv(here::here('csv', 'ttm_fixed_equal.csv')) + +ttm_or2 <- ttm_or |> pivot_longer(cols = starts_with('travel_time'), names_to = 'percentile', values_to = 'travel_time_or') +ttm_fx2 <- ttm_fx |> pivot_longer(cols = starts_with('travel_time'), names_to = 'percentile', values_to = 'travel_time_fx') + +ttm_j <- left_join(ttm_or2, ttm_fx2) |> + mutate(eq = travel_time_or == travel_time_fx) diff --git a/r-package/tests/tests_rafa/create_sample_points.R b/r-package/tests/tests_rafa/create_sample_points.R deleted file mode 100644 index 68dce8ae..00000000 --- a/r-package/tests/tests_rafa/create_sample_points.R +++ /dev/null @@ -1,30 +0,0 @@ -# create sample points used in poa and spo examples - -library(aopdata) -library(data.table) - - - -####### POA ------------------------------------ - -data_path_poa <- system.file("extdata/poa", package = "r5r") - -list.files(data_path_poa) - -# load origin/destination points -points_poa <- fread(file.path(data_path_poa, "poa_hexgrid.csv")) -head(points_poa) - - - - -####### SPO ------------------------------------ - -data_path_spo <- system.file("extdata/spo", package = "r5r") - -list.files(data_path_spo) - -# load origin/destination points -points_spo <- fread(file.path(data_path_spo, "spo_hexgrid.csv")) -head(points_spo) - diff --git a/r-package/tests/tests_rafa/isochrone.R b/r-package/tests/tests_rafa/isochrone.R deleted file mode 100644 index c322bf34..00000000 --- a/r-package/tests/tests_rafa/isochrone.R +++ /dev/null @@ -1,302 +0,0 @@ -### TO DO -# allow users to pass an sf points or polygons as "destination" input -# update entire documentation based on latest docs of r5r -# expose to users the same parameters available in travel_time_matrix() - # max_trip_duration should be == to max(cutoffs) -#'# prep grid with destinations -#'dest_points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) -#'grid <- h3jsr::cell_to_polygon(input = dest_points$id, simple = FALSE) -#'grid$id <- dest_points$id -#' - - - -#' Estimate isochrones from a given location -#' -#' @description Fast computation of isochrones from a given location. -#' -#' @template r5r_core -#' @param origins Either a `POINT sf` object with WGS84 CRS, or a -#' `data.frame` containing the columns `id`, `lon` and `lat`. -#' @param cutoffs numeric vector. Number of minutes to define time span of each -#' each Isochrone. Defaults to `c(0, 15, 30)`. -#' @param mode A character vector. The transport modes allowed for access, -#' transfer and vehicle legs of the trips. Defaults to `WALK`. Please see -#' details for other options. -#' @param mode_egress A character vector. The transport mode used after egress -#' from the last public transport. It can be either `WALK`, `BICYCLE` or -#' `CAR`. Defaults to `WALK`. Ignored when public transport is not used. -#' @param departure_datetime A POSIXct object. Please note that the departure -#' time only influences public transport legs. When working with public -#' transport networks, please check the `calendar.txt` within your GTFS feeds -#' for valid dates. Please see details for further information on how -#' datetimes are parsed. -#' @param max_walk_time An integer. The maximum walking time (in minutes) to -#' access and egress the transit network, or to make transfers within the -#' network. Defaults to no restrictions, as long as `max_trip_duration` is -#' respected. The max time is considered separately for each leg (e.g. if -#' you set `max_walk_time` to 15, you could potentially walk up to 15 minutes -#' to reach transit, and up to _another_ 15 minutes to reach the destination -#' after leaving transit). Defaults to `Inf`, no limit. -#' @param max_bike_time An integer. The maximum cycling time (in minutes) to -#' access and egress the transit network. Defaults to no restrictions, as long -#' as `max_trip_duration` is respected. The max time is considered separately -#' for each leg (e.g. if you set `max_bike_time` to 15 minutes, you could -#' potentially cycle up to 15 minutes to reach transit, and up to _another_ 15 -#' minutes to reach the destination after leaving transit). Defaults to `Inf`, -#' no limit. -#' @param max_car_time An integer. The maximum driving time (in minutes) to -#' access and egress the transit network. Defaults to no restrictions, as long -#' as `max_trip_duration` is respected. The max time is considered separately -#' for each leg (e.g. if you set `max_car_time` to 15 minutes, you could -#' potentially drive up to 15 minutes to reach transit, and up to _another_ 15 -#' minutes to reach the destination after leaving transit). Defaults to `Inf`, -#' no limit. -#' @param max_trip_duration An integer. The maximum trip duration in minutes. -#' Defaults to 120 minutes (2 hours). -#' @param walk_speed A numeric. Average walk speed in km/h. Defaults to 3.6 -#' km/h. -#' @param bike_speed A numeric. Average cycling speed in km/h. Defaults to 12 -#' km/h. -#' @param max_rides An integer. The maximum number of public transport rides -#' allowed in the same trip. Defaults to 3. -#' @param max_lts An integer between 1 and 4. The maximum level of traffic -#' stress that cyclists will tolerate. A value of 1 means cyclists will only -#' travel through the quietest streets, while a value of 4 indicates cyclists -#' can travel through any road. Defaults to 2. Please see details for more -#' information. -#' @param n_threads An integer. The number of threads to use when running the -#' router in parallel. Defaults to use all available threads (Inf). -#' @param progress A logical. Whether to show a progress counter when running -#' the router. Defaults to `FALSE`. Only works when `verbose` is set to -#' `FALSE`, so the progress counter does not interfere with `R5`'s output -#' messages. Setting `progress` to `TRUE` may impose a small penalty for -#' computation efficiency, because the progress counter must be synchronized -#' among all active threads. -#' @template time_window_related_args -#' @template draws_per_minute -#' @template verbose -#' -#' @return A `POLYGON "sf" "data.frame"` -#' -#' -#' @family Isochrone -#' @examples \donttest{ -#' library(r5r) -#' -#' # build transport network -#' data_path <- system.file("extdata/poa", package = "r5r") -#' r5r_core <- setup_r5(data_path = data_path) -#' -#' # load origin/point of interest -#' origins <- read.csv(file.path(data_path, "poa_hexgrid.csv"))[c(700,936),] -#' -#' departure_datetime <- as.POSIXct( -#' "13-05-2019 14:00:00", -#' format = "%d-%m-%Y %H:%M:%S" -#' ) -#' -#'# estimate travel time matrix -#'iso <- isochrone(r5r_core, -#' origin = origin, -#' mode = c("WALK", "TRANSIT"), -#' departure_datetime = departure_datetime, -#' cutoffs = c(0, 15, 30, 45, 60, 75, 90, 120), -#' max_walk_dist = Inf) -#' }' -#' -#' @export -isochrone <- function(r5r_core, - origins, - mode = "transit", - mode_egress = "WALK", - cutoffs = c(0, 15, 30), - departure_datetime = Sys.time(), - time_window = 1L, - percentiles = 50L, - max_walk_time = Inf, - max_bike_time = Inf, - max_car_time = Inf, - max_trip_duration = 120L, - walk_speed = 3.6, - bike_speed = 12, - max_rides = 3, - max_lts = 2, - draws_per_minute = 5L, - n_threads = Inf, - verbose = FALSE, - progress = TRUE){ - - -# check inputs ------------------------------------------------------------ - - # check cutoffs - checkmate::assert_numeric(cutoffs, lower = 0) - - # max cutoff is used as max_trip_duration - max_trip_duration = as.integer(max(cutoffs)) - - # include 0 in cutoffs - if (min(cutoffs) > 0) {cutoffs <- sort(c(0, cutoffs))} - - -# IF no destinations input ------------------------------------------------------------ - - # use all network nodes as destination points - network <- r5r::street_network_to_sf(r5r_core) - destinations = network$vertices - - # # sample proportion of nodes to reduce computation ? - # sample_size <- ifelse(nrow(destinations) < 10000, 1, .33) # - # index_sample <- sample(1:nrow(destinations), size = nrow(destinations) * sample_size, replace = FALSE) - # destinations <- destinations[index_sample,] - - names(destinations)[1] <- 'id' - destinations$id <- as.character(destinations$id) - - - # estimate travel time matrix - ttm <- travel_time_matrix(r5r_core = r5r_core, - origins = origins, - destinations = destinations, - mode = mode, - mode_egress = mode_egress, - departure_datetime = departure_datetime, - time_window = time_window, - percentiles = percentiles, - max_walk_time = max_walk_time, - max_bike_time = max_bike_time, - max_car_time = max_car_time, - max_trip_duration = max_trip_duration, - walk_speed = walk_speed, - bike_speed = bike_speed, - max_rides = max_rides, - max_lts = max_lts, - draws_per_minute = draws_per_minute, - n_threads = n_threads, - verbose = verbose, - progress = progress - ) - - - # aggregate travel-times - ttm[, isochrone := cut(x=travel_time_p50, breaks=cutoffs)] - - # fun to get isochrones for each origin - prep_iso <- function(orig){ # orig = '89a901280b7ffff' - - temp_ttm <- subset(ttm, from_id == orig) - - # join ttm results to destinations - dest <- subset(destinations, id %in% temp_ttm$to_id) - data.table::setDT(dest)[, id := as.character(id)] - dest[temp_ttm, on=c('id' ='to_id'), c('travel_time_p50', 'isochrone') := list(i.travel_time_p50, i.isochrone)] - - # build polygons with {concaveman} - # obs. {isoband} is much slower - dest <- sf::st_as_sf(dest) - - get_poly <- function(cut){ # cut = 30 - temp <- subset(dest, travel_time_p50 <= cut) - temp_iso <- concaveman::concaveman(temp) - temp_iso$isochrone <- cut - return(temp_iso) - } - - iso_list <- lapply(X=cutoffs[cutoffs>0], FUN=get_poly) - iso <- data.table::rbindlist(iso_list) - iso$id <- orig - iso <- sf::st_sf(iso) - iso <- iso[ order(-iso$isochrone), ] - data.table::setcolorder(iso, c('id', 'isochrone')) - # plot(iso) - return(iso) - } - - # get the isocrhone from each origin - iso_list <- lapply(X = unique(origins$id), FUN = prep_iso) - - # put output together - iso <- data.table::rbindlist(iso_list) - iso <- sf::st_sf(iso) - #plot(iso) - return(iso) - - # ggplot() + - # geom_sf(data=subset(iso, id==unique(iso$id)[1]), aes(fill=isochrone)) - # ggplot() + - # geom_sf(data=subset(iso, id==unique(iso$id)[2]), aes(fill=isochrone)) - - # # join ttm results to destinations - # dest <- subset(destinations, id %in% ttm$to_id) - # data.table::setDT(dest)[, id := as.character(id)] - # dest[ttm, on=c('id' ='to_id'), c('travel_time_p50', 'isochrone') := list(i.travel_time_p50, i.isochrone)] - # - # - # # build polygons with {concaveman} - # # obs. {isoband} is much slower - # dest <- sf::st_as_sf(dest) - # - # get_poly <- function(cut){ # cut = 30 - # temp <- subset(dest, travel_time_p50 <= cut) - # temp_iso <- concaveman::concaveman(temp) - # temp_iso$isochrone <- cut - # return(temp_iso) - # } - # - # iso_list <- lapply(X=cutoffs[cutoffs>0], FUN=get_poly) - # iso <- data.table::rbindlist(iso_list) - # iso <- sf::st_sf(iso) - # iso <- iso[ order(-iso$isochrone), ] - # # plot(iso) - - return(iso) - - -# # IF destinations input are polygons ------------------------------------------------------------ -# -# if(!is.null(destinations)){ -# -# # check input -# if (!any(class(destinations) %like% 'sf')) {stop("'destinations' must be of class 'sf' or 'sfc'")} -# if (!any(sf::st_geometry_type(destinations) %like% 'POLYGON')) {stop("'destinations' must have geometry type 'POLYGON'")} -# if (!'id' %in% names(destinations)) {stop("'destinations' must have an 'id' colum")} -# -# centroids <- sf::st_centroid(destinations) -# -# # estimate travel time matrix -# ttm <- travel_time_matrix(r5r_core=r5r_core, -# origins = origin, -# destinations = centroids, -# mode = mode, -# departure_datetime = departure_datetime, -# # max_walk_dist = max_walk_dist, -# max_trip_duration = max_trip_duration, -# progress = TRUE -# # walk_speed = 3.6, -# # bike_speed = 12, -# # max_rides = max_rides, -# # n_threads = n_threads, -# # verbose = verbose -# ) -# -# # aggregate travel-times -# ttm[, isochrone := cut(x=travel_time_p50, breaks=cutoffs)] -# -# # add isochrone cat to polygon of origin -# -# # join ttm results to destinations -# data.table::setDT(destinations) -# destinations[ttm, on=c('id' ='to_id'), c('travel_time_p50', 'isochrone') := list(i.travel_time_p50, i.isochrone)] -# -# destinations <- sf::st_as_sf(destinations) -# # ggplot() + geom_sf(data=destinations, aes(fill=isochrone), color=NA) -# -# return(destinations) -# } - -} - - - - diff --git a/r-package/tests/tests_rafa/isochrone_lines.R b/r-package/tests/tests_rafa/isochrone_lines.R new file mode 100644 index 00000000..a5878dd8 --- /dev/null +++ b/r-package/tests/tests_rafa/isochrone_lines.R @@ -0,0 +1,55 @@ +options(java.parameters = "-Xmx2G") +library(r5r) +library(ggplot2) + +# build transport network +data_path <- system.file("extdata/poa", package = "r5r") +r5r_core <- setup_r5(data_path = data_path) + +# load origin/point of interest +points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) +origin_1 <- points[936,] + +departure_datetime <- as.POSIXct( + "13-05-2019 14:00:00", + format = "%d-%m-%Y %H:%M:%S" +) + + +iso_poly <- isochrone(r5r_core, + origins = origin_1, + mode = "walk", + polygon_output = T, + departure_datetime = departure_datetime, + cutoffs = seq(0, 100, 10) +) + +ggplot() + + geom_sf(data=iso_poly, aes(fill=factor(isochrone))) + + scale_fill_manual(values = colors, name='Isochrone(min.)') + + geom_point(data=origin_1, aes(x=lon, y=lat), color='red')+ + theme_void() + +# estimate line-based isochrone from origin_1 +iso_lines <- isochrone(r5r_core, + origins = origin_1, + mode = "walk", + polygon_output = F, + departure_datetime = departure_datetime, + cutoffs = seq(0, 100, 10) +) +head(iso_lines) + + +#### plot +colors <- c('#ffe0a5','#ffcb69','#ffa600','#ff7c43','#f95d6a', + '#d45087','#a05195','#665191','#2f4b7c','#003f5c') + + +# lines +ggplot() + + geom_sf(data=iso_lines, aes(color=factor(isochrone))) + + scale_color_manual(values = colors, name='Isochrone(min.)') + + geom_point(data=origin_1, aes(x=lon, y=lat), color='red')+ + theme_void() + diff --git a/r-package/tests/tests_rafa/line_mid_point.R b/r-package/tests/tests_rafa/line_mid_point.R new file mode 100644 index 00000000..9954f4ec --- /dev/null +++ b/r-package/tests/tests_rafa/line_mid_point.R @@ -0,0 +1,43 @@ +# https://gis.stackexchange.com/questions/277219/sf-equivalent-of-r-maptools-packages-spatiallinesmidpoints + +get_mid_point <- function(a){ + new_shape <- sf::st_segmentize(x = a, dfMaxLength = 50) + new_shape <- sfheaders::sf_cast(new_shape, "POINT") + + m_position <- (nrow(new_shape) /2) |> round(digits = 0) + mid_point <- new_shape[m_position,] + return(mid_point) +} + +m <- get_mid_point(a) + + + +mapview(m) +library(ggplot2) + + +c <- st_centroid(a) + +ss <- st_line_midpoints(a) + +ggplot() + +geom_sf(data = new_shape, color='blue')+ + geom_sf(data = a, color='gray')+ + geom_sf(data = m, color='red') + + geom_sf(data = c, color='green') + + geom_sf(data = ss, color='orange') + + +library(bench) + +bench::mark(check=F, + get_mid_point(a), + st_line_midpoints(sf_lines = a) +) + +expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc + + 1 get_mid_point(a) 25.2ms 27.1ms 36.7 322KB 2.30 16 1 +2 st_line_midpoints(… 799.9µs 826.1µs 1139. 12.7KB 0 570 0 + # ℹ 5 more variables: total_time , result , memory , diff --git a/r-package/tests/tests_rafa/r5r_coverage.rds b/r-package/tests/tests_rafa/r5r_coverage.rds deleted file mode 100644 index c6ab21d5..00000000 Binary files a/r-package/tests/tests_rafa/r5r_coverage.rds and /dev/null differ diff --git a/r-package/tests/tests_rafa/test_iso.R b/r-package/tests/tests_rafa/test_iso.R deleted file mode 100644 index b56e5a16..00000000 --- a/r-package/tests/tests_rafa/test_iso.R +++ /dev/null @@ -1,33 +0,0 @@ -options(java.parameters = '-Xmx7G') -library(r5r) -library(ggplot2) -library(mapview) - - -a <- read.csv(file.path(data_path, "poa_hexgrid.csv"))[c(700,936),] -a - - -t <- isochrone(r5r_core = r5r_core, - origins = a, - mode = c("TRANSIT"), # TRANSIT WALK - cutoffs = c(15, 30), - departure_datetime = departure_datetime - ) - -mapview(t) -mapview(t2) - -ggplot() + - geom_sf(data=subset(t, id==unique(t$id)[1]), aes(fill=isochrone)) - -ggplot() + - geom_sf(data=subset(t, id==unique(t$id)[3]), aes(fill=isochrone)) - - - - - -aaaa <- sfheaders::sf_point(points , x='lon', y='lat', keep = T) -mapview(aaaa) - diff --git a/r-package/tests/tests_rafa/test_rafa.R b/r-package/tests/tests_rafa/test_rafa.R index 5a96931d..14514be3 100644 --- a/r-package/tests/tests_rafa/test_rafa.R +++ b/r-package/tests/tests_rafa/test_rafa.R @@ -1,3 +1,31 @@ +#> Travel-time results are currently limited to 4 million destinations, +#> and 16 million total origin-destination pairs. Path detail results are currently +#> limited to 5000 destinations + + + + +link <- 'https://ftp.ibge.gov.br/Trabalho_e_Rendimento/Pesquisa_Nacional_por_Amostra_de_Domicilios_continua/Trimestral/Microdados/2023/PNADC_032023.zip' +file <- basename(link) + + +tic() +httr::GET(url = link, + # httr::timeout(10), + httr::progress(), + httr::write_disk(file, overwrite = T)) +toc() + + +tic() +link |> + httr2::request() |> + httr2::req_progress() |> + httr2::req_perform(path = file) +toc() + + + # devtools::install_github("ipeaGIT/r5r", subdir = "r-package", force=T) options(java.parameters = '-Xmx10G') library(sf) @@ -14,7 +42,6 @@ library(testthat) library(ggplot2) library(checkmate) library(geobr) -library(gtfs2gps) library(tictoc) library(mapview) mapviewOptions(platform = 'leafgl') @@ -686,6 +713,7 @@ covr::function_coverage(fun=r5r::street_network_to_sf, test_file("tests/testthat covr::function_coverage(fun=r5r::transit_network_to_sf, test_file("tests/testthat/test-transit_network_to_sf.R")) +a <- covr::function_coverage(fun=r5r::r5r_cache, test_file("tests/testthat/test-z_r5r_cache.R")) covr::function_coverage(fun=r5r::set_max_walk_distance, test_file("tests/testthat/test-utils.R")) @@ -762,8 +790,15 @@ lapply(X=docs, FUN = tools::showNonASCIIfile) library(tictoc) library(beepr) +# run only the tests +testthat::test_local() + # LOCAL +utils::remove.packages('r5r') +jar_dir <- tools::R_user_dir("r5r", which = "cache") +unlink(jar_dir, recursive = TRUE) + tictoc::tic() Sys.setenv(NOT_CRAN = "true") devtools::check(pkg = ".", cran = FALSE, env_vars = c(NOT_CRAN = "true")) @@ -776,6 +811,7 @@ tictoc::tic() Sys.setenv(NOT_CRAN = "false") devtools::check(pkg = ".", cran = TRUE, env_vars = c(NOT_CRAN = "false")) tictoc::toc() +beepr::beep() devtools::check_win_release(pkg = ".") @@ -793,6 +829,14 @@ devtools::check(pkg = ".", cran = TRUE, env_vars = c(NOT_CRAN = "false")) tictoc::toc() +# extrachecks ----------------- +#' https://github.com/JosiahParry/extrachecks +#' remotes::install_github("JosiahParry/extrachecks") + +library(extrachecks) +extrachecks::extrachecks() + + # submit to CRAN ----------------- usethis::use_cran_comments('teste 2222, , asdadsad') @@ -919,3 +963,4 @@ ggplot(data=df, aes(x=cost, y=time, label = modes)) + theme_classic() + diff --git a/r-package/tests/tests_rafa/v7.0_vs_7.1.R b/r-package/tests/tests_rafa/v7.0_vs_7.1.R new file mode 100644 index 00000000..a5696759 --- /dev/null +++ b/r-package/tests/tests_rafa/v7.0_vs_7.1.R @@ -0,0 +1,81 @@ +tictoc::tic() + +options(java.parameters = "-Xmx10G") + + +library(r5r) + +# build transport network +data_path <- system.file("extdata/poa", package = "r5r") +r5r_core <- setup_r5(data_path) + +# load origin/destination points +points <- read.csv(file.path(data_path, "poa_hexgrid.csv")) + +departure_datetime <- as.POSIXct( + "13-05-2019 14:00:00", + format = "%d-%m-%Y %H:%M:%S" +) + +ttm <- travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = departure_datetime, + max_trip_duration = 60 +) +head(ttm) + +# using a larger time window +ttm <- travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = departure_datetime, + time_window = 30, + max_trip_duration = 60 +) +head(ttm) + +# selecting different percentiles +ttm <- travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = departure_datetime, + time_window = 30, + percentiles = c(25, 50, 75), + max_trip_duration = 60 +) +head(ttm) + +# use a fare structure and set a max fare to take monetary constraints into +# account +fare_structure <- read_fare_structure( + file.path(data_path, "fares/fares_poa.zip") +) +ttm <- travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = departure_datetime, + fare_structure = fare_structure, + max_fare = 5, + max_trip_duration = 60, +) +head(ttm) + + +tictoc::toc() + +# 7.1 = 16.17 segundos +# 7.0 = 14.84 segundos + + +# 7.1 = 424.78 segundos +# 7.0 = 382.08 segundos + diff --git a/r-package/tests/testthat/setup.R b/r-package/tests/testthat/setup.R index a0fb36c3..ea80c226 100644 --- a/r-package/tests/testthat/setup.R +++ b/r-package/tests/testthat/setup.R @@ -3,22 +3,23 @@ testthat::skip_on_cran() options(java.parameters = "-Xmx2G") if (Sys.getenv("NOT_CRAN") != "false") { + data_path <- system.file("extdata/poa", package = "r5r") - r5r_core <- setup_r5(data_path, verbose = FALSE) + r5r_core <- r5r::setup_r5(data_path, verbose = FALSE) points <- data.table::fread(file.path(data_path, "poa_hexgrid.csv")) pois <- data.table::fread(file.path(data_path, "poa_points_of_interest.csv")) departure_datetime <- as.POSIXct( "13-05-2019 14:00:00", format = "%d-%m-%Y %H:%M:%S" ) - fare_structure <- read_fare_structure( + fare_structure <- r5r::read_fare_structure( system.file("extdata/poa/fares/fares_poa.zip", package = "r5r") ) spo_path <- system.file("extdata/spo", package = "r5r") - spo_core <- setup_r5(spo_path, verbose = FALSE) + spo_core <- r5r::setup_r5(spo_path, verbose = FALSE) spo_points <- data.table::fread(file.path(spo_path, "spo_hexgrid.csv")) spo_points[, opportunities := 1] - spo_fare_struc <- setup_fare_structure(spo_core, 5) + spo_fare_struc <- r5r::setup_fare_structure(spo_core, 5) spo_fare_struc$fares_per_transfer <- data.table::data.table(NULL) } diff --git a/r-package/tests/testthat/test-download_r5.R b/r-package/tests/testthat/test-download_r5.R index d4efa8e4..629dc376 100644 --- a/r-package/tests/testthat/test-download_r5.R +++ b/r-package/tests/testthat/test-download_r5.R @@ -21,7 +21,6 @@ test_that("download_r5 - expected behavior", { test_that("download_r5 - expected errors", { testthat::expect_error( download_r5(version = "0") ) - testthat::expect_error( download_r5(version = NULL ) ) testthat::expect_error(download_r5(force_update = 'a')) testthat::expect_error(download_r5(quiet = 'a')) testthat::expect_error(download_r5(temp_dir = 'a')) diff --git a/r-package/tests/testthat/test-isochrone.R b/r-package/tests/testthat/test-isochrone.R index b83df27a..87122ffb 100644 --- a/r-package/tests/testthat/test-isochrone.R +++ b/r-package/tests/testthat/test-isochrone.R @@ -15,6 +15,7 @@ tester <- function(r5r_core = get("r5r_core", envir = parent.frame()), mode = "WALK", mode_egress = "WALK", departure_datetime = Sys.time(), + polygon_output = TRUE, max_walk_time = Inf, max_bike_time = Inf, max_trip_duration = 120L, @@ -34,6 +35,7 @@ tester <- function(r5r_core = get("r5r_core", envir = parent.frame()), mode = mode, mode_egress = mode_egress, departure_datetime = departure_datetime, + polygon_output = polygon_output, max_walk_time = max_walk_time, max_bike_time = max_bike_time, max_trip_duration = max_trip_duration, @@ -81,6 +83,7 @@ test_that("errors due to incorrect input types - other inputs", { expect_error(tester(cutoffs = "50")) expect_error(tester(cutoffs = -5)) + expect_error(tester(polygon_output = 'banana')) expect_error(tester(sample_size = "50")) expect_error(tester(sample_size = 2)) expect_error(tester(sample_size = .1)) @@ -88,7 +91,7 @@ test_that("errors due to incorrect input types - other inputs", { }) - +# test polygon-based output test_that("output is an sf with correct columns", { iso <- tester() expect_s3_class(iso, "sf") @@ -96,6 +99,7 @@ test_that("output is an sf with correct columns", { expect_type(iso$id, "character") expect_type(iso$isochrone, "double") expect_type(iso$polygons, "list") + expect_true("sfc_POLYGON" %in% class(iso$polygons[1])) # more cutoffs means more rows @@ -105,3 +109,13 @@ test_that("output is an sf with correct columns", { ) }) + +# test line-based output +test_that("output is an sf with correct columns", { + iso <- tester(polygon_output = FALSE) + expect_s3_class(iso, "sf") + + expect_type(iso$edge_index, "character") + expect_type(iso$isochrone, "double") + expect_true("sfc_LINESTRING" %in% class(iso$geometry[1])) +}) diff --git a/r-package/tests/testthat/test-setup_r5.R b/r-package/tests/testthat/test-setup_r5.R index e41681fc..43b3c9cf 100644 --- a/r-package/tests/testthat/test-setup_r5.R +++ b/r-package/tests/testthat/test-setup_r5.R @@ -63,3 +63,23 @@ test_that("'overwrite' parameter works correctly", { ) }) + +test_that("throws error if write access to given dir is denied", { + # this test only works correctly with unix OSes. not sure how to change + # permissions from inside R in windows + skip_if_not(.Platform$OS.type == "unix") + + invisible(file.copy(path, tempdir(), recursive = TRUE)) + + tmpdir <- file.path(tempdir(), "poa") + + data_files <- list.files(tmpdir, full.names = TRUE) + files_to_remove <- data_files[grepl("network|\\.pbf\\.mapdb", data_files)] + if (length(files_to_remove) > 0) invisible(file.remove(files_to_remove)) + + Sys.chmod(tmpdir, "555") + + expect_error(setup_r5(tmpdir), class = "dir_permission_denied") + + Sys.chmod(tmpdir, "755") +}) diff --git a/r-package/tests/testthat/test-write_fare_structure.R b/r-package/tests/testthat/test-write_fare_structure.R index 8c74d28c..554e4971 100644 --- a/r-package/tests/testthat/test-write_fare_structure.R +++ b/r-package/tests/testthat/test-write_fare_structure.R @@ -29,3 +29,6 @@ test_that("written structure is identical to original", { written_struc <- read_fare_structure(tmpfile) expect_identical(struc, written_struc) }) + +# clean cache +r5r::r5r_cache(delete_file = 'all') diff --git a/r-package/tests/testthat/test-z_r5r_cache.R b/r-package/tests/testthat/test-z_r5r_cache.R new file mode 100644 index 00000000..fa8c2cfe --- /dev/null +++ b/r-package/tests/testthat/test-z_r5r_cache.R @@ -0,0 +1,56 @@ +context("r5r_cache") + +# skip tests because they take too much time +skip_if(Sys.getenv("TEST_ONE") != "") +testthat::skip_on_cran() + +try(silent = TRUE, r5r::stop_r5()) + +# Reading the data ----------------------- + +test_that("r5r_cache", { + + # simply list files + testthat::expect_message( r5r::r5r_cache() ) + + ## delete existing + + # download + r5r::download_r5(force_update = FALSE) + + # cache dir + cache_d <- paste0('r5r/r5_jar_v', r5r_env$r5_jar_version) + cache_dir <- tools::R_user_dir(cache_d, which = 'cache') + + # list cached files + fname_full <- list.files(cache_dir, full.names = TRUE) + fname <- basename(fname_full) + + testthat::expect_true( file.exists(fname_full) ) + testthat::expect_message( r5r::r5r_cache(delete_file = fname) ) + # testthat::expect_false( file.exists(fname_full) ) + + ## delete ALL + # download + r5r::download_r5(force_update = FALSE) + + testthat::expect_true( file.exists(fname_full) ) + testthat::expect_message( r5r::r5r_cache(delete_file = 'all') ) + # testthat::expect_true( length(list.files(cache_dir)) == 0 ) + + # if file does not exist, simply print message + testthat::expect_message( r5r::r5r_cache(delete_file ='aaa') ) + + }) + + +# ERRORS and messages ----------------------- +test_that("r5r_cache", { + + testthat::expect_error(r5r_cache(list_files= 999)) + testthat::expect_error(r5r_cache(delete_file = 999)) + }) + + +# clean cache +r5r_cache(delete_file = 'all') diff --git a/r-package/vignettes/fare_structure.Rmd b/r-package/vignettes/fare_structure.Rmd index f0439790..8eecb6f2 100644 --- a/r-package/vignettes/fare_structure.Rmd +++ b/r-package/vignettes/fare_structure.Rmd @@ -342,17 +342,21 @@ points <- read.csv(system.file("extdata/poa/poa_hexgrid.csv", package = "r5r")) # calculate travel times function calculate_travel_times <- function(fare) { - - ttm_df <- travel_time_matrix(r5r_core, - origins = points, - destinations = points, - departure_datetime = as.POSIXct("13-05-2019 14:00:00", - format = "%d-%m-%Y %H:%M:%S"), - mode = c("WALK", "TRANSIT"), - fare_structure = fare_structure, - max_fare = fare, - max_trip_duration = 45, - max_walk_time = 30) + ttm_df <- travel_time_matrix( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = as.POSIXct( + "13-05-2019 14:00:00", + format = "%d-%m-%Y %H:%M:%S" + ), + time_window = 1, + fare_structure = fare_structure, + max_fare = fare, + max_trip_duration = 40, + max_walk_time = 20 + ) return(ttm_df) } @@ -426,24 +430,26 @@ and compare the results the accessibility unconstrained by monetary costs: ```{r} # calculate accessibility function calculate_accessibility <- function(fare, fare_string) { - - access_df <- accessibility(r5r_core, - origins = points, - destinations = points, - departure_datetime = as.POSIXct("13-05-2019 14:00:00", - format = "%d-%m-%Y %H:%M:%S"), - opportunities_colname = "healthcare", - mode = c("WALK", "TRANSIT"), - cutoffs = 45, - fare_structure = fare_structure, - max_fare = fare, - max_trip_duration = 45, - max_walk_time = 30, - progress = FALSE) + access_df <- accessibility( + r5r_core, + origins = points, + destinations = points, + mode = c("WALK", "TRANSIT"), + departure_datetime = as.POSIXct( + "13-05-2019 14:00:00", + format = "%d-%m-%Y %H:%M:%S" + ), + time_window = 1, + opportunities_colname = "healthcare", + cutoffs = 40, + fare_structure = fare_structure, + max_fare = fare, + max_trip_duration = 40, + max_walk_time = 20, + progress = FALSE) access_df$max_fare <- fare_string - return(access_df) } @@ -472,6 +478,7 @@ ggplot(data = access) + theme_minimal() + theme(legend.position = "bottom", axis.text = element_blank()) + ``` diff --git a/r-package/vignettes/r5r.Rmd b/r-package/vignettes/r5r.Rmd index 3f051566..71b7189d 100644 --- a/r-package/vignettes/r5r.Rmd +++ b/r-package/vignettes/r5r.Rmd @@ -3,7 +3,7 @@ title: 'Intro to r5r: Rapid Realistic Routing with R5 in R' author: "Rafael H. M. Pereira, Marcus Saraiva, Daniel Herszenhut, Carlos Kaue Braga" date: "`r Sys.Date()`" output: rmarkdown::html_vignette -abstract: "`r5r` is an R package for rapid realistic routing on multimodal transport networks (walk, bike, public transport and car) using R5. The package allows users to generate detailed routing analysis or calculate travel time matrices using seamless parallel computing on top of the R5 Java machine " +abstract: "`{r5r}` is an R package for rapid realistic routing on multimodal transport networks (walk, bike, public transport and car) using R5. The package allows users to generate detailed routing analysis or calculate travel time matrices using seamless parallel computing on top of the R5 Java machine " urlcolor: blue vignette: > %\VignetteIndexEntry{Intro to r5r: Rapid Realistic Routing with R5 in R} @@ -41,37 +41,60 @@ https://doi.org/10.32866/001c.21262). # 2. Installation -You can install `r5r` from CRAN, or the development version from github. +You can install `{r5r}` from CRAN, or the development version from github. ```{r, eval = FALSE} -# CRAN +# from CRAN install.packages('r5r') -# dev version on github +# dev version with latest features devtools::install_github("ipeaGIT/r5r", subdir = "r-package") ``` -Please bear in mind that you need to have *Java SE Development Kit 11* installed on your computer to use `r5r`. No worries, you don't have to pay for it. The jdk 11 is freely available from the options below: -- [OpenJDK](https://jdk.java.net/java-se-ri/11) -- [Oracle](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) -If you don't know what version of Java you have installed on your computer, you can check it by running this on R console. +Please bear in mind that you need to have *Java Development Kit (JDK) 21* installed +on your computer to use `{r5r}`. No worries, you don't have to pay for it. There are +numerous open-source JDK implementations, and you only need to install one JDK. Here are a few options: + +- [Adoptium/Eclipse Temurin](https://adoptium.net/) (our preferred option) +- [Amazon Corretto](https://aws.amazon.com/corretto/) +- [Oracle OpenJDK](https://jdk.java.net/21/). + +The easiest way to install JDK is using the new [{rJavaEnv}](https://www.ekotov.pro/rJavaEnv/) package in R: + ```{r, eval = FALSE} - rJava::.jinit() - rJava::.jcall("java.lang.System", "S", "getProperty", "java.version") +# install {rJavaEnv} from CRAN +install.packages("rJavaEnv") + +# check version of Java currently installed (if any) +rJavaEnv::java_check_version_rjava() + +## if this is the first time you use {rJavaEnv}, you might need to run this code +## below to consent the installation of Java. +# rJavaEnv::rje_consent(provided = TRUE) + +# install Java 21 +rJavaEnv::java_quick_install(version = 21) + +# check if Java was successfully installed +rJavaEnv::java_check_version_rjava() ``` # 3. Usage -Before we start, we need to increase the memory available to Java. This is necessary because, by default, `R` allocates only 512MB of memory for Java processes, which is not enough for large queries using `r5r`. To increase available memory to 2GB, for example, we need to set the `java.parameters` option at the beginning of the script, as follows: +First, we need to increase the memory available to Java. This has to be done **before** loading the `{r5r}` library because, by default, `R` allocates only 512MB of memory for Java processes, which is not enough for large queries using `{r5r}`. To increase available memory to 2GB, for example, we need to set the `java.parameters` option at the beginning of the script, as follows: ```{r, message = FALSE} options(java.parameters = "-Xmx2G") + +# By default, {r5r} uses all CPU cores available. If you want to limit the +# number of CPUs to 4, for example, you can run: +options(java.parameters = c("-Xmx2G", "-XX:ActiveProcessorCount=4")) ``` -Note: It's very important to allocate enough memory before loading `r5r` or any other Java-based package, since `rJava` starts a Java Virtual Machine only once for each R session. It might be useful to restart your R session and execute the code above right after, if you notice that you haven't succeeded in your previous attempts. +Note: It's very important to allocate enough memory before loading `{r5r}` or any other Java-based package, since `rJava` starts a Java Virtual Machine only once for each R session. It might be useful to restart your R session and execute the code above right after, if you notice that you haven't succeeded in your previous attempts. Then we can load the packages used in this vignette: @@ -82,9 +105,9 @@ library(data.table) library(ggplot2) ``` -The `r5r` package has seven **fundamental functions**: +The `{r5r}` package has seven **fundamental functions**: -1. `setup_r5()` to initialize an instance of `r5r`, that also builds a routable +1. `setup_r5()` to initialize an instance of `{r5r}`, that also builds a routable transport network; 2. `accessibility()` for fast computation of access to opportunities considering @@ -117,7 +140,8 @@ The package also includes a few **support functions**. ## 3.1 Data requirements: -To use `r5r`, you will need: +To use `{r5r}`, you will need: + - A road network data set from OpenStreetMap in `.pbf` format (*mandatory*) - A public transport feed in `GTFS.zip` format (optional) - A raster file of Digital Elevation Model data in `.tif` format (optional) @@ -141,7 +165,7 @@ Here are a few places from where you can download these data sets: - Nasa's SRTMGL1 website -Let's have a quick look at how `r5r` works using a sample data set. +Let's have a quick look at how `{r5r}` works using a sample data set. @@ -149,7 +173,7 @@ Let's have a quick look at how `r5r` works using a sample data set. ## Data -To illustrate the functionalities of `r5r`, the package includes a small sample data for the city of Porto Alegre (Brazil). It includes seven files: +To illustrate the functionalities of `{r5r}`, the package includes a small sample data for the city of Porto Alegre (Brazil). It includes seven files: * An OpenStreetMap network: `poa_osm.pbf` * Two public transport feeds: `poa_eptc.zip` and `poa_trensurb.zip` @@ -186,7 +210,7 @@ head(points) ## 4.1 Building routable transport network with `setup_r5()` -The first step is to build the multimodal transport network used for routing in R5. This is done with the `setup_r5` function. This function does two things: (1) downloads/updates a compiled JAR file of R5 and stores it locally in the `r5r` package directory for future use; and (2) combines the osm.pbf and gtfs.zip data sets to build a routable network object. +The first step is to build the multimodal transport network used for routing in R5. This is done with the `setup_r5` function. This function does two things: (1) downloads/updates a compiled JAR file of R5 and stores it locally in the `{r5r}` package directory for future use; and (2) combines the osm.pbf and gtfs.zip data sets to build a routable network object. ```{r, message = FALSE} # Indicate the path where OSM and GTFS data are stored @@ -355,11 +379,19 @@ knitr::include_graphics("https://github.com/ipeaGIT/r5r/blob/master/r-package/in ### Cleaning up after usage -`r5r` objects are still allocated to any amount of memory previously set after they are done with their calculations. In order to remove an existing `r5r` object and reallocate the memory it had been using, we use the `stop_r5` function followed by a call to Java's garbage collector, as follows: +`{r5r}` objects are still allocated to any amount of memory previously set after they are done with their calculations. In order to remove an existing `{r5r}` object and reallocate the memory it had been using, we use the `stop_r5` function followed by a call to Java's garbage collector, as follows: ```{r, message = FALSE} r5r::stop_r5(r5r_core) rJava::.jgc(R.gc = TRUE) ``` +```{r, eval = TRUE, include = FALSE, message = FALSE} +# clean cache (CRAN policy) +r5r::r5r_cache(delete_file = 'all') + +``` + If you have any suggestions or want to report an error, please visit [the package GitHub page](https://github.com/ipeaGIT/r5r). + + diff --git a/r-package/vignettes/time_window.Rmd b/r-package/vignettes/time_window.Rmd index 6a092158..a8aa1d6a 100644 --- a/r-package/vignettes/time_window.Rmd +++ b/r-package/vignettes/time_window.Rmd @@ -82,8 +82,7 @@ departure_datetime = as.POSIXct("13-05-2019 14:00:00", format = "%d-%m-%Y %H:%M:%S") ``` -ps. Please keep in mind that the Monte Carlo draws in `time_window` only affects the results when the GTFS feeds contain a `frequencies.txt` table. - +ps. Please keep in mind that the Monte Carlo draws in `time_window` only affects the results when the GTFS feeds contain a `frequencies.txt` table. If the GTFS feed does not have a frequency table, r5r still allow for multiple runs over the set `time_window` in a deterministic way. ### 3.2 Accessibility with `time_window`. @@ -194,8 +193,6 @@ The `detailed_itineraries()`, on the other hand, does not return travel times or - - ### Cleaning up after usage `r5r` objects are still allocated to any amount of memory previously set after they are done with their calculations. In order to remove an existing `r5r` object and reallocate the memory it had been using, we use the `stop_r5` function followed by a call to Java's garbage collector, as follows: diff --git a/r-package/vignettes/travel_time_matrix.Rmd b/r-package/vignettes/travel_time_matrix.Rmd index 4caa299c..2fade497 100644 --- a/r-package/vignettes/travel_time_matrix.Rmd +++ b/r-package/vignettes/travel_time_matrix.Rmd @@ -164,6 +164,12 @@ r5r::stop_r5(r5r_core) rJava::.jgc(R.gc = TRUE) ``` +```{r, eval = TRUE, include = FALSE, message = FALSE} +# clean cache (CRAN policy) +r5r::r5r_cache(delete_file = 'all') + +``` + If you have any suggestions or want to report an error, please visit [the package GitHub page](https://github.com/ipeaGIT/r5r). ## References