universal-geocoder

Universal geocoding abstraction server-side and client-side with multiple built-in providers

Usage no npm install needed!

<script type="module">
  import universalGeocoder from 'https://cdn.skypack.dev/universal-geocoder';
</script>

README

Universal Geocoder Logo

Universal Geocoder

CI codecov

npm downloads

dependency-count minified-size tree-shaking types

Universal Geocoder is a universal JavaScript library for server-side and client-side geocoding applications with multiple built-in providers.

Need geocoding 🌍️ in your website or application? Don't want to be vendor-locked to a service? ✨️ Universal Geocoder ✨️ is for you!

Depending of the chosen provider, it can use geocoding, reverse geocoding or IP geolocation.

Universal Geocoder is a TypeScript fork of GeocoderJS, itself a port of the Geocoder PHP library.

This library is platform agnostic: it is available either server-side (Node) or client-side (browsers, React Native, Electron).

It aims to be compatible with a maximum of browsers (even old ones) and provides multiple ways to use it, from an old use (available in global environment, results from callbacks) to the most modern use (available as a module, async / await results).

Installation

Add the library to your project:

npm install --save universal-geocoder

⚠️ Warning: If you need to use this library in an environment not supporting the Promise API such as Internet Explorer, you must install an ES6 Promise compatible polyfill like es6-promise.

Usage

You can either use Universal Geocoder as a module (both CommonJS and ES module syntaxes are provided) or as a direct dependency.

As a module:

import UniversalGeocoder from "universal-geocoder";

const openStreetMapGeocoder = UniversalGeocoder.createGeocoder("openstreetmap");

// async / await syntax
(async () =>
  console.log(await openStreetMapGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC"))
)();

// callback syntax
openStreetMapGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC", (result) => {
  console.log(result);
});

For this example, the output will be something like this:

[
  NominatimGeocoded {
    coordinates: { latitude: 38.8976998, longitude: -77.03655348862276 },
    bounds: {
      latitudeSW: 38.8974898,
      longitudeSW: -77.0368542,
      latitudeNE: 38.897911,
      longitudeNE: -77.0362526
    },
    formattedAddress: undefined,
    streetNumber: '1600',
    streetName: 'Pennsylvania Avenue Northwest',
    subLocality: undefined,
    locality: 'Washington',
    postalCode: '20500',
    region: 'District of Columbia',
    adminLevels: [
      AdminLevel {
        level: 1,
        name: 'District of Columbia',
        code: undefined
      },
      AdminLevel {
        level: 2,
        name: 'Washington',
        code: undefined
      }
    ],
    country: 'United States of America',
    countryCode: 'us',
    timezone: undefined,
    displayName: 'White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States of America',
    osmId: 238241022,
    osmType: 'way',
    category: 'historic',
    type: 'castle',
    attribution: 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright'
  },
  // ... (other results)
]

If you want to use the library as a direct dependecy (for browsers only), copy dist/universal-geocoder.js or dist/universal-geocoder.min.js to your dependencies.

Universal Geocoder will be available in the global environment:

const openStreetMapGeocoder = UniversalGeocoder.createGeocoder("openstreetmap");

openStreetMapGeocoder.geodecode("44.915", "-93.21", (result) => {
  console.log(result);
});

For a more advanced usage, see the example below:

import UniversalGeocoder, { ReverseQuery } from "universal-geocoder";

const googleGeocoder = UniversalGeocoder.createGeocoder({
  provider: "googlemaps",
  apiKey: "YOUR_API_KEY",
  useSsl: true,
  useJsonp: false,
  // other specific provider options
});

(async () =>
  console.log(await googleGeocoder.geocode({
    text: "1600 Pennsylvania Ave, Washington, DC",
    locale: "FR",
    limit: 10,
    // other specific provider parameters
  }))
)();

const reverseQuery = ReverseQuery.create({
  coordinates: {
    latitude: "44.915",
    longitude: "-93.21",
  },
})
.withLocale("FR")
.withLimit(7);
(async () =>
  console.log(await googleGeocoder.geodecode(reverseQuery))
)();

Common Options (createGeocoder method)

  • useSsl: boolean to use the HTTPS API of the providers
  • useJsonp: boolean to use JSONP
  • apiKey: the API key to use for the provider

Common geocode parameters (GeocodeQuery object)

  • text: what is searched
  • ip: the IP searched
  • bounds (object with latitudeSW, longitudeSW, latitudeNE and longitudeNE keys): the bounds to use (either bias or filter the results)
  • locale: the locale to use for the query
  • limit: the maximum number of results to have

Common geodecode parameters (ReverseQuery object)

  • coordinates (object with latitude, longitude keys): the coordinates to search for
  • locale: the locale to use for the query
  • limit: the maximum number of results to have

Common Result Properties (Geocoded object)

The result of a query is a Geocoded object which maps the following common information:

  • Coordinates (object with latitute and longitude keys)
  • Bounds (object with latitudeSW, longitudeSW, latitudeNE, longitudeNE keys)
  • Formatted address
  • Address details: street number, street name, (sub) locality, postal code, region, administration levels, country (with its code)
  • Time zone

You can either use getter methods to retrieve them or use the toObject method to manipulate an object containing the properties.

Providers

Universal Geocoder comes with modules to integrate with various geocoding providers.

The following table summarizes the features of each:

Provider Codename Supports location geocoding? Supports reverse geocoding? Supports IP geolocation?
OpenStreetMap (Nominatim) openstreetmap or nominatim ✅️ yes ✅️ yes ❌️ no
OpenCage opencage ✅️ yes ✅️ yes ❌️ no
Google Maps (Geocoding API) google or googlemaps ✅️ yes ✅️ yes ❌️ no
LocationIQ locationiq ✅️ yes ✅️ yes ❌️ no
Mapbox mapbox ✅️ yes ✅️ yes ❌️ no
MapQuest mapquest ✅️ yes ✅️ yes ❌️ no
Bing bing ✅️ yes ✅️ yes ❌️ no
Yandex yandex ✅️ yes ✅️ yes ❌️ no
GeoPlugin geoplugin ❌️ no ❌️ no ✅️ yes

Specific Provider Usage

The documentation for specific provider options, parameters and results can be found here.

Special Providers

A chain provider is available: it iterates over multiple providers.

For more information, see its documentation.

Dumpers

Dumpers transform a Geocoded object to another format.

GeoJSON

GeoJSON is a format for encoding a variety of geographic data structures.

Usage

import UniversalGeocoder, { GeoJsonDumper } from "universal-geocoder";

const nominatimGeocoder = UniversalGeocoder.createGeocoder("nominatim");

(async () => {
  const result = await nominatimGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC");
  console.log(result);
  console.log("GeoJSON:", GeoJsonDumper.dump(result[0]));
})();

Building

npm run build

Testing

Unit and functional tests are handled by Jasmine. To run tests from the command line, use:

npm test

If you need to record new API calls, use:

npm run test-record

You can also check if the examples are running correctly.

For the Web:

npm run serve

Then go to http://localhost:8080/example/web, choose a provider and open the console.

For Node:

npm run ts-node -- example/node/provider.ts

FAQ

Q: When using Universal Geocoder client-side, how to make sure the API key is not stolen?

A: First of all, there are some providers that do not use an API key, like OpenStreetMap (Nominatim) or GeoPlugin.

If you want to use a provider with an API key, the best approach is generally to use Universal Geocoder in the server-side (Node) and to call it from the client-side. This way the API key is not exposed directly.

Some providers allow to add URL restrictions when generating a token, like Mapbox. Even if the token is visible, it mitigates its unwanted use since the Origin header cannot be changed in a browser environment. However, the stolen token would be still usable in a server environment (for instance with cURL).