Last Updated on 8 years by Mas Herdi
Halo semua, melanjutkan tutorial sebelumnya tentang mendapatkan lokasi pengguna di Android. Kali ini kita masih bermain-main dengan LocationServices API, tentang bagaimana mengubah koordinat (latitude, longitude) menjadi nama alamat menggunakan teknik reverse geocoding dengan Android Geocoder. 🙂
Belajar reverse geocoding konversi lat long menjadi alamat
Dengan menggunakan Fused Location API, kita bisa mendapatkan lokasi pengguna di Android. Naamun setelah mendapatkan lokasi pengguna kita hanya mendapatkan latitude dan longitude yang susah untuk dipahami/dimengerti, kecuali jika kita tampilkan lat long tersebut di atas peta. Karena itu supaya lebih mudah dibaca, kita bisa mengkonversi koordinat tersebut menggunakan teknik reverse geocoding. Dengan bantuan dari built-in API Android Geocoder. 🙂
Pre-Requisites
Seperti biasa kita akan membuat project Hello World dulu di Android Studio. Karena project ini merupakan lanjutan dari tutorial Fused Location API yang sebelumnya saya buat. Maka ada baiknya jika kalian langsung download saja source code nya dari GitHub saya.
Jika sudah, maka pada layout activity_main.xml, kalian bisa tambahkan satu buah Button untuk trigger proses reverse geocoding-nya. Langkah selanjutnya adalah…
Membuat Geocoder Intent Services
Oke, karena proses reverse geocoding ini merupakan sebuah proses yang bisa berjalan lama, dan kemungkinan hasilnya juga bisa bermacam-macam maka reverse geocoding akan dijalankan di background proses menggunakan IntentService. Buatlah sebuah class bernama GeocoderIntentService.
Oh iya, sebelumnya kalian bisa membuat class pembantu bernama Constants.java. Karena kita akan banyak menggunakan constant String di sini.
Constants.java
package id.web.twoh.placesapitutorial.util; /** * Created by Hafizh Herdi on 2/12/2017. */ public final class Constants { public static final int SUCCESS_RESULT = 0; public static final int FAILURE_RESULT = 1; public static final String PACKAGE_NAME = "id.web.twoh.placesapitutorial"; public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER"; public static final String RESULT_DATA_KEY = PACKAGE_NAME + ".RESULT_DATA_KEY"; public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME + ".LOCATION_DATA_EXTRA"; }
GeocoderIntentService.java
import android.app.IntentService; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.os.Bundle; import android.os.ResultReceiver; import android.text.TextUtils; import android.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import id.web.twoh.placesapitutorial.util.Constants; /** * Created by Hafizh Herdi on 2/12/2017. */ public class GeocoderIntentService extends IntentService { private static final String TAG = GeocoderIntentService.class.getSimpleName(); protected ResultReceiver mReceiver; public GeocoderIntentService() { // Use the TAG to name the worker thread. super(TAG); } @Override protected void onHandleIntent(Intent intent) { String errorMessage = ""; mReceiver = intent.getParcelableExtra(Constants.RECEIVER); // Cek apakah ada receiver untuk menerima hasil if (mReceiver == null) { Log.wtf(TAG, "Tidak ada receiver, sehingga hasil tidak bisa dikirim"); return; } // Mendapatkan location yang dikirim ke intent service lewat extra Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA); // Make sure jika location yang dikirim tidak null if (location == null) { errorMessage = "Location tidak ditemukan"; Log.wtf(TAG, errorMessage); deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); return; } Geocoder geocoder = new Geocoder(this, Locale.getDefault()); List<Address> addresses = null; try { /** * Proses reverse geocoding */ addresses = geocoder.getFromLocation( location.getLatitude(), location.getLongitude(), 1); } catch (IOException ioException) { // Menangkap apabila ada I/O atau jaringan error errorMessage = "Location Service is not available"; ioException.printStackTrace(); Log.e(TAG, errorMessage, ioException); } catch (IllegalArgumentException illegalArgumentException) { // Apabila invalid latitude longitude errorMessage = "Invalid latitude longitude"; Log.e(TAG, errorMessage + ". " + "Latitude = " + location.getLatitude() + ", Longitude = " + location.getLongitude(), illegalArgumentException); } // Apabila tidak ada alamat yang bisa ditemukan if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { errorMessage = "Alamat tidak ditemukan"; Log.e(TAG, errorMessage); } deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); } else { Address address = addresses.get(0); ArrayList<String> addressFragments = new ArrayList<>(); /** * Kalian juga bisa mendapatkan detail alamat lainnya * seperti kelurahan, kecamatan, kodepos dsb dalam istilah Inggrisnya * Contoh : * getLocality() ("Mountain View", for example) * getAdminArea() ("CA", for example) * getPostalCode() ("94043", for example) * getCountryCode() ("US", for example) * getCountryName() ("United States", for example) */ for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { addressFragments.add(address.getAddressLine(i)); } Log.i(TAG, "alamat ditemukan"); deliverResultToReceiver(Constants.SUCCESS_RESULT, TextUtils.join(System.getProperty("line.separator"), addressFragments)); } } /** * Sends a resultCode and message to the receiver. */ private void deliverResultToReceiver(int resultCode, String message) { Bundle bundle = new Bundle(); bundle.putString(Constants.RESULT_DATA_KEY, message); mReceiver.send(resultCode, bundle); } }
Cara kerja IntentService di atas pada dasarnya adalah melakukan proses reverse geocoding (ubah koordinat menjadi alamat. Kemudian cek apakah ada alamat yang ditemukan, jika ada maka hasilnya akan dikembalikan lewat receiver.
Membuat Geocoder Result Receiver
Kita sudah membuat IntentService-nya, sekarang kita akan membuat ResultReceiver untuk menerima hasil balikan dari proses reverse geocoding tersebut, berupa alamat atau pesan error jika terjadi kendala. Caranya tambahkan class GeocoderResultReceiver pada MainActivity.java sebagai nested class.
GeocoderResultReceiver.java
protected String address; class GeocoderResultReceiver extends ResultReceiver { public GeocoderResultReceiver(Handler handler) { super(handler); } /** * Menerima balikan data dari GeocoderIntentService and menampilkan Toast di MainActivity. */ @Override protected void onReceiveResult(int resultCode, Bundle resultData) { // Menampilkan alamat, atau error yang didapat dari proses reverse geocoding address = resultData.getString(Constants.RESULT_DATA_KEY); // Memunculkan toast message jika ada alamat ditemukan if (resultCode == Constants.SUCCESS_RESULT) { Toast.makeText(MainActivity.this, "Alamat ditemukan \n" + address, Toast.LENGTH_LONG).show(); } dialog.dismiss(); } }
Jika ada alamat yang didapatkan, maka ResultReceiver akan memunculkan sebuah Toast.
Memanggil IntentService dari MainActivity.java
Langkah selanjutnya adalah memanggil IntentService dari MainActivity.java. Sebelum itu, mari kita menginitialize variable-variable yang dibutuhkan di MainActivity.java.
Initialize ResultReceiver
private GeocoderResultReceiver geocoderReceiver; private Button btGeocoding; // letakkan method pemanggilan ini di dalam onCreate geocoderReceiver = new GeocoderResultReceiver(new Handler()); btGeocoding = (Button) findViewById(R.id.bt_geoCoding);
Setelah itu kita akan membuat method startIntentService() di MainActivity, untuk menyalakan GeocoderIntentService. Isi methodnya seperti di bawah :
protected void startIntentService() { // Membuat intent yang mengarah ke IntentService untuk proses reverse geocoding Intent intent = new Intent(this, GeocoderIntentService.class); // Mengirim ResultReceiver sebagai extra ke intent service. intent.putExtra(Constants.RECEIVER, geocoderReceiver); // Mengirim location data sebagai extra juga ke intent service. intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); // Start the service. If the service isn't already running, it is instantiated and started // (creating a process for it if needed); if it is running then it remains running. The // service kills itself automatically once all intents are processed. // tl;dr = menyalakan intent service :) startService(intent); }
Kemudian kita set onClickListener pada tombol btGeocoding, supaya saat diklik nanti proses intentService nya akan dijalankan
btGeocoding.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mGoogleApiClient.isConnected()){ startIntentService(); } } });
Basically, that’s all.
Langkah terakhir adalah me-register IntentService tersebut pada AndroidManifest.xml
<application> <activity> </activity> <service android:name=".GeocoderIntentService" android:exported="false"/> </application>
Jangan lupa juga untuk tambahkan permission
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Demo
Beginilah tampilan jika aplikasi dijalankan :
Kita akan klik tombol “Get Location” terlebih dahulu. Sekadar untuk memastikan apakah ada lat lng yang didapat. Jika sudah kita bisa lanjut klik tombol “Get Coordinate”, tunggu sejenak dan akan muncul alamat hasil reverse geocode dari koordinat tadi. 🙂
That’s all. Source code bisa didownload di Github. Jangan lupa untuk star project nya dan juga follow GitHub saya. 🙂 Silahkan tanya di bagian komentar apabila ada yang kurang jelas.