Android Geocoder class is used for reverse geocoding i.e. retrieving the address from the location on the Google Map.
If you aren’t aware of how to use Google Maps in your Android Application, refer android google maps tutorial before moving ahead.
Android Geocoder
Android Geocoder
class is used for Geocoding as well as Reverse Geocoding. Geocoding refers to transforming street address or any address into latitude and longitude. Reverse Geocoding refers to transforming latitude and longitude into its corresponding street address.
Address
class helps in fetching the street address, locality, sub-locality, city, country, landmark etc. features of the location.
Using the above two classes we’ll be fetching the current marker address on the Google Maps in our application. Let’s start with the android reverse geocoding example project.
Android Geocoder Reverse Geocoding Project Structure
We’ll need the google maps and google places api. So let’s add them in the build.gradle
file as shown below.
apply plugin: 'com.android.application'
allprojects {
repositories {
jcenter()
maven {
url "https://maven.google.com"
}
}
}
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.journaldev.reversegeocoding"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.0.1'
compile 'com.android.support:cardview-v7:26.0.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.google.android.gms:play-services-maps:11.0.4'
compile 'com.google.android.gms:play-services-places:11.0.4'
testCompile 'junit:junit:4.12'
}
Get the relevant API key from the Google Developer Console and add it inside the meta-data in the AndroidManifest.xml
file. If you aren’t aware of integrating Google APIs in your application, refer this tutorial before moving ahead.
Android Reverse Geocoding Code
The code for the activity_main.xml
layout is given below.
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:card_view="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
card_view:cardCornerRadius="0dp"
card_view:cardElevation="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<TextView
android:id="@+id/txtLocationAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:padding="16dp"
android:scrollHorizontally="true"
android:text="Current Marker Address" />
</RelativeLayout>
</android.support.v7.widget.CardView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/mapFragment"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="tag_map_fragment" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:id="@+id/centerMarker"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:src="@drawable/black_marker" />
</FrameLayout>
</FrameLayout>
</RelativeLayout>
Our layout contains a CardView, which holds a TextView that’ll eventually display the current address.
The marker is placed in an ImageView at the center of the screen.
The code for the MainActivity.java
class is given below.
package com.journaldev.reversegeocoding;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.CardView;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocomplete;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
TextView txtLocationAddress;
SupportMapFragment mapFragment;
GoogleMap map;
LatLng center;
CardView cardView;
private static final int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtLocationAddress = findViewById(R.id.txtLocationAddress);
txtLocationAddress.setEllipsize(TextUtils.TruncateAt.MARQUEE);
txtLocationAddress.setSingleLine(true);
txtLocationAddress.setMarqueeRepeatLimit(-1);
txtLocationAddress.setSelected(true);
cardView = findViewById(R.id.cardView);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
Intent intent =
new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
.build(MainActivity.this);
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException e) {
printToast("Google Play Service Repair");
} catch (GooglePlayServicesNotAvailableException e) {
printToast("Google Play Service Not Available");
}
}
});
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapFragment);
mapFragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
map.getUiSettings().setZoomControlsEnabled(true);
LatLng latLng = new LatLng(20.5937, 78.9629);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));
initCameraIdle();
}
});
}
private void initCameraIdle() {
map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
center = map.getCameraPosition().target;
getAddressFromLocation(center.latitude, center.longitude);
}
});
}
private void getAddressFromLocation(double latitude, double longitude) {
Geocoder geocoder = new Geocoder(this, Locale.ENGLISH);
try {
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);
if (addresses.size() > 0) {
Address fetchedAddress = addresses.get(0);
StringBuilder strAddress = new StringBuilder();
for (int i = 0; i < fetchedAddress.getMaxAddressLineIndex(); i++) {
strAddress.append(fetchedAddress.getAddressLine(i)).append(" ");
}
txtLocationAddress.setText(strAddress.toString());
} else {
txtLocationAddress.setText("Searching Current Address");
}
} catch (IOException e) {
e.printStackTrace();
printToast("Could not get address..!");
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Place place = PlaceAutocomplete.getPlace(this, data);
if (!place.getAddress().toString().contains(place.getName())) {
txtLocationAddress.setText(place.getName() + ", " + place.getAddress());
} else {
txtLocationAddress.setText(place.getAddress());
}
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(place.getLatLng(), 16);
map.animateCamera(cameraUpdate);
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
printToast("Error in retrieving place info");
}
}
}
private void printToast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
Let’s break down the above code and see what it does.
- We’ve set the attributes
setEllipize()
and setsetMarqueeRepeatLimit()
on the TextView programmatically since setting the same attributes in xml doesn’t scroll the text on all devices anymore. The above methods make the content of TextView scroll horizontally IF the content length is greater than the TextView width. - Passing -1 in
setMarqueeRepeatLimit()
makes it scroll forever. - The Google Map is displayed using the SupportMapFragment.
getMapAsync()
callback is assigned to the fragment. This callback gets triggered when the Google Play Services exists. - The
onMapReady()
method is the one that displays the Google Map and returns a non-null instance of theGoogleMap
class. We assign it to our instance variablemap
.
map.getUiSettings().setZoomControlsEnabled(true)
is used to set the zoom controls on the screen. map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16))
moves the map to the location specified in thelatLng
(Some location in India!). The camera zooms to the location in the center of the screen where the marker is placed.setOnCameraIdleListener
would listen for the movements/dragging on the map. When the movement ends the methodonCameraIdle()
gets triggered. This is where we retrieve the latitude and longitude of the center of map(because that is where the marker resides) and pass it into the methodgetAddressFromLocation()
which will eventually do the reverse geocoding.- The method
getFromLocation(double latitude, double longitude, int maxResults)
returns a List of Addresses for the current location. - Within the
Address
object, the methodgetAddressLine(int index)
returns a line of the address numbered by the given index or null if no address exists. - We append that address to a StringBuilder which is eventually displayed in the TextView.
getMaxAddressLineIndex()
returns the largest index currently in use to specify an address line.- There are several other important details of an address that can retrieved too. Some of them are listed below:
getThoroughfare()
: This is the street, which contains the delivery point. If it doesn’t exist null would be returned.getSubThoroughfare()
: When a thoroughfare name exists more than once within a town, subThoroughfare name is used for additional information.getLocality()
andgetSublocality()
: Returns the locality of the address, for example “Mountain View” and sub locality respectively if available or null.getFeatureName()
: Returns the nearest landmark name if available or null.getCountryName()
getCountryCode()
: Returns the country name and country code respectively.
- Clicking the
CardView
would launch the PlaceAutoComplete service from the Google Places API. - We use the
PlaceAutocomplete.IntentBuilder
to create Intent and pass the Mode as full screen. - The selected place is returned in the
onActivityResult()
which is eventually displayed in the TextView. The latitude and longitude of thePlace
are returned asplace.getLatLng()
which is eventually zoomed into the center of the screen.
The output of the android reverse geocoding application in action is given below.
This brings an end to android Geocoder example tutorial. You can download the final Android Reverse Geocoding Project from the link below and make sure to add your own API in the meta-data tag inside AndroidManifest.xml
file.
References : Geocoder, Address, Place Autocomplete.
This code:
for (int i = 0; i < fetchedAddress.getMaxAddressLineIndex(); i++) {
strAddress.append(fetchedAddress.getAddressLine(i)).append(" ");
}
needs to be corrected so that i goes all the way UP TO fetchedAddress.getMaxAddressLineIndex(). Otherwise, if only one address is found, the program will not add anything to the StringBuilder.
please give the API used in this code
How do I fetch the point of interest (POI) without having poiclicklistener on map sdk (like Uber) – reverse geocoding
Error in retrieving place info
I’m also getting the same error of “Error in retrieving place info” i have added the API key and Enable the place API too. still mi’m getting this error.
there is no api file in your program.
you wasted my time of 3 hrs
Please download the source code and follow the steps to generate your API key from the links mentioned in the tutorial.
Hello,
I receive the “Error in retreiving place info” – could not run search
api key is ready
Hello,
I receive the “Error in retreiving place info” – could not run search
“Please download the source code and follow the steps to generate your API key from the links mentioned in the tutorial.”
still getting “Error in retreiving place info”