| Title: | Open Postal Code Correspondence |
|---|---|
| Description: | An open, fully reproducible alternative to closed postal code conversion files, giving source-qualified many-to-many correspondence between Ontario postal codes and Statistics Canada 2021 census geographies. Every published artifact can be rebuilt step by step from public sources using the included build functions, so any user can reproduce and audit the conversion end to end. Lookup results retain allocation weights, evidence source, lineage, method, and vintage; unmatched postal codes remain explicit, and versioned release artifacts are checksum-verified before use. A built-in 'shiny' application provides a point-and-click interface for joining, mapping, and exporting results. Correspondences are derived from the Statistics Canada National Address Register <https://www150.statcan.gc.ca/n1/pub/46-26-0002/462600022022001-eng.htm>, the 2021 Census Geographic Attribute File <https://www12.statcan.gc.ca/census-recensement/2021/geo/aip-pia/attribute-attribs/index-eng.cfm>, and the GeoNames postal code export <https://download.geonames.org/export/zip/>. The package does not redistribute Canada Post, PCCF, or PCCF+ data and does not claim authoritative postal assignments. |
| Authors: | Lennon Li [aut, cre, cph] |
| Maintainer: | Lennon Li <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.0.1 |
| Built: | 2026-09-22 10:21:01 UTC |
| Source: | https://github.com/lennon-li/opcc |
Aggregate postal-code-to-DB evidence to DA links
aggregate_da_correspondence(correspondence)aggregate_da_correspondence(correspondence)
correspondence |
A postal-code-to-DB correspondence data frame. |
A data frame with one row per postal_code and DAUID.
# A small in-line postal-code-to-DB evidence table. Allocation weights # must sum to one within each postal code. db_links <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8", "M5V 3A8"), DBUID = c("35200001000", "35200001001", "35200002000"), DAUID = c("35200001", "35200001", "35200002"), allocation_weight = c(0.5, 0.25, 0.25), source_vintage = "2026-06-26", census_vintage = "2021", evidence_class = "NAR", stringsAsFactors = FALSE ) aggregate_da_correspondence(db_links)# A small in-line postal-code-to-DB evidence table. Allocation weights # must sum to one within each postal code. db_links <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8", "M5V 3A8"), DBUID = c("35200001000", "35200001001", "35200002000"), DAUID = c("35200001", "35200001", "35200002"), allocation_weight = c(0.5, 0.25, 0.25), source_vintage = "2026-06-26", census_vintage = "2021", evidence_class = "NAR", stringsAsFactors = FALSE ) aggregate_da_correspondence(db_links)
Reads the extracted NAR Address and Location part files, computes the mean coordinate per postal code, and merges with GeoNames Ontario centroids. NAR centroids take priority; GeoNames fills gaps.
build_centroids(nar_dir, geonames_txt, output_dir = NULL)build_centroids(nar_dir, geonames_txt, output_dir = NULL)
nar_dir |
Path returned by |
geonames_txt |
Path returned by |
output_dir |
Required directory for output files. |
Invisibly, the path to ontario_postal_centroids.csv.
# Consumes the multi-gigabyte NAR and GeoNames downloads, so it is not run # automatically. if (interactive()) { cache <- tempfile("opcc-build") nar_dir <- download_nar(cache_dir = cache) geonames_txt <- download_geonames(cache_dir = cache) build_centroids(nar_dir, geonames_txt, output_dir = file.path(cache, "centroids")) }# Consumes the multi-gigabyte NAR and GeoNames downloads, so it is not run # automatically. if (interactive()) { cache <- tempfile("opcc-build") nar_dir <- download_nar(cache_dir = cache) geonames_txt <- download_geonames(cache_dir = cache) build_centroids(nar_dir, geonames_txt, output_dir = file.path(cache, "centroids")) }
Validates that centroids fall within the Ontario boundary, assigns each to exactly one 2021 Dissemination Block via point-in-polygon, and joins the Geographic Attribute File for higher geographies.
build_db_assignment( centroids_csv, province_shp, db_shp, gaf_csv, output_dir = NULL )build_db_assignment( centroids_csv, province_shp, db_shp, gaf_csv, output_dir = NULL )
centroids_csv |
Path returned by |
province_shp |
Path to the province boundary |
db_shp |
Path to the DB boundary |
gaf_csv |
Path returned by |
output_dir |
Required directory for output files. |
Invisibly, the path to ontario_postal_gaf_rollup.csv.
# Consumes the census boundary and GAF downloads, so it is not run # automatically. if (interactive()) { cache <- tempfile("opcc-build") boundaries <- download_census_boundaries(cache_dir = cache) gaf_csv <- download_gaf(cache_dir = cache) centroids_csv <- build_centroids( download_nar(cache_dir = cache), download_geonames(cache_dir = cache), output_dir = file.path(cache, "centroids") ) build_db_assignment(centroids_csv, boundaries$province, boundaries$db, gaf_csv, output_dir = file.path(cache, "rollup")) }# Consumes the census boundary and GAF downloads, so it is not run # automatically. if (interactive()) { cache <- tempfile("opcc-build") boundaries <- download_census_boundaries(cache_dir = cache) gaf_csv <- download_gaf(cache_dir = cache) centroids_csv <- build_centroids( download_nar(cache_dir = cache), download_geonames(cache_dir = cache), output_dir = file.path(cache, "centroids") ) build_db_assignment(centroids_csv, boundaries$province, boundaries$db, gaf_csv, output_dir = file.path(cache, "rollup")) }
Reads raw NAR address points, assigns them to 2021 Dissemination
Blocks via point-in-polygon, joins the GAF, aggregates evidence per
postal-code/DB pair, and appends GeoNames supplementary links from
the rollup produced by build_db_assignment().
build_m2(nar_dir, db_shp, gaf_csv, rollup_csv, output_dir = NULL)build_m2(nar_dir, db_shp, gaf_csv, rollup_csv, output_dir = NULL)
nar_dir |
Path returned by |
db_shp |
Path to the DB boundary |
gaf_csv |
Path returned by |
rollup_csv |
Path returned by |
output_dir |
Required directory for output files. |
Invisibly, the path to m2_correspondence.csv.
# Consumes the NAR, boundary, and GAF downloads plus the DB-assignment # rollup, so it is not run automatically. if (interactive()) { cache <- tempfile("opcc-build") nar_dir <- download_nar(cache_dir = cache) boundaries <- download_census_boundaries(cache_dir = cache) gaf_csv <- download_gaf(cache_dir = cache) rollup_csv <- file.path(cache, "rollup", "ontario_postal_gaf_rollup.csv") build_m2(nar_dir, boundaries$db, gaf_csv, rollup_csv, output_dir = file.path(cache, "m2")) }# Consumes the NAR, boundary, and GAF downloads plus the DB-assignment # rollup, so it is not run automatically. if (interactive()) { cache <- tempfile("opcc-build") nar_dir <- download_nar(cache_dir = cache) boundaries <- download_census_boundaries(cache_dir = cache) gaf_csv <- download_gaf(cache_dir = cache) rollup_csv <- file.path(cache, "rollup", "ontario_postal_gaf_rollup.csv") build_m2(nar_dir, boundaries$db, gaf_csv, rollup_csv, output_dir = file.path(cache, "m2")) }
Build a source-separated local evidence layer
build_source_layer( data, adapter, on_invalid = c("error", "drop", "quarantine") )build_source_layer( data, adapter, on_invalid = c("error", "drop", "quarantine") )
data |
A user-supplied postal-code evidence data frame. |
adapter |
Source metadata created by |
on_invalid |
How to handle invalid rows: error, drop them, or retain
them in the |
An opcc_source_layer data frame, never merged into a release.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("m5v3a8", "K1A 0A6", "not a postal code"), lat = c(43.6426, 45.4215, NA), lon = c(-79.3871, -75.6972, NA), stringsAsFactors = FALSE ) layer <- build_source_layer(raw, adapter, on_invalid = "drop") layer[c("postal_code", "source_id", "source_retrieval_date")] class(layer)adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("m5v3a8", "K1A 0A6", "not a postal code"), lat = c(43.6426, 45.4215, NA), lon = c(-79.3871, -75.6972, NA), stringsAsFactors = FALSE ) layer <- build_source_layer(raw, adapter, on_invalid = "drop") layer[c("postal_code", "source_id", "source_retrieval_date")] class(layer)
Removes only OPCC files named opcc-* from cache_dir, including the
Shiny app's reusable map artifact in its shiny-app subdirectory. This is
an explicit maintenance action; routine downloads retain a small, verified
cache capped at 64 MiB.
clear_opcc_cache(cache_dir = NULL)clear_opcc_cache(cache_dir = NULL)
cache_dir |
Directory containing downloaded OPCC release artifacts.
Defaults to |
The paths removed, invisibly.
OPCC never writes to your home filespace by default. When cache_dir is
NULL, the location is resolved in this order:
the OPCC.cache_dir option, if set;
the OPCC_CACHE_DIR environment variable, if set;
a recorded consent answer, if you have been asked before;
otherwise a session temporary directory, discarded when R exits.
In an interactive session with no answer on record, OPCC asks once whether
it may keep artifacts in tools::R_user_dir() and remembers the reply. A
non-interactive session is never asked, and writes nothing outside
tempdir() unless you previously granted permission or set the option or
environment variable above.
cache <- tempfile("opcc-cache") dir.create(cache) clear_opcc_cache(cache)cache <- tempfile("opcc-cache") dir.create(cache) clear_opcc_cache(cache)
Create a reviewable local-source contribution bundle
contribution_bundle(layer, output_dir = NULL, fixture_rows = 100L)contribution_bundle(layer, output_dir = NULL, fixture_rows = 100L)
layer |
A layer created by |
output_dir |
Explicit directory in which to create a new bundle directory. |
fixture_rows |
Maximum normalized sample rows to include. |
A named list of generated bundle paths.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) # `output_dir` is required and must be explicit; a session temporary # directory keeps the example self-contained. output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) basename(unlist(bundle)) unlink(output_dir, recursive = TRUE)adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) # `output_dir` is required and must be explicit; a session temporary # directory keeps the example self-contained. output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) basename(unlist(bundle)) unlink(output_dir, recursive = TRUE)
The returned URL opens GitHub's issue composer with bundle provenance prefilled. GitHub does not support file attachments through this URL, so the contributor must attach the generated bundle manually before submitting.
contribution_issue_url(bundle, repository = "lennon-li/OPCC")contribution_issue_url(bundle, repository = "lennon-li/OPCC")
bundle |
A bundle returned by |
repository |
GitHub repository in |
A GitHub issue-composer URL.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) issue_url <- contribution_issue_url(bundle) substr(issue_url, 1, 55) unlink(output_dir, recursive = TRUE)adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) issue_url <- contribution_issue_url(bundle) substr(issue_url, 1, 55) unlink(output_dir, recursive = TRUE)
Downloads the province/territory and Dissemination Block boundary shapefiles. Skips downloads if already cached.
download_census_boundaries(cache_dir = NULL)download_census_boundaries(cache_dir = NULL)
cache_dir |
Required directory for downloaded and extracted files. |
Invisibly, a named list with province and db paths to the
extracted .shp files.
# Downloads two Statistics Canada boundary zips (hundreds of megabytes), # so it is not run automatically. if (interactive()) { boundaries <- download_census_boundaries(cache_dir = tempfile("opcc")) boundaries$province }# Downloads two Statistics Canada boundary zips (hundreds of megabytes), # so it is not run automatically. if (interactive()) { boundaries <- download_census_boundaries(cache_dir = tempfile("opcc")) boundaries$province }
Downloads the Canada-wide dissemination area cartographic boundary
shapefiles (~200 MB zip) and extracts the .shp. Skips the download if
the zip is already cached. On first download a SHA-256 sidecar file is
written next to the zip and every later reuse is verified against it;
Statistics Canada does not publish an official checksum for this file.
download_da_boundaries(cache_dir = NULL)download_da_boundaries(cache_dir = NULL)
cache_dir |
Required directory for downloaded and extracted files. |
Invisibly, a named list with da pointing to the extracted
lda_000b21a_e.shp.
# Downloads a ~200 MB Statistics Canada boundary zip, so it is not run # automatically. if (interactive()) { boundaries <- download_da_boundaries(cache_dir = tempfile("opcc-build")) boundaries$da }# Downloads a ~200 MB Statistics Canada boundary zip, so it is not run # automatically. if (interactive()) { boundaries <- download_da_boundaries(cache_dir = tempfile("opcc-build")) boundaries$da }
Downloads the GAF zip and extracts the CSV. Skips if already cached.
download_gaf(cache_dir = NULL)download_gaf(cache_dir = NULL)
cache_dir |
Required directory for downloaded and extracted files. |
Invisibly, the path to the extracted GAF CSV.
# Downloads the Statistics Canada Geographic Attribute File zip, so it is # not run automatically. if (interactive()) { gaf_csv <- download_gaf(cache_dir = tempfile("opcc-build")) }# Downloads the Statistics Canada Geographic Attribute File zip, so it is # not run automatically. if (interactive()) { gaf_csv <- download_gaf(cache_dir = tempfile("opcc-build")) }
Downloads the GeoNames CA_full.csv.zip and extracts the tab-delimited text file. Skips the download if already cached.
download_geonames(cache_dir = NULL)download_geonames(cache_dir = NULL)
cache_dir |
Required directory for downloaded and extracted files. |
Invisibly, the path to the extracted CA_full.txt.
# Downloads the GeoNames CA_full.csv.zip, so it needs network access and # is not run automatically. if (interactive()) { geonames_txt <- download_geonames(cache_dir = tempfile("opcc-build")) }# Downloads the GeoNames CA_full.csv.zip, so it needs network access and # is not run automatically. if (interactive()) { geonames_txt <- download_geonames(cache_dir = tempfile("opcc-build")) }
Downloads the NAR release zip (~1.6 GB) and extracts the Ontario Address and Location part files. Skips the download if the zip is already cached.
download_nar(cache_dir = NULL)download_nar(cache_dir = NULL)
cache_dir |
Required directory for downloaded and extracted files. |
Invisibly, the path to the NAR scratch directory containing the extracted Ontario CSV files.
# Downloads the ~1.6 GB Statistics Canada NAR release, so it is not run # automatically. Pass an explicit cache directory to keep files local. if (interactive()) { nar_dir <- download_nar(cache_dir = tempfile("opcc-build")) }# Downloads the ~1.6 GB Statistics Canada NAR release, so it is not run # automatically. Pass an explicit cache directory to keep files local. if (interactive()) { nar_dir <- download_nar(cache_dir = tempfile("opcc-build")) }
Builds a point layer for the supplied postal codes, one feature per unique
code, carrying the dissemination-area roll-up from joined as attributes,
and writes it to a zipped ESRI Shapefile. The points are the same
source-qualified postal centroids the OPCC Shiny app draws on its map, so a
script generated by the app reproduces exactly what the app displayed.
export_postal_points( postal_code, joined, zipfile, vintage = "2026-06-26", cache_dir = NULL, offline = FALSE )export_postal_points( postal_code, joined, zipfile, vintage = "2026-06-26", cache_dir = NULL, offline = FALSE )
postal_code |
Character vector of postal codes. |
joined |
A data frame produced by joining records to an OPCC
correspondence, carrying the |
zipfile |
Path of the |
vintage |
Centroid artifact vintage. |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
If |
Codes with no point evidence cannot appear in the output; that is a property
of the evidence, not a failure. When no supplied code has any point
evidence, nothing is written and the function returns NULL.
The zipfile path, invisibly, or NULL when no points were
available to write.
# Writing the shapefile downloads a versioned centroid artifact, so this # runs only in an interactive session. if (interactive() && requireNamespace("sf", quietly = TRUE)) { joined <- data.frame(opcc_postal_code = "M5S1A1", DAUID = "35200001") export_postal_points( "M5S1A1", joined, file.path(tempdir(), "opcc_postal_points.zip") ) }# Writing the shapefile downloads a versioned centroid artifact, so this # runs only in an interactive session. if (interactive() && requireNamespace("sf", quietly = TRUE)) { joined <- data.frame(opcc_postal_code = "M5S1A1", DAUID = "35200001") export_postal_points( "M5S1A1", joined, file.path(tempdir(), "opcc_postal_points.zip") ) }
Load the versioned GeoNames supplementary-point adapter
geonames_supplementary_adapter()geonames_supplementary_adapter()
An opcc_source_adapter for the packaged GeoNames point artifact.
adapter <- geonames_supplementary_adapter() adapter$source_id adapter$coordinate_method adapter$licenceadapter <- geonames_supplementary_adapter() adapter$source_id adapter$coordinate_method adapter$licence
Download, cache, and verify a correspondence release
get_correspondence(vintage = "2026-06-26", cache_dir = NULL, offline = FALSE)get_correspondence(vintage = "2026-06-26", cache_dir = NULL, offline = FALSE)
vintage |
A value returned by |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
Require an already cached verified file. |
A data frame of postal-code-to-DB links.
list_vintages("DB") # Pass an explicit cache directory; never write to the default user cache # from an example or a test. cache <- tempfile("opcc-cache") # Downloads and checksum-verifies a release artifact, so it needs network # access and is not run automatically. if (interactive()) { m2 <- get_correspondence("2026-06-26", cache_dir = cache) utils::head(m2[c("postal_code", "DBUID", "DAUID", "allocation_weight")]) }list_vintages("DB") # Pass an explicit cache directory; never write to the default user cache # from an example or a test. cache <- tempfile("opcc-cache") # Downloads and checksum-verifies a release artifact, so it needs network # access and is not run automatically. if (interactive()) { m2 <- get_correspondence("2026-06-26", cache_dir = cache) utils::head(m2[c("postal_code", "DBUID", "DAUID", "allocation_weight")]) }
Download, cache, and verify a direct DA correspondence release
get_da_correspondence( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE )get_da_correspondence( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE )
vintage |
A value returned by |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
Require an already cached verified file. |
A data frame of postal-code-to-DA links with contributing DB lineage.
list_vintages("DA") # Pass an explicit cache directory; never write to the default user cache # from an example or a test. cache <- tempfile("opcc-cache") # Downloads and checksum-verifies a release artifact, so it needs network # access and is not run automatically. if (interactive()) { m5 <- get_da_correspondence("2026-06-26", cache_dir = cache) utils::head(m5[c("postal_code", "DAUID", "allocation_weight")]) }list_vintages("DA") # Pass an explicit cache directory; never write to the default user cache # from an example or a test. cache <- tempfile("opcc-cache") # Downloads and checksum-verifies a release artifact, so it needs network # access and is not run automatically. if (interactive()) { m5 <- get_da_correspondence("2026-06-26", cache_dir = cache) utils::head(m5[c("postal_code", "DAUID", "allocation_weight")]) }
List supported correspondence release vintages
list_vintages(level = c("DB", "DA"))list_vintages(level = c("DB", "DA"))
level |
Geography level, |
A character vector of release vintages.
list_vintages("DB") list_vintages("DA")list_vintages("DB") list_vintages("DA")
Define a source adapter for a local evidence layer
new_source_adapter( source_id, licence, lineage, retrieval_date = Sys.Date(), schema_map = list(postal_code = "postal_code"), endpoint = NULL, checksum = NULL, location_type = "unknown", coordinate_method = "unknown", authority_level = "unknown", coverage_type = "unknown", update_frequency = "unknown" )new_source_adapter( source_id, licence, lineage, retrieval_date = Sys.Date(), schema_map = list(postal_code = "postal_code"), endpoint = NULL, checksum = NULL, location_type = "unknown", coordinate_method = "unknown", authority_level = "unknown", coverage_type = "unknown", update_frequency = "unknown" )
source_id |
Stable, lower-case source identifier. |
licence |
Licence or permission statement for the source. |
lineage |
Source lineage and collection method. |
retrieval_date |
Source retrieval or creation date. |
schema_map |
Named mapping from OPCC fields to source fields. |
endpoint |
Optional public retrieval endpoint. |
checksum |
Optional SHA-256 checksum of the source artifact. |
location_type |
Source location type: |
coordinate_method |
Coordinate derivation method: |
authority_level |
Source authority classification. |
coverage_type |
Source coverage classification. |
update_frequency |
Expected source update frequency. |
An opcc_source_adapter object.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon"), location_type = "physical", coordinate_method = "address_point" ) adapter$source_id adapter$schema_mapadapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon"), location_type = "physical", coordinate_method = "address_point" ) adapter$source_id adapter$schema_map
Normalize Canadian postal codes
normalize_postal_code(x, strict = FALSE)normalize_postal_code(x, strict = FALSE)
x |
A character vector of postal codes. |
strict |
If |
A character vector in A1A 1A1 form.
normalize_postal_code(c("m5v3a8", "M5V 3A8", "M5V-3A8")) # Invalid values become NA unless strict = TRUE, which raises an error. normalize_postal_code(c("M5V 3A8", "not a postal code")) try(normalize_postal_code("not a postal code", strict = TRUE))normalize_postal_code(c("m5v3a8", "M5V 3A8", "M5V-3A8")) # Invalid values become NA unless strict = TRUE, which raises an error. normalize_postal_code(c("M5V 3A8", "not a postal code")) try(normalize_postal_code("not a postal code", strict = TRUE))
Checksum-verified, source-qualified correspondence releases and postal-code lookup helpers for linking Ontario postal codes to census dissemination blocks and dissemination areas.
OPCC does not substitute GeoNames point evidence for NAR address evidence. Local source layers are always source-separated and never modify a canonical OPCC release. Canada Post, PCCF, and PCCF+ data are rejected. Each local-data function invites users to contribute redistributable evidence through a reviewable bundle.
Open a GitHub source-proposal issue for a contribution bundle
open_contribution_issue(bundle, repository = "lennon-li/OPCC", open = FALSE)open_contribution_issue(bundle, repository = "lennon-li/OPCC", open = FALSE)
bundle |
A bundle returned by |
repository |
GitHub repository in |
open |
Whether to open the returned URL in a browser. Defaults to
|
Invisibly, the GitHub issue-composer URL.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) # open = FALSE keeps submission an explicit user action, so nothing is # sent and no browser is opened here. issue_url <- suppressMessages(open_contribution_issue(bundle)) substr(issue_url, 1, 55) unlink(output_dir, recursive = TRUE)adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15") ) layer <- build_source_layer( data.frame(postal_code = c("M5V 3A8", "K1A 0A6")), adapter ) output_dir <- tempfile("opcc-bundle") bundle <- contribution_bundle(layer, output_dir = output_dir) # open = FALSE keeps submission an explicit user action, so nothing is # sent and no browser is opened here. issue_url <- suppressMessages(open_contribution_issue(bundle)) substr(issue_url, 1, 55) unlink(output_dir, recursive = TRUE)
All DB links are returned by default. Set all_links = FALSE only when a
single best link is specifically needed.
pc_to_geo( postal_code, level = c("DB", "DA"), all_links = TRUE, correspondence = NULL, ... )pc_to_geo( postal_code, level = c("DB", "DA"), all_links = TRUE, correspondence = NULL, ... )
postal_code |
Character vector of Canadian postal codes. |
level |
Geography level, |
all_links |
Whether to retain every allocated DB link. |
correspondence |
Optional already-loaded correspondence data. |
... |
Passed to |
A data frame; unmatched normalized postal codes are stored in its
unmatched attribute.
# Supplying `correspondence` keeps the lookup fully offline. da_links <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8"), DAUID = c("35200001", "35200002"), allocation_weight = c(0.75, 0.25), n_contributing_dbs = c(2L, 1L), contributing_dbuids = c("35200001000|35200001001", "35200002000"), source_vintages = "2026-06-26", census_vintages = "2021", evidence_classes = "NAR", best_link = c(TRUE, FALSE), stringsAsFactors = FALSE ) pc_to_geo("M5V 3A8", level = "DA", correspondence = da_links) # A single best link, when one is specifically needed. pc_to_geo("m5v3a8", level = "DA", correspondence = da_links, all_links = FALSE) # Unmatched postal codes stay explicit rather than being dropped silently. found <- pc_to_geo(c("M5V 3A8", "K1A 0A6"), level = "DA", correspondence = da_links) attr(found, "unmatched")# Supplying `correspondence` keeps the lookup fully offline. da_links <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8"), DAUID = c("35200001", "35200002"), allocation_weight = c(0.75, 0.25), n_contributing_dbs = c(2L, 1L), contributing_dbuids = c("35200001000|35200001001", "35200002000"), source_vintages = "2026-06-26", census_vintages = "2021", evidence_classes = "NAR", best_link = c(TRUE, FALSE), stringsAsFactors = FALSE ) pc_to_geo("M5V 3A8", level = "DA", correspondence = da_links) # A single best link, when one is specifically needed. pc_to_geo("m5v3a8", level = "DA", correspondence = da_links, all_links = FALSE) # Unmatched postal codes stay explicit rather than being dropped silently. found <- pc_to_geo(c("M5V 3A8", "K1A 0A6"), level = "DA", correspondence = da_links) attr(found, "unmatched")
Look up source-qualified point observations
pc_to_point( postal_code, vintage = "2026-07-19", point_file = NULL, cache_dir = NULL, offline = FALSE, source = NULL )pc_to_point( postal_code, vintage = "2026-07-19", point_file = NULL, cache_dir = NULL, offline = FALSE, source = NULL )
postal_code |
Character vector of Canadian postal codes. |
vintage |
Point-release vintage. |
point_file |
Optional local gzip CSV file. Supplying this enables fully offline and air-gapped use. |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
Require an already cached verified file. |
source |
Optional character vector of |
All matching source-qualified point observations, including DB/DA fields when a point intersects a 2021 Ontario dissemination block.
# `point_file` accepts any local gzip CSV with the required columns, which # makes point lookups fully offline and air-gapped. points <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8"), latitude = c(43.6426, 43.6430), longitude = c(-79.3871, -79.3875), point_source = c("nar", "geonames"), point_method = c("address_point", "centroid"), stringsAsFactors = FALSE ) point_file <- tempfile("opcc-points", fileext = ".csv.gz") connection <- gzfile(point_file, "w") utils::write.csv(points, connection, row.names = FALSE) close(connection) pc_to_point("M5V 3A8", point_file = point_file) # Restrict the evidence to one source. pc_to_point("M5V 3A8", point_file = point_file, source = "nar") unlink(point_file)# `point_file` accepts any local gzip CSV with the required columns, which # makes point lookups fully offline and air-gapped. points <- data.frame( postal_code = c("M5V 3A8", "M5V 3A8"), latitude = c(43.6426, 43.6430), longitude = c(-79.3871, -79.3875), point_source = c("nar", "geonames"), point_method = c("address_point", "centroid"), stringsAsFactors = FALSE ) point_file <- tempfile("opcc-points", fileext = ".csv.gz") connection <- gzfile(point_file, "w") utils::write.csv(points, connection, row.names = FALSE) close(connection) pc_to_point("M5V 3A8", point_file = point_file) # Restrict the evidence to one source. pc_to_point("M5V 3A8", point_file = point_file, source = "nar") unlink(point_file)
Profile a local source layer
profile_source_layer(layer)profile_source_layer(layer)
layer |
A layer created by |
A list of coverage and coordinate-quality metrics.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("M5V 3A8", "K1A 0A6"), lat = c(43.6426, 45.4215), lon = c(-79.3871, -75.6972), stringsAsFactors = FALSE ) layer <- build_source_layer(raw, adapter) profile_source_layer(layer)adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("M5V 3A8", "K1A 0A6"), lat = c(43.6426, 45.4215), lon = c(-79.3871, -75.6972), stringsAsFactors = FALSE ) layer <- build_source_layer(raw, adapter) profile_source_layer(layer)
Read and verify a release manifest
release_manifest( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE, level = c("DB", "DA") )release_manifest( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE, level = c("DB", "DA") )
vintage |
A value returned by |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
Require an already cached verified file. |
level |
Geography level, |
A parsed JSON list.
list_vintages("DB") cache <- tempfile("opcc-cache") # Downloads and checksum-verifies the release manifest, so it needs # network access and is not run automatically. if (interactive()) { manifest <- release_manifest("2026-06-26", cache_dir = cache) names(manifest) }list_vintages("DB") cache <- tempfile("opcc-cache") # Downloads and checksum-verifies the release manifest, so it needs # network access and is not run automatically. if (interactive()) { manifest <- release_manifest("2026-06-26", cache_dir = cache) names(manifest) }
Launches a Shiny app that joins a user-uploaded CSV to the OPCC postal-code-to-DA correspondence: pick the postal-code column (an auto-detected candidate is preselected), click Join to see the result table, and draw the matched dissemination areas on a map. The joined CSV, the map as HTML, and an R script reproducing both artifacts are downloadable.
run_app(...)run_app(...)
... |
Passed to |
Reusing the simplified map between sessions requires a writable cache. The
app never writes one without permission: set OPCC.shiny_da_cache_dir (or
OPCC_SHINY_DA_CACHE_DIR) to choose the location yourself, or answer the
one-time prompt shown in an interactive session. The answer is remembered,
so a recorded "yes" also applies to later non-interactive launches, and a
recorded "no" is honored until you delete the record. Without permission
the map is rebuilt in a session-only temporary directory, which is correct
but takes several minutes per session.
Called for its side effect (launches the Shiny app). Invisibly
returns NULL.
if (interactive()) { run_app() }if (interactive()) { run_app() }
Validate a verified correspondence release
validate_release( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE, level = c("DB", "DA") )validate_release( vintage = "2026-06-26", cache_dir = NULL, offline = FALSE, level = c("DB", "DA") )
vintage |
A value returned by |
cache_dir |
Directory for the small, verified, actively managed
runtime cache. Defaults to |
offline |
Require an already cached verified file. |
level |
Geography level, |
Invisibly TRUE, or an error describing a failed invariant.
list_vintages("DB") cache <- tempfile("opcc-cache") # Downloads the release and its manifest, so it needs network access and # is not run automatically. if (interactive()) { validate_release("2026-06-26", cache_dir = cache) }list_vintages("DB") cache <- tempfile("opcc-cache") # Downloads the release and its manifest, so it needs network access and # is not run automatically. if (interactive()) { validate_release("2026-06-26", cache_dir = cache) }
Validate local postal-code evidence
validate_source_data( data, adapter, on_invalid = c("error", "drop", "quarantine") )validate_source_data( data, adapter, on_invalid = c("error", "drop", "quarantine") )
data |
A data frame with a postal-code field named by |
adapter |
Source metadata created by |
on_invalid |
How to handle invalid rows: error, drop them, or retain
them in the |
A normalized data frame with postal_code and validation metadata.
Coordinate-bearing rows outside the inclusive broad Ontario bounds
(latitude 41.6 to 56.9, longitude -95.2 to -74.3) are invalid and counted
in outside_ontario_bounds_rows.
adapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("m5v3a8", "K1A 0A6", "not a postal code"), lat = c(43.6426, 45.4215, NA), lon = c(-79.3871, -75.6972, NA), stringsAsFactors = FALSE ) clean <- validate_source_data(raw, adapter, on_invalid = "quarantine") clean$postal_code attr(clean, "opcc_validation_report")[c("accepted_rows", "rejected_rows")] attr(clean, "opcc_quarantine")$.opcc_validation_reasonadapter <- new_source_adapter( source_id = "example_open_addresses", licence = "Open Government Licence - Ontario", lineage = "Municipal open address points, retrieved from a city portal", retrieval_date = as.Date("2026-01-15"), schema_map = list(postal_code = "pc", latitude = "lat", longitude = "lon") ) raw <- data.frame( pc = c("m5v3a8", "K1A 0A6", "not a postal code"), lat = c(43.6426, 45.4215, NA), lon = c(-79.3871, -75.6972, NA), stringsAsFactors = FALSE ) clean <- validate_source_data(raw, adapter, on_invalid = "quarantine") clean$postal_code attr(clean, "opcc_validation_report")[c("accepted_rows", "rejected_rows")] attr(clean, "opcc_quarantine")$.opcc_validation_reason