What is this: This tutorial shows how to use the Android built in access to the Google DrivingDirections API. The first step to create an actual Navigation-System.
Difficulty: 2.5 of 5

What it will look like:
Screenshot was taken with sdk-version m3 (m5 looks similar)


Description: Creating a Navigation-System has never been so easy. Get the DrivingDirections from A to B with less then a handful of codelines.
0.) As usual lets start with the Layout. Its pretty simple, just a "Do it"-Button and a MapView:
Using java Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <Button android:id="@+id/cmd_submit"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Show me the Route!"/>
- <view class="com.google.android.maps.MapView"
- android:id="@+id/myMapView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"/>
- </LinearLayout>
Parsed in 0.166 seconds, using GeSHi 1.0.8.4
1.) Now to the DrivingDirections this tutorial is actually about. The DrivingDirections which are very very close related to GoogleMaps are fully integrated into Android. You can find them in the package: "com.google.googlenav"-Package.
Using java Syntax Highlighting
- import com.google.googlenav.DrivingDirection;
Parsed in 0.129 seconds, using GeSHi 1.0.8.4
It provides the DrivingDirection-Class with the following Constructor:
Using java Syntax Highlighting
- new DrivingDirection(MapPoint from, String from_string, MapPoint to, String to_string);
Parsed in 0.142 seconds, using GeSHi 1.0.8.4
You can call it either with two MapPoints or with the Description of two places (in this case pass null to the MapPoints-Parameters). In this Tutorial we will be using two pre-defined gps-coordinates, from the Google-Headquarters to the next beach
The request for the DrivingDirections will be fired on a Click to our Button we defined in xml.
Using java Syntax Highlighting
- /* Submit one dummy search */
- findViewById(R.id.cmd_submit).setOnClickListener(new OnClickListener(){
- @Override
- public void onClick(View arg0) {
- MapPoint mpFrom = new MapPoint(37423157, -122085008); // 37.423157,-122.085008
- MapPoint mpTo = new MapPoint(37495999, -122457508); // 37.495999,-122.457508
- MyDrivingDirectionsActivity.this.startFetchDirections(mpFrom,"", mpTo, "");
- }
- });
Parsed in 0.133 seconds, using GeSHi 1.0.8.4
So, a click to that button will finally cause a field in our class to be set, which was initiated with null:
Using java Syntax Highlighting
- private DrivingDirection myDD = null;
Parsed in 0.058 seconds, using GeSHi 1.0.8.4
2.) In the onCreate(...);-Function we applied a custom Overlay to our MapView using:
Using java Syntax Highlighting
- /* Find the MapView we defined in the main.xml. */
- MapView mapViewFromXML = (MapView)this.findViewById(R.id.myMapView);
- /* Retrieve its OverlayController. */
- OverlayController myOC = mapViewFromXML.createOverlayController();
- /* Add a new instance of our fancy Overlay-Class to the MapView. */
- myOC.add(new MyMapDrivingDirectionsOverlay(this), true);
Parsed in 0.057 seconds, using GeSHi 1.0.8.4
3.) This custom Overlay will draw the DrivingDirections-Route(if found) above the Map. It mainly contains just code in the onDraw()-Function which will draw the route in a fancy way, like it can be seen in the picture in the beginning. The path-drawing-code is the following:
Using java Syntax Highlighting
- //...
- /* Check to see if the route is too long. */
- if (!dd.routeTooLong()) {
- /* Retrieve all (Map)Points of the route Found. */
- MapPoint[] route = dd.getRoute();
- if(route == null)
- return;
- /* Loop through all MapPoints returned. */
- for (MapPoint current : route) {
- /* Transform current MapPoint's Lat/Lng
- * into corresponding point on canvas
- * using the pixelCalculator. */
- if(current != null){
- MapPointToScreenCoords(current, screenCoords, pxC);
- /* Add point to path. */
- thePath.lineTo(screenCoords[0], screenCoords[1]);
- }
- }
- }
- this.pathPaint.setStyle(Paint.Style.STROKE);
- /* Draw the actual route to the canvas. */
- canvas.drawPath(thePath, this.pathPaint);
- //...
Parsed in 0.061 seconds, using GeSHi 1.0.8.4
:idea: Note: After having clicked the Button the DrivingDirections are not instantly drawn. You need to move the map at least one pixel, because we need to invalidate() the MapView once. This can also be accomplished using a Handler like shown in the The Pizza Timer-Tutorial.
The Full Source:
"/res/layout/main.xml":
Using java Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <Button android:id="@+id/cmd_submit"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Show me the Route!"/>
- <view class="com.google.android.maps.MapView"
- android:id="@+id/myMapView"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"/>
- </LinearLayout>
Parsed in 0.061 seconds, using GeSHi 1.0.8.4
"/src/your_package_structure/MyDrivingDirectionsActivity.java":
Using java Syntax Highlighting
- package org.anddev.android.drivingdirections;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import com.google.android.maps.MapActivity;
- import com.google.android.maps.MapView;
- import com.google.android.maps.OverlayController;
- import com.google.googlenav.DrivingDirection;
- import com.google.googlenav.map.MapPoint;
- public class MyDrivingDirectionsActivity extends MapActivity {
- private DrivingDirection myDD = null;
- private boolean foundDirections = false;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.main);
- /* Find the MapView we defined in the main.xml. */
- MapView mapViewFromXML = (MapView)this.findViewById(R.id.myMapView);
- /* Retrieve its OverlayController. */
- OverlayController myOC = mapViewFromXML.createOverlayController();
- /* Add a new instance of our fancy Overlay-Class to the MapView. */
- myOC.add(new MyMapDrivingDirectionsOverlay(this), true);
- /* Submit one dummy search */
- findViewById(R.id.cmd_submit).setOnClickListener(new OnClickListener(){
- @Override
- public void onClick(View arg0) {
- MapPoint mpFrom = new MapPoint(37423157, -122085008); // 37.423157,-122.085008
- MapPoint mpTo = new MapPoint(37495999, -122457508); // 37.495999,-122.457508
- MyDrivingDirectionsActivity.this.startFetchDirections(mpFrom,"", mpTo, "");
- }
- });
- }
- /** Offers the DrivingDirections to the Overlay. */
- public DrivingDirection getDrivingDirections() {
- return this.myDD;
- }
- private void startFetchDirections(MapPoint from_pos, String from_name,
- MapPoint to_pos, String to_name) {
- /* mDD is a class variable for the activity that will
- * hold an instance of the DrivingDirection object created here. */
- this.myDD = new DrivingDirection(from_pos, from_name, to_pos, to_name);
- if (this.myDD != null) {
- /* Add the request the dispatcher */
- this.getDispatcher().addDataRequest(this.myDD);
- Thread t = new Thread(new Runnable() {
- public void run() {
- /* Wait for the search to be complete... */
- while (!MyDrivingDirectionsActivity.this.myDD.isComplete()) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- Log.e("DEBUGTAG", "'!MyDrivingDirectionsActivity.this.myDD.isComplete()' was interruoted.",e);
- }
- }
- /* Check to see if any Placemarks were found..
- * if 0 then there is no route! */
- if (MyDrivingDirectionsActivity.this.myDD.getState() != DrivingDirection.SUCCESS_STATUS) {
- /* Set a flag to let the program know
- * the directions are done... */
- MyDrivingDirectionsActivity.this.foundDirections = true;
- } else{ /* no route.. */
- MyDrivingDirectionsActivity.this.foundDirections = true;
- MyDrivingDirectionsActivity.this.myDD = null;
- /* Let the user know that no route was found... */
- }
- }
- });
- t.start();
- }
- }
- }
Parsed in 0.076 seconds, using GeSHi 1.0.8.4
"/src/your_package_structure/MyMapDrivingDirectionsOverlay.java":
Using java Syntax Highlighting
- // Created by plusminus on 14:00:27 - 30.01.2008
- package org.anddev.android.drivingdirections;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Path;
- import android.util.Log;
- import com.google.android.maps.Overlay;
- import com.google.android.maps.Point;
- import com.google.googlenav.DrivingDirection;
- import com.google.googlenav.map.MapPoint;
- public class MyMapDrivingDirectionsOverlay extends Overlay {
- // ===========================================================
- // Final Fields
- // ===========================================================
- /** The tip of the pin is located at x=5, y=29 */
- private final android.graphics.Point PIN_HOTSPOT = new android.graphics.Point(5,29);
- // ===========================================================
- // Fields
- // ===========================================================
- MyDrivingDirectionsActivity itsMapActivity = null;
- Paint pathPaint = null;
- DrivingDirection dd = null;
- private Bitmap PIN_START = null;
- private Bitmap PIN_END = null;
- private Bitmap INFO_LOWER_LEFT = null;
- // ===========================================================
- // "Constructors"
- // ===========================================================
- public MyMapDrivingDirectionsOverlay(MyDrivingDirectionsActivity map) {
- itsMapActivity = map;
- this.pathPaint = new Paint();
- this.pathPaint.setAntiAlias(true);
- PIN_START = BitmapFactory.decodeResource(this.itsMapActivity.getResources(), R.drawable.mappin_blue);
- PIN_END = BitmapFactory.decodeResource(this.itsMapActivity.getResources(), R.drawable.mappin_red);
- INFO_LOWER_LEFT = BitmapFactory.decodeResource(this.itsMapActivity.getResources(), R.drawable.lower_left_info);
- }
- // ===========================================================
- // Getter & Setter
- // ===========================================================
- // ===========================================================
- // Methods
- // ===========================================================
- /** This function does some fancy drawing of the
- * Driving-Directions. It could be shortened a lot. */
- public void draw(Canvas canvas, PixelCalculator pxC, boolean b) {
- super.draw(canvas, pxC, b);
- /* Reset our paint. */
- this.pathPaint.setStrokeWidth(4);
- this.pathPaint.setARGB(100, 113, 105, 252);
- // holders of mapped coords...
- int[] screenCoords = new int[2];
- // method in the custom map view to return the DrivingDirection object.
- dd = itsMapActivity.getDrivingDirections();
- if (dd != null) {
- /* First get Start end End Point of the route. */
- MapPoint startPoint = dd.getStartPoint();
- MapPoint endPoint = dd.getEndPoint();
- MapPointToScreenCoords(startPoint, screenCoords, pxC);
- /* Create a path-that will be filled with map-points
- * and will be drawn to the canvas in the end*/
- Path thePath = new Path();
- thePath.moveTo(screenCoords[0], screenCoords[1]);
- /* Check to see if the route is too long. */
- if (!dd.routeTooLong()) {
- /* Retrieve all (Map)Points of the route Found. */
- MapPoint[] route = dd.getRoute();
- if(route == null)
- return;
- /* Loop through all MapPoints returned. */
- for (MapPoint current : route) {
- /* Transform current MapPoint's Lat/Lng
- * into corresponding point on canvas
- * using the pixelCalculator. */
- if(current != null){
- MapPointToScreenCoords(current, screenCoords, pxC);
- /* Add point to path. */
- thePath.lineTo(screenCoords[0], screenCoords[1]);
- }
- }
- }
- this.pathPaint.setStyle(Paint.Style.STROKE);
- /* Draw the actual route to the canvas. */
- canvas.drawPath(thePath, this.pathPaint);
- /* Finally draw a fancy PIN to mark the start... */
- MapPointToScreenCoords(endPoint, screenCoords, pxC);
- canvas.drawBitmap(PIN_END,
- screenCoords[0] - PIN_HOTSPOT.x,
- screenCoords[1] - PIN_HOTSPOT.y,
- pathPaint);
- /* ...and the end of the route.*/
- MapPointToScreenCoords(startPoint, screenCoords, pxC);
- canvas.drawBitmap(PIN_START,
- screenCoords[0] - PIN_HOTSPOT.x,
- screenCoords[1] - PIN_HOTSPOT.y,
- pathPaint);
- /* Get the height of the underlying MapView.*/
- int mapViewHeight = this.itsMapActivity.findViewById(R.id.myMapView).getHeight();
- /* Now some real fancy stuff !*/
- /* Draw a info-menu for the route.*/
- canvas.drawBitmap(this.INFO_LOWER_LEFT, 0, mapViewHeight - this.INFO_LOWER_LEFT.height(), pathPaint);
- /* And draw i.e.the distance and time left to the info-menu.*/
- String distance = dd.getFormattedDistance();
- String time = dd.getFormattedTime().replace("Time: ", "");
- pathPaint.setARGB(255,255,255,255);
- this.pathPaint.setStrokeWidth(1);
- this.pathPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- pathPaint.setTextSize(24);
- canvas.drawText(time, 5, mapViewHeight - 5, pathPaint);
- pathPaint.setTextSize(16);
- canvas.drawText(distance, 4, mapViewHeight - 35, pathPaint); // 2, 271
- /* These methods are to illustrate some
- * of what you can get out of the system. */
- // String startName = dd.getRouteStartLocation();
- // String info = dd.getRouteInfoDescriptor();
- Log.d("DEBUGTAG", dd.getTurns()[0]);
- }
- }
- private void MapPointToScreenCoords(MapPoint mp, int[] targetScreenCoords, PixelCalculator pxc){
- Point p = new Point(mp.getLatitude(), mp.getLongitude());
- pxc.getPointXY(p, targetScreenCoords);
- }
- }
Parsed in 0.091 seconds, using GeSHi 1.0.8.4
Thats it 

Regards,
plusminus












Now I know what you were doing...
