Hands On

How to use Geocoding in Java with the HERE Geocoding & Search API

By Richard Süselbeck | 10 August 2020

40.70714,-74.01086

Do you know what these two numbers are? These are the latitude and longitude of the famous Wall Street in New York. Do you know how I was able to get these? I used the HERE Geocoding & Search API. Geocoding is the process of converting a street address into geographic coordinates like latitude and longitude. Let’s see how we can write a Java application to do this!

This blog post is about using the HERE Geocoding & Search API with Java. Specifically, we will be looking at forward Geocoding, which is the process of converting a street address into geographic coordinates (latitude and longitude). Let’s get started!

The HERE Geocoding & Search API

The HERE Geocoding & Search API is a REST API which returns a JSON response. For example, to geocode the street address “11 Wall St, New York, NY 10005” into geographic coordinates, we can use the following API call:


https://geocode.search.hereapi.com/v1/geocode
?apiKey=YOUR_API_KEY
&q=11 Wall St, New York, NY 10005

The response will then look as follows.


{
    "items": [
        {
            "title": "11 Wall St, New York, NY 10005-1916, United States",
            "id": "here:af:streetsection:-7Fh2DzbEeXIadSOteSAOB:CggIBCCEwoHNAhABGgIxMShk",
            "resultType": "houseNumber",
            "houseNumberType": "PA",
            "address": {
                "label": "11 Wall St, New York, NY 10005-1916, United States",
                "countryCode": "USA",
                "countryName": "United States",
                "state": "New York",
                "county": "New York",
                "city": "New York",
                "district": "Financial District",
                "street": "Wall St",
                "postalCode": "10005-1916",
                "houseNumber": "11"
            },
            "position": {
                "lat": 40.70714,
                "lng": -74.01086
            },
            "access": [
                {
                    "lat": 40.70721,
                    "lng": -74.01079
                }
            ],
            "mapView": {
                "west": -74.01205,
                "south": 40.70624,
                "east": -74.00967,
                "north": 40.70804
            },
            "scoring": {
                "queryScore": 1.0,
                "fieldScore": {
                    "state": 1.0,
                    "city": 1.0,
                    "streets": [
                        1.0
                    ],
                    "houseNumber": 1.0,
                    "postalCode": 1.0
                }
            }
        }
    ]
}

As we can see, it contains the position of the address as a geocoordinates and it provides details about the address itself, like the full postal code and district name.

Prerequisites

To use this API in Java, we need to be able make HTTP calls to the Geocoding & Search API as well as parse the JSON response. To call the API, we will be using the HttpClient, which has been available since Java 11. Unfortunately, Java does not have built-in JSON support, so we will be using an external library called Jackson to parse the response.

To include Jackson in our project, we add the following to the dependencies in our build.gradle file.


dependencies {
	implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.0'
}

If using Maven, we can include this dependency.



    com.fasterxml.jackson.core
    jackson-databind
    2.11.0

Calling the Geocoding & Search API (with HttpClient)

The following example creates a class Geocoder with a method GeocodeSync. This method synchronously calls the Geocoding & Search API using an HttpClient. (We will look at an asynchronous example below.)


import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Geocoder {

    private static final String GEOCODING_RESOURCE = "https://geocode.search.hereapi.com/v1/geocode";
    private static final String API_KEY = "YOUR_API_KEY";

    public String GeocodeSync(String query) throws IOException, InterruptedException {

        HttpClient httpClient = HttpClient.newHttpClient();

        String encodedQuery = URLEncoder.encode(query,"UTF-8");
        String requestUri = GEOCODING_RESOURCE + "?apiKey=" + API_KEY + "&q=" + encodedQuery;

        HttpRequest geocodingRequest = HttpRequest.newBuilder().GET().uri(URI.create(requestUri))
                .timeout(Duration.ofMillis(2000)).build();

        HttpResponse geocodingResponse = httpClient.send(geocodingRequest,
                HttpResponse.BodyHandlers.ofString());

        return geocodingResponse.body();
    }

}

Let’s walk through this code in detail. First, we need to create an HttpClient instance. You can use the class’s builder to set various options, but for us the default options are just fine. We can therefore use the default constructor HttpClient.newHttpClient().

Next, we need to create a URI to pass into an HttpRequest object. This URI is simply the REST API call from the first part of this blog post. It consists of a resource and various parameters. The minimum required parameters for a geocoding request are an apiKey (your credentials) and a query string q (the address you want to geocode). Note that there are additional parameters available, which you can check out in the API Reference.

If you’re wondering where to get credentials, you can sign-up for our Freemium plan, which gives you 250.000 API transactions for free every month. Yes, that means 250.000 geocodes (or other API calls) for free, not even a credit card required.

Note that the HttpClient does not provide a URI components builder, so we are concatenating the resource and parameters manually. Be sure to encode the query string correctly using URLEncode.

Next, we create an HttpRequest object, which we build using our URI and a timeout (we should always provide a timeout, otherwise the request may wait forever). Note that a request is immutable once created and can be sent multiple times.

We then pass our HttpRequest to the HttpClient and send() it. Using the appropriate BodyHandler we tell the HttpRequest to handle the response as a string.

Finally, the .body() function of the HttpResponse instance will return the JSON response as a string. (Note that we are omitting error handling here but be sure to check geocodingResponse.statusCode() and handle any non-200 responses accordingly).

Parsing the API Response

In the following example we are using Jackson to parse the API response and log both the geocoordinates and the full address from the response.


import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class GeocodingExample {

    public static void main(String[] args) throws IOException, InterruptedException {

        ObjectMapper mapper = new ObjectMapper();
        Geocoder geocoder = new Geocoder();

        String response = geocoder.GeocodeSync("11 Wall St, New York, NY 10005");
        JsonNode responseJsonNode = mapper.readTree(response);

        JsonNode items = responseJsonNode.get("items");

        for (JsonNode item : items) {
            JsonNode address = item.get("address");
            String label = address.get("label").asText();
            JsonNode position = item.get("position");

            String lat = position.get("lat").asText();
            String lng = position.get("lng").asText();
            System.out.println(label + " is located at " + lat + "," + lng + ".");
        }
    }
}

Once again, let’s look at this in more detail. Jackson provides a class called ObjectMapper which allows us to deserialize our JSON String into Java objects. We will use the method readTree() to convert our response into a tree structure of JsonNode objects. This tree will reflect the structure of our original JSON response. Using the method get() on a JsonNode instance allows us to get the value for a given key.

Let’s look back at the example from the beginning of the blog post to understand how a response looks like. You will notice that the API returns not just one result, but a whole list of items. This is because the HERE Geocoding & Search API is capable of geocoding even incomplete or misspelled queries. These queries can be ambiguous, which means the API will return multiple matching results. (Try geocoding “11 Wall St, New York”, without the postal code to see an example of this.)

We therefore need to iterate over all items in the response. Each item then contains an address and a position entry. From these we grab the label and lat, lng values, respectively. If we did everything correctly, our code should now log the following to the console.


> Task :GeocodingExample.main()
11 Wall St, New York, NY 10005-1916, United States is located at 40.70714,-74.01086.

That’s it! We’ve successfully geocoded an address in Java.!

Calling the Geocoding API Asynchronously

In many cases you may not wish to call the API synchronously.[PS5] The example below achieves the same result as above but calls the API asynchronously. The main difference is that the GeocodeAsync function returns a CompletableFuture on which we can call join() to block and get the response.


import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;

public class Geocoder {
    
    private static final String GEOCODING_RESOURCE = "https://geocode.search.hereapi.com/v1/geocode";
    private static final String API_KEY = "YOUR_API_KEY";

    public CompletableFuture<httpresponse> GeocodeAsync(String query) throws IOException, InterruptedException {

        HttpClient httpClient = HttpClient.newHttpClient();

        String encodedQuery = URLEncoder.encode(query,"UTF-8");
        String REQUEST_URI = GEOCODING_RESOURCE + "?apiKey=" + API_KEY + "&q=" + encodedQuery;

        HttpRequest geocodingRequest = HttpRequest.newBuilder().GET().uri(URI.create(REQUEST_URI))
                .timeout(Duration.ofMillis(500)).build();

        CompletableFuture<httpresponse> response = httpClient.sendAsync(geocodingRequest, HttpResponse.BodyHandlers.ofString());
        return response;
    }

}
</httpresponse</httpresponse

import java.io.IOException;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class GeocodingExample {

    public static void main(String[] args) throws IOException, InterruptedException {

        ObjectMapper mapper = new ObjectMapper();
        Geocoder geocoder = new Geocoder();

        CompletableFuture<httpresponse> futureResponse = geocoder.GeocodeAsync("11 Wall St, New York, NY 10005");
        String response = futureResponse.join().body();
        JsonNode responseJsonNode = mapper.readTree(response);

        JsonNode items = responseJsonNode.get("items");

        for (JsonNode item : items) {
            JsonNode address = item.get("address");
            String label = address.get("label").asText();
            JsonNode position = item.get("position");

            String lat = position.get("lat").asText();
            String lng = position.get("lng").asText();
            System.out.println(label + " is located at " + lat + "," + lng + ".");
        }
    }
}
</httpresponse

Please let us know in the comments if this was helpful to you and if you’d like more Java-specific content. Happy Coding