Menampilkan Peta Dengan Google Maps API

Pada artikel Memakai Database SQLite Di Android, saya membuat aplikasi yang mencari dan menyimpan koordinat lokasi yang diperoleh melalui GPS. Walaupun, saya bisa menampilkan lokasi ini satu per satu melalui Intent, akan lebih baik bila saya menambahkan fitur untuk menampilkan seluruh lokasi yang tersimpan di database ke dalam sebuah peta tunggal. Dengan demikian, saya bisa menganalisa hubungan setiap lokasi secara mudah. Untuk itu, saya perlu menyisipkan peta langsung di dalam aplikasi sehingga bisa saya atur sesuka hati.

Akan tetapi membuat kode program untuk menampilkan peta bukanlah hal mudah dan bisa membutuhkan waktu lama. Salah satu solusi yang lebih mudah adalah dengan memakai Google Maps yang bisa disisipkan ke dalam aplikasi Android melalui Google Maps API yang merupakan bagian dari layanan dari Google Play Services. Fasilitas ini bukan bagian dari Android Open Source Project (AOSP) melainkan layanan tersendiri dari Google. Hal ini berarti perangkat Android yang murni memakai AOSP (produsennya tidak ‘membayar’ lebih kepada Google) tidak bisa menjalankan aplikasi yang memakai Google Play Services. Selain itu, walaupun Google Maps API bisa dipakai secara gratis oleh programmer Android, penggunaan yang berlebihan seperti membuka lebih dari 25.000 peta setiap hari selama lebih dari 90 hari berturut-turut akan dikenakan biaya.

Untuk memakai Google Play Services, saya perlu menambahkan baris berikut ini pada build.gradle (modul):

...
dependencies {
    ...
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.google.android.gms:play-services:7.0.0'
}

Saya kemudian men-klik Sync Now agar Android Studio men-download dependency (file JAR) yang dibutuhkan. Setelah itu, saya juga menambahkan baris berikut ini pada AndroidManifest.xml:

<manifest ...>

  ...

  <application ...>

    ...

    <meta-data android:name="com.google.android.gms.version"
       android:value="@integer/google_play_services_version" />

  </application>

</manifest>

Setiap aplikasi Android selalu di-sign oleh sebuah sertifikat digital. Khusus untuk aplikasi yang dijalankan pada perangkat atau emulator sebagai debug release, saya dapat menemukan lokasi sertifikat digital tersebut di ~/.android/debug.keystore. Sebagai contoh, di Linux, saya dapat memberikan perintah berikut ini:

$  keytool -list -v -keystore ~/.android/debug.keystore 
-alias androiddebugkey -storepass android -keypass android

untuk menampilkan informasi sertifikat digital yang dipakai oleh Android Studio saat menghasilkan aplikasi pada debug release. Dengan kata lain, semua aplikasi yang saya jalankan pada debug release bisa dikenali melalui sertifikat digital ini.

Saya kemudian mencatat nilai SHA1 pada output di atas. Nilai ini dibutuhkan untuk mendapatkan sebuah Android API key yang dapat dipakai di aplikasi saya. Untuk memperoleh key ini, saya membuka https://console.developers.google.com. Pada situs tersebut, saya men-klik tombol Create Project. Setelah proyek dibuat, saya memilih APIs & auth, APIs, Google Maps APIs, dan men-klik pada Google Maps Android API. Saya kemudian men-klik tombol Enable API. Setelah itu, saya memilih APIs & auth, Credentials dan men-klik pada Create new Key. Pada dialog yang muncul, saya memilih Android key. Saya perlu mengisi input yang ada dengan nilai SHA1 yang saya peroleh dari sertifikat digital diikuti dengan tanda titik koma beserta nama package seperti XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX;com.snake.latihanku. Setelah selesai, saya akan memperoleh sebuah API key yang bisa dipakai pada proyek Android saya.

Untuk memakai API key yang dihasilkan dari Google, saya menambahkan baris berikut ini pada AndroidManifest.xml:

<manifest ...>

  ...

  <application ...>

    ...

    <meta-data android:name="com.google.android.geo.API_KEY"
       android:value="xxxxxxxxxxxxxxxxx" />

  </application>

</manifest>

Berkat key unik ini, Google dapat mengenali seberapa sering aplikasi saya memanggil Google Maps API. Pengunaan berlebihan yang membutuhkan pembayaran dihitung berdasarkan seberapa sering API dipanggil melalui key ini.

Untuk memakai Google Maps API, saya perlu terhubung ke server Google Maps melalui internet. Selain itu, ia juga akan men-cache data ke penyimpanan eksternal guna mengirit akses internet. Oleh sebab itu, saya menambahkan permission berikut ini di AndroidManifest.xml:

<manifest ...>

  ...

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  ...

</manifest>

Sekarang, saya siap untuk membuat kode program. Saya akan mulai dengan membuat sebuah menu baru seperti:

<menu ...>

  ...

  <item android:id="@+id/menu_semua" app:showAsAction="ifRoom"
     android:title="Lihat Semua" />

  ...

</menu>

Pada saat menu ini di-klik, kode program berikut ini akan dikerjakan:

public class LokasiActivity extends Activity implements LocationListener, SensorEventListener {

  ...

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    ...
    // Tampilkan semua
    menu.findItem(R.id.menu_semua).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
      @Override
      public boolean onMenuItemClick(MenuItem item) {
        startActivity(new Intent(LokasiActivity.this, SemuaLokasiActivity.class));
        return true;
      }
    });

    return true;
  }

  ...

}

Kode program di atas akan menjalankan SemuaLokasiActivity yang isinya seperti berikut ini:

public class SemuaLokasiActivity extends Activity implements OnMapReadyCallback {

    private MapFragment mapFragment;
    private LinearLayout layout;
    private List<Lokasi> daftarLokasi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        layout = new LinearLayout(this);
        layout.setId(View.generateViewId());
        setContentView(layout);
        GoogleMapOptions options = new GoogleMapOptions();
        options.mapType(GoogleMap.MAP_TYPE_SATELLITE);
        mapFragment = MapFragment.newInstance(options);
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.add(layout.getId(), mapFragment);
        fragmentTransaction.commit();

        // Baca semua lokasi dari database SQLite
        daftarLokasi = new ArrayList<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                DbHelper dbHelper = new DbHelper(SemuaLokasiActivity.this);
                SQLiteDatabase db = dbHelper.getReadableDatabase();
                Cursor cursor = db.rawQuery("SELECT * FROM lokasi", null);
                while (cursor.moveToNext()) {
                    Lokasi lokasi = new Lokasi(
                        cursor.getString(cursor.getColumnIndexOrThrow("nama")),
                        cursor.getDouble(cursor.getColumnIndexOrThrow("longitude")),
                        cursor.getDouble(cursor.getColumnIndexOrThrow("latitude")),
                        cursor.getDouble(cursor.getColumnIndexOrThrow("altitude")),
                        cursor.getFloat(cursor.getColumnIndexOrThrow("akurasi")),
                        cursor.getFloat(cursor.getColumnIndexOrThrow("azimuth"))
                    );
                    daftarLokasi.add(lokasi);

                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mapFragment.getMapAsync(SemuaLokasiActivity.this);
                    }
                });
            }
        }).start();
    }


    @Override
    public void onMapReady(GoogleMap map) {
        for (Lokasi lokasi: daftarLokasi) {
            map.addMarker(new MarkerOptions()
                .position(new LatLng(lokasi.getLatitude(), lokasi.getLongitude()))
                .title(lokasi.getNama())
                .flat(true)
                .rotation(lokasi.getAzimuth())
        );
        }
    }
}

Pada method onCreate() di atas, saya mendaftarkan sebuah MapFragment baru sebagai tampilan untuk activity ini. Kode program options.mapType(GoogleMap.MAP_TYPE_SATELLITE) menyebabkan peta yang ditampilkan adalah hasil pencitraan dari satelit. Setelah itu, saya membuat thread baru untuk membaca data lokasi dari database. Di dalam thread baru tersebut, setelah berhasil membaca dari database, saya memanggil mapFragment.getMapAsync() untuk menambahkan titik marker pada peta. Karena getMapAsync() harus dikerjakan di UI thread (main thread), maka saya memanggilnya melalui runOnUiThread().

Setelah peta berhasil ditampilkan, kode program onMapReady() akan dikerjakan. Pada kode program di method onMapReady(), saya menambahkan marker pada peta untuk masing-masing lokasi yang tersimpan di database. Saya juga menggunakan method rotation() untuk memutar marker sesuai dengan arah kompas yang terekam.

Sampai disini, bila aplikasi dijalankan dan saya memilih menu Lihat Semua, maka sebuah peta (yang berdasarkan Google Maps) akan ditampilkan dimana terdapat marker untuk setiap posisi yang pernah tersimpan, seperti yang terlihat pada gambar berikut ini:

Contoh tampilan aplikasi yang mengandung Google Maps beserta marker buatan sendiri

Contoh tampilan aplikasi yang mengandung Google Maps beserta marker buatan sendiri

Agar lebih jelas, saya bisa menampilkan garis yang menghubungkan masing-masing posisi tersebut. Untuk itu, saya mengubah kode program untuk method onMapReady() menjadi seperti berikut ini:

...
@Override
public void onMapReady(GoogleMap map) {
  PolylineOptions lineOptions = new PolylineOptions();
  for (Lokasi lokasi: daftarLokasi) {
    LatLng posisi = new LatLng(lokasi.getLatitude(), lokasi.getLongitude());
    map.addMarker(new MarkerOptions()
      .position(posisi)
      .title(lokasi.getNama())
      .flat(true)
      .rotation(lokasi.getAzimuth())
    );
    lineOptions.add(posisi);
  }
  map.addPolyline(lineOptions.color(Color.BLUE));
}
...

Pada kode program di atas, saya memanggil method addPolyline() dari GoogleMap untuk menggambar garis pada peta. Bila saya menjalankan aplikasi, kali ini setiap titik yang tersimpan di database akan ditampilkan dengan dihubungkan oleh sebuah garis berwarna biru, seperti yang terlihat pada gambar berikut ini:

Menggambar garis di Google Maps

Menggambar garis di Google Maps

Sebagai pelengkap, saya akan menampilkan informasi jarak sebuah lokasi terhadap lokasi sebelumnya, dengan mengubah method onMapReady() menjadi seperti berikut ini:

...
@Override
public void onMapReady(GoogleMap map) {
  PolylineOptions lineOptions = new PolylineOptions();
  Lokasi lokasiSebelumnya = null;
  float[] jarak = new float[1];
  for (Lokasi lokasi: daftarLokasi) {
    LatLng posisi = new LatLng(lokasi.getLatitude(), lokasi.getLongitude());
    MarkerOptions markerOptions = new MarkerOptions()
      .position(posisi)
      .title(lokasi.getNama())
      .flat(true)
      .rotation(lokasi.getAzimuth());
    if (lokasiSebelumnya != null) {
      Location.distanceBetween(lokasiSebelumnya.getLatitude(), lokasiSebelumnya.getLongitude(),
        posisi.latitude, posisi.longitude, jarak);
      markerOptions.snippet("Jarak Ke " + lokasiSebelumnya.getNama() + " = " + jarak[0] + " m");
    }
    map.addMarker(markerOptions);
    lineOptions.add(posisi);
    lokasiSebelumnya = lokasi;
  }
  map.addPolyline(lineOptions.color(Color.BLUE));
}
...

Pada kode program di atas, saya menghitung jarak dari lokasi dengan menggunakan method Location.distanceBetween(). Method ini akan menyimpan nilai jarak dalam satuan meter pada elemen pertama dari array yang dilewatkan sebagai argumen. Untuk menampilkan informasi jarak ini pada saat pengguna menyentuh sebuah marker, saya menggunakan method snippet() pada MarkerOptions untuk marker tersebut. Bila saya menyentuh sebuah marker, saya akan memperoleh informasi jarak seperti yang terlihat pada gambar berikut ini:

Menambahkan snippet berisi informasi jarak ke marker di Google Maps

Menambahkan snippet berisi informasi jarak ke marker di Google Maps

Mencari Arah Dengan Kompas Digital Di Android

Pada artikel Mencari Lokasi Dengan GPS Di Android, saya menggunakan GPS untuk menentukan posisi perangkat Android di peta. Pada artikel ini, saya akan menambahkan sebuah informasi lain berupa arah perangkat pada saat posisi tersebut diambil. Beberapa (tidak semua!) perangkat Android dilengkapi dengan sensor yang disebut sebagai magnetometer atau sering disebut sebagai kompas digital yang mengukur nilai dalam satuan micro-Tesla (uT).

Sama seperti pada kompas analog, sensor ini pada dasarnya dipengaruhi oleh medan magnet disekitarnya, terutama oleh medan magnet bumi. Akan tetapi, berbeda pada kompas analog yang hanya mengukur pada satu arah, perangkat Android biasanya menggunakan vector magnetometer yang mengukur medan magnet pada masing-masing 3 arah (x, y, dan z). Untuk menunjukkannya, saya akan menampilkan medan magnet yang dibaca pada 3 vector tersebut dengan menambahkan kode program berikut ini di LokasiActivity:

public class LokasiActivity extends Activity implements LocationListener, SensorEventListener {

    ...

    private float magnetX, magnetY, magnetZ;
    private SensorManager sensorManager;

    @Override
    protected void onStart() {
        ...
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        Sensor magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magnetometer != null) {
            sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        ...
        String strOutput = "DMS: " + lokasiTerakhir.koordinatDMS() + "n" +
            "DD: " + lokasiTerakhir.koordinatDD() + "n" +
            "Altitude: " + lokasiTerakhir.getAltitude() + " mn" +
            "Akurasi: " + lokasiTerakhir.getAkurasi() + " mn" +
            "Magnet X: " + magnetX + " uTn" +
            "Magnet Y: " + magnetY + " uTn" +
            "Magnet Z: " + magnetZ + " uTn";
        output.setText(strOutput);
        ...
    }

    @Override
    protected void onPause() {
        ...
        sensorManager.unregisterListener(this);
    }

    ...

    @Override
    public void onSensorChanged(SensorEvent event) {
        magnetX = event.values[0];
        magnetY = event.values[1];
        magnetZ = event.values[2];
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

}

Pada saat aplikasi dijalankan, saya akan memperoleh nilai besarnya medan magnet pada sumbu x, y, dan z. Walaupun demikian, saya tidak bisa memperoleh sesuatu yang bisa dimengerti atau berguna secara langsung dari nilai ini. Yang saya butuhkan adalah sebuah nilai tunggal seperti azimuth. Nilai azimuth adalah derajat yang menunjukkan jarak dari utara searah dengan jarum jam (misalnya, nilai azimuth untuk posisi di utara adalah 0 derajat, posisi timur 90 derajat, posisi selatan 180 derajat dan posisi barat 270 derajat).

Beruntungnya, Android Sensor API sudah menyediakan method yang akan melakukan kalkulasi rumit bagi saya. Langkah pertama yang saya perlu saya lakukan adalah mendapatkan nilai rotation matrix (R) yang berguna untuk memetakan koordinat perangkat ke koordinat dunia nyata. Koordinat dunia nyata adalah koordinat dimana X selalu menghadap timur, Y selalu menghadap kutub utara dan Z selalu menghadap ke langit, tanpa peduli pada orientasi perangkat. Untuk mendapatkan nilai R, saya perlu memperoleh nilai dari sensor accelerometer dan magnetometer. Nilai yang diperoleh dari sensor akan dilewatkan sebagai parameter pada method getRotationMatrix() milik SensorManager. Sebagai contoh, saya membatalkan perubahan sebelumnya dan mengubah kode program saya menjadi seperti berikut ini:

public class LokasiActivity extends Activity implements LocationListener, SensorEventListener {

    ...

    private float[] magnet, percepatan;
    private SensorManager sensorManager;

    @Override
    protected void onStart() {
        ...
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        Sensor magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magnetometer != null) {
            sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI);
        }
        Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        // Hitung rotasi
        if ((magnet!=null) && (percepatan!=null)) {
            float R[] = new float[9];
            float I[] = new float[9];
            if (SensorManager.getRotationMatrix(R, I, percepatan, magnet)) {
                // Proses disini nanti!
            }
        }
        ...
    }

    ...

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
        sensorManager.unregisterListener(this);
    }


    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            percepatan = event.values;
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magnet = event.values;
        }
    }

    ...
}

Pada kode program di atas, saya sudah memperoleh matrix R yang mewakili rotation matrix. Sekarang, saya bisa menggunakan nilai tersebut untuk dipakai pada method getOrientation() di SensorManager, seperti pada contoh berikut ini:

...
@Override
public void onLocationChanged(Location location) {
  // Hitung rotasi
  float azimuth = 0;
  if ((magnet!=null) && (percepatan!=null)) {
    float R[] = new float[9];
    float I[] = new float[9];
    if (SensorManager.getRotationMatrix(R, I, percepatan, magnet)) {
      float orientation[] = new float[3];
      azimuth = orientation[0];
    }
  }
  ...
}
....

Nilai azimuth yang dikembalikan oleh getOrientation() berada dalam satuan radian dimana nilai positif mewakili arah berlawanan dengan jarum jam. Untuk menerjemahkan nilai ini ke dalam derajat, saya menambahkan kode program berikut ini:

...
azimuth = (float) Math.toDegrees(orientation[0]);
if (orientation[0] < 0) {
    azimuth += 360;
}
...

Setelah itu, saya bisa menambahkan property baru pada Lokasi untuk menampung informasi azimuth. Saya juga perlu mengubah sebuah constructor pada class tersebut sehingga dapat menerima nilai azimuth. Ssebagai langkah terakhir, saya tinggal menampilkan nilai azimuth dari Lokasi ke dalam TextView (bersamaan dengan nilai yang diperoleh dari GPS).

Nilai azimuth yang diperoleh adalah azimuth compass yang relatif terhadap magnetic north bukan terhadap true north. Walaupun mendekati true north, nilai magnetic north selalu memiliki selisih tergantung pada wilayah dan waktu dimana pengguna berada. Selisih ini disebut sebagai magnetic declination. Struktur wilayah, jumlah besi yang terkandung di dalam tanah dan faktor lainnya merupakan contoh faktor yang mempengaruhi magnetic declination. Selain itu, metal dan magnet di sekitar perangkat bisa mempengaruhi nilai yang dibaca oleh magnetometer. Kelemahan lainnya adalah nilai azimuth hanya akurat bila perangkat berada dalam keadaan mendatar terhadap bumi dimana layar menghadap ke atas (sama seperti layaknya saat kompas digunakan). Bila perangkat berada dalam keadaan vertikal, gimbal lock akan menyebabkan hasil perhitungan getOrientation() milik SensorManager menjadi tidak akurat sehingga dibutuhkan perhitungan yang lebih kompleks untuk memperoleh azimuth yang diharapkan.

Mencari Lokasi Dengan GPS Di Android

Saat ini sudah terdapat banyak perangkat Android yang dilengkapi dengan perangkat GPS receiver. Hardware ini akan membaca informasi dari satelit GPS untuk menentukan lokasi perangkat. Jarak perangkat ke sebuah satelit dapat ditentukan dengan mengirim gelombang radio ke satelit dan mengukur selisih waktu dari receiver ke satelit. Setelah menemukan jarak untuk beberapa satelit, posisi perangkat dapat ditemukan dengan menggunakan perhitungan yang disebut trilateration. Sebagai programmer untuk perangkat Android, saya tidak perlu terlibat dalam proses yang kompleks ini karena saya hanya perlu menggunakan class yang ada di package android.location untuk memperoleh data lokasi melalui GPS.

Untuk bisa memakai GPS, saya mendaftarkan penggunaan permission berikut ini pada AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Pada Android API, sebuah lokasi diwakili oleh class Location. Karena class ini terlalu umum, saya akan membuat versi yang lebih spesifik dengan kebutuhan saya (domain class) seperti berikut ini:

public class Lokasi {

    private String nama;
    private double longitude;
    private double latitude;    
    private double altitude;
    private float akurasi;

    public Lokasi(Location location) {
        this.longitude = location.getLongitude();
        this.latitude = location.getLatitude();  
        this.altitude = location.getAltitude();
        this.akurasi = location.getAccuracy();
    }

    public Lokasi(double longitude, double latitude, double altitude, float akurasi) {
        this.longitude = longitude;
        this.latitude = latitude;
        this.altitude = altitude;
        this.akurasi = akurasi;
    }

    public Lokasi(String nama, double longitude, double latitude, double altitude, float akurasi) {
        this(longitude, latitude, altitude, akurasi);
        this.nama = nama;                
    }

    public String getNama() {
        return nama;
    }

    public void setNama(String nama) {
        this.nama = nama;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getAltitude() {
        return altitude;
    }

    public void setAltitude(double altitude) {
        this.altitude = altitude;
    }

    public float getAkurasi() {
        return akurasi;
    }

    public void setAkurasi(float akurasi) {
        this.akurasi = akurasi;
    }

}

Nilai latitude (posisi horizontal terhadap khatulistiwa) dan longitude (posisi vertikal terhadap prime meridian) dapat dipakai untuk menentukan posisi sebuah lokasi di bumi. Nilai yang dimiliki oleh Location (sekaligus dipakai di Lokasi) berada dalam format yang disebut sebagai Decimal Degrees (DD). Sebagai contoh, nilai latitude berupa 38,8897 derajat sama dengan 38 derajat 53 menit 23 detik utara pada format Degrees, Minutes, and Seconds (DMS). Angka positif pada latitude mewakili utara dan angka negatif mewakili selatan relatif terhadap khatulistiwa. Pada longitude, angka positif mewakili timur dan negatif mewakili barat relatif terhadap prime meridian.

Agar lebih mudah dibaca oleh pengguna, saya akan menambahkan method koordinatDMS() dan koordinatDD() di class Lokasi yang akan mengembalikan koordinat lokasi dalam format yang dikehendaki seperti pada:

class Lokasi {

  ...

  public String koordinatDMS() {
    return Location.convert(latitude, Location.FORMAT_SECONDS) + " " +
      Location.convert(longitude, Location.FORMAT_MINUTES);
  }

  public String koordinatDD() {
    return latitude + "," + longitude;
  }

}

Nilai altitude pada Location (sekaligus dipakai di Lokasi) adalah nilai dalam satuan meter yang mewakili ketinggian perangkat dihitung mulai dari posisi WGS-84 reference ellipsoid. Perlu diperhatikan bahwa nilai ini bukan ketinggian yang dihitung dari permukaan laut. Hal ini karena nilai permukaan laut yang sesungguhnya terlalu kompleks untuk diproses oleh GPS receiver.

Nilai accuracy pada Location (sekaligus dipakai sebagai akurasi di Lokasi) menunjukkan seberapa akurat nilai latitude dan longitude yang diperoleh dari GPS receiver. Bila sebuah lingkaran digambar dengan jari-jari sepanjang nilai accuracy (dalam satuan meter), maka ada kemungkinan 68% lokasi perangkat berada di dalam lingkaran ini.

Untuk memperoleh sebuah Location, saya perlu memanggil method requestLocationUpdates() dari LocationManager untuk mendaftarkan sebuah LocationListener. Pada listener tersebut, bila lokasi dari GPS telah diperoleh, method onLocationChanged() akan dipanggil. Sebagai latihan, saya membuat sebuah activity dengan nama LokasiActivity yang isinya seperti berikut ini:

public class LokasiActivity extends Activity implements LocationListener {

    private LocationManager locationManager;
    private Lokasi lokasiTerakhir;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_lokasi);
    }

    @Override
    protected void onStart() {
        super.onStart();
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
    }

    @Override
    public void onLocationChanged(Location location) {
        lokasiTerakhir = new Lokasi(location);
        TextView output = (TextView) findViewById(R.id.output);
        String strOutput = "DMS: " + lokasiTerakhir.koordinatDMS() + "n" +
            "DD: " + lokasiTerakhir.koordinatDD() + "n" +
            "Altitude: " + lokasiTerakhir.getAltitude() + " mn" +
            "Akurasi: " + lokasiTerakhir.getAkurasi() + " mn";
        output.setText(strOutput);
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
    }

}

Bila activity di atas dijalankan, informasi dari GPS akan ditampilkan pada sebuah TextView.

Apa yang bisa saya lakukan selain hanya melihat koordinat dari GPS? Sebagai contoh, saya men-share koordinat ini. Untuk itu, saya membuat sebuah deklarasi menu XML seperti berikut ini:

<menu ...>

   <item android:id="@+id/menu_share" app:showAsAction="ifRoom"
      android:title="Share"
      android:actionProviderClass="android.widget.ShareActionProvider" />

</menu>

Saya kemudian memakai menu baru ini dengan menambahkan method onCreateOptionsMenu() pada LokasiActivity:

public class LokasiActivity extends Activity implements LocationListener {

  prvate ShareActionProvider shareActionProvider;
  ...

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    MenuItem menuShare =  menu.findItem(R.id.menu_share);
    shareActionProvider = (ShareActionProvider) menuShare.getActionProvider();
    return true;
  }

}

Selanjutnya, saya menentukan nilai lokasi yang bisa di-share dengan menambahkan kode program berikut ini pada method onLocationChanged():

public class LokasiActivity extends Activity implements LocationListener {

  ...

  @Override
  public void onLocationChanged(Location location) {
    ...

    // Share lokasi DD
    Intent lokasiDDIntent = new Intent();
    lokasiDDIntent.setAction(Intent.ACTION_SEND);
    lokasiDDIntent.putExtra(Intent.EXTRA_TEXT, lokasiTerakhir.koordinatDD());
    lokasiDDIntent.setType("text/plain");
    shareActionProvider.setShareIntent(lokasiDDIntent);
  }

}

Selain itu, saya juga bisa menambahkan menu untuk menampilkan lokasi pada aplikasi peta default seperti Google Map atau Google Earth. Sebagai contoh, saya menambahkan sebuah item menu baru di deklarasi XML:

<menu ...>

  ...

  <item android:id="@+id/menu_tampil" app:showAsAction="ifRoom"
     android:title="Tampilkan" />

</menu>

Setelah itu, saya menambahkan aksi bila menu tersebut dipilih dengan menggunakan kode program seperti berikut ini:

public class LokasiActivity extends Activity implements LocationListener {

  ...

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    ...
    // Tampilkan di aplikasi peta
    menu.findItem(R.id.menu_tampil).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
      @Override
      public boolean onMenuItemClick(MenuItem item) {
        if (lokasiTerakhir != null) {
          Intent intent = new Intent(Intent.ACTION_VIEW);
          intent.setData(Uri.parse("geo:" + lokasiTerakhir.koordinatDD()));
          if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
          }
        }
        return true;
      }
    });

    return true;
  }

  ...

}

Sekarang, bila menu Tampilkan dipilih, lokasi akan ditampilkan oleh aplikasi yang dapat menampilkan koordinat peta seperti Google Map dan Google Earth.