Geocoding is the term used to describe the proccess of exchanging address into corresponding geographical data (coordinates).
If you’re looking for out-of-the-box geocoding services there are plenty of them. For example, you can consider the most popular one – Google Maps Geocoding API which is pretty good but it’s not really free to use.
There’s a different solution, especially if you’re using OpenStreetMaps-related maps engine. It’s called Nominatim and basically it’s the OSM searching engine with open web API.
In this article I’m going to cover basic Nominatim API integration with Angular 8 application which uses Leaflet library for interactive maps.
Before starting, it’s important to remark that I’m going to focus on the most important areas only. So if you’re looking for working example you can take a look at the GitHub repository I provided.
Okay, so let’s begin with basic Angular app initialization and map library (Leaflet) integration.
In order to do it, please take a look at this post it covers all the initial steps we will need to do.
The next step will be to prepare our app components and provide geocoding service integration (Nominatim).
Generating new components
This app will be rather simple. What we need is map with text input box, the results list and the form with map point name and coordinates.
We would need following components:
- MapComponent – which will contain Leaflet map, it will be the master component
- GeocodingComponent – which will handle geocoding input and API integration
- ResultsListComponent – which will handle results from geocoding API
- MapPointFormComponent – which will contain information about selected map point
We have already generated MapComponent during initial steps so let’s generate missing components with Angular CLI.
Define the output – MapPoint class
The end result of geocoding operation should be a MapPoint object.
Let’s create map-point.model.ts file in /src/app/shared/models directory.
As you can see we need point’s name with geocoordinates – latitude and longitude.
Starting with geocoding
Take a look at the response object for GET /search?format=json&q=bakery+in+london example query.
As you can see we’ve got plenty of data over here. For our MapPoint class we will need just lat (latitude), lon (longitude) and display_name fields.
So let’s write basic service which will responsible for communication with Nominatim API.
Create nominatim-service.ts file in /src/app/services directory.
As you can see I have delegated some of the constants into the separate file (app.constants.ts) in order to keep the service file clean.
Here’s an excerpt from constants file:
Apart from root URL for API I used default view box (which roughly covers Europe) in order to get the results only from this territory.
Please note that if you’d like Nominatim to respect this view box, you have to provide bounded=1 parameter in the path.
Anyway, let’s focus on the NominatimService code now.
As you can see, there’s nothing more than just basic HttpClient usage.
Another thing is that I didn’t want to use properties I receive straight from the API, so I provided NominatimResponse wrapper class with my own fields and transformed response objects into my own using map operator inside pipe function.
We will use them for displaying the results list.
Take a look at nominatim-response.model.ts (located in /src/app/shared/models)
It’s time to see our NominatimService in action. Don’t forget to place it in providers in AppModule (/src/app/app.module.ts).
Now, let’s move into GeocodingComponent (/src/app/geocoding/geocoding.component.ts).
First of all let’s define EventEmitter and declare searchResults array.
onSearch will later be used to emit the searchResults into ResultsListComponent.
If you’re not familiar with the concepts of @Output decorators and EventEmitters you can read about them on my blog.
We have to inject NominatimService in constructor as well. The purpose is to trigger the request sending when the string entered by user in the text input element will be longer than 3 characters.
The entire file will look like this:
The addressLookup function will be triggered in HTML template of this component.
As you can see, addressLookup function is being triggered here on keyup event. So it occurs every time when user presses and releases a key.
We also need (click)=”$event.stopPropagation()” which is a workaround to avoid Leaflet map clicking event propagation when we click on input element.
When it comes to styling, address-lookup CSS class contains just a single rule:
It’s just a small detail but it in my opinion it gives the user better experience.
Pass search results into map component
We will handle onSearch event emitted in GeocodingComponent a little bit later. Now, let’s go back to MapComponent. It’s time to provide results array there.
In order to set the value into this variable we will use this simple method called refreshSearchList.
Now we can use it MapComponent template file:
We would like to place the text field over the map, in the bottom left corner of it.
To achieve it we use leaflet-bottom and leaflet-left CSS classes provided out-of-the-box by Leaflet.
Preparing results list component
We won’t process the results data in MapComponent. We want to pass them into one of the child components – ResultsListComponent.
We used @Input decorator here and previously used @Output with EventsEmitter.
The former will be used for receiving data from external component (in this case from MapComponent), the latter will be used for events emitting when user selects the result object from the list.
HTML template for this component contains list that will be populated from results array. We will use selectResult method on click event.
Include ResultsListComponent selector into the map.component.html:
As you can see we passed results into ResultsListComponent and we want to trigger getAddress on locationSelected event.
Display selected object on the map
Let’s implement getAddress method along with a handful of other stuff which will help us render the marker on the map.
At this point you’ll be able to search for the object and display the chosen one on the map.
But in order to get complete picture we’ll provide the form for the MapPoint object.
Providing the form
MapPointForm code is pretty simple:
It receives the MapPoint object from the MapComponent.
We will display it in the template of this component:
Of course it isn’t a proper HTML form element but there is no need to use it.
It’s enough for purpose of this article.
Let’s put everything together.
Wrapping it up
Let’s see how we gonna use everything in MapComponent template.
Take a look at the template code:
And the final MapComponent code should look like this one:
As you can see there’s additional code for providing some improvements like default MapPoint, handling click event and marker or icon creation etc.
Basically it’s ready to go now.
See the map in action
Before running the project don’t forget to replace auto-generated welcome page content in app.component.html with app-map component selector.
Now you’re able to run Angular app.
Browse to http://localhost:4200 and feel free to use the map.
Now you’re able to use Nominatim API for geocoding with Leaflet map in your Angular app. Althought this geocoding service isn’t perfect, it should do the job in the most common use cases.
The source code used in this article can be found on GitHub.
For more detailed information please refer to the docs: