06 December 2015

Using Google Direction API on Google Maps in Android with Google Direction Library

Updated on

        Generally, Google has provided a Google Maps library for Android. This make it more convenient and faster for android app development. Also Google Place API.

        But if you want to show route navigation on Google Maps. You have to using Google Maps Direction API that doesn't provide as an android library. It's burdensome to call it with simple HTTP operation and get response data in JSON or XML format. because you have to digest these information by yourself.

        To more convenience, I write a library to make it easier to using Google Maps Direction API. At least until Google provide a Google Maps Direction library for android.

About Google Direction Library

        Google Direction Library was created to use with Android and related with Google Maps. So this library contain with Google Play Service (Maps) dependencies and also with Retrofit 2.0, GSON and Parceler.

        API Key from Google Developer Console is important for using this library. Otherwise it's sound like an API Key of Google Maps for Android API. But Google Maps Direction API is require a Server Key, not and Android Key.

        Don't forget to enable the Google Maps Direction API before get a new server key.



        To get a Server Key. It's same with Android Key, but you have to choosing a Server Key instead.




        Unnecessary for name of key, you can set any name. And leave the IP address field empty.

Usage

        Add dependencies for this library into build.gradle on your project.

dependencies {
    ...
    compile 'com.akexorcist:google-direction-library:1.2.0'
}

        The commands are very simple. This is a most simple example code.

GoogleDirection.withServerKey(String serverKey)
    .from(LatLng origin)
    .to(LatLng destination)
    .execute(DirectionCallback callback);

        serverKey An API Key that you get from Google Developer Console 
        origin An origin point in LatLng class that you want to get direction from there.
        destination A destination point in LatLgn class that you want to get direction to there.
        callback The response callback.

        Example.

String serverKey = "AIzaSyDFWdlR5DG1VYXSaMwG62ilxxxxxxxxx";
LatLng origin = new LatLng(37.7849569, -122.4068855);
LatLng destination = new LatLng(37.7814432, -122.4460177);
GoogleDirection.withServerKey(serverKey)
    .from(origin)
    .to(destination)
    .execute(new DirectionCallback() {
        @Override
        public void onDirectionSuccess(Direction direction, String rawBody) {
            // Do something here 
        }

        @Override
        public void onDirectionFailure(Throwable t) {
            // Do something here 
        }
    });

        The callback is contain with 2 methods, Success and Failure. If receive response from server successfully, onDirectionSuccess method will called.

        But if something problem was happen, such as internet unavailable or connection timeout, onDirectionFailure method will called instead. So you have to diagnose a problem from Throwable instance to solve a problem or show message to user.

Optional Parameters

        Besides these important methods that required to request direction information, Optional parameters are also available. Just call after to and before execute method.

transportMode(String transportMode)
language(String language)
unit(String unit)
avoid(String avoid)
transitMode(String transitMode)
alternativeRoute(boolean alternative)

Transport Mode

        Define the transport mode by using static value from TransportMode class.

TransportMode.DRIVING
TransportMode.WALKING
TransportMode.BICYCLING
TransportMode.TRANSIT

        The route will vary by transport mode. Bicycling is available for some place and Transit will combination of transit and walking (Example : walk to take a transit bus).

.withServerKey(serverKey)
    .from(...)
    .to(...)
    .transportMode(TransportMode.WALKING)
    .execute(...);

Language

        Define the language of response data from Google Maps Direction API by using static value from Language class.

Language.GERMAN
Language.ENGLISH
Language.KOREAN
Language.JAPAN
...

         Response data will be a define language. But not always, define language should be related with the country of request location.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .language(Language.THAILAND)
    .execute(...);


Unit

      Define the unit of distance in response data by using static value from Unit class.

Unit.METRIC
Unit.IMPERIAL

        METRIC Specifies usage of the metric system.
        IMPERIAL Specifies usage of the Imperial system.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .unit(Unit.METRIC)
    .execute(...);

Avoid

        Define in which way should be avoid by using static value from AvoidType class.

AvoidType.TOLLS
AvoidType.HIGHWAYS
AvoidType.FERRIES
AvoidType.INDOOR

        TOLLS Avoid toll roads/bridges.
        HIGHWAYS Avoid highways.
        FERRIES Avoid ferries.
        INDOOR Avoid indoor steps for walking and transit directions.

        This method can define more than one preferred type. Just call this method again with avoid type that you want to add. Because this method is adder not setter.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .avoid(AvoidType.FERRIES)
    .avoid(AvoidType.INDOOR)
    .execute(...);

Transit Mode

        Define the transit mode by using static value from TransitMode class.

TransitMode.BUS
TransitMode.SUBWAY
TransitMode.TRAIN
TransitMode.TRAM
TransitMode.RAIL

        BUS Prefer travel by bus.
        SUBWAY Prefer travel by subway.
        TRAIN Prefer travel by train.
        TRAM Prefer travel by tram and light rail.
        RAIL Prefer travel by train, tram, light rail, and subway.

        This method can define more than one preferred mode like the avoid method.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .transitMode(TransitMode.TRAIN)
    .transitMode(TransitMode.BUS)
    .transitMode(TransitMode.SUBWAY)
    .execute(...);

Alternative Route

        Define the response data can be more than one direction. Normally, the Google Maps Direction API will be calculate for the best direction way. But also can be calculate for multiple direction and send to client if request with alternative route.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .alternativeRoute(true)
    .execute(...);


        All of these methods can be set simultaneously.

GoogleDirection.withServerKey(serverKey)
    .from(...)
    .to(...)
    .transportMode(TransportMode.TRANSIT)
    .transitMode(TransitMode.BUS)
    .unit(Unit.METRIC)
    .execute(...);

Handle the Response Data

Check response status

        The response data will be in instance of Direction class. But it can be unsuccessful at anytime, so you have to check it before using. In the Direction class, can get status by call getStatus method.

@Override
public void onDirectionSuccess(Direction direction) {
    String status = direction.getStatus();
    // Do something
}

        All status type can check with static value in RequestResult class.

RequestResult.OK
RequestResult.NOT_FOUND
RequestResult.ZERO_RESULTS
RequestResult.MAX_WAYPOINTS_EXCEEDED
RequestResult.INVALID_REQUEST
RequestResult.OVER_QUERY_LIMIT
RequestResult.REQUEST_DENIED
RequestResult.UNKNOWN_ERROR

        OK Valid response.
        NOT_FOUND Origin or destination or waypoints could not be geocoded.
        ZERO_RESULTS No route could be found.
        MAX_WAYPOINTS_EXCEEDED indicates that too many waypoints were provided in the request (My library don't have waypoint setting method yet, so don't worry.)
        INVALID_REQUEST Request was invalid. Common causes of this status include an invalid parameter or parameter value. (My library will not request with invalid parameter, so don't worry about this status too.)
        OVER_QUERY_LIMIT Service has received too many requests from your application within the allowed time period.
        REQUEST_DENIED Service denied use of the directions service by your application. Maybe cause of invalid API key.
        UNKNOWN_ERROR Directions request could not be processed due to a server error. The request may succeed if you try again.

@Override
public void onDirectionSuccess(Direction direction) {
    String status = direction.getStatus();
    if(status.equals(RequestResult.OK)) {
        // Do something
    } else if(status.equals(RequestResult.NOT_FOUND)) {
        // Do something
    }
}

        If you want checking for OK status only, you can use isOK method.

@Override
public void onDirectionSuccess(Direction direction) {
    if(direction.isOK()) {
        // Do something
    }
}

Route

        Direction from origin to destination location, called Route. And Google Maps Direction API can provide more than one route if you request for alternative route.

Waypoint

        Google Maps Direction API can request for visiting some place in once navigation. Example : you want to go to eat some food at restaurant and walk around shopping mall a few minutes, then go to work in your office. The place between direction called Waypoint.


        Unfortunately, my library doesn't supported for waypoint now. But I will add this feature later.

Leg

        The direction way from one location to another location, called Leg. If your direction have many waypoint. The leg will increase as the waypoint.


        But my library doesn't supported for waypoint yet. So leg in response direction from my library will have only one leg.

Step

        In the navigation, it should have an instruction on the journey. E.g. "Straight forward", "Turn Left". That's called Step. And one route can be contain with many steps by depending on the complexity of the route.


        The image above, there are 7 steps.

Using direction information in your app

        The result will have at least 1 route if response status is OK. (Or multiple route for alternative route request). The Route will be in the form of array.


        And 1 route will have 1 leg (No waypoint). The Leg also in the form of array and contain with step array.

        To retrieve the Route instance from array at index 0. (Yes, it have only 1 member)

Route route = direction.getRouteList().get(0);

        To retrieve the Leg instance from array at index 0. (Yeah, still have only 1 member)

Route route = direction.getRouteList().get(0);
Leg leg = route.getLegList().get(0);

       Because 1 leg can be contain with many step. So you have to retrieve the Step in array.

Route route = direction.getRouteList().get(0);
Leg leg = route.getLegList().get(0);
List<StepList> = leg.getStepList();


        In Step instance, you can retrieve start location and end location of it directly by using getStartLocation and getEndLocation then using getCoordination method. Location data will be in LatLgn instance.

LatLng start = step.getStartLocation().getCoordination();
LatLng end = step.getEndLocation().getCoordination();

        But it's too boring, right? because you have to retrieve all of these value with for loop then use as needed later.

        So you can retrieve them in LatLgn array directly. Just using getDirectionPoint method.

ArrayList<LatLng> pointList = leg.getDirectionPoint();

        For some transit direction. If the Step instance is walking direction, it can be contain with Step inside.


        Because the transit direction is combination of transit and walking.


        To determine the step, using getTravelMode method.

String travelMode = step.getTravelMode();
        And compare result with the static value in TransportMode class.

        If you want to retrieve the location of the each step.

ArrayList<LatLng> sectionList = leg.getSectionPoint();

        For the total distance and duration of the route.

Info distanceInfo = leg.getDistance();
Info durationInfo = leg.getDuration();
String distance = distanceInfo.getText();
String duration = durationInfo.getText();


       Retrieve the distance and duration of the each step.

Info distanceInfo = step.getDistance();
Info durationInfo = step.getDuration();
String distance = distanceInfo.getText();
String duration = durationInfo.getText();

        Navigation instruction is available by retrieve from Maneuver instance of the each step. HTML instruction also available too but don't forget to convert them from HTML format to show in your app.

String maneuver = step.getManeuver();
String instruction = step.getHtmlInstruction();

Show Direction on Google Maps

        To draw a direction route on Google Maps. It must be in PolylineOptions instance. You can convert the route into PolylineOptions by retrieve Leg instance from Route instance before, then convert it by using DirectionConverter class.

ArrayList<LatLng> directionPositionList = leg.getDirectionPoint();
PolylineOptions polylineOptions = DirectionConverter.createPolyline(this, directionPositionList, 5, Color.RED);
googleMap.addPolyline(polylineOptions);

        The 5 value is the width of line in DP unit and Color.Red is the color of the line.


        For transit navigation, you have to retrieve Step in array, then convert them into PolylineOptions in array like this.

List<Step> stepList = direction.getRouteList().get(0).getLegList().get(0).getStepList();
ArrayList<PolylineOptions> polylineOptionList = DirectionConverter.createTransitPolyline(this, stepList, 5, Color.RED, 3, Color.BLUE);
for (PolylineOptions polylineOption : polylineOptionList) {
    googleMap.addPolyline(polylineOption);
}

        The transit line is red color with 5dp width. The walking line is blue with 3dp width.


        Each step can retrieve the transit information with TransitDetail instance.

TransitDetail transitDetail = step.getTransitDetail();

        Transit information contain with many detail. So you can using these data to show to user.

StopPoint arrivalStopPoint = transitDetail.getArrivalStopPoint();
StopPoint departureStopPoint = transitDetail.getDepartureStopPoint();
TimeInfo arriveTimeInfo = transitDetail.getArrivalTime();
TimeInfo departureTimeInfo = transitDetail.getDepartureTime();
String headSign = transitDetail.getHeadsign();
String stopNumber = transitDetail.getStopNumber();
Line transitLine = transitDetail.getLine();

What's new in v1.0.5

        In version 1.0.5, some issue was fixed and GoogleDirectionConfiguration class was added for customize something.

Enable the log from when service calling

GoogleDirectionConfiguration.getInstance().setLogEnabled(true);

        This configuration will set to false by default.

Using your custom OkHttpClient instance

OkHttpClient client = ....
GoogleDirectionConfiguration.getInstance().setCustomClient(client);

        Call this method only once before request the direction.

Proguard Rules

-keep class com.akexorcist.googledirection.** { *; }
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }
-keep class retrofit.** { *; }
-keepclasseswithmembers class * {
@retrofit.http.* <methods>;
}
-dontwarn com.squareup.okhttp.**
-dontwarn rx.**
-dontwarn retrofit2.**
-dontwarn okio.*

Demo

        Preview this library by install demo app on Play Store
        Demo App for Google Direction Library [Play Store]

Source Code

        If you want  the source code of this library. Get it on GitHub. It's open source for everyone.
        Google Direction Library [GitHub]

Reference Documentation

        Intro : The Google Maps Directions API {Google Developers]