¿Cómo dibujar un camino en un mapa usando el archivo kml?

¿Puedo analizar el archivo kml para mostrar rutas o puntos en Android? ¿Podrías ayudarme con eso?

Este es el código de muestra kml que me gustaría mostrar en el mapa de google de Android:

   Paths Examples of paths. Note that the tessellate tag is by default set to 0. If you want to create tessellated lines, they must be authored (or edited) directly in KML.   7f00ffff 4   7f00ff00    Absolute Extruded Transparent green wall with yellow outlines #yellowLineGreenPoly  1 1 absolute  -112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357 -112.2564540158376,36.08395660588506,2357 -112.2580238976449,36.08511401044813,2357 -112.2595218489022,36.08584355239394,2357 -112.2608216347552,36.08612634548589,2357 -112.262073428656,36.08626019085147,2357 -112.2633204928495,36.08621519860091,2357 -112.2644963846444,36.08627897945274,2357 -112.2656969554589,36.08649599090644,2357      

Cuando estoy cargando este archivo en el mapa de google web estándar, lo muestra muy bien, pero cuando bash lo mismo con el mapa de Google Android, no funciona. Simplemente me lleva a algunos lugares y eso es todo. Estaba pensando en cambiar la clase de oyente. Actualmente parece que:

 private class MyLocationListener implements LocationListener { @Override public void onLocationChanged(Location loc) { if (loc != null) { latitude = (loc.getLatitude() * 1E6); longitude = (loc.getLongitude() * 1E6); Toast.makeText(getBaseContext(), "Location changed : Lat: " + latitude + " Lng: " + longitude, Toast.LENGTH_SHORT).show(); GeoPoint p = new GeoPoint( (int) (loc.getLatitude() * 1E6), (int) (loc.getLongitude() * 1E6)); mc.animateTo(p); mapView.invalidate(); } } //--------------------------------------------------------------- @Override public void onProviderDisabled(String provider) { // TODO Auto-generated method stub } @Override public void onProviderEnabled(String provider) { // TODO Auto-generated method stub } @Override public void onStatusChanged(String provider, int status, Bundle extras) { //TODO Auto-generated method stub } 

¿Puede alguien decirme qué estoy haciendo mal aquí?

En el código anterior, no pasas los datos kml a tu mapView en ningún lugar de tu código, hasta donde puedo ver. Para mostrar la ruta, debe analizar los datos kml, es decir, mediante el analizador SAX, y luego mostrar los marcadores de ruta en el mapa.

Consulte el siguiente código para ver un ejemplo, pero no está completo, solo como referencia y para tener una idea.

Este es un bean simple que utilizo para contener la información de ruta que analizaré.

 package com.myapp.android.model.navigation; import java.util.ArrayList; import java.util.Iterator; public class NavigationDataSet { private ArrayList placemarks = new ArrayList(); private Placemark currentPlacemark; private Placemark routePlacemark; public String toString() { String s= ""; for (Iterator iter=placemarks.iterator();iter.hasNext();) { Placemark p = (Placemark)iter.next(); s += p.getTitle() + "\n" + p.getDescription() + "\n\n"; } return s; } public void addCurrentPlacemark() { placemarks.add(currentPlacemark); } public ArrayList getPlacemarks() { return placemarks; } public void setPlacemarks(ArrayList placemarks) { this.placemarks = placemarks; } public Placemark getCurrentPlacemark() { return currentPlacemark; } public void setCurrentPlacemark(Placemark currentPlacemark) { this.currentPlacemark = currentPlacemark; } public Placemark getRoutePlacemark() { return routePlacemark; } public void setRoutePlacemark(Placemark routePlacemark) { this.routePlacemark = routePlacemark; } } 

Y el controlador de SAX para analizar el kml:

 package com.myapp.android.model.navigation; import android.util.Log; import com.myapp.android.myapp; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.myapp.android.model.navigation.NavigationDataSet; import com.myapp.android.model.navigation.Placemark; public class NavigationSaxHandler extends DefaultHandler{ // =========================================================== // Fields // =========================================================== private boolean in_kmltag = false; private boolean in_placemarktag = false; private boolean in_nametag = false; private boolean in_descriptiontag = false; private boolean in_geometrycollectiontag = false; private boolean in_linestringtag = false; private boolean in_pointtag = false; private boolean in_coordinatestag = false; private StringBuffer buffer; private NavigationDataSet navigationDataSet = new NavigationDataSet(); // =========================================================== // Getter & Setter // =========================================================== public NavigationDataSet getParsedData() { navigationDataSet.getCurrentPlacemark().setCoordinates(buffer.toString().trim()); return this.navigationDataSet; } // =========================================================== // Methods // =========================================================== @Override public void startDocument() throws SAXException { this.navigationDataSet = new NavigationDataSet(); } @Override public void endDocument() throws SAXException { // Nothing to do } /** Gets be called on opening tags like: *  * Can provide attribute(s), when xml was like: * */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (localName.equals("kml")) { this.in_kmltag = true; } else if (localName.equals("Placemark")) { this.in_placemarktag = true; navigationDataSet.setCurrentPlacemark(new Placemark()); } else if (localName.equals("name")) { this.in_nametag = true; } else if (localName.equals("description")) { this.in_descriptiontag = true; } else if (localName.equals("GeometryCollection")) { this.in_geometrycollectiontag = true; } else if (localName.equals("LineString")) { this.in_linestringtag = true; } else if (localName.equals("point")) { this.in_pointtag = true; } else if (localName.equals("coordinates")) { buffer = new StringBuffer(); this.in_coordinatestag = true; } } /** Gets be called on closing tags like: *  */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (localName.equals("kml")) { this.in_kmltag = false; } else if (localName.equals("Placemark")) { this.in_placemarktag = false; if ("Route".equals(navigationDataSet.getCurrentPlacemark().getTitle())) navigationDataSet.setRoutePlacemark(navigationDataSet.getCurrentPlacemark()); else navigationDataSet.addCurrentPlacemark(); } else if (localName.equals("name")) { this.in_nametag = false; } else if (localName.equals("description")) { this.in_descriptiontag = false; } else if (localName.equals("GeometryCollection")) { this.in_geometrycollectiontag = false; } else if (localName.equals("LineString")) { this.in_linestringtag = false; } else if (localName.equals("point")) { this.in_pointtag = false; } else if (localName.equals("coordinates")) { this.in_coordinatestag = false; } } /** Gets be called on the following structure: * characters */ @Override public void characters(char ch[], int start, int length) { if(this.in_nametag){ if (navigationDataSet.getCurrentPlacemark()==null) navigationDataSet.setCurrentPlacemark(new Placemark()); navigationDataSet.getCurrentPlacemark().setTitle(new String(ch, start, length)); } else if(this.in_descriptiontag){ if (navigationDataSet.getCurrentPlacemark()==null) navigationDataSet.setCurrentPlacemark(new Placemark()); navigationDataSet.getCurrentPlacemark().setDescription(new String(ch, start, length)); } else if(this.in_coordinatestag){ if (navigationDataSet.getCurrentPlacemark()==null) navigationDataSet.setCurrentPlacemark(new Placemark()); //navigationDataSet.getCurrentPlacemark().setCoordinates(new String(ch, start, length)); buffer.append(ch, start, length); } } } 

y un simple frijol placeMark:

 package com.myapp.android.model.navigation; public class Placemark { String title; String description; String coordinates; String address; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getCoordinates() { return coordinates; } public void setCoordinates(String coordinates) { this.coordinates = coordinates; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } 

Finalmente, la clase de servicio en mi modelo que llama al cálculo:

 package com.myapp.android.model.navigation; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import com.myapp.android.myapp; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import android.util.Log; public class MapService { public static final int MODE_ANY = 0; public static final int MODE_CAR = 1; public static final int MODE_WALKING = 2; public static String inputStreamToString (InputStream in) throws IOException { StringBuffer out = new StringBuffer(); byte[] b = new byte[4096]; for (int n; (n = in.read(b)) != -1;) { out.append(new String(b, 0, n)); } return out.toString(); } public static NavigationDataSet calculateRoute(Double startLat, Double startLng, Double targetLat, Double targetLng, int mode) { return calculateRoute(startLat + "," + startLng, targetLat + "," + targetLng, mode); } public static NavigationDataSet calculateRoute(String startCoords, String targetCoords, int mode) { String urlPedestrianMode = "http://maps.google.com/maps?" + "saddr=" + startCoords + "&daddr=" + targetCoords + "&sll=" + startCoords + "&dirflg=w&hl=en&ie=UTF8&z=14&output=kml"; Log.d(myapp.APP, "urlPedestrianMode: "+urlPedestrianMode); String urlCarMode = "http://maps.google.com/maps?" + "saddr=" + startCoords + "&daddr=" + targetCoords + "&sll=" + startCoords + "&hl=en&ie=UTF8&z=14&output=kml"; Log.d(myapp.APP, "urlCarMode: "+urlCarMode); NavigationDataSet navSet = null; // for mode_any: try pedestrian route calculation first, if it fails, fall back to car route if (mode==MODE_ANY||mode==MODE_WALKING) navSet = MapService.getNavigationDataSet(urlPedestrianMode); if (mode==MODE_ANY&&navSet==null||mode==MODE_CAR) navSet = MapService.getNavigationDataSet(urlCarMode); return navSet; } /** * Retrieve navigation data set from either remote URL or String * @param url * @return navigation set */ public static NavigationDataSet getNavigationDataSet(String url) { // urlString = "http://192.168.1.100:80/test.kml"; Log.d(myapp.APP,"urlString -->> " + url); NavigationDataSet navigationDataSet = null; try { final URL aUrl = new URL(url); final URLConnection conn = aUrl.openConnection(); conn.setReadTimeout(15 * 1000); // timeout for reading the google maps data: 15 secs conn.connect(); /* Get a SAXParser from the SAXPArserFactory. */ SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); /* Get the XMLReader of the SAXParser we created. */ XMLReader xr = sp.getXMLReader(); /* Create a new ContentHandler and apply it to the XML-Reader*/ NavigationSaxHandler navSax2Handler = new NavigationSaxHandler(); xr.setContentHandler(navSax2Handler); /* Parse the xml-data from our URL. */ xr.parse(new InputSource(aUrl.openStream())); /* Our NavigationSaxHandler now provides the parsed data to us. */ navigationDataSet = navSax2Handler.getParsedData(); /* Set the result to be displayed in our GUI. */ Log.d(myapp.APP,"navigationDataSet: "+navigationDataSet.toString()); } catch (Exception e) { // Log.e(myapp.APP, "error with kml xml", e); navigationDataSet = null; } return navigationDataSet; } } 

Dibujo:

 /** * Does the actual drawing of the route, based on the geo points provided in the nav set * * @param navSet Navigation set bean that holds the route information, incl. geo pos * @param color Color in which to draw the lines * @param mMapView01 Map view to draw onto */ public void drawPath(NavigationDataSet navSet, int color, MapView mMapView01) { Log.d(myapp.APP, "map color before: " + color); // color correction for dining, make it darker if (color == Color.parseColor("#add331")) color = Color.parseColor("#6C8715"); Log.d(myapp.APP, "map color after: " + color); Collection overlaysToAddAgain = new ArrayList(); for (Iterator iter = mMapView01.getOverlays().iterator(); iter.hasNext();) { Object o = iter.next(); Log.d(myapp.APP, "overlay type: " + o.getClass().getName()); if (!RouteOverlay.class.getName().equals(o.getClass().getName())) { // mMapView01.getOverlays().remove(o); overlaysToAddAgain.add(o); } } mMapView01.getOverlays().clear(); mMapView01.getOverlays().addAll(overlaysToAddAgain); String path = navSet.getRoutePlacemark().getCoordinates(); Log.d(myapp.APP, "path=" + path); if (path != null && path.trim().length() > 0) { String[] pairs = path.trim().split(" "); Log.d(myapp.APP, "pairs.length=" + pairs.length); String[] lngLat = pairs[0].split(","); // lngLat[0]=longitude lngLat[1]=latitude lngLat[2]=height Log.d(myapp.APP, "lnglat =" + lngLat + ", length: " + lngLat.length); if (lngLat.length<3) lngLat = pairs[1].split(","); // if first pair is not transferred completely, take seconds pair //TODO try { GeoPoint startGP = new GeoPoint((int) (Double.parseDouble(lngLat[1]) * 1E6), (int) (Double.parseDouble(lngLat[0]) * 1E6)); mMapView01.getOverlays().add(new RouteOverlay(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; if (lngLat.length >= 2 && gp1.getLatitudeE6() > 0 && gp1.getLongitudeE6() > 0 && gp2.getLatitudeE6() > 0 && gp2.getLongitudeE6() > 0) { // for GeoPoint, first:latitude, second:longitude gp2 = new GeoPoint((int) (Double.parseDouble(lngLat[1]) * 1E6), (int) (Double.parseDouble(lngLat[0]) * 1E6)); if (gp2.getLatitudeE6() != 22200000) { mMapView01.getOverlays().add(new RouteOverlay(gp1, gp2, 2, color)); Log.d(myapp.APP, "draw:" + gp1.getLatitudeE6() + "/" + gp1.getLongitudeE6() + " TO " + gp2.getLatitudeE6() + "/" + gp2.getLongitudeE6()); } } // Log.d(myapp.APP,"pair:" + pairs[i]); } //routeOverlays.add(new RouteOverlay(gp2,gp2, 3)); mMapView01.getOverlays().add(new RouteOverlay(gp2, gp2, 3)); } catch (NumberFormatException e) { Log.e(myapp.APP, "Cannot draw route.", e); } } // mMapView01.getOverlays().addAll(routeOverlays); // use the default color mMapView01.setEnabled(true); } 

Esta es la clase RouteOverlay:

 package com.myapp.android.activity.map.nav; 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 RouteOverlay 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 RouteOverlay(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 RouteOverlay(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.BLACK); // 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(defaultColor==Color.parseColor("#6C8715")?220: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.BLACK); // Color.GREEN else paint.setColor(defaultColor); Point point2 = new Point(); projection.toPixels(gp2, point2); paint.setStrokeWidth(5); paint.setAlpha(defaultColor==Color.parseColor("#6C8715")?220: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); } } 

¡Gracias Mathias Lin, probado y funciona!

Además, la implementación de muestra del método de Mathias en la actividad puede ser la siguiente.

 public class DirectionMapActivity extends MapActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.directionmap); MapView mapView = (MapView) findViewById(R.id.mapview); mapView.setBuiltInZoomControls(true); // Acquire a reference to the system Location Manager LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); String locationProvider = LocationManager.NETWORK_PROVIDER; Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider); StringBuilder urlString = new StringBuilder(); urlString.append("http://maps.google.com/maps?f=d&hl=en"); urlString.append("&saddr=");//from urlString.append( Double.toString(lastKnownLocation.getLatitude() )); urlString.append(","); urlString.append( Double.toString(lastKnownLocation.getLongitude() )); urlString.append("&daddr=");//to urlString.append( Double.toString((double)dest[0]/1.0E6 )); urlString.append(","); urlString.append( Double.toString((double)dest[1]/1.0E6 )); urlString.append("&ie=UTF8&0&om=0&output=kml"); try{ // setup the url URL url = new URL(urlString.toString()); // create the factory SAXParserFactory factory = SAXParserFactory.newInstance(); // create a parser SAXParser parser = factory.newSAXParser(); // create the reader (scanner) XMLReader xmlreader = parser.getXMLReader(); // instantiate our handler NavigationSaxHandler navSaxHandler = new NavigationSaxHandler(); // assign our handler xmlreader.setContentHandler(navSaxHandler); // get our data via the url class InputSource is = new InputSource(url.openStream()); // perform the synchronous parse xmlreader.parse(is); // get the results - should be a fully populated RSSFeed instance, or null on error NavigationDataSet ds = navSaxHandler.getParsedData(); // draw path drawPath(ds, Color.parseColor("#add331"), mapView ); // find boundary by using itemized overlay GeoPoint destPoint = new GeoPoint(dest[0],dest[1]); GeoPoint currentPoint = new GeoPoint( new Double(lastKnownLocation.getLatitude()*1E6).intValue() ,new Double(lastKnownLocation.getLongitude()*1E6).intValue() ); Drawable dot = this.getResources().getDrawable(R.drawable.pixel); MapItemizedOverlay bgItemizedOverlay = new MapItemizedOverlay(dot,this); OverlayItem currentPixel = new OverlayItem(destPoint, null, null ); OverlayItem destPixel = new OverlayItem(currentPoint, null, null ); bgItemizedOverlay.addOverlay(currentPixel); bgItemizedOverlay.addOverlay(destPixel); // center and zoom in the map MapController mc = mapView.getController(); mc.zoomToSpan(bgItemizedOverlay.getLatSpanE6()*2,bgItemizedOverlay.getLonSpanE6()*2); mc.animateTo(new GeoPoint( (currentPoint.getLatitudeE6() + destPoint.getLatitudeE6()) / 2 , (currentPoint.getLongitudeE6() + destPoint.getLongitudeE6()) / 2)); } catch(Exception e) { Log.d("DirectionMap","Exception parsing kml."); } } // and the rest of the methods in activity, eg drawPath() etc... 

MapItemizedOverlay.java

 public class MapItemizedOverlay extends ItemizedOverlay{ private ArrayList mOverlays = new ArrayList(); private Context mContext; public MapItemizedOverlay(Drawable defaultMarker, Context context) { super(boundCenterBottom(defaultMarker)); mContext = context; } public void addOverlay(OverlayItem overlay) { mOverlays.add(overlay); populate(); } @Override protected OverlayItem createItem(int i) { return mOverlays.get(i); } @Override public int size() { return mOverlays.size(); } } 

El código de Mathias Lin funciona maravillosamente. Sin embargo, es posible que desee considerar cambiar esta parte dentro del método drawPath:

  if (lngLat.length >= 2 && gp1.getLatitudeE6() > 0 && gp1.getLongitudeE6() > 0 && gp2.getLatitudeE6() > 0 && gp2.getLongitudeE6() > 0) { 

GeoPoint también puede ser menor que cero, cambio el mío a:

  if (lngLat.length >= 2 && gp1.getLatitudeE6() != 0 && gp1.getLongitudeE6() != 0 && gp2.getLatitudeE6() != 0 && gp2.getLongitudeE6() != 0) { 

Gracias: D

Ahora hay una versión beta disponible de Google Maps KML Importing Utility .

Es parte de la biblioteca de la herramienta API de Android de Google Maps . Tal como está documentado, permite cargar archivos KML de secuencias

 KmlLayer layer = new KmlLayer(getMap(), kmlInputStream, getApplicationContext()); 

o recursos locales

 KmlLayer layer = new KmlLayer(getMap(), R.raw.kmlFile, getApplicationContext()); 

Después de haber creado un KmlLayer, llame a addLayerToMap () para agregar los datos importados al mapa.

 layer.addLayerToMap();