Select Page

Getting Locations Programmatically

4 minute read

ScaleUp! Community, previously we wrote about geolocation, geofences, how to acquire locations for your app and, most recently, defining the process of geocoding. This article will focus on getting locations (longitudes, latitudes) programmatically, using the Nominatim service, provided by OpenStreetMap.

Background

In our previous article on geocoding we discussed the process of compiling a list of names and / or addresses for places of interest for your app. Examples of these are: Hospitals; pharmacies; grocery stores; dialysis centers; clinical trial sites; etc. Once you have compiled your list of places of interest, you will want to code up some automation that will geocode each of the places by querying Nominatim. After getting the longitude and latitude from Nominatim, your automation should execute data quality tests, then transform the returned locations into a format that can be easily inputted into your app. 

Geocoding Search Parameters

There are 2 ways to geocode using Nominatim: 1. Freeform query; 2. Structured query. The root URL, which is https://nominatim.openstreetmap.org/search, is the same for both of these searches. However, the query parameters are different. 

For freeform searches, the query string takes the following form: q=<query>, where <query> can be something like “mass general hospital”. 

For structured searches, in the query string, you specify data elements by name. For example, street=<housenumber> <streetname> or city=<city>.

Structured searches are faster but you are more likely to find a match with freeform searches. For best results, run both structured and freeform searches. Your automation can use a waterfall approach to decide which geocoding actually flows into your app. For example, prefer structured results when available, otherwise use freeform results, otherwise report location which could not be found so that you can investigate. Complete information on the Nominatim searches can be found here: https://nominatim.org/release-docs/latest/api/Search/

Search Request Constants

The javascript below shows constant portions of the Nominatim search request:

// The base URL without search parameters
const osmSearchUrl = 'https://nominatim.openstreetmap.org/search?';

// Search parameter fragment specifying how the response data will be formatted
const osmSearchOutputFormat = '&amp;format=json&amp;limit=1';

Structured Search URL Construction

The javascript code below demonstrates how to assemble the full URLs for Nominatim structured searches:

// These are the structured search query parameters
const structuredUrlQueryParams = `amenity=${amenity}&amp;street=${address}&amp;city=${city}&amp;state=${state}&amp;postalcode=${zip}&amp;country=${country}${osmSearchOutputFormat}`;

// This is the full structured search URL
const structuredUrl = `${osmSearchUrl}${structuredUrlQueryParams}`;

Freeform Search by Street Address URL Construction

The javascript code below demonstrates how to assemble the full URLs for Nominatim freeform searches, where you search for a place by full street address:

// These are the freeform search query parameters
const freeformQueryParams = `q=${address},${city},${state},${zip},${country}${osmSearchOutputFormat}`;

// This is full freeform search URL
const freeformUrl = `${osmSearchUrl}${freeformQueryParams}`;

Freeform Search by Name URL Construction

The javascript code below demonstrates how to assemble the full URLs for Nominatim freeform searches, where you search for a place by name versus its street address:

// These are the freeform search by place name query parameters
const freeformQueryByNameParams = `q=${placeName},${city},${state},${zip},${country}${osmSearchOutputFormat}`;

// This is full freeform search URL
const freeformUrl = `${osmSearchUrl}${freeformQueryByNameParams}`;

Executing Geocoding HTTP Requests

The javascript function below can be used to call the Nominatim search URLs. Note that the function below calls sleepAsync before it makes the HTTP post request. This sleep is 1000 milliseconds long (1 second) and is important if postUrlAsync is called as you process all of your places of interest in a loop, because Nominatim’s terms of use state that you cannot make more than 1 request per second. The function below uses the Axios library for making HTTP requests. 

async function postUrlAsync(label, url) {

 await sleepAsync(1000);
 const response = await axios.post(url);
 const hasResponse = typeof response !== 'undefined' 
   && response !== null
   && response.status === 200
   && Array.isArray(response.data) 
   && response.data.length !== 0;

 if (hasResponse) {
   if (response.data.length > 1) {
     // This should be logged so you can investigate because the HTTP request returned
     // more than 1 response.
   }
   return {
     lat: response.data[0].lat,
     lng: response.data[0].lon,
   };
 }

 return null;
}

This is a helper function used by postUrlAsync:

module.exports.sleepAsync = (delay) => new Promise((resolve) => setTimeout(resolve, delay));

Wrapping Up

Starting from your list of places of interest, you can run the code snippets above in a node.js server to perform bulk geocoding. The runtime of the script will vary depending on how many places of interest you have – remembering that you will make 1 request per second. Once you have the longitude and latitude for each of your places of interest, they can be loaded into your application database. Then your mobile application can build location-dependent logic triggered by events for your places of interest.

Twitter

LinkedIn