Today in this tutorial we are going to learn how we can get full address from location coordinates in our Android application. If you are searching about how to get location in Android. Please read this first: How to get current GPS location in Android. And then again come back here.
Here we will only be focusing on the coordinates which we already have. Eg: 41.40338, 2.17403
. Let us see how we can accomplish this task without using any Google location APIs.
Quick Navigation
What is Geocode ?
Geocode is the non human readable piece of information such as a place name or address on the surface of the earth. Or simply we can understand that Geocode is the latitude and longitude. It represents a specific point on the surface of the earth.
Unlike us humans, who can make sense of address. It is hard for computers to understand human readable form. So these points are used as the index to retrieve more information about that place.
And, Geocoding is the process of converting any input such as address or human readable location to latitude and longitude.
In this post we will be looking at how to convert coordinates into human readable form. The process is known as reverse Geocoding. But before diving in I had to provide you some context about Geocoding.
Also Read : Learn how to use fileprovider in Android with example
TECHENUM
What is Reverse Geocode ?
In the section above we have seen that how the process of converting readable information to coordinates is Geocoding.
Reverse Geocoding is just the opposite of that. Reverse Geocode is the human readable information for a certain point. Or in regular terms, address.
We have multiple ways on how to do this. We can use the API provided by Google or any other map provider. Here we will learn how to do this without using any other online services.
Using the Geocode class in Android
To do what I have described, we must make use of Geocode
class in Android SDK.
This will not cost us anything whatsoever. But sometimes the rate limiting might not provide information about the coordinates. But this has never happened with me.
Let us see how we can do just that. We are going to use this address here : 37.474976, -122.171610
Want to get your device’s current location instead ? Read this: How to get current GPS location in Android
Also Read : 3 Essential Datastructures in Object Oriented Programming
TECHENUM
Preparing UI for showing address
Let us first prepare a UI for showing the address.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/result"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Please enter latitude and longitude below"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/input"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="@+id/input"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.24000001" />
<EditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="latitude, longitude"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/result" />
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textAlignment="center"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/result"
app:layout_constraintEnd_toEndOf="@+id/result"
app:layout_constraintStart_toStartOf="@+id/result"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Code language: HTML, XML (xml)
Now mapping our views to activity class member instances as under. Notice we have created extra methods which are not required. This is just a scaffold for what we are doing up next.
class MainActivity : AppCompatActivity() {
private lateinit var mInput: EditText
private lateinit var mResult: TextView
private lateinit var mDone: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mInput = findViewById(R.id.input)
mResult = findViewById(R.id.result)
mDone = findViewById(R.id.done)
mDone.setOnClickListener {
val latLon: Pair<Double, Double> = parseInput()
doReverseGeoCode(latLon)
}
}
private fun doReverseGeoCode(latLon: Pair<Double, Double>) {
}
private fun parseInput(): Pair<Double, Double> {
}
}
Code language: Kotlin (kotlin)
Performing a Reverse Geocode
Now that we have our scaffold ready from previous section. Let us actually do some reverse Geocoding. But first let us parse the user input into Pair<*>
of Double
.
Also Read : Async Task in Android is Deprecated: There are Better Ways
TECHENUM
Parsing the input
This is optional because it is related to this example project only. Just prepare a model or another pair for your data.
I have created a method to parse the user input and convert it into Pair<>
of latitude and longitude.
private fun parseInput(): Pair<Double, Double> {
var lat = 0.0
var lon = 0.0
val input = mInput.text.toString()
val arrInput = input.split(",")
// invalid input
if (arrInput.size != 2) return Pair(lat, lon)
try {
lat = arrInput[0].toDouble()
lon = arrInput[1].toDouble()
} catch (ex: NumberFormatException) {
// no a double value return the pointer
return Pair(lat, lon)
}
return Pair(lat, lon)
}
Code language: Kotlin (kotlin)
All the code above does is parses the input and returns a Pair<>
.
Doing the reverse Geocoding
Now after we have successfully parsed the input. The next step is actually performing reverse Geocoding as follows.
private fun doReverseGeoCode(latLon: Pair<Double, Double>) {
mExecutorService.submit {
val geocoder = Geocoder(this, Locale.ENGLISH)
val addresses = geocoder.getFromLocation(latLon.first, latLon.second, 1)
if (addresses.isEmpty()) {
updateUi("address not found for: ${latLon.first}, ${latLon.second}")
return@submit
}
val address = addresses.first()
val premises = address.premises
val featureName = address.featureName
val countryName = address.countryName
val adminArea = address.adminArea
val subAdminArea = address.subAdminArea
val locality = address.locality
val subLocality = address.subLocality
val thoroughfare = address.thoroughfare
val subThoroughfare = address.subThoroughfare
val result =
"premise: $premises\nfeature: $featureName\ncountry: $countryName\nadmin area: $adminArea\nsub admin area: $subAdminArea\nlocality: $locality\nsub locality: $subLocality\nthoroughfare: $thoroughfare\nsub thoroughfare: $subThoroughfare"
updateUi(result)
}
}
Code language: Kotlin (kotlin)
Finally create the missing method named updateUi() { }
like below. All is does is runs the UI update code in the UiThread
.
private fun updateUi(result: String) {
runOnUiThread {
mResult.text = result
}
}
Code language: Kotlin (kotlin)
And now invoke these methods in the click listener that we have set in onCreate() { }
.
val latLon: Pair<Double, Double> = parseInput()
doReverseGeoCode(latLon)
Code language: Kotlin (kotlin)
The full code for MainActivity looks like this :
class MainActivity : AppCompatActivity() {
private val mExecutorService = Executors.newSingleThreadExecutor()
private lateinit var mInput: EditText
private lateinit var mResult: TextView
private lateinit var mDone: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mInput = findViewById(R.id.input)
mResult = findViewById(R.id.result)
mDone = findViewById(R.id.done)
mDone.setOnClickListener {
val latLon: Pair<Double, Double> = parseInput()
doReverseGeoCode(latLon)
}
}
private fun updateUi(result: String) {
runOnUiThread {
mResult.text = result
}
}
private fun doReverseGeoCode(latLon: Pair<Double, Double>) {
mExecutorService.submit {
val geocoder = Geocoder(this, Locale.ENGLISH)
val addresses = geocoder.getFromLocation(latLon.first, latLon.second, 1)
if (addresses.isEmpty()) {
updateUi("address not found for: ${latLon.first}, ${latLon.second}")
return@submit
}
val address = addresses.first()
val premises = address.premises
val featureName = address.featureName
val countryName = address.countryName
val adminArea = address.adminArea
val subAdminArea = address.subAdminArea
val locality = address.locality
val subLocality = address.subLocality
val thoroughfare = address.thoroughfare
val subThoroughfare = address.subThoroughfare
val result =
"premise: $premises\nfeature: $featureName\ncountry: $countryName\nadmin area: $adminArea\nsub admin area: $subAdminArea\nlocality: $locality\nsub locality: $subLocality\nthoroughfare: $thoroughfare\nsub thoroughfare: $subThoroughfare"
updateUi(result)
}
}
private fun parseInput(): Pair<Double, Double> {
var lat = 0.0
var lon = 0.0
val input = mInput.text.toString()
val arrInput = input.split(",")
// invalid input
if (arrInput.size != 2) return Pair(lat, lon)
try {
lat = arrInput[0].toDouble()
lon = arrInput[1].toDouble()
} catch (ex: NumberFormatException) {
// no a double value return the pointer
return Pair(lat, lon)
}
return Pair(lat, lon)
}
}
Code language: Kotlin (kotlin)
That is all there is to reverse Geocoding. And in this article we have seen how to get address from location coordinates in Android.
Leave a comment if you think I made any mistakes. Keep learning.
For more on this you can see Official Documentation.