Universal Geocoder
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 providersuseJsonp
: boolean to use JSONPapiKey
: the API key to use for the provider
Common geocode
parameters (GeocodeQuery
object)
text
: what is searchedip
: the IP searchedbounds
(object withlatitudeSW
,longitudeSW
,latitudeNE
andlongitudeNE
keys): the bounds to use (either bias or filter the results)locale
: the locale to use for the querylimit
: the maximum number of results to have
Common geodecode
parameters (ReverseQuery
object)
coordinates
(object withlatitude
,longitude
keys): the coordinates to search forlocale
: the locale to use for the querylimit
: 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
andlongitude
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).