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.
Open Postal Code Correspondence -- an open, reproducible R package for linking Ontario postal codes to Statistics Canada 2021 census geographies (Dissemination Blocks and Dissemination Areas).
OPCC does not redistribute Canada Post, PCCF, or PCCF+ data. Its evidence unit is an observed postal association from redistributable public sources.
Requires R >= 4.1.
From CRAN, once the submission is accepted:
install.packages("OPCC")
Development version from GitHub:
install.packages("pak")
pak::pak("lennon-li/OPCC")
Artifacts are downloaded, checksum-verified, and cached automatically on
first use. The cache is session-only by default, so OPCC never writes to your
home filespace uninvited. To keep artifacts across sessions, pass cache_dir,
set options(OPCC.cache_dir = "/your/path") or the OPCC_CACHE_DIR
environment variable, or accept the one-time prompt an interactive session
offers; clear_opcc_cache() removes the cache again.
library(OPCC)
# Best DA link for a postal code
pc_to_geo("M5V 3A8", level = "DA", all_links = FALSE)
# Multiple postal codes at once
pc_to_geo(c("M5V 3A8", "K1A 0A6"), level = "DA", all_links = FALSE)
# Join census geographies to your own data
da <- get_da_correspondence(vintage = "2026-07-20")
my_data <- data.frame(postal_code = c("M5V 3A8", "K1A 0A6"),
stringsAsFactors = FALSE)
merge(my_data, da[da$best_link, c("postal_code", "DAUID", "allocation_weight")],
by = "postal_code", all.x = TRUE)
# Verify artifact integrity
validate_release(vintage = "2026-07-20", level = "DA")
Full guide with dplyr joins, offline use, DB lookups, point observations,
and local source import:
vignette("using-opcc", package = "OPCC")
Upload a CSV, pick its postal-code column, and join it to the postal-code-to-DA correspondence: view the joined table, draw the matched dissemination areas on a map, and download the joined CSV, the map as HTML, and an R script that reproduces both. You can also type postal codes directly; entries that are not valid Canadian postal codes are listed back to you and skipped, and the join continues with the valid ones. The header shows the installed package version.
run_app()
Requires shiny, bslib, DT, leaflet, htmlwidgets, and sf.
The app keeps the roughly 200 MB Statistics Canada boundary download and its
extracted build inputs in a unique session directory and deletes them when the
session ends. Only the small derived simplified-map RDS can persist, and only
with your permission. There are three ways to grant it: set
options(OPCC.shiny_da_cache_dir = "/managed/path") before run_app(), set
the OPCC_SHINY_DA_CACHE_DIR environment variable, or answer the one-time
prompt shown when you launch the app in an interactive session. The R option
takes precedence over the environment variable, and both take precedence over
the prompt. Your answer to the prompt is remembered, so a recorded refusal is
honored until you delete the record, and clear_opcc_cache() removes the
cache itself. Without any of these the map artifact is session-only, which is
correct but means each new session re-downloads and re-simplifies it, taking
several minutes rather than about a second.
OPCC never places the raw archive or extraction in that managed directory. Shared-cache builds use an owner lock. A lock owned by a live process on the same host is never expired based on age; cross-host locks carry a build-phase heartbeat and are recoverable after two hours without progress. If a live builder outlasts a session's wait, that session falls back to its private raw cache rather than overwriting the shared artifact.
Rebuild every artifact from raw Statistics Canada sources using package
functions only (requires sf, dplyr, readr). Raw inputs are large, so the
build functions require an explicit directory that you control:
build_cache <- file.path(tempdir(), "opcc-build")
dir.create(build_cache, recursive = TRUE, showWarnings = FALSE)
nar_dir <- download_nar(cache_dir = build_cache)
geonames_txt <- download_geonames(cache_dir = build_cache)
bounds <- download_census_boundaries(cache_dir = build_cache)
gaf_csv <- download_gaf(cache_dir = build_cache)
# Every build step writes to a directory you name; there is no default.
centroids_csv <- build_centroids(nar_dir, geonames_txt,
output_dir = file.path(build_cache, "centroids"))
rollup_csv <- build_db_assignment(centroids_csv, bounds$province, bounds$db,
gaf_csv,
output_dir = file.path(build_cache, "rollup"))
m2_csv <- build_m2(nar_dir, bounds$db, gaf_csv, rollup_csv,
output_dir = file.path(build_cache, "m2"))
db <- utils::read.csv(m2_csv, stringsAsFactors = FALSE)
da <- aggregate_da_correspondence(db)
Each step messages its progress. See Part 2 of the vignette for detail.
M5 DA correspondence was benchmarked against an authorised PCCF-derived Ontario DA export: 99.46% any-link agreement, 95.65% best-link containment, 93.92% pair F1 across 280,649 comparable postal codes. See docs/validation-summary.md.
Agreement with that benchmark does not make OPCC authoritative, certified, or a drop-in replacement for Canada Post, PCCF, or PCCF+ products. The comparison is DA-level only, and the benchmark's vintage predates the OPCC release it was compared against. The evidence unit remains an observed postal association, and unmatched postal codes stay explicit rather than being filled in.
Contribute only open, redistributable evidence. See CONTRIBUTING.md, GOVERNANCE.md, SECURITY.md, and CITATION.cff.
To report a bug, open an issue with your OPCC and R versions, a minimal reproducible example, the expected and observed results, and any relevant release vintage or source identifier. Do not include Canada Post, PCCF, PCCF+, or other restricted data.
OPCC is not affiliated with Canada Post or Statistics Canada.