Nominatim geocoder

Nominatim geocoder is a free, open-source address search engine that you can run on your own computer, even without an Internet connection! And this is one of few solutions which allows you to do high volume, batch geocoding for any address on our planet on budget.

Nominatim can do both forward and reverse geocoding and supports all countries, languages and address formats in the world. It is developed and maintained by the OpenStreetMap community and used as a default geocoding service for OSM data.

Nominatim pros and cons

Sounds good so far? Great!

But before you invest hours and days into getting Nominatim up and running, let’s have a quick overview of its strong and weak sides, and make sure it can be a good fit for your needs.

PROSCONS
Free! No license fees, and very few limitations (you need to adhere to ODBL license)You need to invest your time and resources to get it up and running
Based on rich OSM and Wikipedia data, collected and updated by millions of volunteers, companies, and organizations around the worldMost address records are not authoritative, coverage can be spotty and some data can be obsolete or even incorrect
Community project – good activity and number of articles and configuration instructions in InternetNo guarantees, no support, no SLA; incomplete and often outdated instructions
Open source, you can extend or fix any part of itSometimes code is your only documentation
Works on your servers, also offlineYou need (powerful) servers and somebody who can maintain them
Very configurable queriesYou need to know which options to use to get results you want

To be fair, all Nominatim weaknesses are common for any big, community-driven, open-source project. And we at Geoapify have extensive experience with Nominatim and will be happy to support you.

Geocoding addresses with Nominatim

Nominatim accepts geocoding requests on the /search endpoint, in one of two exclusive modes – free-form and structured search.

Free-form address search

Free-form address query requires “q” parameter, like

Nominatim free-form search results for "Chicago"
Nominatim free-form search results for /search?q=chicago

In that mode, Nominatim supports special phrases like “in”, “near”

Nominatim search with special phrase "restaurant in Chicago"
Nominatim search with special phrase /search?q=restaurant+in+chicago

Structured address search

Alternatively, you can use a structured search, where address defined by a combination of:

street=<number> <name>
city=<city>
county=<county>
state=<state>
postalcode=<code>
Nominatim structured country search
Nominatim structured country search with /search?country=us

Nominatim structured geocoding is not as useful as it could be, because it doesn’t guarantee that search will be performed with requested granularity and prevents Nominatim from applying some internal geocoding heuristics. It also less flexible and does not support Nominatim’s special phrases.

We can only recommend to use it as a workaround for scenarios where Nominatim cannot correctly parse the address.

Filtering by location

If you know that results should belong to a specific country or area, it is highly recommended to narrow down search results with “countrycodes=<comma-separated list of country codes>” and “viewbox=<lon1>,<lat1>,<lon2>,<lat2>” parameters.

Nominatim search with location filter
Nominatim search with location filter
/search?q=berlin&countrycodes=us&viewbox=-89.8,39.7,-90,39.8

By default “viewbox” parameter works as location bias and includes results both inside and outside of provided bounding box.

Adding “bounded=1” parameter instructs Nominatim to return only results included in viewbox.

Search by type and amenity

Nominatim has only very limited support for places search. In most cases, you will get better and more reliable results with other services, for instance, Geoapify Places API or Overpass. But for simple cases, Nominatim can be good enough.

There are three ways to perform a search by type and amenity in Nominatim:

  • Using special phrases like “in” or “near”,
    for example
    /search?q=pub+in+new+york
  • Using search bounded by the “viewbox” together with “amenity” parameter,
    for example, searching for pubs in Manhattan area
    /search?viewbox=-74.02631,40.73022,-73.96202,40.69769&bounded=1&amenity=pub
  • Using search bounded by the “viewbox” together with place type in square brackets,
    for example /search?q=[pub]&viewbox=-74.02631,40.73022,-73.96202,40.69769&bounded=1
Searching pubs in New York with Nominatim
Searching pubs in New York with Nominatim

You can find an extensive list of supported place types and amenities on OpenStreetMap wiki.

Reverse geocoding with Nominatim

Nominatim can find and return addresses corresponding to a given latitude and longitude coordinate. For that, you need to use /reverse endpoint together with “lat” and “lon” parameters.

Finding Statue of Liberty by its GPS coordinate with Nominatim
Finding Statue of Liberty by its GPS coordinate with Nominatim
/reverse?lon=-74.044548&lat=40.6892532&format=html

Results granularity

Nominatim currently does not support filtering by object type nor specific geocoding granularity like “city”, “administrative area” or “street”. But there are several workarounds.

First, Nominatim /search endpoint accepts the (undocumented) “featuretype” parameter with one of the following values: “country”, “state”, “city”, and “settlement” (anything below country and above the street level).

Then, reverse geocoding supports “zoom” parameter and returns the results of different granularity depending on its value:

ZOOMRESULT GRANULARITY
3country
5state
8county
10city
14suburb
16major streets
17major and minor streets
18building

In the case of forward geocoding, you will need to do the second call to reverse geocoding with previously looked up coordinates and desired zoom level.

Another alternative requires a call to /details endpoint together with addressdetails=1 parameter and “place_id” matching the id of the previously geocoded result. This way you get full hierarchy from given the place up to country level, including administrative and postal code boundaries.

Output formats

By default Nominatim returns a formatted HTML page, that is ready to be displayed in the browser. If you need a machine-readable response format, you will need to add the “format” parameter and set it to one of “xml”, “json”, “geojson” and “geocodejson” output formats.

We recommend using “geojson” as the only standard and widely supported format. The second favorite format is “geocodejson”, which is generally compatible with GeoJSON specification and in the case of Nominatim includes the most complete and standartized address structure.

Example geocoding output for “/search?q=berlin&format=geojson&limit=1”

{
    "type": "FeatureCollection",
    "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "place_id": 595794,
                "osm_type": "node",
                "osm_id": 240109189,
                "display_name": "Berlin, 10117, Deutschland",
                "place_rank": 15,
                "category": "place",
                "type": "city",
                "importance": 0.887539028249136,
                "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png"
            },
            "bbox": [
                13.2288599,
                52.3570365,
                13.5488599,
                52.6770365
            ],
            "geometry": {
                "type": "Point",
                "coordinates": [
                    13.3888599,
                    52.5170365
                ]
            }
        }
    ]
}

Geometry and additional details

Nominatim can lookup and return some additional information

addressdetails=1
extratags=1
namedetails=1

Geocoding example with additional fields
“/search?q=berlin&format=geojson&limit=1&addressdetails=1&extratags=1&namedetails=1”

{
    "type": "FeatureCollection",
    "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "place_id": 595794,
                "osm_type": "node",
                "osm_id": 240109189,
                "display_name": "Berlin, 10117, Deutschland",
                "place_rank": 15,
                "category": "place",
                "type": "city",
                "importance": 0.887539028249136,
                "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
                "address": {
                    "city": "Berlin",
                    "state": "Berlin",
                    "postcode": "10117",
                    "country": "Deutschland",
                    "country_code": "de"
                },
                "extratags": {
                    "email": "[email protected]",
                    "capital": "yes",
                    "website": "https://www.berlin.de/",
                    "wikidata": "Q64",
                    "wikipedia": "de:Berlin",
                    "population": "3531201",
                    "name:prefix": "Land und Kreisfreie Stadt"
                },
                "namedetails": {
                    "name": "Berlin",
                    "name:ab": "Берлин",
                    "name:af": "Berlyn",
                    "name:am": "በርሊን",
                    "name:an": "Berlín",
                    "name:ar": "برلين",
                    "name:short": "BE",
                    "name:nds-nl": "Berlien",
                    "old_name:vi": "Bá Linh",
                    "name:bat-smg": "Berlīns",
                    "name:cbk-zam": "Berlín",
                    "name:fiu-vro": "Berliin",
                    "name:roa-rup": "Berlin",
                    "name:roa-tara": "Berline",
                    "name:be-tarask": "Бэрлін"
                }
            },
            "bbox": [
                13.2288599,
                52.3570365,
                13.5488599,
                52.6770365
            ],
            "geometry": {
                "type": "Point",
                "coordinates": [
                    13.3888599,
                    52.5170365
                ]
            }
        }
    ]
}

It is also possible to return the original geometry of the results instead of the coordinate and bounding box. For that you need to specify one of the following parameters:

polygon_geojson=1
polygon_kml=1
polygon_svg=1
polygon_text=1

Multilanguage support

By default, Nominatim uses “Accept-Language” HTTP header to choose the preferred language of the result. This can be changed by “accept-language” parameter, which can be set to one or multiple (comma-separated) language codes – in order of preference. If both the HTTP header and parameter are missing, Nominatim will return each geocoding result using the native language of the place.

Installation and hardware requirements

General installation instructions are very well documented in Nominatim Administration Guide, and there is no point in repeating the same information here.

You should plan that initial production-ready setup and configuration will take at least 2 days and up to several weeks of effort, depending on your situation, requirements, and level of expertise. You also need to have some level of confidence with Linux, Apache web server, PHP and PostgreSQL database to follow the instructions.

Hardware requirements depend on desired coverage (full planet, country, region or city) and if you plan to apply daily or hourly updates to the Nominatim database. For planet import, we recommend a server with at least 8 cores, 32 GB RAM and 700 GB of fast SSD/NVMe storage. With that, you can expect initial data import to take approximately 2-3 days. For smaller regional datasets your hardware requirements will be proportionally lower, but as a bare minimum, you need at least 2 CPUs, 4 GB memory and 50 GB of fast storage.

A possible alternative for testing and small non-profit projects you can use the free community-supported Nominatim instance. It is funded by the OpenStreetMap foundation and normally has the most up to date Nominatim version and data. As a downside, there are no up-time or performance guarantees, and your requests may be blocked without any further notice if you generate than 1 request per second, or send repetitive, systematic or batch queries.

Again, it is normally acceptable for testing and very small projects, but and serious business usage is discouraged. Please check the OSM Nominatim Acceptable Use Policy for more details.

As an alternative, we at Geoapify can offer you a full range of Nominatim support – from business-friendly, managed Nominatim instances, to dedicated installations, as well as consultation and assistance if you need help with configuration.

So, should I use Nominatim for geocoding?

You should definitely consider Nominatim if you need to do high-volume or batch geocoding with thousands and millions of addresses. It also can be very useful for scenarios when you need to do geocoding in secure environments or offline.

Another benefit comes from the open data that Nominatim uses. If something is missing or wrong in underlying OSM or Wikipedia data, you or anyone else can easily add or correct that information. It is normally much more challenging to do that with any alternative services.

Another benefit comes from the open data that Nominatim uses. If something is missing or wrong in underlying OSM or Wikipedia data, you or anyone else can easily add or correct that information. It is normally much more challenging to do that with any alternative services.

But do not expect Nominatim to be as smart as e.g. Google Maps API, especially if you deal with manually entered addresses. On average Nominatim does great job and correctly geocodes around 70% of addresses, while best commercial services can geocode around 90%.

Also, full-text-search in Nominatim is somewhat limited. Elasticsearch based alternatives like Photon or Pelias can give better results if you need to lookup places by name instead of addresses.

And don’t forget about time and resources that need to be invested into configuring, running and maintaining Nominatim installations.

If that is too much for you and you just need smart geocoding that works well, we can recommend giving our Geocoding API a try. It has very liberal pricing, generous free tier that is eligible for business usage and combines a number of technologies and know-how to deliver the best results as fast as possible.

Nominatim vs Photon geocoder

Looking up a location given free-form address is one of the most frequent operations on geospatial data. Almost every business needs to recognize and locate customer addresses. At the same time, differences in languages, country-specific address and postcode formats, typos and ambiguous names make it very hard to do it right.

This is why one normally uses a geocoder – a specialized search engine that is designed to return the most relevant location for a given address string.

Feature comparison

Nominatim geocoder is a “de-facto” standard in the world of open source geocoding engines. It is developed and maintained by the OpenStreetMap community.

Photon is another popular open-source geocoder with the focus on search-as-you-type suggestions and typos correction. It is developed and maintained by Komoot.

Both geocoders are fine pieces of software, supporting both forward and reverse geocoding and primarily use the OpenStreetMap dataset for address lookups.

FeatureNominatimPhoton
Structured searchyesno
Search-as-you-typenoyes
Hardware requirementshighmedium
Typo toleranceaveragegood
Full-text searchnoyes
Filtering resultsbounding box
county code
bounding box
OSM tag name and value
Special phrases (in, near)yesno
Configurable results rangingnolocation bias
DatabasePostGISElasticsearch
Data sourcesOpenStreetMap
Wikipedia
US Tiger & Postcodes
UK Postcodes
OpenStreetMap
Response formatsGeoJSON
JSON
HTML
XML
GeoJSON
Returned datafull, including geometrybasic
User interfaceyesno

Conclusion

So, which geocoder is the best, Nominatim or Photon? As usual, the answer is – it depends.

Photon excels in search-as-you-type scenarios. It tolerates typos and spelling mistakes. Photon is good for a general, free-form text geocoding. It is relatively lightweight and not very difficult to maintain – setting up your own instance takes from few hours to few days.

Nominatim on another hand has superior structured address search and filtering capabilities. It supports a number of output formats and advanced filters. Nominatim works best in scenarios when you at least roughly know what you’re looking for and where. Nominatim comes with significant complexity, hardware requirements, and maintenance overhead. It may take days and weeks to configure your own functional Nominatim instance.

In both cases, you’ll need to have expertise and time to set up your own geocoder instance. Alternatively, you can use our managed Photon and Nominatim geocoder instances. We take care of all aspects – configuration, updates, maintenance, availability, and security. We also provide consulting and custom software development. In case you’re looking for high-volume batch geocoding or cannot use 3rd party services due to security or privacy regulations, we can also set up a dedicated geocoder instance for you.

Travel time map: TOP 10 use cases for businesses and individuals

Travel time is one of the most important criteria when you choose a location. That’s why a travel time map or isochrone is often used as the main analytics tool to answer spatial questions. In this article, we would like to show you some scenarios of how travel time maps can help to make a location decision.

Travel time map use cases for businesses

While Location Intelligence becomes one of the key tools of Business Intelligence travel time maps become more and more popular in the business world. Here are a few examples of the isochrones application which could solve business tasks.

Operations on travel time maps
Union and intersection of travel time maps

Choose a new office location

Relocation is always a big step for a company. It’s important not only to satisfy the needs of the existing employees but also choose an attractive location for the new employees.

You can build reachability maps with desired time for each of your current employees and check if there is an intersection between them. Moreover, with isochrones, you can filter statistics and check how many potential workers are living in the bounded area of a new office.

Check coverage of your network

You can build isochrones for each of your selling points and check their union to see the coverage of your network. Different transportation modes, for example, “by public transport”, “driving”, or “walking”, help you to see if there are empty spots on your coverage map.

Choose the best place for a new retail shop

Not only vacant spots but also potentially profitable locations could be found with Location Intelligence tools and isochrones. For example, you can see if your target auditory can reach a new location easily. Moreover, you can filter properties by isoline and check exact numbers for the location.

Estimate a place reachability

It’s always good to rank locations by reachability. How good is the public transport connection for a place? How big reachability area by car? Better connectivity will always provide more potential and perspectives for a location. In short, travel time maps are designed to solve this task as well.

Study opportunities and potential of a location

When you have statistical data, for example, demographic data, you can bound the data by travel time maps and get statistics by isochrones. So you can study opportunities and potential clients, who can reach the location within the desired amount of time.

Travel time map use cases for individuals

Not only businesses but also individuals can benefit using isochrones. Thus if you provide the service on your website, the website becomes more valuable and attractive for your customers.

Transit isochrone
30 min isochrone for public transport

Optimize commute time by choosing the right place to live

It’s know fact, that fewer time people spend commuting, happier they are. Isochrones can help you to optimize commute time for each family member to their working or study place. If you build the travel time maps and then calculate the intersection of them, you can see locations reachable from each point of interests.

Optimize commute time by choosing the right place to work

Moreover, the inverse problem also could be solved with isochrones. If you plan to change your working place, the travel time map could show you all the possible areas where a new working place could be.

Study amenities you can reach

Would you like to see on the map only amenities you can reach within the desired time? Sure, it would be great! Filtering amenities by an isochrone provides you the answer.

Amenities filtered by the travel time map
All dentists within 30 min by public transport

Choose the best place to stay in a new city

When you plan a trip to a city you’ve never visited before it always a challenge to book accommodation in a good location. Often you need a good transport connection to multiple points of interest. For example, to the airport and conference place. Intersection on travel time maps of each of the locations will show you the perfect place for accommodation.

Book a hotel within desired travel time from seaside or ski resort

Isolines will also help you to book a hotel within walking or driving distance from seaside or ski resort. By calculating union of beach entries or ski lifts reachability maps, you can see on a map where hotels with an acceptable walking or drive time are.

Geoapify provides Isoline API to build travel time maps

Isolines API allows you to build travel time maps for different traveling modes and all over the world. Our APIs work via HTTP Get requests and return GeoJSON object as a result. Moreover, we provide Geometry API which calculates intersections and unions of isochrones. So you can quickly and with ease create your own Location Intelligence solution or extend an existing one. Register and start building isochrones for free.

Map animation with Mapbox GL

What can be more eye-catching than smooth, interactive, three-dimensional map animation? Impress your visitors by displaying your map data in stunning 3D!

Previously we have compared OpenLayers and Leaflet map libraries. Both are great choices if you want to add interactive map to your website. They are free, battle-tested, extensible and supported by active communities.

But what if you need a very fast, animated, eye-catching 3D map? Both Leaflet API and OpenMapLayersAPI do not support 3D and free-form map rotation. In this case Mapbox GL map library can be a great option.

Mapbox GL JS is one of the most advanced JavaScript map rendering libraries when it comes to smooth interactive animation. Mapbox GL API is slightly harder to use and not compatible with existing Leaflet plugins and examples. But it is using hardware-accelerated WebGL technology to dynamically draw data with the speed and smoothness of a video game.

In this article I’ll show you how to animate and implement 3D map rotation around a selected point. The end result should look like this:

Map rotation animation in 3D with Mapbox GL

If you short on time and just looking for the complete HTML code, you can find it at the bottom of the page.

Setting up our 3D map

As first step, we need to add imports for Mapbox GL library and its CSS style sheet into the HEAD of our page

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.53.1/mapbox-gl.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.53.1/mapbox-gl.js"></script>

Once imported, we can define a <div> element which will host map visualization and add Javascript code which will initialize Mapbox GL to render our map in 3D with specified initial location, zoom, pitch and bearing. Please do not forget to put your real API key instead of “YOUR_API_KEY” placeholder in the map tiles URL.

<div id='map'></div>
<script>
var map = new mapboxgl.Map({
    container: 'map',
    style: {
        "version": 8,
        "sources": {
            "basemap": {
                "type": "raster",
                // map tile source 
                "tiles": [
                    "https://maps.geoapify.com/v1/tile/carto/{z}/{x}/{y}.png?api_key=YOUR_API_KEY"
                ],
                "tileSize": 256
            }
        },
        "layers": [{
            "id": "basemap",
            "type": "raster",
            "source": "basemap",
            "minzoom": 0,
            "maxzoom": 22
        }]
    },
    center: [-73.991462, 40.724637], // starting position
    zoom: 12, // starting zoom
    pitch: 60, // starting pitch in degrees
    bearing: 0, // starting bearing in degree
});

This should give us basic interactive 3D map. You should be able to move the map to a different locations, zoom in and out, and change view angle.

Interactive 3D map view of Manhattan, NY
Interactive 3D map view of Manhattan, NY

Adding map rotation animation

As next step, let’s add dynamic map animation, which will change view angle to create effect of flying around the map center. Please add the following code into our <script> block and refresh the page:

function rotateCamera(timestamp) {
    // rotate at approximately ~10 degrees per second
    map.rotateTo((timestamp / 100) % 360, {duration: 0});
    // request the next frame of the animation
    requestAnimationFrame(rotateCamera);
}

map.on('load', function () {
    // start the animation
    rotateCamera(0);
});

Final steps

As the last step, let’s add basic map navigation controls and basemap attribution.

// map navigation controls
map.addControl(new mapboxgl.NavigationControl());

// attribution
map.addControl(new mapboxgl.AttributionControl({
	compact: false,
	customAttribution: 'Powered by <a href="https://geoapify.com/">Geoapify</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}));
Complete map with controls and attribution
Complete map with controls and attribution

Complete HTML code

Final version of the HTML page could look like this. Please feel free to copy the code, insert API key and open it in your browser to see how it works.

<html>
<head>
    <meta charset='utf-8' />
    <title>Mapbox GL example: rotate map animation in 3D</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.53.1/mapbox-gl.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.53.1/mapbox-gl.js"></script>
    <style>
        body { margin:0; padding:0; }
        #map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
</head>
<body>

<div id='map'></div>
<script>
var map = new mapboxgl.Map({
    container: 'map',
    style: {
        "version": 8,
        "sources": {
            "basemap": {
                "type": "raster",
                // map tile source 
                "tiles": [
                    "https://maps.geoapify.com/v1/tile/carto/{z}/{x}/{y}.png?api_key=YOUR_API_KEY"
                ],
                "tileSize": 256
            }
        },
        "layers": [{
            "id": "basemap",
            "type": "raster",
            "source": "basemap",
            "minzoom": 0,
            "maxzoom": 22
        }]
    },
    center: [-73.991462, 40.724637], // starting position
    zoom: 12, // starting zoom
    pitch: 60, // starting pitch in degrees
    bearing: 0, // starting bearing in degree
});

function rotateCamera(timestamp) {
    // rotate at approximately ~10 degrees per second
    map.rotateTo((timestamp / 100) % 360, {duration: 0});
    // request the next frame of the animation
    requestAnimationFrame(rotateCamera);
}

map.on('load', function () {
    // start the animation
    rotateCamera(0);
});

// map navigation controls
map.addControl(new mapboxgl.NavigationControl());

// attribution
map.addControl(new mapboxgl.AttributionControl({
	compact: false,
	customAttribution: 'Powered by <a href="https://geoapify.com/">Geoapify</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}));
	
</script>

</body>
</html>

Summary

Mapbox GL generally requires more JavaScript code to be written than Leaflet and can be more complicated to maintain.

So, you may ask when to use Leaflet and when to use Mapbox GL?

The answer is simple – if you don’t need 3D and extremely fast animation then Leaflet would be the best choice. It has biggest community, best documentation, extensive set of plugins and works well in any situation. And if you really need advanced 3D maps with animations and top rendering speed on modern devices – then Mapbox GL is your friend.

Create a map with Location Intelligence components

Does it make sense to create a custom map for your website? Sure, a custom map brings a lot of benefits and advantages to your website. For instance, with your own map you are able to show the important locations to customers, highlight areas you want to pay attention to, and visualize data which could be potentially very interesting for both sites.

Geoapify helps you to create a map from the beginning and add Location Intelligence components into it. Read how to add a map to your website.

However, location visualizations are not only what people look for on a map. It’s also important to make the map interactive and “alive”. So the users can interact and “play” with it and make location decisions.

Make your custom map “alive”

But what does make a custom map “alive”? How to make the map interactive? The answer is simple: add components to your custom map, where a user can enter his data and analyze outcome.

This could be an input to search a location, popups opening on a click and giving some details or location analytics components like routing or isochrones.

Turn your map into Location Intelligence application

Geoapify offers APIs and map components which help to turn your map not just into the interactive map but into a Location Intelligence application of the full value.

Geocoding API to create a location input field

It’s always great to have a location input on your map. However, it’s forgotten by many map developers. The service which allows you to look for locations address details by search string is called Geocoding.

As a result, with Geoapify Geocoding API you are able to specify the search and retrieve accurate results. As well as implement autocomplete fields.

Location autocomplete input

Check our Geoacoding API in the Playground.

Reverce Geocoding API to get a location address by its coordinates

Another useful geospatial tool is Reverse Geocoding API. It returns a place address by its coordinates. With Reverse Geocoding API you are able to answer the question “What is located here?”, when a user clicks on the map.

Check our Reverse Geoacoding API in the Playground.

Routing API to build a path

Often people search locations on a map which are close to their route. Although route building is so popular, it’s present only on very several maps. By using Routing API together with Geocoding API, you can create a convenient routing tool for your map on your website.

Routing for your web site

Check our Routing API in the Playground.

Isolines API to analyse travel times

Can you show your customers what can the get from their living or working place? Sure! With Isolines API! By using the API you will be able to highlight areas on a map reachable within a given time. Together with driving and walking modes, our Isoline APIs work with transit and bicycle modes.

Isochrone for travel time analytics

Check our Isoline API in the Playground.

Places API to show amenities near by

Last but not least are amenities and points of interest. Show your customers places they interested in. Restaurants, fitness studios, schools, and other places.

Check our Places API in the Playground.

Use Geoapify APIs to create a map for your website

Read more about our APIs here.

Location autocomplete with Angular

Geoapify provides an API which allows searching a location by the query string. In this article, we provide you an example of how to create a location autocomplete field by using Geoapify Geocoding API.

In our example, we use Angular platform together with Angular Material framework.

Geoapify Geocoding API Playground contains a working example of the location autocomplete described in this article.

Pre-requirements

  • Angular Material installed. Read about installation here.

Step 1. Create a new component for a location autocomplete

Create a new component with Angular Cli command:

ng generate component Autocomplete

Step 2. Required imports

To make the component from this tutorial work, it’s required to import the following modules into your module:

@NgModule({
  declarations: [..., AutocompleteComponent],
  imports: [
    CommonModule,
    HttpClientModule,
    MatInputModule,
    MatFormFieldModule,
    FormsModule,
    ReactiveFormsModule,
    MatAutocompleteModule,
    MatTooltipModule,
    ...
  ],
  exports: [...]
})

Step 3. HTML template

We use the following components:

  • mat-form-input
  • mat-input
  • mat-autocomplete
  • mat-tooltip
  • formControl from ReactiveFormsModule.

Add the following code into your component html template:

<mat-form-field floatLabel="never">
  <input matInput type="text" [matAutocomplete]="auto" 
    [formControl]="inputFieldFormControl" placeholder="Enter location here" />

  <mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
    <mat-option *ngFor="let option of searchOptions | async" [value]="option.shortAddress" 
      (onSelectionChange)="optionSelectionChange(option, $event)"
      [matTooltip]="option.fullAddress" matTooltipShowDelay="1000">
      <span class="mat-body">{{ option.shortAddress }}</span>
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

Here is an idea of how the location autocomplete will work:

  • The string value will be stored in the “inputFieldFormControl” reactive form field.
  • The “inputFieldFormControl” field fires an event when its value was changed.
  • When an event fired we send an HTTP Get request to Geocoding API to retrieve place suggestions and store them in “searchOptions”.
  • mat-autocomplete loads options from the “searchOptions” asynchronously.

Step 4. AutocompleteComponent class

Here is the code of the AutocompleteComponent class:

import { Component, Output, EventEmitter, OnDestroy } from '@angular/core';
import { MatOptionSelectionChange } from '@angular/material';
import { Subject, Subscription } from 'rxjs';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-autocomplete',
  templateUrl: './autocomplete.component.html',
  styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnDestroy {
  @Output()
  locationChange: EventEmitter<PlaceSuggestion> = new EventEmitter<PlaceSuggestion>();

  searchOptions: Subject<PlaceSuggestion[]> = new Subject<PlaceSuggestion[]>();
  inputFieldFormControl: FormControl = new FormControl();

  private valueChangesSub: Subscription;
  private choosenOption: PlaceSuggestion;

  private userInputTimeout: number;
  private requestSub: Subscription;

  constructor(private http: HttpClient) {
    this.valueChangesSub = this.inputFieldFormControl.valueChanges.subscribe((value) => {
      if (this.userInputTimeout) {
        window.clearTimeout(this.userInputTimeout);
      }

      if (this.choosenOption && this.choosenOption.shortAddress === value) {
        this.searchOptions.next(null);
        return;
      }

      if (!value || value.length < 3) {
        // do not need suggestions until for less than 3 letters
        this.searchOptions.next(null);
        return;
      }

      this.userInputTimeout = window.setTimeout(() => {
        this.generateSuggestions(value);
      }, 300);
    });
  }

  ngOnDestroy() {
    this.valueChangesSub.unsubscribe();
  }

  private generateSuggestions(text: string) {
    const url = `https://api.geoapify.com/v1/geocode/api?q=${text}&limit=5&api_key=${YOUR_API_KEY}`;

    if (this.requestSub) {
      this.requestSub.unsubscribe();
    }

    this.requestSub = this.http.get(url).subscribe((data: GeoJSON.FeatureCollection) => {
      const placeSuggestions = data.features.map(feature => {
        const properties: GeocodingFeatureProperties = (feature.properties as GeocodingFeatureProperties);

        return {
          shortAddress: this.generateShortAddress(properties),
          fullAddress: this.generateFullAddress(properties),
          data: properties
        }
      });

      this.searchOptions.next(placeSuggestions.length ? placeSuggestions : null);
    }, err => {
      console.log(err);
    });
  }

  private generateShortAddress(properties: GeocodingFeatureProperties): string {
    let shortAddress = properties.name;

    if (!shortAddress && properties.street && properties.housenumber) {
      // name is not set for buildings
      shortAddress = `${properties.street} ${properties.housenumber}`;
    }

    shortAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    shortAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    shortAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';

    return shortAddress;
  }

  private generateFullAddress(properties: GeocodingFeatureProperties): string {
    let fullAddress = properties.name;
    fullAddress += properties.street ? `, ${properties.street}` : '';
    fullAddress += properties.housenumber ? ` ${properties.housenumber}` : '';
    fullAddress += (properties.postcode && properties.city) ? `, ${properties.postcode}-${properties.city}`: '';
    fullAddress += (!properties.postcode && properties.city && properties.city  !== properties.name) ? `, ${properties.city}`: '';
    fullAddress += properties.state ? `, ${properties.state}`: '';
    fullAddress += (properties.country && properties.country !== properties.name) ? `, ${properties.country}` : '';
    return fullAddress;
  }

  public optionSelectionChange(option: PlaceSuggestion, event: MatOptionSelectionChange) {
    if (event.isUserInput) {
      this.choosenOption = option;
      this.locationChange.emit(option);
    }
  }
}

export interface PlaceSuggestion {
  shortAddress: string;
  fullAddress: string;
  data: GeocodingFeatureProperties;
}

interface GeocodingFeatureProperties {
  name: string;
  country: string;
  state: string;
  postcode: string;
  city: string;
  street: string;
  housenumber: string;
}

inputFieldFormControl

As described above, the field holds the value of the search string, which is observed by this.inputFieldFormControl.valueChanges.subscribe().

To keep the code clean we save the created subscription in the variable valueChangesSub and unsubscribe on destroy.

userInputTimeout

To avoid too many unnecessary requests and decrease the application load, we perform HTTP request only when a user stops to type.

This implemented by using userInputTimeout, which sets every time when the user enters a new value.

searchOptions

Contain the values returned by Geocoding API and displayed by autocomplete control.

When we set this.searchOptions.next(null), the autocomplete control is hidden.

PlaceSuggestion & GeocodingFeatureProperties

We use the interfaces to simplify work with JSON object returned by the Geocoding API. We export PlaceSuggestion to be able to use the interface in other components, services, and modules.

locationChange

Is an Output() of the Autocomplete component. We this.locationChange.emit(option) when a new place suggestion was selected.

generateShortAddress() & generateFullAddress()

As Geocoding API returns value with address components, but not a formatted address, we need to generate an address string of required format. generateShortAddress() & generateFullAddress() are examples of how an address string could be generated.

Step 5. Add the Location Autocomplete component into your code

The new component could be added into your code in the following way:

<app-autocomplete (locationChange)="autocompleteChanged($event)"></app-autocomplete>

When a new value was chosen in the location autocomplete, the event is fired:

autocompleteChanged(value: PlaceSuggestion) {}