Blog | Tristan Kernan

“That some of us should venture to embark on a synthesis of facts and theories, albeit with second-hand and incomplete knowledge of some of them – and at the risk of making fools of ourselves” (Erwin Schrödinger)

OSM Ecosystem

I've been working on a few projects leveraging the incredible open ecosystem provided by the Open Street Maps network - I want to document a few, because the landscape is quite confusing to a newcomer.

Geocoding

Geocoding is the process of converting raw, text addresses (e.g. 1600 Pennsylvania Ave.) to lat/lng geo-coordinates, which are necessary for geospatial applications like viewing on a map, calculating distances, and so on. Nominatim is built on top of OSM data, and provides a free API (at a low rps limit).

To call the public API from JS:

JavaScript
let nominatimUrl = 'https://nominatim.openstreetmap.org/search?addressdetails=1&format=jsonv2&limit=1';
nominatimUrl += '&q=' + encodeURIComponent('1600 Pennsylvania Ave');

const addressSearchResults = await fetch(nominatimUrl).then(res => res.json());

This will return a payload containing geo-coordinates, and other interesting data points sourced from OSM.

Point of interest search

Points of interest include parks, businesses, museums, libraries, etc. - all the various land uses one finds around the world. OSM contains a rich dataset of crowdsourced data on points of interest. That dataset can be queried by Overpass, via its API. Overpass is interfaced with a complex query language (QL), described in the wiki as a "procedural, imperative programming language written with a C style syntax."

If you are still with me, then take solace that simple queries in Overpass QL are thankfully simple to write. As an example, Finding all cafes in a given bounding box:

Text Only
[out:json][timeout:25];
// gather results
node({{bbox}})["amenity"="cafe"];
// print results
out geom;

Where {{bbox}} is a comma separated list of the form: southernmost latitude, westernmost longitude, northernmost latitude, and easternmost longitude. OSM tags include multiple amenity types, including cinemas, bars, libraries, etc.

Making a request against the API took some figuring out; it seems the approach is to make a POST request against the interpreter endpoint, having encoded the query into the payload with a body= prefix, as so:

JavaScript
const overpassUrl = 'https://overpass-api.de/api/interpreter';

const overpassQuery = `
[out:json][timeout:25];
// gather results
node(${overpassBoundingBox})["amenity"="cafe"];
// print results
out geom;
`;

const overpassResults = await fetch(overpassUrl, {
    method: 'POST',
    body: 'data=' + encodeURIComponent(overpassQuery),
}).then(res => res.json());

This will result in potentially a lot of elements returned; each element includes geo-coordinates and a set of tags, which usually (but is not guaranteed to) includes name, address components, etc. Note that the overpass-api.de service is intermittently flaky; there are other free public hosts listed in the project wiki.

Routing

The traveling salesman problem asks the question, given N cities to visit, what's the optimal order to visit them in, so as to minimize drive time or distance? Generalized, the vehicle routing problem posits multiple vehicles driving multiple routes, looking to visit all locations in the optimal order. To solve these problems, it's necessary to provide driving distance or duration matrices. OSRM and Valhalla are two (of many) routing-based engines that operate on top of OSM route data (think streets, highways, etc.), providing the distance matrices necessary to solve the vehicle routing problem.

To generate the distance matrix via OSRM:

Bash
curl "https://router.project-osrm.org/table/v1/driving/-73.9855,40.7580;-74.0134,40.7127;-73.9718,40.7678;-73.9614,40.7143"

Which can then be provided to VRP solvers, e.g. vroom, or-tools, pyvrp, etc.