| Author |
Message |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Sat Feb 09, 2008 4:17 pm Post subject: Google Driving Directions - MapView overlayed |
|
|
Google Driving Directions - MapOverlayed
Part of the AndNav!-Application 
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.
Designed/Tested with sdk-version: m5-rc14
Problems/Questions: post right below...
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:
| Java: | <?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> |
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.
| Java: | import com.google.googlenav.DrivingDirection; |
It provides the DrivingDirection-Class with the following Constructor:
| Java: | new DrivingDirection(MapPoint from, String from_string, MapPoint to, String to_string); |
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.
| Java: | /* 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, "");
}
}); |
So, a click to that button will finally cause a field in our class to be set, which was initiated with null:
| Java: | private DrivingDirection myDD = null; |
2.) In the onCreate(...);-Function we applied a custom Overlay to our MapView using:
| Java: | /* 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); |
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:
| Java: | //...
/* 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);
//... |
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":
| Java: | <?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> |
"/src/your_package_structure/MyDrivingDirectionsActivity.java":
| Java: | 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();
}
}
} |
"/src/your_package_structure/MyMapDrivingDirectionsOverlay.java":
| Java: | // 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);
}
}
|
Thats it 
Regards,
plusminus
| Description: |
For SDK-version m5 :!: Full Project Source |
|
 Download |
| Filename: |
DrivingDirections_m5.zip |
| Filesize: |
56.04 KB |
| Downloaded: |
1184 Time(s) |
| Description: |
For SDK-version m3 :!: Full Project Source |
|
 Download |
| Filename: |
DrivingDirections.zip |
| Filesize: |
57.46 KB |
| Downloaded: |
422 Time(s) |
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials
Last edited by plusminus on Sun Feb 17, 2008 3:45 pm; edited 7 times in total |
|
| Back to top |
|
 |
gvkreddyvamsi Developer

Joined: 21 Jan 2008 Posts: 43 Location: INDIA
|
Posted: Mon Feb 11, 2008 7:13 am Post subject: Driving directions application is nice |
|
|
HI,
I have done things well. But i cann't resolve R.java in this application.
can u send zip for this application?
by
vamsi
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Mon Feb 11, 2008 2:28 pm Post subject: |
|
|
Hello,
attached it above
Regards,
plusminus
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
tv_sathish Developer

Joined: 09 Jan 2008 Posts: 29
|
Posted: Tue Feb 12, 2008 12:20 pm Post subject: Questions regarding this tutorial... |
|
|
Hi Plusminus,
I am getting a compilation error in the line:
| Code: |
/* Submit one dummy search */
findViewById(R.id.cmd_submit).setOnClickListener(new OnClickListener(){
@Override
[b]public void onClick(View arg0) {[/b]
|
Error is The method onClick(View) of type new View.OnClickListener(){} must override a superclass method MyDrivingDirectionsActivity/src/org/anddev/android/drivingdirections/MyDrivingDirectionsActivity.java line 34
What could be the problem?
By the by, if we are using the inbuilt DrivingDirections, why haven't u used DrivingDirectionsRenderer to draw the Driving Directions? Is that class not fully implemented?
And I had posted a few questions in the "Coding Problems" forum, waiting for your reply, let me know if I am disturbing you during exam time...
Hey, I was having a similar idea that you are planning as AndNav, but since you are already into it and 75% through, let me not compete and spoil your chances too...all the best man for that entry...
Regards,
Sathish
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Tue Feb 12, 2008 1:40 pm Post subject: |
|
|
Hello tv_sathish,
you've got two possibilities:
a.) simply Comment the @Override-Annotation out
b.) Update to JDK 1.6 (preferred)
This happens because the JDK 1.5, you are probably running, does accept @Override-Annotation only for overridden methods of Super-Classes and not for overriding functions of Interfaces we are implementing here.
On JDK 1.6 this Annotation is possible.
Hm googling for DrivingDirectionRenderer returns unbelievable two results In the SDK-Documentation, even DrivingDirection returns zero results .
Both are probably not 100% ready to be used
Feel free to submit any similar application, because competition is the best for the market
Regards,
plusminus
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
wrapware Freshman

Joined: 17 Jan 2008 Posts: 7 Location: Germany
|
Posted: Wed Feb 13, 2008 2:54 pm Post subject: |
|
|
Hi plusminus,
its a really nice app. There is one question:
Why are you assign two time the 'true' to foundDirections?
| Java: | if (MyDrivingDirectionsActivity.this.myDD.numPlacemarks() > 0) {
/* 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... */
} |
In your code is no reference to the variable foundDirections, so whats the reason for the whole thread processing with the foundDirections member? I know the user should not wait in the main thread, but ...
Maybe there is a dig deeper I'm unable to see?
Regards,
ww
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Wed Feb 13, 2008 3:01 pm Post subject: |
|
|
Hello wrapware,
the second one should be false of course (corrected it above)
I didn't remove it because it could become useful if one takes this code as a base for another Application.
Regards,
plusminus
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
wrapware Freshman

Joined: 17 Jan 2008 Posts: 7 Location: Germany
|
Posted: Fri Feb 15, 2008 9:30 am Post subject: |
|
|
Hi plusminus
Yesterday I switch to m5_rc14 and after some modifications, your code run still fine, but with one difference.
The onDraw method is now called immediately after returning, when nothing is available for drawing (route==null)
| Java: | /** 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);
// ...
if(route == null)
return; |
So the cpu usage run up to 100%, but there is nothing to draw. Is there a way to tell the | Java: | MyMapDrivingDirectionsOverlay | or the to giveup redrawing? I dump the callers hierarchie when if(route == null) happens, and thats the stack for every cycle:
| Java: | at MyMapDrivingDirectionsOverlay.draw(MyMapDrivingDirectionsOverlayjava:88 )
at com.google.android.maps.Overlay.draw(Overlay.java:154)
at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:43)
at com.google.android.maps.MapView.onDraw(MapView.java:337)
at android.view.View.draw(View.java:4574)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4550)
at android.widget.FrameLayout.draw(FrameLayout.java:208)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4578)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4550)
at android.widget.FrameLayout.draw(FrameLayout.java:208)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4550)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4550)
at android.widget.FrameLayout.draw(FrameLayout.java:208)
at android.view.ViewGroup.drawChild(ViewGroup.java:1089)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:915)
at android.view.View.draw(View.java:4578)
at android.widget.FrameLayout.draw(FrameLayout.java:208)
at android.view.ViewRoot.draw(ViewRoot.java:531)
at android.view.ViewRoot.performTraversals(ViewRoot.java:429)
at android.view.ViewRoot.handleMessage(ViewRoot.java:584)
at android.os.Handler.dispatchMessage(Handler.java:80)
at android.os.Looper.loop(Looper.java:91)
at android.app.ActivityThread.main(ActivityThread.java:3052)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect. |
| | |