---
title: "Updating code written for version 0.3"
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
    math_method: mathml
vignette: >
  %\VignetteIndexEntry{Updating code written for version 0.3}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r}
#| label: knitr-options
#| include: false

knitr::opts_chunk$set(
  collapse = FALSE,
  comment = "#>",
  message = FALSE,
  fig.width = 7,
  fig.height = 5
)
```

catchmentACS 0.4 changed some defaults and outputs that code written for 0.3
may rely on. Estimates of medians and per-person values computed with 0.3 can
also change, because of a correction made in 0.5.0 that
`vignette("porting-v04-to-v05", package = "catchmentACS")` describes with the
other changes of that version.

```{r}
#| label: setup
#| eval: true

library(catchmentACS)
library(sf)  # needed to subset the bundled sf objects with [
```

```{r}
#| label: setup-cache
#| include: false

# Compute every result in this article instead of reading saved ones; the
# option is restored at the end of the article.
old_options <- options(catchmentACS.cache_enabled = FALSE)
```

The examples use two files installed with the package. One holds drive-time
areas for 5, 10, and 15 minutes around made-up sites, drawn as circles, and
the examples take those of `AL_SITE_07`. The other holds American Community
Survey (ACS) estimates that are random numbers for small squares standing in
for census tracts.

```{r}
#| label: example-data
#| eval: true

iso <- readRDS(system.file(
  "extdata", "legacy_2025_isochrones.rds", package = "catchmentACS"
))
acs <- readRDS(system.file(
  "extdata", "sample_alabama_subset.rds", package = "catchmentACS"
))
iso_07 <- iso[iso$site_id == "AL_SITE_07", ]
site_07 <- data.frame(site_id = "AL_SITE_07", lon = -85.365, lat = 31.655)
```

## The value of the expression in `cacs_capture_conditions()`

`cacs_capture_conditions()` evaluates an expression and returns a table of the
messages and warnings of catchmentACS given while it runs. In 0.3 it returned
only that table. An assignment inside the expression, as in
`cacs_capture_conditions(result <- cacs_run(site_07, state = "AL"))`, does not
keep the value, because the expression is evaluated in a new environment.

From 0.4 on, `return_value = "both"` returns a list with the value of the
expression, `result`, and the table, `conditions`:

```{r}
#| label: capture-both
#| eval: true

out <- cacs_capture_conditions(
  cacs_run(site_07, state = "AL",
           precomputed_isochrones = iso_07, acs = acs),
  return_value = "both"
)
result <- out$result
table(out$conditions$class)
```

The captured conditions here are the progress messages of `cacs_run()`, which
are not shown. The default is still `return_value = "conditions"`, and
`options(catchmentACS.capture_return_value = "both")` changes it for the
session.

The approach used with 0.3, an assignment with `<<-` inside the expression,
still works. R assigns the value to the first variable of that name that it
finds, searching from the environment in which `cacs_capture_conditions()` is
called, and creates the variable in the global environment when there is none
(`?assignOps`). Inside a function, creating the variable first, as with
`result <- NULL`, keeps the value in the function.

## Drive-time areas built on the public OSRM server

With the Open Source Routing Machine (OSRM), the default routing service,
`cacs_isochrone()` used `res = 70L` in 0.3 when `res` was not given. Since
0.4, it uses `res = 30L` with `osrm_mode = "demo"`, the default, which sends
the requests to the public OSRM demo server, and still `res = 70L` with
`osrm_mode = "docker"`.

The coarser grid sends fewer requests, so the demo server is less likely to
respond with HTTP status 429 (too many requests), but it can give different
areas, and so different estimates. To keep the grid of a 0.3 script, set
`res = 70L` in the call to `cacs_isochrone()`, or `iso_args = list(res = 70L)`
in `cacs_run()`:

```{r}
#| label: osrm-res
#| eval: false

# Needs the osrm package and an internet connection.
iso_70 <- cacs_isochrone(site_07, drive_times = c(5, 10, 15), res = 70L)
```

`options(catchmentACS.osrm_demo_budget_protect = FALSE)` makes `res = 70L` the
default with both values of `osrm_mode`. `cacs_validate_osrm_endpoint()`, added
in 0.4, checks whether an OSRM server is accepting requests before the areas
are built. `?cacs_isochrone` describes the grid and the waiting time on the
demo server, and `vignette("providers", package = "catchmentACS")` describes
the servers and this check.

## The `isochrone` column of the list-column form

With `output = "list_column"`, `cacs_run()` returns a row for each site and
drive time, with the ACS estimates and the rates in list-columns of tibbles.
In 0.3, its column `isochrone` held `NULL`. Since 0.4, it holds the
drive-time area of the row, as a one-row sf object, when the areas are
supplied through `precomputed_isochrones`, and since 0.5.0 also when
`cacs_run()` builds them:

```{r}
#| label: list-column
#| eval: true

by_area <- cacs_run(
  site_07, state = "AL", precomputed_isochrones = iso_07, acs = acs,
  output = "list_column", verbose = FALSE
)
class(by_area$isochrone[[1]])
```

The same holds for the element `list_column` of the result with
`output = "both"`. Code that kept the areas next to the result, for example to
map them, can take them from this column.

## The row order of `as_tibble()`

In 0.3, `tibble::as_tibble()` returned the rows of a result of `cacs_run()`
in their order. Versions 0.4 to 0.5.1 sorted them by `site_id`,
`drive_time_min`, and `variable`, with the rows of the five rates first for
each site and drive time. Version 0.6.0 keeps the rows in their order again,
and `rate_first = TRUE` sorts them in the same way as 0.4 to 0.5.1:

```{r}
#| label: row-order
#| eval: true

head(tibble::as_tibble(result)$variable)
head(tibble::as_tibble(result, rate_first = TRUE)$variable)
```

Code written for 0.3 that selects rows by their position after
`tibble::as_tibble()` gets them in the order of the result, as it did with
0.3. `options(catchmentACS.rate_first_default = TRUE)` makes
`tibble::as_tibble()` put the rate rows first without `rate_first`, but only
when it is called from code run in the global environment, such as the
console, a script, or a document; a message says so the first time in a
session.

Functions of other packages that convert a result with `tibble::as_tibble()`
keep the order, even with that option, so `dplyr::left_join()`,
`dplyr::semi_join()`, `dplyr::anti_join()`, `tidyr::pivot_wider()`, and
`dplyr::summarise()` or `dplyr::reframe()` after `dplyr::rowwise()` work on a
result as on any other table (`?as_tibble.cacs_run_result`). In 0.4 to 0.5.1,
these functions got the sorted rows unless
`options(catchmentACS.rate_first_default = FALSE)` was set.
`dplyr::semi_join()` and `dplyr::anti_join()` could then return the wrong rows,
and summaries after `dplyr::rowwise()` with grouping variables could have the
wrong labels, without a warning; such results should be computed again.

## Rates by site in `summary()`, `print()`, and Markdown tables

From 0.4 on, `summary()` of a result also has the elements `rates_per_site`
and `rates_per_site_moe`, tables of the five rates with a row for each site and
drive time (`site_id` and `drive_time_min`). In `rates_per_site_moe`, each cell
gives the estimate and its margin of error (MOE), the half-width of its
confidence interval at the 90 percent level by default. `print()` shows the
first table for results with at most
`getOption("catchmentACS.summary_per_site_max")` sites, 12 by default, and
`cacs_summary_as_markdown()` formats either table as Markdown:

::: {style="overflow-x: auto;"}

```{r}
#| label: markdown-table
#| eval: true

cacs_summary_as_markdown(summary(result)$rates_per_site_moe)
```

:::

None of these additions requires a change to 0.3 code.

## Values in `rates_breakdown` before 0.4

In 0.2 and 0.3, the rows of the element `rates_breakdown` of `summary()`
were shifted by one: the mean, standard deviation, and count of missing values
shown for each rate belonged to another rate. Since 0.4, `dplyr::group_by()`
has a method for results of `cacs_run()`, which have the class
`cacs_run_result`: `group_by(cacs_run_result, ...)` removes that class before
grouping, so tables summarized from a result are ordinary tibbles
(`?group_by.cacs_run_result`). Values from `rates_breakdown` reported with
0.2 or 0.3 need to be computed again.

```{r}
#| label: restore-options
#| include: false

options(old_options)
rm(old_options)
```
