Open In App

Reverse Geocoding in Android

Improve
Improve
Like Article
Like
Save
Share
Report

Reverse-Geocoding is a process used to convert coordinates (latitude and longitude) to human-readable addresses. This is not exactly the opposite of Geocoding. In Geocoding, the Place is associated with a name and fixed coordinates. These coordinates are Double in nature. Negligible change in these coordinates may still refer to the same place, but we shall never get the place name as it is associated with only those fixed coordinates. Therefore, we shall definitely get the complete address in reverse geocoding, but the place name is not guaranteed. Through this article, we will show you an example of how to perform reverse-geocoding in Android. But before moving ahead, please refer to the below articles:

  1. Google Cloud Platform – Creating Google Cloud Console Account & Projects
  2. Generating API Keys For Using Any Google APIs
  3. How to Hide API and Secret Keys in Android Studio?
  4. How to Implement Google’s Places AutocompleteBar in Android?
  5. How to Implement Current Location Button Feature in Google Maps in Android?

Project Structure

For reverse-geocoding, we will need latitude and longitude in Double data type. So we will implement a Google Map and consider its center as our primary latitude and longitude. We can easily get the center coordinates using the camera position (refer to main code). Whenever the Map is dragged, the center will change and cause a change in latitude and longitude. Once the Map is idle, i.e. it stops being dragged or moving, the reverse geocoding algorithm will consider the center coordinates and process them to get a complete address. This will then be instantly posted to a textView. This will confirm that reverse-geocoding works fine in the code. Follow the below steps to create this project.

What we are going to build in this article? 

Step 1: Create a New Project in Android Studio

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. We demonstrated the application in Kotlin, so make sure you select Kotlin as the primary language while creating a New Project.

Step 2: Get and hide API Key

  1. Our application utilizes Google’s Places API, so we need to get the Places API key from Google. To get an API key, please refer to Generating API Keys For Using Any Google APIs.
  2. Hiding an API key is essential and to do so, please refer to How to Hide API and Secret Keys in Android Studio?.

Step 3: Add these dependencies

This dependency will be required for reverse geocoding.

Kotlin




dependencies {
    implementation 'com.google.android.libraries.places:places:2.4.0'
}


Step 4: Adding a Map fragment, a custom location marker, and a text view in the layout (activity_main.xml) file

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <!--Map Fragment-->
    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/map"
        tools:context=".MapsActivity"
        android:name="com.google.android.gms.maps.SupportMapFragment" />
  
    <!--This layout overlays the Map Fragment 
        which matches parent width and height-->
    <!--We want to display our TextView over 
        the Map with good aesthetics-->
    <LinearLayout
        android:layout_margin="20sp"
        android:id="@+id/ll1"
        android:layout_width="match_parent"
        android:layout_height="100sp"
        android:background="@drawable/shape"
        android:layout_alignBottom="@id/map"
        android:orientation="horizontal">
  
          <!--TextView for displaying Lat and
             Lng along with Address-->
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10sp"
            />
  
    </LinearLayout>
  
      <!--This is only for reference to the center 
        of the screen, can be any element-->
      <!--We have set fixed this element at the parent
         center, which is the actual centre of the screen-->
    <Button
        android:id="@+id/centerReferencePoint"
        android:layout_width="0sp"
        android:layout_height="0sp"
        android:layout_centerInParent="true"/>
  
      <!--This image is the Marker-->
    <ImageView
        android:id="@+id/marker"
        android:layout_width="30sp"
        android:layout_height="40sp"
        android:src="@drawable/marker"
        android:layout_centerInParent="true"
        android:layout_above="@id/centerReferencePoint"
        />
  
</RelativeLayout>


Marker:

We downloaded this image in PNG format from the internet. It has no background color and can be referred to as a transparent image. Once downloaded, you can directly copy it from wherever it is stored, open Android Studio, and paste it into the drawable folder present in the res folder. While doing this, we renamed it as “marker” which you may find in the ImageView attributes in the activity_main.xml.

Shape.xml file (Background of the Linear Layout in activity_main.xml)

We have set a white background and corner radius with some value. This is to make the layout look better.

XML




<shape
    android:shape="rectangle"   >
  
    <solid
        android:color="#ffffff" >
    </solid>
  
    <corners
        android:radius="11dp"   >
    </corners>
  
</shape>


Preview of Shape.xml

Preview of activity_main.xml:

activity_main.xml preview

Step 5: Working on the backend (MainActivity.kt)

We take the coordinates of the place present at the center of the screen and convert them into text addresses. Once the screen is dragged, the center coordinates change and the address change respectively. Changes take place once the screen is idle and not in motion and that’s why we implemented our reverse-geocoding algorithm in the setOnCameraIdleListener. The below code is quite easy to understand. We have provided some comments to help you understand better.

Kotlin




package org.geeksforgeeks.reversegeocoding
  
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.location.Address
import android.location.Geocoder
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.libraries.places.api.Places
import java.io.IOException
import java.util.*
  
  
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
  
    private lateinit var mMap: GoogleMap
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // Fetching API_KEY which we wrapped
        val ai: ApplicationInfo = applicationContext.packageManager.getApplicationInfo(applicationContext.packageName, PackageManager.GET_META_DATA)
        val value = ai.metaData["com.google.android.geo.API_KEY"]
        val apiKey = value.toString()
  
        // Initializing the Places API
          // with the help of our API_KEY
        if (!Places.isInitialized()) {
            Places.initialize(applicationContext, apiKey)
        }
  
        // Initializing map
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)
  
    }
  
    override fun onMapReady(p0: GoogleMap) {
        
        mMap = p0
        
          // These are GeeksforGeeks Noida Office Coordinates.
        val india = LatLng(28.5021359, 77.4054901)
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(india,17F))
  
        mMap.setOnCameraIdleListener {
            val lat = mMap.cameraPosition.target.latitude
            val lng = mMap.cameraPosition.target.longitude
            val addressTV = findViewById<TextView>(R.id.tv)
  
            // Initializing Geocoder
            val mGeocoder = Geocoder(applicationContext, Locale.getDefault())
            var addressString= ""
  
              // Reverse-Geocoding starts
            try {
                val addressList: List<Address> = mGeocoder.getFromLocation(lat, lng, 1)
  
                // use your lat, long value here
                if (addressList != null && addressList.isNotEmpty()) {
                    val address = addressList[0]
                    val sb = StringBuilder()
                    for (i in 0 until address.maxAddressLineIndex) {
                        sb.append(address.getAddressLine(i)).append("\n")
                    }
  
                    // Various Parameters of an Address are appended
                      // to generate a complete Address
                    if (address.premises != null)
                        sb.append(address.premises).append(", ")
                          
                    sb.append(address.subAdminArea).append("\n")
                    sb.append(address.locality).append(", ")
                    sb.append(address.adminArea).append(", ")
                    sb.append(address.countryName).append(", ")
                    sb.append(address.postalCode)
                      
                    // StringBuilder sb is converted into a string
                     // and this value is assigned to the 
                    // initially declared addressString string.
                    addressString = sb.toString()
                }
            } catch (e: IOException) {
                Toast.makeText(applicationContext,"Unable connect to Geocoder",Toast.LENGTH_LONG).show()
            }
              
            // Finally, the address string is posted in the textView with LatLng.
            addressTV.text = "Lat: $lat \nLng: $lng \nAddress: $addressString"
        }
    }
}


Output:

Observe, the address changes with a change in lat-long values. Lat-long values change when the user drags the screen, as the center keeps changing.



Last Updated : 30 Mar, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads