Skip to contents

One of this package’s seven data sources — one of the four online services. vignette("data-sources") is the overview and puts it in context.

Natural Resources Canada runs a national geolocation service on the Canadian Geospatial Platform, at https://geolocator.api.geo.ca/geolocation/en/locate. It takes one string, covers the whole country, needs no key, and is what nrcan_geocode() and the "nrcan" tier of geocode() talk to.

The service is open source, at Canadian-Geospatial-Platform/geoview-api-geolocator, which is unusual enough to be worth saying out loud: most of what this package knows about the other online geocoders is inferred from their answers, and here it was read from the code that produces them.

What this adds to the package

Reach beyond the local database, for the whole country. The offline tiers answer from NAR, Quebec’s register and the road network file; all three are snapshots, and an address newer than the release — or one no contributor ever filed — is not in any of them. The geolocator is queried live and is the only national source here that is.

What that is worth has been measured on the same 5,000 Corporations Canada filings the package measures everything on: the "nrcan" tier places 8.1% of what the offline tiers leave unplaced. That is real and it is small — for comparison the offline road network tier recovers 24.5% of the same residual — and the reason is not that the service is bad but that it is answering a different question. It matches one string against a national index; the addresses that reach it are the ones that failed against a national address file, and those are disproportionately addresses that do not exist in any index.

It is also the only tier available for a province that has neither a provincial geocoder nor a local import: outside BC and Quebec, this is the fallback.

Licence

Results are subject to NRCan’s terms and the Open Government Licence – Canada, an attribution licence like the Statistics Canada Open Licence covering NAR and the road network file. It composes with the rest of the package’s data without further conditions, unlike the ODbL data behind osm_geocode() — see vignette("source-osm").

Using it

nrcan_geocode() is the direct binding. It takes address strings and returns the same match_method / uncertainty_m contract the rest of the package uses:

nrcan_geocode("1 Rue Notre-Dame Ouest, Montreal, QC", retries = 8) |>
  select(match_method, uncertainty_m, n_matches, nrcan_qualifier, nrcan_title, lon, lat)
#>   match_method uncertainty_m n_matches       nrcan_qualifier
#> 1        nrcan           150         1 INTERPOLATED_POSITION
#>                                nrcan_title       lon     lat
#> 1 1 Rue Notre-Dame Ouest, Montréal, Quebec -73.55562 45.5062

As a tier it is opt-in, because it makes a network request per address. These three are addresses the offline tiers cannot place at all:

hard <- c("1435 Celebration Dr, Pickering, ON L1W 0C4",
          "1545 Maley Dr, Sudbury, ON P3A 4R7",
          "192037A TWP RD 665, Athabasca County, AB T0A 0M0")

geocode(hard, method = c("nar", "nar_interpolate", "nrcan")) |>
  select(input, match_method, n_matches, uncertainty_m)
#>                                              input match_method n_matches uncertainty_m
#> 1       1435 Celebration Dr, Pickering, ON L1W 0C4         none         0            NA
#> 2               1545 Maley Dr, Sudbury, ON P3A 4R7        nrcan         1           150
#> 3 192037A TWP RD 665, Athabasca County, AB T0A 0M0         none         0            NA

One of the three comes back, which is roughly the rate to expect: this tier is a thin recovery on a hard residual, not a second opinion on everything.

Re-run that and you may get zero. The service drops roughly one request in twelve as a clean HTTP 500 — nrcan_geocode() retries, and a request that fails the retries too returns none with nothing to distinguish it from an address the service does not hold. That is worth about 8 points of coverage, and earlier measurements of this tier silently charged it to the geolocator’s accuracy. The two direct nrcan_geocode() calls below pass retries = 8 rather than the default 3 for exactly that reason — over one or two addresses a drop is likely enough to change what the example shows.

What to watch out for

The service always answers

This is the single most important thing about it. Ask for an address that does not exist and you do not get an error, or an empty result — you get a confident position somewhere else:

nrcan_geocode(c("99999 Nowhere Rd, Nowhereville, SK",
                "28 Silver St, Corner Brook, NL"), retries = 8) |>
  select(match_method, nrcan_qualifier, nrcan_title, nrcan_reject)
#>   match_method       nrcan_qualifier                                              nrcan_title
#> 1         none INTERPOLATED_CENTROID                        Road To Nowhere, Iqaluit, Nunavut
#> 2         none INTERPOLATED_POSITION 28 Brook Street, Corner Brook, Newfoundland and Labrador
#>                                  nrcan_reject
#> 1 best result is Street/INTERPOLATED_CENTROID
#> 2                 street name SILVER != BROOK

Neither is a match, and neither would have been obvious from a coordinate. The first degraded to a street centroid — for a road in Iqaluit, Nunavut. The second is worse, because it looks completely normal: it silently substituted a different street in the right town.

nrcan_geocode() therefore does not trust a response. Every candidate has to clear two floors — it must be a Street result carrying INTERPOLATED_POSITION, and its returned title has to agree with the query component by component — and nrcan_reject says which floor a rejected address failed. The qualifiers matter because they were measured: INTERPOLATED_CENTROID means “found the street, not the civic number” and lands p50 499 m and p90 11 km from NAR’s point, and a Geoname answer is the service falling back to a populated place, at p50 133 km.

The ranking is not the signal it looks like

Results come back ranked, and the rank is close to useless as a measure of correctness. Asked for 1 Rue Notre-Dame Ouest, Montreal, QC the service ranks Lorrainville first and Montréal seventh; asked for 330 Spadina Rd, Toronto it ranks Spadina Avenue first and Spadina Road seventh. In both cases the correct answer is already in the response body.

So the whole list is scanned rather than just the first result, at no extra request, and the floor decides. A candidate at position 7 that passes is exactly as verified as one at position 1 that passes. When more than one candidate passes — the same street name in two municipalities, both satisfying containment — n_matches reports it rather than hiding it.

It loses about one request in twelve

Measured over 300 national addresses: 24 came back as a clean HTTP 500, and every one of the 24 succeeded on retry, 23 of them on the very first attempt with no delay. One query succeeded first and then failed three times in a row afterwards, which is what rules out the failure being a property of the query. Left unretried they were silently costing about 8% of recall — indistinguishable in the output from the service having no answer. nrcan_geocode() retries them.

uncertainty_m is 150, and it is a percentile on a long tail

Unlike the provincial geocoders’ figures, this one is measured: it is the 90th percentile of the distance between a surviving answer and NAR’s own building point, over the sample data-raw/probe_geolocator.R draws.

Do not read it as comparable to nar_blockface’s 176 m even though it is smaller. A blockface error is bounded by the length of a blockface. This one is a percentile on a long tail: p95 212 m, p99 648 m, worst survivor 2.7 km. Half the survivors land inside 33 m and a few land in the wrong part of town.

Where the measurements live

file.show(system.file("notes", "nrcan-geolocator.md", package = "cangeocode"))
file.show(system.file("notes", "geocoding-status.md", package = "cangeocode"))

nrcan-geolocator.md is what reading the service’s own source settled — what INTERPOLATED_POSITION certifies, which of this package’s checks turn out to be vacuous, the dead expand=component parameter that would have changed the design, and the aggregator endpoint the package deliberately does not use. data-raw/probe_geolocator.R reproduces the measurements.