Pemanfaatan Google Maps dan Google Directions sebagai penunjuk jalan sebenarnya mau saya buat seperti ini “
Pemanfaatan Google Maps dan Google Directions sebagai penunjuk jalan menuju lokasi wisata yang ada di Jogja” kelihatan panjang banget dan mirip dengan judul skripsi


Tampilan Home Screen Aplikasi
Oke kali ini saya mau cerita mengenai sebuah aplikasi yang saya buat dalam waktu tidak terlalu lama, kenapa saya buat aplikasi ini, tujuannya adalah untuk membantu saya menemukan lokasi wisata yang ingin saya tuju, kemarin waktu libur imlek saya berencana untuk keluar dari rutinitas sehari-hari(ngoding, tidur, main dota) dengan mengunjungi lokasi wisata yang ada disekitaran jogja terutama di gunung kidul, bosen kalo mesti ke parangtritis lagi,

jadilah ditetapkan,, “Yuk pantai, gunung kidul”.
Destinasinya sudah ditetapkan, kami akan ke salah satu pantai yang ada digunung kidul yaitu pantai Sadranan. Masalah lain muncul, tanya ke temen yang mau diajakin tahu jalannya atau tidak, dengan polos dan lugunya bilang, “gak ngerti… T.T (muka mewek)”, dan dijawab lagi dengan polos “owh (muka datar).
Jiwa IT nya lalu keluar, kenapa gak make google maps, owh iya, setelah dicek via google maps browser ternyata lumayan jauh, dan banyak persimpangan yang dapat membuat hati gegana (gelisah galau merana) karena nyasar T.T terus adalagi, gimana ntar kalo disana gak ada signal ? bisa po buka google maps

owh owh
Jadi teringat beberapa tahun lalu ketika masih kuliah pernah buat sebuah aplikasi yang memanfaatkan google directions dan google maps untuk memandu ke lokasi wisata, setelah dicari – cari beberapa menit, gak ketemu sourcenya, ini akibat ganti komputer beberapa tahun lalu, dan sepertinya sourcenya gak ke copy, dulu masih polos gak ngerti cara make git T.T
gambaran aplikasi

Tampilan ketika user memilih destination

Tampilan map dan rute ke lokasi tujuan

Tetap dapat menggunakan maps walaupun kondisi tidak ada jaringan
Konsep aplikasinya adalah pengembangan dari
artikel yang saya tulis duluuu sekali, bedanya adalah dah gak make AsyncTask, udah diganti networking library
Volley, terus parsing JSONnnya gak make cara lama parsing satu persatu (lamaaa bro) udah make
Gson kemudian database tidak menggunakan SQLite tapi menggunakan
Realm salah satu database NoSQL yang ada di android.
Oke sekarang nyeritain alur aplikasinya seperti apa, jadi gini nanti apps akan coba mengambil data directions yang dihasilkan dari google directions, kemudian hasil JSONnya ini diparsing oleh library Gson dan polylinenya disimpan kedalam database Realm, setelah disimpan dalam database baru ditampilkan kedalam maps, kenapa disimpan kedalam database polylinenya ? agar nanti ketika dibuka untuk selanjutnya tidak memerlukan internet lagi, tinggal baca dari database hal ini untuk menanggulangi ketika diperjalanan tidak adanya koneksi internet tapi signal gps masih ada.
Sebelum user dapat menekan tombol generate route, user harus memasukkan starting point dimana rute akan mulai digenerate, saya menggunakan maps dan sebuah marker untuk meminta inputan user dimana starting pointya, classnya seperti ini
| public class AddMapActivity extends FragmentActivity implements GoogleMap.OnMapClickListener, GoogleApiClient.ConnectionCallbacks, |
| GoogleApiClient.OnConnectionFailedListener { |
|
|
| private double lat, lng; |
| private GoogleMap mMap; // Might be null if Google Play services APK is not available. |
| private Location location; |
| private GoogleApiClient googleApiClient; |
|
|
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_add_map); |
| ButterKnife.inject(this); |
| setUpMapIfNeeded(); |
|
|
|
|
| googleApiClient = new GoogleApiClient.Builder(this) |
| .addConnectionCallbacks(this) // connection callback |
| .addOnConnectionFailedListener(this) // when connection failed |
| .addApi(LocationServices.API) // called api |
| .build(); |
| } |
|
|
| @Override |
| protected void onResume() { |
| super.onResume(); |
| setUpMapIfNeeded(); |
| } |
|
|
| @Override |
| protected void onStart() { |
| super.onStart(); |
| googleApiClient.connect(); |
| } |
|
|
| @Override |
| protected void onStop() { |
| super.onStop(); |
| if (googleApiClient.isConnected()) { |
| googleApiClient.disconnect(); |
| } |
| } |
|
|
| /** |
| * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly |
| * installed) and the map has not already been instantiated.. This will ensure that we only ever |
| * call {@link #setUpMap()} once when {@link #mMap} is not null. |
| * <p/> |
| * If it isn't installed {@link SupportMapFragment} (and |
| * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to |
| * install/update the Google Play services APK on their device. |
| * <p/> |
| * A user can return to this FragmentActivity after following the prompt and correctly |
| * installing/updating/enabling the Google Play services. Since the FragmentActivity may not |
| * have been completely destroyed during this process (it is likely that it would only be |
| * stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this |
| * method in {@link #onResume()} to guarantee that it will be called. |
| */ |
| private void setUpMapIfNeeded() { |
| // Do a null check to confirm that we have not already instantiated the map. |
| if (mMap == null) { |
| // Try to obtain the map from the SupportMapFragment. |
| mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)) |
| .getMap(); |
| // Check if we were successful in obtaining the map. |
| if (mMap != null) { |
| setUpMap(); |
| } |
| } |
| } |
|
|
| /** |
| * This is where we can add markers or lines, add listeners or move the camera. In this case, we |
| * just add a marker near Africa. |
| * <p/> |
| * This should only be called once and when we are sure that {@link #mMap} is not null. |
| */ |
| private void setUpMap() { |
| mMap.setOnMapClickListener(this); |
| mMap.setMyLocationEnabled(true); |
| } |
|
|
| @Override |
| public void onMapClick(LatLng latLng) { |
| mMap.clear(); |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(latLng.latitude, latLng.longitude)) |
| .title("Starting Point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
| lat = latLng.latitude; |
| lng = latLng.longitude; |
| } |
|
|
| @OnClick(R.id.fab) |
| public void onFabClick() { |
| Toast.makeText(this, "fab click", Toast.LENGTH_SHORT).show(); |
| Bundle data = new Bundle(); |
| data.putDouble("lat", lat); |
| data.putDouble("lng", lng); |
| Intent resultIntent = new Intent(); |
| resultIntent.putExtras(data); |
| setResult(RESULT_OK, resultIntent); |
| finish(); |
| } |
|
|
| @Override |
| public void onConnected(Bundle bundle) { |
| if (location == null) { |
| // get last location device |
| location = LocationServices.FusedLocationApi.getLastLocation(googleApiClient); |
| if (mMap != null) { |
| mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 13)); |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(location.getLatitude(), location.getLongitude())) |
| .title("Starting Point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
| lat = location.getLatitude(); |
| lng = location.getLongitude(); |
|
|
| Toast.makeText(this, "Lokasi kamu saat ini, sebagai patokan titik awal perjalanan kamu kak :')", Toast.LENGTH_LONG).show(); |
| } |
| } |
|
|
| } |
|
|
| @Override |
| public void onConnectionSuspended(int i) { |
|
|
| } |
|
|
| @Override |
| public void onConnectionFailed(ConnectionResult connectionResult) { |
|
|
| } |
| } |
jika diperhatikan script diatas, saya menggunakan FusedLocationProvider untuk menentukan lokasi user saat ini berada, dapat dilihat dari kode dibawah
| @Override |
| public void onConnected(Bundle bundle) { |
| if (location == null) { |
| // get last location device |
| location = LocationServices.FusedLocationApi.getLastLocation(googleApiClient); |
| if (mMap != null) { |
| mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 13)); |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(location.getLatitude(), location.getLongitude())) |
| .title("Starting Point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
| lat = location.getLatitude(); |
| lng = location.getLongitude(); |
|
|
| Toast.makeText(this, "Lokasi kamu saat ini, sebagai patokan titik awal perjalanan kamu kak :')", Toast.LENGTH_LONG).show(); |
| } |
| } |
|
|
| } |
ketika googleApiClient berhasil terkoneksi, dan FusedLocationProvider dapat mengakses last location maka saya akan menggerakkan camera maps ke location user sekarang, kemudian menambahkan marker diatasnya.
Agar lebih fleksibel saya membuat user dapat memilih starting point sesuai dengan kehendaknya, caranya adalah dengan mengklik dimana saja diatas peta maka lokasi klik tersebut akan menjadi starting point, caranya dalah dengan menambahkan event onMapClickListener ke peta, setelah itu implement method onMapClick
| @Override |
| public void onMapClick(LatLng latLng) { |
| mMap.clear(); |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(latLng.latitude, latLng.longitude)) |
| .title("Starting Point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
| lat = latLng.latitude; |
| lng = latLng.longitude; |
| } |
setelah dirasa cukup user tinggal menekan tombol centang yang ada dibawah yang akan menjalankan code berikut
| @OnClick(R.id.fab) |
| public void onFabClick() { |
| Toast.makeText(this, "fab click", Toast.LENGTH_SHORT).show(); |
| Bundle data = new Bundle(); |
| data.putDouble("lat", lat); |
| data.putDouble("lng", lng); |
| Intent resultIntent = new Intent(); |
| resultIntent.putExtras(data); |
| setResult(RESULT_OK, resultIntent); |
| finish(); |
| } |
kode diatas adalah untuk mengembalikan ke activity sebelumnya dengan Result OK dan nilai latitude dan longitude yang sudah dipilih.
Selanjutnya user akan memilih destinasi wisata tujuan yang ditampilkan kedalam Spinner, lokasi didapet dari mana ? masih dihardcode, jadi langsung ditulis kedalam code, belom ada niat ambil dari sebuah API
| /** |
| * hekz! |
| * Hardcode doloe gan.. |
| * forgive me ~ |
| */ |
| private void generateLocationDummy() { |
| locations.add(new Location("Sadranan", -8.146982, 110.6076521)); |
| locations.add(new Location("Ngrenehan", -8.1235303, 110.5161231)); |
| locations.add(new Location("Indrayanti", -8.1501091, 110.6121113)); |
| locations.add(new Location("KaliBiru", -7.8017879,110.1318019)); |
| } |
Setelah memilih destinasi maka selanjutnya tombol generate route akan memanggil activity ViewRoute dengan membawa beberapa parameter
| @OnClick(R.id.btnGetRoute) |
| public void getRoute() { |
| if (!TextUtils.isEmpty(etFrom.getText())) { |
| Location loc = locations.get(spDestination.getSelectedItemPosition()); |
| Bundle data = new Bundle(); |
| data.putDouble("latAwal", lat); |
| data.putDouble("lngAwal", lng); |
| data.putDouble("latTujuan", loc.getLat()); |
| data.putDouble("lngTujuan", loc.getLng()); |
| data.putInt("status", MainActivity.FROM_NET); |
| data.putString("tujuan", loc.getName()); |
|
|
| startActivity(new Intent(this, ViewRouteActivity.class).putExtras(data)); |
| } else { |
| Toast.makeText(this, "can't empty", Toast.LENGTH_SHORT).show(); |
| } |
| } |
Setetal memilih starting point dan destinasi tujuan, langkah selanjutnya adalah mendapatkan titik-titik lokasi yang digunakan untuk menggambar jalur kedalam peta, dibagian inilah fungsi dari Google Directions, untuk melakukan request ke google directions saya menggunakan volleylibrary dengan class batuan JsonObjectRequestnya, sehingga hasil dari requestnya otomatis dibuatkan JsonObject
| private void getDirections(String URL) { |
| JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, |
| URL, null, |
| new Response.Listener<JSONObject>() { |
| @Override |
| public void onResponse(JSONObject jsonObject) { |
| progressDialog.dismiss(); |
| response = new Gson().fromJson(jsonObject.toString(), com.pratamawijaya.panggilpeta.json.Response.class); |
| Log.d("debug", "" + response.getRouteList().get(0).getLegsList().get(0).getDistance()); |
|
|
| // Fetch Directions From Json |
| fetchDirection(response.getRouteList().get(0).getLegsList().get(0).getStepsList()); |
|
|
| saveToDb(response.getRouteList().get(0).getLegsList().get(0)); |
| // TODO: Save to DB |
| } |
| }, |
| new Response.ErrorListener() { |
| @Override |
| public void onErrorResponse(VolleyError volleyError) { |
| progressDialog.dismiss(); |
| Log.d("error", "" + volleyError.toString()); |
| } |
| }); |
| progressDialog.show(); |
| BaseApplication.getInstance().addToRequestQueue(request, "tag"); |
| } |
jika diperhatikan dari method getDirections didalam method onResponse saya akan memanggil method fetchDirections, dimana didalam method inilah proses pengambilan data berubah lat lng yang akan dijadikan polyline
| private void fetchDirection(List<Steps> stepsList) { |
| Log.d("debug", "size list awal: " + latLngList.size()); |
| for (Steps data : stepsList) { |
| // add start |
| latLngList.add(new LatLng(data.getStartLocation().getLat(), data.getStartLocation().getLng())); |
|
|
| // decode poly |
| List<LatLng> decodedPoly = Utils.decodePoly(data.getPolyline().getPoints()); |
| for (LatLng point : decodedPoly) |
| latLngList.add(new LatLng(point.latitude, point.longitude)); |
|
|
| latLngList.add(new LatLng(data.getEndLocation().getLat(), data.getEndLocation().getLng())); |
| // add end |
| } |
| Log.d("debug", "size list akhir : " + latLngList.size()); |
|
|
| drawDirectionToMap(latLngList); |
| } |
setelah didapatkan polylinenya baru digambar kedalam mapsnya, prosesnya menggunakan method drawDirections berikut ini
| private void drawDirectionToMap(List<LatLng> latLngList) { |
| PolylineOptions line = new PolylineOptions().width(3).color(Color.BLUE); |
| for (int i = 0; i < latLngList.size(); i++) { |
| line.add(latLngList.get(i)); |
| } |
| mMap.addPolyline(line); |
| mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latLngList.get(0).latitude, latLngList.get(0).longitude), 14)); |
|
|
| // add marker diawal |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(latLngList.get(0).latitude, latLngList.get(0).longitude)) |
| .title("starting point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
|
|
| // add marker di akhir |
| int index = latLngList.size() - 1; |
| mMap.addMarker(new MarkerOptions() |
| .position(new LatLng(latLngList.get(index).latitude, latLngList.get(index).longitude)) |
| .title("end point") |
| .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker)) |
| ); |
|
|
| Toast.makeText(this, "Jalur berhasil digambar kak, hati2 dijalan pelan2 saja,,, ", Toast.LENGTH_SHORT).show(); |
| } |
nah untuk proses pengambilan data via internet seperti itu, lalu untuk yang offline alurnya seperti apa, nah alurnya seperti ini, semuanya berawal dari class MainActivity berikut ini
| public class MainActivity extends BaseActivity implements RecyclerItemClickListener.OnItemClickListener { |
|
|
| public static final int FROM_LOCAL = 0; |
| public static final int FROM_NET = 1; |
|
|
|
|
| @InjectView(R.id.recyclerview) |
| RecyclerView recyclerView; |
| @InjectView(R.id.emptytext) |
| TextView emptyText; |
| @InjectView(R.id.fab) |
| FloatingActionButton fab; |
|
|
| private ListRouteAdapter adapterRoute; |
| private List<LocationModel> locationModels; |
|
|
| private Realm realm; |
|
|
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| ButterKnife.inject(this); |
|
|
| realm = Realm.getInstance(this); |
|
|
| getSupportActionBar().setTitle(getResources().getString(R.string.txt_liburan)); |
|
|
| locationModels = new ArrayList<>(); |
| adapterRoute = new ListRouteAdapter(this, locationModels); |
|
|
| updateView(); |
|
|
| LinearLayoutManager layoutManager = new LinearLayoutManager(this); |
| recyclerView.setLayoutManager(layoutManager); |
| recyclerView.setAdapter(adapterRoute); |
| recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, this)); |
| readDataFromDb(); |
| } |
|
|
| private void updateView() { |
| if (locationModels.isEmpty()) { |
| emptyText.setVisibility(View.VISIBLE); |
| recyclerView.setVisibility(View.GONE); |
| } else { |
| emptyText.setVisibility(View.GONE); |
| recyclerView.setVisibility(View.VISIBLE); |
| } |
| } |
|
|
| private void readDataFromDb() { |
| if (locationModels.size() > 0) |
| locationModels.clear(); |
| RealmResults<LocationModel> result = realm |
| .where(LocationModel.class) |
| .findAll(); |
| // sortiny by id. |
| // show greater first |
| result.sort("id", RealmResults.SORT_ORDER_DESCENDING); |
| for (LocationModel data : result) { |
| Log.d("tag", "datanya " + data.getId()); |
| locationModels.add(data); |
| } |
| // notify adapter |
| adapterRoute.notifyDataSetChanged(); |
|
|
| // update view |
| updateView(); |
| } |
|
|
| @OnClick(R.id.fab) |
| public void onFabClick() { |
| startActivity(new Intent(this, AddRouteActivity.class)); |
| } |
|
|
| @Override |
| public void onItemClick(View v, int position) { |
| Toast.makeText(this, "" + locationModels.get(position).getTujuan(), Toast.LENGTH_SHORT).show(); |
| Bundle data = new Bundle(); |
| data.putInt("status", FROM_LOCAL); |
| data.putString("id", locationModels.get(position).getId()); |
| startActivity(new Intent(this, ViewRouteActivity.class).putExtras(data)); |
| } |
|
|
| @Override |
| protected void onResume() { |
| super.onResume(); |
| readDataFromDb(); |
| } |
| } |
Yang pertama adalah jelas membaca data yang ada ditabase Realm, method yang bertanggung jawab untuk tugas itu adalah method readDataFromDb berikut ini
| private void readDataFromDb() { |
| if (locationModels.size() > 0) |
| locationModels.clear(); |
| RealmResults<LocationModel> result = realm |
| .where(LocationModel.class) |
| .findAll(); |
| // sortiny by id. |
| // show greater first |
| result.sort("id", RealmResults.SORT_ORDER_DESCENDING); |
| for (LocationModel data : result) { |
| Log.d("tag", "datanya " + data.getId()); |
| locationModels.add(data); |
| } |
| // notify adapter |
| adapterRoute.notifyDataSetChanged(); |
|
|
| // update view |
| updateView(); |
| } |
intinya dari method itu adalah mencoba untuk query kedalam class model, kemudian disorting berdasarkan id secara descending, saya set seperti ini saya pengennya rute yang terakhir saya generate yang akan berada diatas sendiri.
kemudian untuk event agar ketika list route di klik saya menggunakan event RecyclerItemClickListener.OnItemClickListener yang dibuat sendiri dan berada di package helper, isi dari methodnya adalah
| @Override |
| public void onItemClick(View v, int position) { |
| Toast.makeText(this, "" + locationModels.get(position).getTujuan(), Toast.LENGTH_SHORT).show(); |
| Bundle data = new Bundle(); |
| data.putInt("status", FROM_LOCAL); |
| data.putString("id", locationModels.get(position).getId()); |
| startActivity(new Intent(this, ViewRouteActivity.class).putExtras(data)); |
| } |
yang perlu diperhatikan disini adalah saya menambahkan key untuk menentukan route yang digambar itu dari database atau dari internet, dan id dari database rute yang akan saya gambar.
Selanjutnya kembali lagi ke class ViewRouteActivity untuk melihat bagaiman proses menggambar polyline yang didapat dari database. Proses utamanya ada di method onCreate yang ada diclass ViewRoute activity berikut
| data = getIntent().getExtras(); |
| if (data.getInt("status") == MainActivity.FROM_LOCAL) { |
| // baca db |
| latLngList = readDataFromDB(data.getString("id")); |
|
|
| // draw |
| drawDirectionToMap(latLngList); |
| } else { |
| latAwal = data.getDouble("latAwal"); |
| lngAwal = data.getDouble("lngAwal"); |
|
|
| latTujuan = data.getDouble("latTujuan"); |
| lngTujuan = data.getDouble("lngTujuan"); |
| tujuan = data.getString("tujuan"); |
|
|
| getDirections(getGoogleDirectionsUrl(latAwal, lngAwal, latTujuan, lngTujuan)); |
| } |
pertama saya akan cek statusnya apakah rute tersebut diambil dari database atau dari internet, cara ngeceknya make key itu tadi, di bagian MainActivity saya membuat key untuk membedakan data diambil dari db maupun internet
|
if (data.getInt("status") == MainActivity.FROM_LOCAL)
|
jika data yang diambil dari database, maka selanjutnya adalah melalukan read ke database berdasarkan dari id rute,
|
latLngList = readDataFromDB(data.getString("id"));
|
sedangkan isi dari method readDataFromDB itu sendiri adalah seperti ini
| private List<LatLng> readDataFromDB(String id) { |
| RealmResults<LocationModel> result = realm.where(LocationModel.class).equalTo("id", id).findAll(); |
| List<LatLng> listlatlng = new ArrayList<>(); |
| for (LatLngModel data : result.get(0).getLatLngRealmList()) |
| listlatlng.add(new LatLng(data.getLat(), data.getLng())); |
| return listlatlng; |
| } |
di method tersebut saya coba melakukan query ke LocationModel class dengan id berdasarkan dari id yang dibawa class sebelumnya, kemudian memasukkan data lat lng kedalam list yang disiapkan untuk menampung data latitude dan longitude, setelah data berhasil dibaca kemudian method drawDirectionToMap dijalankan dengan parameter List dari latlng yang telah didapatkan sebelumnya.
Sumber : https://pratamawijaya.com/programming/android-programming/android-studio-project-pemanfaatan-google-maps-dan-google-directions-sebagai-penunjuk-jalan
0 komentar:
Post a Comment
Mari kita saling berkomentar. Jika kalian suka dengan artikel ini, ayo kita "Share" untuk membagikan informasi ini kepada sobat yang lain agar lebih bermanfaat. Terima kasih, Salam Blogger . . .