Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find route with Androids Google Maps API

I want to be able to display the route between two user-defined geographical points using the Google Maps API for Android. I also want to be able to let the user choose what type of route to display, whether it be walking, cycling, car etc. Additionally, I want to be able to calculate the time and distance it would take to use this route. I've tried searching the web and looking at other stackoverflow questions, but to no avail. How would I go about this? How would I be able to code this.

//----EDIT----//

I would also like to get traffic information such as busy routes, congestion, etc.

like image 672
Buneme Kyakilika Avatar asked Apr 13 '13 17:04

Buneme Kyakilika


2 Answers

Here's some code to help you.

String url= 
"http://maps.googleapis.com/maps/api/directions/json?origin=" 
+ origin.latitude + "," + origin.longitude +"&destination=" 
+ destination.latitude + "," + destination.longitude + "&sensor=false";

To fetch the data with androidhttpclient, do something like this:

HttpResponse response;
HttpGet request;
AndroidHttpClient client = AndroidHttpClient.newInstance("somename");

request = new HttpGet(url);
response = client.execute(request);

InputStream source = response.getEntity().getContent();
String returnValue = buildStringIOutils(source);

return returnValue;

where buildStringIOUtils is:

private String buildStringIOutils(InputStream is) {
    try {
        return IOUtils.toString(is, "UTF-8");
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

You can then extract the actual polyline from the JSON-response with something like this:

JSONObject result = new JSONObject(returnValue);
JSONArray routes = result.getJSONArray("routes");

                    long distanceForSegment = routes.getJSONObject(0).getJSONArray("legs").getJSONObject(0).getJSONObject("distance").getInt("value");

                    JSONArray steps = routes.getJSONObject(0).getJSONArray("legs")
                            .getJSONObject(0).getJSONArray("steps");

                    List<LatLng> lines = new ArrayList<LatLng>();

                    for(int i=0; i < steps.length(); i++) {
                        String polyline = steps.getJSONObject(i).getJSONObject("polyline").getString("points");

                        for(LatLng p : decodePolyline(polyline)) {
                            lines.add(p);
                        }
                    }

where the method decodePolyline is this:

    /** POLYLINE DECODER - http://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java **/
    private List<LatLng> decodePolyline(String encoded) {

        List<LatLng> poly = new ArrayList<LatLng>();

        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng((double) lat / 1E5, (double) lng / 1E5);
            poly.add(p);
        }

        return poly;
    }

You can then add the polyline to the map with this:

Polyline polylineToAdd = mMap.addPolyline(new PolylineOptions().addAll(lines).width(3).color(Color.RED));

To change mode, add this to the url (See https://developers.google.com/maps/documentation/directions/): &mode=YOUR_MODE

driving (default) indicates standard driving directions using the road network.

walking requests walking directions via pedestrian paths & sidewalks (where available).

bicycling requests bicycling directions via bicycle paths & preferred streets (where available).

transit requests directions via public transit routes (where available).

Edit: About the "I would also like to get traffic information such as busy routes, congestion, etc." I have not looked into this, but my code should get you started pretty good.

Edit2: Found this in the google directions api: "For Driving Directions: Maps for Business customers can specify the departure_time to receive trip duration considering current traffic conditions. The departure_time must be set to within a few minutes of the current time."

like image 118
ullstrm Avatar answered Oct 28 '22 19:10

ullstrm


Android Google Maps Routing example code using Wrapper library

Use Android Studio Gradle entry:

compile 'com.github.jd-alexander:library:1.1.0'

MainActivity.java

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.directions.route.Route;
import com.directions.route.RouteException;
import com.directions.route.Routing;
import com.directions.route.RoutingListener;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

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

public class MainActivity extends FragmentActivity implements OnMapReadyCallback, LocationListener, GoogleMap.OnMarkerClickListener, RoutingListener {

    private GoogleMap mMap = null;
    private LocationManager locationManager = null;
    private FloatingActionButton fab = null;
    private TextView txtDistance, txtTime;

    //Global UI Map markers
    private Marker currentMarker = null;
    private Marker destMarker = null;
    private LatLng currentLatLng = null;
    private Polyline line = null;

    //Global flags
    private boolean firstRefresh = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);
        Constants.POINT_DEST = new LatLng(18.758663, 73.382025);        //Lonavala destination.
        //Load the map fragment on UI
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
        txtDistance = (TextView)findViewById(R.id.txt_distance);
        txtTime = (TextView)findViewById(R.id.txt_time);
        fab = (FloatingActionButton)findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MainActivity.this.getRoutingPath();
                Snackbar.make(v, "Fetching Route", Snackbar.LENGTH_SHORT).show();
            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        firstRefresh = true;
        //Ensure the GPS is ON and location permission enabled for the application.
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        if (!PermissionCheck.getInstance().checkGPSPermission(this, locationManager)) {
            //GPS not enabled for the application.
        } else if (!PermissionCheck.getInstance().checkLocationPermission(this)) {
            //Location permission not given.
        } else {
            Toast.makeText(MainActivity.this, "Fetching Location", Toast.LENGTH_SHORT).show();
            try {
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 0, this);
            } catch(Exception e)
            {
                Toast.makeText(MainActivity.this, "ERROR: Cannot start location listener", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    protected void onPause() {
        if (locationManager != null) {
            //Check needed in case of  API level 23.

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            }
            try {
                locationManager.removeUpdates(this);
            } catch (Exception e) {
            }
        }
        locationManager = null;
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    @Override
    public void onMapReady(GoogleMap googleMap)
    {
        mMap = googleMap;
        //mMap.getUiSettings().setZoomControlsEnabled(true);
        mMap.getUiSettings().setCompassEnabled(true);
        mMap.getUiSettings().setAllGesturesEnabled(true);
        mMap.setOnMarkerClickListener(this);
    }

    /**
     * @desc LocationListener Interface Methods implemented.
     */

    @Override
    public void onLocationChanged(Location location)
    {
        double lat = location.getLatitude();
        double lng = location.getLongitude();
        currentLatLng = new LatLng(lat, lng);
        if(firstRefresh)
        {
            //Add Start Marker.
            currentMarker = mMap.addMarker(new MarkerOptions().position(currentLatLng).title("Current Position"));//.icon(BitmapDescriptorFactory.fromResource(R.drawable.location)));
            firstRefresh = false;
            destMarker = mMap.addMarker(new MarkerOptions().position(Constants.POINT_DEST).title("Destination"));//.icon(BitmapDescriptorFactory.fromResource(R.drawable.location)));
            mMap.moveCamera(CameraUpdateFactory.newLatLng(Constants.POINT_DEST));
            mMap.animateCamera(CameraUpdateFactory.zoomTo(15));
            getRoutingPath();
        }
        else
        {
            currentMarker.setPosition(currentLatLng);
        }
    }

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

    @Override
    public void onProviderEnabled(String provider) {}

    @Override
    public void onProviderDisabled(String provider) {}

    /**
     * @desc MapMarker Interface Methods Implemented.
     */

    @Override
    public boolean onMarkerClick(Marker marker)
    {
        if(marker.getTitle().contains("Destination"))
        {
            //Do some task on dest pin click
        }
        else if(marker.getTitle().contains("Current"))
        {
            //Do some task on current pin click
        }
        return false;
    }

    /**
     *@desc Routing Listener interface methods implemented.
     **/
    @Override
    public void onRoutingFailure(RouteException e)
    {
        Toast.makeText(MainActivity.this, "Routing Failed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onRoutingStart() { }

    @Override
    public void onRoutingSuccess(ArrayList<Route> list, int i)
    {
        try
        {
            //Get all points and plot the polyLine route.
            List<LatLng> listPoints = list.get(0).getPoints();
            PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true);
            Iterator<LatLng> iterator = listPoints.iterator();
            while(iterator.hasNext())
            {
                LatLng data = iterator.next();
                options.add(data);
            }

            //If line not null then remove old polyline routing.
            if(line != null)
            {
                line.remove();
            }
            line = mMap.addPolyline(options);

            //Show distance and duration.
            txtDistance.setText("Distance: " + list.get(0).getDistanceText());
            txtTime.setText("Duration: " + list.get(0).getDurationText());

            //Focus on map bounds
            mMap.moveCamera(CameraUpdateFactory.newLatLng(list.get(0).getLatLgnBounds().getCenter()));
            LatLngBounds.Builder builder = new LatLngBounds.Builder();
            builder.include(currentLatLng);
            builder.include(Constants.POINT_DEST);
            LatLngBounds bounds = builder.build();
            mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
        }
        catch (Exception e) 
        {
            Toast.makeText(MainActivity.this, "EXCEPTION: Cannot parse routing response", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRoutingCancelled() 
    {
        Toast.makeText(MainActivity.this, "Routing Cancelled", Toast.LENGTH_SHORT).show();
    }

    /**
     * @method getRoutingPath
     * @desc Method to draw the google routed path.
     */
    private void getRoutingPath()
    {
        try
        {
            //Do Routing
            Routing routing = new Routing.Builder()
                    .travelMode(Routing.TravelMode.DRIVING)
                    .withListener(this)
                    .waypoints(currentLatLng, Constants.POINT_DEST)
                    .build();
            routing.execute();
        }
        catch (Exception e)
        {
            Toast.makeText(MainActivity.this, "Unable to Route", Toast.LENGTH_SHORT).show();
        }
    }

}

Constants.java

/**
 * @class Constants
 * @desc Constant class for holding values at runtime.
 */
public class Constants
{

    //Map LatLong points
    public static LatLng POINT_DEST = null;

}

activity_map.xml

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/viewA"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.1"
        android:orientation="horizontal">

        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context="com.packagename.MainActivity" />


    </LinearLayout>

    <LinearLayout
        android:id="@+id/viewB"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.9"
        android:gravity="center|left"
        android:paddingLeft="20dp"
        android:background="#FFFFFF"
        android:orientation="vertical" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16dp"
            android:text="Distance ?"
            android:paddingTop="3dp"
            android:paddingLeft="3dp"
            android:paddingBottom="3dp"
            android:id="@+id/txt_distance" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="17dp"
            android:paddingLeft="3dp"
            android:text="Duration ?"
            android:id="@+id/txt_time" />

    </LinearLayout>

</LinearLayout>

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:clickable="true"
    android:src="@android:drawable/ic_dialog_map"
    app:layout_anchor="@id/viewA"
    app:layout_anchorGravity="bottom|right|end"/>

</android.support.design.widget.CoordinatorLayout>
like image 31
Rahul Raina Avatar answered Oct 28 '22 19:10

Rahul Raina