in Android Tutorial

Penerapan Haversine Formula Pada Aplikasi Android


 
Haversine Formula adalah persamaan penting dalam navigasi, memberikan jarak yang jauh lingkaran antara dua titik pada bola dari garis bujur (longitude) dan garis lintang (latitude). Haversine formula merupakan kasus khusus dari rumus yang lebih umum di trigonometri bola, hukum haversines, yang berkaitan dengan sisi dan sudut segitiga bola. Tabel pertama haversines dalam bahasa Inggris diterbitkan oleh James Andrew pada tahun 1805. Baca selengkapnya pada Wikipedia atau Movable Type’s site.
 

Dalam tutorial ini perhitungan harversine formula berada pada sintak SQL di web service. Alur jalannya aplikasi yaitu ketika perangkat Android meluncurkan aplikasi ini akan mengirim parameter koordinat bumi (Latitude, Longitude) berdasarkan lokasi perangkat Android ke web service kemudian melakukan perhitungan dan menampilkan data lokasi-lokasi dari lokasi terdekat sampai lokasi terjauh dari perangkat Android pengguna. berikut ini adalah sintak SQL haversine formula pada web service.

SELECT id, nama, gambar, (6371 * ACOS(SIN(RADIANS(lat)) * SIN(RADIANS($lat)) + COS(RADIANS(lng - $lng)) * COS(RADIANS(lat)) * COS(RADIANS($lat)))) AS jarak FROM wisata HAVING jarak < 6371 ORDER BY jarak ASC

 

device-2016-04-02-110654

 
Langkah pertama yaitu membuat database dengan nama kuncoro_haversine kemudian membuat tabel dengan nama wisata dan struktur tabelnya seperti berikut ini :

Column Name Data Type Lenght Primary Key Not null Auto Increment
id int 5 v v v
nama varchar 30
gambar varchar 100
lat double
lng double

 

Membuat web service untuk parsing data aplikasi android.
koneksi.php
Sebagai koneksi aplikasi ke database. Coding-nya disini.
 
haversine.php
Untuk menampilkan nama lokasi-lokasi terdekat dari perangkat Andorid setelah menerima parameter koordinat bumi lokasi perangkat Android. Coding-nya disini.
 

Buat project baru di Android Studio File ⇒ New Project. Kemudian pilih Empty Activity dan melanjutkannya hingga selesai.

activity_main.xml
Layout untuk menampilkan data-data lokasi terdekat dari posisi perangkat Andorid pengguna.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="@color/list_divider"
        android:dividerHeight="2dp"
        android:listSelector="@drawable/list_row_selector" />

</android.support.v4.widget.SwipeRefreshLayout>

 

list_row.xml
Sebagai tampilan custom listview yang berisi gambar, nama dan jarak terdekat dari posisi pengguna.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_row_selector"
    android:padding="8dp" >

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/gambar"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentLeft="true"
        android:layout_marginRight="8dp" />

    <TextView
        android:id="@+id/nama"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/gambar"
        android:layout_toRightOf="@+id/gambar"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/jarak"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@+id/gambar"
        android:layout_below="@+id/nama" />

</RelativeLayout>

Buat folder drawable didalam res dan isi file baru dengan nama list_row_bg.xml, list_row_bg_hover.xml, dan list_row_selector.xml sebagai style listview.
list_row_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:startColor="@color/list_row_start_color"
        android:endColor="@color/list_row_end_color"
        android:angle="270" />
</shape>

 

list_row_bg_hover.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:angle="270"
        android:endColor="@color/list_row_hover_end_color"
        android:startColor="@color/list_row_hover_start_color" />

</shape>

 

list_row_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/list_row_bg" android:state_pressed="false" android:state_selected="false"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="true"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="false" android:state_selected="true"/>

</selector>

 

Masuk folder res=>values=>color.xml dan tambahkan code seperti berikut :
color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
    <color name="list_divider">#d9d9d9</color>
    <color name="list_row_start_color">#ffffff</color>
    <color name="list_row_end_color">#ffffff</color>
    <color name="list_row_hover_start_color">#ebeef0</color>
    <color name="list_row_hover_end_color">#ebeef0</color>
</resources>

 

Buka build.gradle dan tambahkan volley library didalamnya.

compile 'com.mcxiaoke.volley:library:1.0.19'

build.gradle

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.1'
    compile 'com.mcxiaoke.volley:library:1.0.19' /*tambahan*/
}

 

Agar project terstruktur dan terorganisir, buat 4 paket dengan nama adapter, app, module, dan util. Untuk membuat paket baru , klik kanan pada src=>New=>Peckage dan memberikan nama paket . Contoh : com.dedykuncoro.kuncorohaversine.

struktur haversine

 
Buat class dengan nama LruBitmapCache.java didalam package util dan tambah coding seperti dibawah ini. Class ini berfingsi untuk mengatur caching network image dalam penyimpanan.
LruBitmapCache.java

package com.dedykuncoro.kuncorohaversine.util;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

import com.android.volley.toolbox.ImageLoader;

/**
 * Created by Kuncoro on 03/29/2016.
 */
public class LruBitmapCache extends LruCache<String, Bitmap> implements
        ImageLoader.ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;

        return cacheSize;
    }

    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }

    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
}

 

Buat class AppController.java didalam package app dan tambah coding seperti dibawah ini. Class tunggal yang menginisialisasi class global yang diperlukan. Semua objek yang berhubungan dengan volley diinisialisasi di sini.
AppController.java

package com.dedykuncoro.kuncorohaversine.app;

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import com.dedykuncoro.kuncorohaversine.util.LruBitmapCache;

/**
 * Created by Kuncoro on 03/29/2016.
 */
public class AppController extends Application {

    public static final String TAG = AppController.class.getSimpleName();

    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;

    private static AppController mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized AppController getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

 

Buat class Jarak.java didalam package module dan tambahkan coding seperti dibawah ini. Class ini berfungsi sebagai membuat objek untuk setiap item yang diparsing JSON. Objek ini berisi informasi seperti id, nama, jarak, dan url gambar.
Jarak.java

package com.dedykuncoro.kuncorohaversine.module;

/**
 * Created by Kuncoro on 03/29/2016.
 */
public class Jarak {

    private String nama, jarak, gambar;

    public Jarak() {
    }

    public Jarak(String nama, String jarak, String gambar) {
        this.nama = nama;
        this.jarak = jarak;
        this.gambar = gambar;
    }

    public String getNama() {
        return nama;
    }

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

    public String getJarak() {
        return jarak;
    }

    public void setJarak(String jarak) {
        this.jarak = jarak;
    }

    public String getGambar() {
        return gambar;
    }

    public void setGambar(String gambar) {
        this.gambar = gambar;
    }

}

 

Buat class CustomListAdapter.java didalam package adapter dan tambahkan coding seperti dibawah ini. Class ini berfungsi sebagai menampilkan data seperti id, nama, jarak, data url gambar kemudian ditampilkan ke dalam listview.
CustomListAdapter.java

package com.dedykuncoro.kuncorohaversine.adapter;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.NetworkImageView;
import com.dedykuncoro.kuncorohaversine.R;
import com.dedykuncoro.kuncorohaversine.app.AppController;
import com.dedykuncoro.kuncorohaversine.module.Jarak;

import java.util.List;

/**
 * Created by Kuncoro on 03/29/2016.
 */
public class CustomListAdapter extends BaseAdapter {
    private Activity activity;
    private LayoutInflater inflater;
    private List<Jarak> jarakItems;
    ImageLoader imageLoader;

    public CustomListAdapter(Activity activity, List<Jarak> jarakItems) {
        this.activity = activity;
        this.jarakItems = jarakItems;
    }

    @Override
    public int getCount() {
        return jarakItems.size();
    }

    @Override
    public Object getItem(int location) {
        return jarakItems.get(location);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (inflater == null)
            inflater = (LayoutInflater) activity
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (convertView == null)
            convertView = inflater.inflate(R.layout.list_row, null);

        if (imageLoader == null)
            imageLoader = AppController.getInstance().getImageLoader();
        NetworkImageView thumbNail = (NetworkImageView) convertView
                .findViewById(R.id.gambar);
        TextView nama = (TextView) convertView.findViewById(R.id.nama);
        TextView jarak = (TextView) convertView.findViewById(R.id.jarak);

        Jarak j = jarakItems.get(position);

        thumbNail.setImageUrl(j.getGambar(), imageLoader);
        nama.setText(j.getNama());
        jarak.setText(j.getJarak()+" Km");

        return convertView;
    }

}

 

Buka MainActivity.java dan tambahkan coding seperti dibawah ini. Class ini digunakan untuk mengirim parameter koordinat bumi perangkat android ke web service, kemudian web service merespon dan menghitung menggunakan haversine formula jarak antara lokasi perangkat Android pengguna dengan lokasi-lokasi yang berada dalam database. Setelah dilakukan perhitungan, maka web service akan menampilkan JSON data-data lokasi berdasarkan lokasi terdekat dari perangkat pengguna dan data tersebut akan ditampilkan pada listview aplikasi Android.
MainActivity.java

package com.dedykuncoro.kuncorohaversine;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import android.widget.Toast;

import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.JsonArrayRequest;
import com.dedykuncoro.kuncorohaversine.adapter.CustomListAdapter;
import com.dedykuncoro.kuncorohaversine.app.AppController;
import com.dedykuncoro.kuncorohaversine.module.Jarak;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Kuncoro on 03/29/2016.
 */
public class MainActivity extends AppCompatActivity implements LocationListener,
        SwipeRefreshLayout.OnRefreshListener {

    SwipeRefreshLayout swipe;
    ListView list;
    CustomListAdapter adapter;
    List<Jarak> itemList = new ArrayList<>();
    Double latitude, longitude;
    Criteria criteria;
    Location location;
    LocationManager locationManager;
    String provider;

    // sesuaikan ip laptop/PC atau menggunakan ip emulator bawaan android studio 10.0.2.2
    private static final String url = "http://172.20.10.2/android/haversine/haversine.php?lat=";
    private static final String TAG = MainActivity.class.getSimpleName();

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

        // menyamakan variabel pada layout dan java
        list    = (ListView) findViewById(R.id.list);
        swipe   = (SwipeRefreshLayout) findViewById(R.id.swipe);

        // mengisi data dari adapter ke listview
        adapter = new CustomListAdapter(this, itemList);
        list.setAdapter(adapter);

        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        criteria = new Criteria();

        provider = locationManager.getBestProvider(criteria, false);

        swipe.setOnRefreshListener(this);

        swipe.post(new Runnable() {
                       @Override
                       public void run() {
                           swipe.setRefreshing(true);
                           lokasi();
                       }
                   }
        );

    }

    @Override
    public void onRefresh(){
        lokasi();
    }

    // fungsi ngecek lokasi GPS device pengguna
    private void lokasi(){
        location = locationManager.getLastKnownLocation(provider);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }

        // permintaan update lokasi device dalam waktu 10 detik
        locationManager.requestLocationUpdates(provider, 10000, 1, this);

        if(location!=null){
            onLocationChanged(location);
            callListVolley(latitude, longitude);
        }else{
            Toast.makeText(getBaseContext(), "Lokasi device pengguna tidak ditemukan.\nMohon hidupkan GPS.",
                    Toast.LENGTH_LONG).show();
           /* latitude longitude Alun-alun Demak sebagai default jika tidak ditemukan lokasi dari device pengguna */
            callListVolley(-6.894796, 110.638413);
        }
    }

    // untuk menampilkan lokasi wisata terdekat dari device pengguna
    private void callListVolley(double lat, double lng) {
        itemList.clear();
        adapter.notifyDataSetChanged();

        swipe.setRefreshing(true);

        JsonArrayRequest jArr = new JsonArrayRequest(url + lat +"&lng="+ lng,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.d(TAG, response.toString());

                        // Parsing json
                        for (int i = 0; i < response.length(); i++) {
                            try {

                                JSONObject obj = response.getJSONObject(i);
                                Jarak j = new Jarak();
                                j.setNama(obj.getString("nama"));
                                j.setGambar(obj.getString("gambar"));

                                double jarak = Double.parseDouble(obj.getString("jarak"));

                                j.setJarak(""+round(jarak, 2));

                                itemList.add(j);

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }

                        }

                        // memberitahu adapter jika ada perubahan data
                        adapter.notifyDataSetChanged();

                        swipe.setRefreshing(false);
                    }
                }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                VolleyLog.d(TAG, "Error: " + error.getMessage());
                Toast.makeText(getBaseContext(), error.getMessage(), Toast.LENGTH_LONG).show();
                swipe.setRefreshing(false);
            }
        });

        // menambah permintaan ke queue
        AppController.getInstance().addToRequestQueue(jArr);
    }

    @Override
    public void onBackPressed(){
        finish();
        System.exit(0);
    }

    // untuk menyederhanakan angka dibelakan koma jarak
    public static double round(double value, int places) {
        if (places < 0) throw new IllegalArgumentException();

        long factor = (long) Math.pow(10, places);
        value = value * factor;
        long tmp = Math.round(value);
        return (double) tmp / factor;
    }

    // untuk menentukan lokasi gps dari device pengguna
    @Override
    public void onLocationChanged(Location location) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();

        // untuk melihat latitude longitude posisi device pengguna pada logcat ditemukan atau tidak
        Log.d(TAG, " "+ latitude +", "+longitude);
    }

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

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }
}

 

Tambahkan beberapa perijinan pada AndroidManifest.xml seperti dibawah ini :
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dedykuncoro.kuncorohaversine">

    <!-- Tambahan -->
    <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"/>
    <!-- The following two permissions are not required to use
         Google Maps Android API v2, but are recommended. -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <!-- Tambahan -->

    <application
        android:name=".app.AppController" <!-- Tambahan -->
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 
Run Aplikasinya.

Download Source Code :

Download

Please share 🙂


Share on FacebookTweet about this on TwitterShare on Google+Pin on PinterestShare on LinkedInEmail this to someone

Related Posts