Article

A small R function estimates a signpost location from city distances

The primary source introduces an R function that geocodes city names and estimates the current signpost location from distances to multiple cities. With the constraints of Nominatim, geosphere, and maps in view, the playful example becomes a useful geospatial data exercise.

Share

Koharu's reading tip

The idea is playful, but API usage limits, spherical distance approximation, and older city data are practical checks. Those three points matter if you adapt it beyond a toy example.

Koharu's reading tip

A small R function estimates a signpost location from city distances

The R-bloggers source, “Little useless-useful R functions – Honest geographical location signposts,” looks at a playful question: whether a tourist signpost that lists distances to famous cities really matches the place where it stands. The source introduces sign_location_finder(), an R function that geocodes city names and searches for a point whose distances to those cities are close to the signpost values.

The function uses OpenStreetMap Nominatim for geocoding and geosphere::distHaversine() for distance calculations. In the source example, distances from Koper, Celje, Maribor, and Kranj are used with the expectation that the result should be Ljubljana, Slovenia. The premise is light, but the workflow is a useful miniature exercise in geospatial data processing.

koharu tone="tip" portrait="characters/deformed/confident-smile.webp" Even a playful function can teach a lot when you separate the parts. Look at the API, the distance formula, and the age of the supporting dataset.

Nominatim Search API turns city names into latitude and longitude

The source’s geocode_city() queries https://nominatim.openstreetmap.org/search with q, format=json, and limit=1. The official Nominatim Search API supports looking up locations from text or addresses, including free-form search through q=<query>, and limit controls how many results are returned. The details are in the Nominatim 5.3.2 Search API manual.

The operational constraint is important. The OpenStreetMap Foundation Nominatim Usage Policy sets an absolute maximum of one request per second for the public API and requires a valid HTTP Referer or User-Agent that identifies the application. The source code sets user_agent("PointingSignFinder/1.0 (R script)") and sleeps for 1.1 seconds, so it is clearly written with that policy in mind.

The same policy strongly limits bulk or repeated geocoding. The source works well as a small local experiment, but adapting the idea for a service or scheduled batch would require caching, a proxy, a self-hosted Nominatim instance, or another provider.

geosphere Haversine distances are readable but approximate

For each candidate location, the function evaluates the distance to every city with geosphere::distHaversine() and compares it with the signpost distance. The geosphere reference manual describes distHaversine() as a great-circle distance function that takes longitude/latitude inputs, uses a default Earth radius of 6378137 meters, and returns distances in meters by default. See the geosphere reference manual for the function details.

The key caveat is that the Haversine method treats Earth as a sphere and ignores ellipsoidal effects. The same manual points to distVincentyEllipsoid() when greater accuracy is needed. For a signpost whose values may be rounded, approximate, or based on “air distance” rather than road distance, the source’s tolerance-based approach is a reasonable design choice for a playful search.

Grid search makes the algorithm easy to inspect while trading off precision

The source first creates a broad search box around the target cities, scores a coarse latitude/longitude grid, then refines around the best coarse point. For each candidate point, it computes the distance error to each city and uses the maximum error as the score.

This is not presented as an advanced optimizer. Its value is that the algorithm is easy to read and the intermediate steps are visible. The tradeoff is controlled by coarse_res, fine_res, and tolerance: smaller grid steps cost more computation and produce finer candidate positions.

The CRAN description of sf says the package supports standardized handling of spatial vector data and uses s2 by default for geometry operations on geodetic longitude/latitude coordinates. If this example becomes a broader geospatial workflow, the sf reference is a natural next place to review coordinate reference systems and geometry behavior.

maps::world.cities is a helper dataset for nearby-city context

After estimating the location, the function looks up nearby cities from maps::world.cities. The CRAN world.cities documentation describes the dataset as primarily containing cities with populations above about 40,000, plus capital cities and many smaller towns.

The same documentation also says the population values are approximate as of January 2006, some names may be out of date, and there are no recent updates available from the original source. That makes world.cities useful for rough nearby-city context, but not for current population or administrative-name accuracy.

Small experiments should separate geocoding, distance assumptions, and API etiquette

If you run this function locally, first check whether each city name resolves to the intended place. limit=1 keeps the code compact, but ambiguous city names or duplicate place names can return a different top candidate. Including a country name and inspecting display_name are practical safeguards.

Next, separate straight-line distances from road distances. The source’s Ljubljana example uses air-distance values. Road-sign values may follow driving routes, so mixing them with Haversine straight-line distance can shift the estimated point.

Finally, keep the public Nominatim API limits in mind. For a few city lookups, this is a small experiment. For repeated use, the geocoding layer, not the local grid search, becomes the main operational constraint.

Source

Share

Related Articles

These articles share nearby categories or tags, so you can keep reading along the same thread.