Abstract
The DrivingDirection package (com.google.googlenav.DrivingDirection) is removed since Android SDK 1.1. However, in this article, I will show you how to
adopt driving direction function in MapView object without the DrivingDirection package.
摘要 地圖駕駛導航的功能在Android SDK 1.1以後已經被移除,不過這篇文章我將會展示如何
在沒有DrivingDirection這個package的情況下,依然可以使用駕駛導航的功能,必且顯示在MapView物件。
1. Prepare the map resource and Internet accessibility.
1.1 Open the main.xml file in layout directory, and add a map reource in the file.
| XML: |
| <?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" > <com.google.android.maps.MapView android:id="@+id/myMapView1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_x="0px" android:enabled="true" android:clickable="true" android:apiKey="0mRN-6bSm63hZJtPZSmcjoZAzdCztLnZv-O4SZw" android:layout_y="105px"> </com.google.android.maps.MapView> </LinearLayout> |
1.1.1 You have to apply a android map api key for your computer. Find the debug.keystore path in Eclpise(Window->Rreferences).
1.1.2 In cmd console, type
| cmd: |
keytool -list -alias androiddebugkey -keystore "C:\Documents and Settings\Administrator\Local Settings\Application Data\ Android\debug.keystore" -storepass android -keypass android |
1.1.3 Go to
http://code.google.com/intl/zh-TW/android/maps-api-signup.html ,type your MD5 fingerprint, so that you can get the map api key as follow.
1.2 Open AndroidManifest.xml, add
<uses-library android:name="com.google.android.maps"/> and
<uses-permission android:name="android.permission.INTERNET" /> Therefore, the file would be something like:
| XML: |
| <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.goolge" android:versionCode="1" android:versionName="1.0.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".RoutePath" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <uses-library android:name="com.google.android.maps"/> </application> <uses-permission android:name="android.permission.INTERNET" /> </manifest> |
2. Draw the route path in your map.
2.1 Get KML route file from google.
For a normal user, the google maps help them get the route path in map figures. However, we would like to get the KML file of the route path. I found a parameter in the google map URL controls the output type.
- output= Output format (blank (default) is a standard webpage for user)
- output=html Uses the old style Google Local page format from before it merged with Google Maps, with the small map and large sidebar.
- output=js Outputs JavaScript object literals and function calls used by Google Maps, including encoded polyline data for driving directions, and stage information in HTML format.
- output=kml Outputs a KML file containing information representing the current map. (works with Normal Searches, Directions and MyMaps)
- output=nl Outputs a small KML file containing a NetworkLink wrapper linking to a URL from which Google Earth and Google Maps can obtain the information (only known to work with MyMaps).
- output=embed Outputs HTML suitable for embedding in third party sites, only works with the presence of the encrypted s= param, presumably to stop arbitrary content being included.
- output=dragdir returns a JSON object that contains the reverse geocode and a an encoded polyline for a given saddr (start point of the route) and daddr (endpoint of the route)
- output=georss (Geo)RSS output for the current map - probably only MyMaps
And the latitude and longitude of source and destination are determined by saddr and daddr parameter, respectively.
For example, a route KML file can be accessed through this URL:
http://maps.google.com/maps?f=d&hl=en&saddr=25.04202,121.534761&daddr=25.05202,121.554761&ie=UTF8&0&om=0&output=kml
In the KML file, each point in the route path is shown in terms of (longitude, latitude, heigth).
2.2 Create DrawPath(…) in your activity. This function do the following procedure.
a) Building the URL from src and dest.
b) Connecting to the URL and create a DocumentBuilder to parse the KML file.
c) Split each point in the path and draw each the line on the mMapView01.
| Java: |
| private void DrawPath(GeoPoint src,GeoPoint dest, int color, MapView mMapView01) { // connect to map web service StringBuilder urlString = new StringBuilder(); urlString.append("http://maps.google.com/maps?f=d&hl=en"); urlString.append("&saddr=");//from urlString.append( Double.toString((double)src.getLatitudeE6()/1.0E6 )); urlString.append(","); urlString.append( Double.toString((double)src.getLongitudeE6()/1.0E6 )); urlString.append("&daddr=");//to urlString.append( Double.toString((double)dest.getLatitudeE6()/1.0E6 )); urlString.append(","); urlString.append( Double.toString((double)dest.getLongitudeE6()/1.0E6 )); urlString.append("&ie=UTF8&0&om=0&output=kml"); Log.d("xxx","URL="+urlString.toString()); // get the kml (XML) doc. And parse it to get the coordinates(direction route). Document doc = null; HttpURLConnection urlConnection= null; URL url = null; try { url = new URL(urlString.toString()); urlConnection=(HttpURLConnection)url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.setDoOutput(true); urlConnection.setDoInput(true); urlConnection.connect(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); doc = db.parse(urlConnection.getInputStream()); if(doc.getElementsByTagName("GeometryCollection").getLength()>0) { //String path = doc.getElementsByTagName("GeometryCollection").item(0).getFirstChild().getFirstChild().getNodeName(); String path = doc.getElementsByTagName("GeometryCollection").item(0).getFirstChild().getFirstChild().getFirstChild().getNodeValue() ; Log.d("xxx","path="+ path); String [] pairs = path.split(" "); String[] lngLat = pairs[0].split(","); // lngLat[0]=longitude lngLat[1]=latitude lngLat[2]=height // src GeoPoint startGP = new GeoPoint((int)(Double.parseDouble(lngLat[1])*1E6),(int)(Double.parseDouble(lngLat[0])*1E6)); mMapView01.getOverlays().add(new MyOverLay(startGP,startGP,1)); GeoPoint gp1; GeoPoint gp2 = startGP; for(int i=1;i<pairs.length;i++) // the last one would be crash { lngLat = pairs[i].split(","); gp1 = gp2; // watch out! For GeoPoint, first:latitude, second:longitude gp2 = new GeoPoint((int)(Double.parseDouble(lngLat[1])*1E6),(int)(Double.parseDouble(lngLat[0])*1E6)); mMapView01.getOverlays().add(new MyOverLay(gp1,gp2,2,color)); Log.d("xxx","pair:" + pairs[i]); } mMapView01.getOverlays().add(new MyOverLay(dest,dest, 3)); // use the default color } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } |
2.3 Adding MyOverlay class – Drawing the points and lines on the ViewMap.
| Java: |
| package com.goolge; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.RectF; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; import com.google.android.maps.Projection; public class MyOverLay extends Overlay { private GeoPoint gp1; private GeoPoint gp2; private int mRadius=6; private int mode=0; private int defaultColor; private String text=""; private Bitmap img = null; public MyOverLay(GeoPoint gp1,GeoPoint gp2,int mode) // GeoPoint is a int. (6E) { this.gp1 = gp1; this.gp2 = gp2; this.mode = mode; defaultColor = 999; // no defaultColor } public MyOverLay(GeoPoint gp1,GeoPoint gp2,int mode, int defaultColor) { this.gp1 = gp1; this.gp2 = gp2; this.mode = mode; this.defaultColor = defaultColor; } public void setText(String t) { this.text = t; } public void setBitmap(Bitmap bitmap) { this.img = bitmap; } public int getMode() { return mode; } @Override public boolean draw (Canvas canvas, MapView mapView, boolean shadow, long when) { Projection projection = mapView.getProjection(); if (shadow == false) { Paint paint = new Paint(); paint.setAntiAlias(true); Point point = new Point(); projection.toPixels(gp1, point); // mode=1:start if(mode==1) { if(defaultColor==999) paint.setColor(Color.BLUE); else paint.setColor(defaultColor); RectF oval=new RectF(point.x - mRadius, point.y - mRadius, point.x + mRadius, point.y + mRadius); // start point canvas.drawOval(oval, paint); } // mode=2:path else if(mode==2) { if(defaultColor==999) paint.setColor(Color.RED); else paint.setColor(defaultColor); Point point2 = new Point(); projection.toPixels(gp2, point2); paint.setStrokeWidth(5); paint.setAlpha(120); canvas.drawLine(point.x, point.y, point2.x,point2.y, paint); } /* mode=3:end */ else if(mode==3) { /* the last path */ if(defaultColor==999) paint.setColor(Color.GREEN); else paint.setColor(defaultColor); Point point2 = new Point(); projection.toPixels(gp2, point2); paint.setStrokeWidth(5); paint.setAlpha(120); canvas.drawLine(point.x, point.y, point2.x,point2.y, paint); RectF oval=new RectF(point2.x - mRadius,point2.y - mRadius, point2.x + mRadius,point2.y + mRadius); /* end point */ paint.setAlpha(255); canvas.drawOval(oval, paint); } } return super.draw(canvas, mapView, shadow, when); } } |
3. How to use DrawPath(…) function in your activity.
3.1 Your activity must extends MapActivity, instead of Activity.
| Java: |
| ublic class RoutePath extends MapActivity { /** Called when the activity is first created. */ MapView mapView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); MapView mapView = (MapView) findViewById(R.id.myMapView1); double src_lat = 25.04202; // the testing source double src_long = 121.534761; double dest_lat = 25.05202; // the testing destination double dest_long = 121.554761; GeoPoint srcGeoPoint = new GeoPoint((int) (src_lat * 1E6), (int) (src_long * 1E6)); GeoPoint destGeoPoint = new GeoPoint((int) (dest_lat * 1E6), (int) (dest_long * 1E6)); DrawPath(srcGeoPoint, destGeoPoint, Color.GREEN, mapView); mapView.getController().animateTo(srcGeoPoint); mapView.getController().setZoom(15); } @Override protected boolean isRouteDisplayed() { // TODO Auto-generated method stub return false; } private void DrawPath(GeoPoint src, GeoPoint dest, int color, MapView mMapView01) { // code in section 2.2 } } |
3.2 The screenshot
Emulator:
HTC G1: Actually, the driving direction is just one of the function of our project. Our project is so-called
Dynamic ridesharing.
4. Trouble Shooting
When the program is executed, we can observe the logcat in Eclipse to see if the parsing procedure works properly or not. If there is no path shown on the map, you should check if the distance of source and destination is too long(e.g., from Taiwan to Japan). No routing path will be shown in this case.
To enable the logcat, please refer to:
http://csie-tw.blogspot.com/2009/05/enable-android-log-androidlog.html
5. Source code
Download here http://www.mediafire.com/?tlfshkkq58l38p1#
or http://webtoolplus.com/downloads/RoutePath.zip.
Please let me know if it's broken.
6. References
[1] Google Map Parameters:
http://mapki.com/index.php?title=Google_Map_Parameters
[2] Enable Android log:
http://csie-tw.blogspot.com/2009/05/enable-android-log-androidlog.html
[3] Setup the Android (Trad. Chinese):
http://csie-tw.blogspot.com/2008/01/androideclipse.html
[4] Android – Update current location by LocationProvider
http://csie-tw.blogspot.com/2009/09/android-update-current-location-by.html