Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Reverse Geocoding Made Easy: Getting Location Details with the TomTom Android SDK

29 Jan 2019 1  
In this article, we’re going to explore the capabilities available within the new TomTom SDKs.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Geocodes -- which mean GPS coordinates -- are great if you’re a computer and can easily calculate exactly where they map to. If you’re a humble human being, however, working with geocodes is tough, because humans don’t think or talk about locations in terms of geocodes. Instead, we use street names, addresses and landmarks to determine where things are.

This is why reverse geocoding is such an important feature for bridging the gap between computers and humans. Reverse geocoding allows applications to convert geocodes into street other human-readable location information. Reverse geocoding thus makes it possible to use the precision of GPS and geocodes for tracking locations within an application, while still presenting information to human users in ways that humans can easily interpret.

The tricky thing about reverse geocoding, however, is that there has not traditionally been a simple way to leverage it within an application. Unlike, say, IP addresses, which you can easily translate into domain names using a DNS service, there is no standardized service for translating geocodes into human-readable location information.

Fortunately, TomTom has made things much easier for developers in this respect by adding reverse geocoding to its SDKs. The feature makes it quite easy to implement reverse geocoding into an app using simple API calls.

To provide a sense of how reverse geocoding works and how developers can easily take advantage of it with the help of TomTom’s SDKs, this article walks through a scenario involving the display of location data in an Android mobile app. We’ll start by discussing what you need to be able to use the SDK; then we’ll create our app to determine the location of the device, find out more information about the location using the TomTom SDK, and then display the results on the screen.

In this article, we’re going to explore the capabilities available within the new TomTom SDKs. The example we’ll be building in this article uses the TomTom SDK for Android. The same functionality is available with the Web and iOS SDKs. You can also use the TomTom APIs directly from your applications. You can follow links to the IOS and Web SDKs below.

The TomTom SDKs

The TomTom Developer Portal is where you’ll find everything you need to get up and running with the TomTom APIs. The first thing you’ll want to do is create an account on the portal. From the home page, enter your email address and click on the “Get a free API key” button.

Fig. 1 The TomTom Developer Portal

The next step in the process is to select a username and read through the terms and conditions. Your free account supports up to 2,500 free transactions per day. If 2,500 API transactions per day aren’t enough, you can purchase more transactions by visiting the My Credits screen in the developer dashboard.

An email contains a link to set the password for your account, and with that, you’ll be ready to go. You need to configure an application before you can request an API Key. From your dashboard, click on the + Add an App button.

Applications require a name, and you’ll need to enable the products which the application needs to access. For this example, the product we’ll be using is the Search API product. If you’re following along, select the Search API product and click on Create App.

Fig. 2 Creating a New Application

You can receive a unique API key by visiting the TomTom Developer Portal, and entering your email address for a “Free API Key.” You’ll need to read and agree to the terms and conditions and select a username. An email prompts you to set a password, and from there, you can navigate to your dashboard and create a new application.

Applications are approved quickly and appear on your dashboard. The entry shows the Consumer API Key and information about your application, including the approval status of the application, and each of the products it uses.

Fig. 3 Application Details, Including the API Key

We’ll need this API as we add the TomTom SDK to our project and use it to get detailed information about our location.

Setting Up Our Project

I used Android Studio to create this project and began with a blank activity screen. Opening up activity_main.xml, I added a button (btnGetInformation) to trigger the information request, and then a group of text fields below to display information about the location. We’ll include fields for:

  • The Street Address - txtStreet
  • City - txtCity
  • Zip Code - txtZipCode
  • State - txtState

Fig. 4 Layout for Our App

Now let’s open MainActivity.java, connect to each of these controls, and we can add an EventListener to the button, and connect to the device’s Location Manager.

public class MainActivity extends AppCompatActivity {

    private Button btnInformation;
    private TextView txtStreet;
    private TextView txtCity;
    private TextView txtState;
    private TextView txtZipCode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnInformation = findViewById(R.id.btnGetInformation);
        txtStreet = findViewById(R.id.txtStreet);
        txtCity = findViewById(R.id.txtCity);
        txtZipCode = findViewById(R.id.txtZipCode);
        txtState = findViewById(R.id.txtState);
    }
}
Fig. 5 Initializing the Controls for the Main Activity (MainActivity.java)

Capturing Location Information from the Device

We’re going to set our application to get location updates from the GPS Location Service on the device. Before we can do this, we need to add a uses-permission entry in the AndroidManifest.xml file.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Fig. 6 Adding Permissions for the Location Service

Next, we’ll add a LocationManager and a LocationListener to the MainActivity.java file.

private LocationManager locationManager;
private LocationListener locationListener;

Now, we’ll initialize them inside the onCreate method. The LocationListener has four methods we need to implement. We’ll just be implementing the onLocationChanged method later in this project.

locationManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras){
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }
};
Fig. 7 Implementing the LocationManager and LocationListener

Now we need to enable the LocationManager and ensure that the user has granted permissions for us to access the Location Service. We’ll check for permissions in the onCreate method, and we’ll also add the check to the overridden onRequestPermissionResult method. The command that we execute to start receiving updates is:

locationManager.requestLocationUpdates(
    LocationManager.GPS_PROVIDER, 
    5000, 
    1,  
    locationListener
);

The parameters specify:

  • The source of the location (GPS provider in our case)
  • The minimum time between updates (we selected 5000 milliseconds or 5 seconds)
  • The minimum distance between updates (we selected 1 meter)
  • The LocationListener object.

We’ll check for permissions in onCreate:

if (ActivityCompat.checkSelfPermission(
    this,
    Manifest.permission.ACCESS_FINE_LOCATION
    ) != PackageManager.PERMISSION_GRANTED
         && ActivityCompat.checkSelfPermission(
             this,
             Manifest.permission.ACCESS_COARSE_LOCATION
         ) != PackageManager.PERMISSION_GRANTED) {
             ActivityCompat.requestPermissions(
                 this,
                 new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                 1
             );
} else {
    locationManager.requestLocationUpdates(
        LocationManager.GPS_PROVIDER, 
        5000, 
        1, 
        locationListener
    );
}
Fig. 8 Checking Permissions in the onCreate Method

We’ll also add the permissions check to the onRequestPermissionsResult method.

@Override
public void onRequestPermissionsResult(
    int requestCode, 
    @NonNull String[] permissions, 
    @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(
            requestCode, 
            permissions, 
            grantResults
        );

        if (grantResults.length > 0 
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (ContextCompat.checkSelfPermission(
                this, 
                Manifest.permission.ACCESS_FINE_LOCATION) ==             
                PackageManager.PERMISSION_GRANTED) {                      
                    locationManager.requestLocationUpdates(
                         LocationManager.GPS_PROVIDER, 
                         5000, 
                         1, 
                         locationListener
                    ); 
                }
        }
}
Fig. 9 Checking Permissions in the onRequestPermissionsResult Method

So, now whether our user has already granted permissions for the app to use the location of the device, or they give permission when opening the app, the app can now access the location. Let’s add one more thing before we continue. We’ll initialize a variable called currentLocation of type Location, and we’ll set this in the onLocationChanged method of the LocationListener.

private Location currentLocation;

We’ll set currentLocation whenever the Location Service broadcasts a change.

@Override
public void onLocationChanged(Location location) {
    currentLocation = location;
}
Fig. 10 Setting the currentLocation property inside the LocationListener

Let’s add the TomTom SDK so we can take the current location and request more information about it to complete our app.

Adding TomTom Support

We need to add the TomTom SDK as a dependency of our project. The first step is to add the TomTom repository to our build.gradle file. We want the Gradle build file for the project, and we need to locate the allprojects object within the file. Add the TomTom repository to this object.

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url "https://maven.tomtom.com:8443/nexus/content/repositories/releases/"
        }
    }
}
Fig. 11 Adding the TomTom Repository

Now that we can access the repository, open the Gradle build file for the app, and add the following dependencies to the dependencies object.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.tomtom.online:sdk-search:2.+'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
Fig. 12 Adding the TomTom Search SDK

You may need to refresh the Gradle libraries for your project first, but these changes add TomTom search functionality to your project. The final step is to authorize API access with the API Key we created through the TomTom Developer Portal.

Open the AndroidManifest.xml file, and add the following meta-data object after the last activity object and before the application object is closed. Ensure that you replace ReplaceWithAPIKey with your API Key.

<meta-data
    android:name="OnlineSearch.Key"
    android:value="ReplaceWithAPIKey" />
Fig. 13 Adding the API Key for TomTom Search SDK

Reverse Geocoding the Location

The first thing we’re going to do is implement a couple of methods to configure the API and allow building a query. Add the following functions to the MainActivity.java file. We’ll pass in the Application Context, which is how the API is configured with our API Key.

protected SearchApi createSearchAPI() {
    SearchApi searchApi = OnlineSearchApi.create(getApplicationContext());
    return searchApi;
}
Fig. 14 Function to Initialize the SearchAPI

The next function constructs a search query for us. We accept the latitude and longitude values and return a query object.

protected ReverseGeocoderSearchQuery createReverseGeocoderQuery(
    double latitude, 
    double longitude) {
        return ReverseGeocoderSearchQueryBuilder
                .create(latitude, longitude)
                .build();
}
Fig. 15 Function to Build our ReverseGeocoderQuery Object

Finally, let’s build our reverseGeocode function, which configures the connection to the API, builds the query, and then handles the results.

protected void reverseGeocode(
    final double latitude, 
    final double longitude) {
        SearchApi searchAPI = createSearchAPI();
        ReverseGeocoderSearchQuery reverseGeocoderQuery =
            createReverseGeocoderQuery(latitude, longitude);
        searchAPI.reverseGeocoding(
            reverseGeocoderQuery, new RevGeoSearchResultListener() {
                @Override
                public void onSearchResult(ReverseGeocoderSearchResponse   response) {
                    List<ReverseGeocoderFullAddress> addresses =   
                        response.getAddresses();
                    if (addresses.size() > 0) {
                        Address address = addresses.get(0).getAddress();
                        txtStreet.setText(address.getStreetNumber() + ' ' + address.getStreetName());
                        txtCity.setText(address.getMunicipality());
                        txtState.setText(address.getCountrySubdivision());
                        txtZipCode.setText(address.getPostalCode());
                    }
                }

                @Override
                public void onSearchError(SearchError error) {
                    Log.d("Address: ",  getApplicationContext().getString(R.string.reverse_geocoding_error));
                }
        });
}
Fig. 15 Function to Build our ReverseGeocoderQuery Object

The last thing we need to do is connect the button to run the reverseGeocode function, using the currentLocation. Add the following to the end of the onCreate function.

btnGetInformation.setOnClickListener(v ->
    reverseGeocode(currentLocation.getLatitude(),
                   currentLocation.getLongitude()));
Fig. 16 Adding a Click Listener to the Button

Running the Application

You can run your app in an emulator, or install it on an Android device. I tested mine with a Pixel 2 Emulator running version 28 of the Android API. I manipulated the coordinates to 45.512046, -122.627833, and then clicked on the GET LOCATION INFORMATION button.

Fig. 17 The Completed App in Action

Additional Information

If you would like to learn more about options available within the TomTom APIs, you can access the online documentation or use the API Explorer to try different parameters and see how they all work together.

If you would like to download the code for this example, you can find it here. You’ll need to add your API key in the AndroidManifest file before you can run the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here