»

Dec 22

Android – Drawing route on MapView

Hi everyone,

I had quite big problem with drawing route on MapView. Tutorials on the web which I found are quite confusing or with bugs. So I took several of them and did own program, which using some code from several tutorials . Here is source of my map activity:

package tvarwebu.projects.map;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;

public class MapProjectActivity extends MapActivity {

GeoPoint myPoint = null;
GeoPoint nextPoint = null;
MapView map;
private Road mRoad;
int latSpan = -1;
boolean moving = false;
long lastTimestamp = 0;
int pointCount = 0;
int lat = 0;
int lng = 0;
Location currLoc = null;
OverlayItem currPoint;
MyItemizedOverlay currItemizedOverlay;
List<Overlay> mapOverlays;
LocationManager lm;

@Override
protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  map = (MapView) findViewById(R.id.mapView);
  map.setBuiltInZoomControls(true);
  map.setStreetView(true);
  map.setSatellite(false);

  // Your position initialize
  lat = 53324388;
  lng = -6263194;

  myPoint = new GeoPoint(lat, lng);

  Drawable drawable = this.getResources().getDrawable(
  R.drawable.flag_green);

  MyItemizedOverlay itemizedoverlay = new MyItemizedOverlay(drawable, this);

  OverlayItem overlayitem = new OverlayItem(myPoint, "Start Position", "Your start position");

  itemizedoverlay.addOverlay(overlayitem);

  // Position of next point
  int lat2 = 53348084;
  int lng2 = -6292434;

  nextPoint = new GeoPoint(lat2, lng2);

  OverlayItem overlayitem2 = new OverlayItem(nextPoint, "Position", "Position of next point");

  itemizedoverlay.addOverlay(overlayitem2);

  mapOverlays = map.getOverlays();
  mapOverlays.add(itemizedoverlay);

  Drawable draw2 = this.getResources().getDrawable(R.drawable.flag_red);

  MyItemizedOverlay over2 = new MyItemizedOverlay(draw2, this);
  over2.addOverlay(overlayitem2);

  mapOverlays.add(over2);

  // zooming to both points
  int maxLatitude = Math.max(lat, lat2);
  int minLatitude = Math.min(lat, lat2);
  int maxLongitude = Math.max(lng, lng2);
  int minLongitude = Math.min(lng, lng2);

  MapController mc = map.getController();
  mc.zoomToSpan(maxLatitude - minLatitude, maxLongitude - minLongitude);
  mc.animateTo(new GeoPoint((maxLatitude + minLatitude) / 2, (maxLongitude + minLongitude) / 2));

  map.invalidate();

  //Drawing path in new Thread
  new Thread() {

  @Override
  public void run() {
    double fromLat = Double.valueOf(myPoint.getLatitudeE6()) / 1000000.0,
    fromLon = Double.valueOf(myPoint.getLongitudeE6()) / 1000000.0;
    double toLat = Double.valueOf(nextPoint.getLatitudeE6()) / 1000000.0,
    toLon = Double.valueOf(nextPoint.getLongitudeE6()) / 1000000.0;

    String url = RoadProvider.getUrl(fromLat, fromLon, toLat, toLon);
    InputStream is = getConnection(url);
    mRoad = RoadProvider.getRoute(is);
    mHandler.sendEmptyMessage(0);
  }
}.start();

}

Handler mHandler = new Handler() {
  public void handleMessage(android.os.Message msg) {
    MapOverlay mapOverlay = new MapOverlay(mRoad, map);
    List<Overlay> listOfOverlays = map.getOverlays();
    // listOfOverlays.clear();
    listOfOverlays.add(mapOverlay);
    map.invalidate();
  };
};

private InputStream getConnection(String url) {
  InputStream is = null;
  try {
    URLConnection conn = new URL(url).openConnection();
    is = conn.getInputStream();
  } catch (MalformedURLException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  return is;
}

@Override
protected boolean isRouteDisplayed() {
  // TODO Auto-generated method stub
  return false;
}
// Map extends overlay for drawing path
class MapOverlay extends com.google.android.maps.Overlay {
  Road mRoad;
  ArrayList<GeoPoint> mPoints;

  public MapOverlay(Road road, MapView mv) {
    mRoad = road;
    // mRoute is field of route points getting from Google Maps
    if (road.mRoute.length > 0) {
      mPoints = new ArrayList<GeoPoint>();
      for (int i = 0; i < road.mRoute.length; i++) {
        mPoints.add(new GeoPoint(
        (int) (road.mRoute[i][1] * 1000000),
        (int) (road.mRoute[i][0] * 1000000)));
      }
    }
  }

  @Override
  public void draw(Canvas canvas, MapView mv, boolean shadow) {
    drawPath(mv, canvas);
  }

  public void drawPath(MapView mv, Canvas canvas) {
    int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
    Paint paint = new Paint();
    latSpan = mv.getLatitudeSpan();
    paint.setColor(Color.parseColor("#998447cc"));
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(6);
    if (mPoints != null) {
      for (int i = 0; i < mPoints.size(); i++) {
        Point point = new Point();
        mv.getProjection().toPixels(mPoints.get(i), point);
        x2 = point.x;
        y2 = point.y;
        if (i > 0) {
          canvas.drawLine(x1, y1, x2, y2, paint);
        }
        x1 = x2;
        y1 = y2;
      }
    }
  }
}
}

In addition, you will need this files:

1. Road.java

package tvarwebu.projects.map;

public class Road {
public String mName;
public String mDescription;
public int mColor;
public int mWidth;
public double[][] mRoute = new double[][] {};
public Point[] mPoints = new Point[] {};

}

2. Point.java

package tvarwebu.projects.map;

public class Point {
  String mName;
  String mDescription;
  String mIconUrl;
  double mLatitude;
  double mLongitude;
}

3. RoadProvider.java

package tvarwebu.projects.map;

import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class RoadProvider {

public static Road getRoute(InputStream is) {
  KMLHandler handler = new KMLHandler();
  try {
    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
    parser.parse(is, handler);
  } catch (ParserConfigurationException e) {
    e.printStackTrace();
  } catch (SAXException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  return handler.mRoad;
}

public static String getUrl(double fromLat, double fromLon, double toLat, double toLon) {// connect to map web service
  StringBuffer urlString = new StringBuffer();
  urlString.append("http://maps.google.com/maps?f=d&hl=en");
  urlString.append("&saddr=");// from
  urlString.append(Double.toString(fromLat));
  urlString.append(",");
  urlString.append(Double.toString(fromLon));
  urlString.append("&daddr=");// to
  urlString.append(Double.toString(toLat));
  urlString.append(",");
  urlString.append(Double.toString(toLon));
  urlString.append("&ie=UTF8&0&om=0&output=kml");

  String ggg = urlString.toString();
  return urlString.toString();
}
}

class KMLHandler extends DefaultHandler {
  Road mRoad;
  boolean isPlacemark;
  boolean isRoute;
  boolean isItemIcon;
  private Stack mCurrentElement = new Stack();
  private String mString;

  public KMLHandler() {
  mRoad = new Road();
}

public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
  mCurrentElement.push(localName);
  if (localName.equalsIgnoreCase("Placemark")) {
    isPlacemark = true;
    mRoad.mPoints = addPoint(mRoad.mPoints);
  } else if (localName.equalsIgnoreCase("ItemIcon")) {
    if (isPlacemark)
      isItemIcon = true;
    }
    mString = new String();
  }

  public void characters(char[] ch, int start, int length) throws SAXException {
    String chars = new String(ch, start, length).trim();
    mString = mString.concat(chars);
  }

  public void endElement(String uri, String localName, String name) throws SAXException {
    if (mString.length() > 0) {
      if (localName.equalsIgnoreCase("name")) {
        if (isPlacemark) {
          isRoute = mString.equalsIgnoreCase("Route");
          if (!isRoute) {
            mRoad.mPoints[mRoad.mPoints.length - 1].mName = mString;
          }
        } else {
          mRoad.mName = mString;
        }
      } else if (localName.equalsIgnoreCase("color") && !isPlacemark) {
        mRoad.mColor = Integer.parseInt(mString, 16);
      } else if (localName.equalsIgnoreCase("width") && !isPlacemark) {
        mRoad.mWidth = Integer.parseInt(mString);
      } else if (localName.equalsIgnoreCase("description")) {
        if (isPlacemark) {
          String description = cleanup(mString);
          if (!isRoute)
            mRoad.mPoints[mRoad.mPoints.length - 1].mDescription = description;
          else
            mRoad.mDescription = description;
        }
      } else if (localName.equalsIgnoreCase("href")) {
        if (isItemIcon) {
          mRoad.mPoints[mRoad.mPoints.length - 1].mIconUrl = mString;
        }
      } else if (localName.equalsIgnoreCase("coordinates")) {
        if (isPlacemark) {
          if (!isRoute) {
            String[] xyParsed = split(mString, ",");
            double lon = Double.parseDouble(xyParsed[0]);
            double lat = Double.parseDouble(xyParsed[1]);
            mRoad.mPoints[mRoad.mPoints.length - 1].mLatitude = lat;
            mRoad.mPoints[mRoad.mPoints.length - 1].mLongitude = lon;
          } else {
            String[] coodrinatesParsed = split(mString, " ");
            int count = 0;
            if(mRoad.mRoute.length < 2)
              mRoad.mRoute = new double[coodrinatesParsed.length][2];
            else {
              double[][] mRouteTmp = mRoad.mRoute;
              mRoad.mRoute = new double[mRouteTmp.length + coodrinatesParsed.length][2];
              //for (int i = 0; i < mRouteTmp.length; i++) {
              //mRoad.mRoute[i] = mRouteTmp[i];
              //mRoad.mRoute[i][i] = mRouteTmp[i][i];
              //}
              System.arraycopy(mRouteTmp, 0, mRoad.mRoute, 0, mRouteTmp.length);

              count = mRouteTmp.length;
            }

            for (int i = count; i < (mRoad.mRoute.length); i++) {
              String[] xyParsed = split(coodrinatesParsed[i - count], ",");
              for (int j = 0; j < 2 && j < xyParsed.length; j++)
                mRoad.mRoute[i][j] = Double.parseDouble(xyParsed[j]);
            }
          }
        }
      }
    }
    mCurrentElement.pop();
    if (localName.equalsIgnoreCase("Placemark")) {
      isPlacemark = false;
      if (isRoute)
      isRoute = false;
    } else if (localName.equalsIgnoreCase("ItemIcon")) {
      if (isItemIcon)
        isItemIcon = false;
    }
  }

  private String cleanup(String value) {
    String remove = "<br/>";
    int index = value.indexOf(remove);
    if (index != -1)
    value = value.substring(0, index);
    remove = " ";
    index = value.indexOf(remove);
    int len = remove.length();
    while (index != -1) {
      value = value.substring(0, index).concat(
      value.substring(index + len, value.length()));
      index = value.indexOf(remove);
    }
    return value;
  }

  public Point[] addPoint(Point[] mPoints) {
    Point[] result = new Point[mPoints.length + 1];
    for (int i = 0; i < mPoints.length; i++)
      result[i] = mPoints[i];
    result[mPoints.length] = new Point();
    return result;
  }

  private static String[] split(String strString, String strDelimiter) {
    String[] strArray;
    int iOccurrences = 0;
    int iIndexOfInnerString = 0;
    int iIndexOfDelimiter = 0;
    int iCounter = 0;
    if (strString == null) {
      throw new IllegalArgumentException("Input string cannot be null.");
    }
    if (strDelimiter.length() <= 0 || strDelimiter == null) {
      throw new IllegalArgumentException("Delimeter cannot be null or empty.");
    }
    if (strString.startsWith(strDelimiter)) {
      strString = strString.substring(strDelimiter.length());
    }
    if (!strString.endsWith(strDelimiter)) {
      strString += strDelimiter;
    }
    while ((iIndexOfDelimiter = strString.indexOf(strDelimiter,
      iIndexOfInnerString)) != -1) {
      iOccurrences += 1;
      iIndexOfInnerString = iIndexOfDelimiter + strDelimiter.length();
    }

    strArray = new String[iOccurrences];
    iIndexOfInnerString = 0;
    iIndexOfDelimiter = 0;
    while ((iIndexOfDelimiter = strString.indexOf(strDelimiter,
      iIndexOfInnerString)) != -1) {
      strArray[iCounter] = strString.substring(iIndexOfInnerString,
      iIndexOfDelimiter);
      iIndexOfInnerString = iIndexOfDelimiter + strDelimiter.length();
      iCounter += 1;
    }

    return strArray;
  }
}

4. MyItemizedOverlay.java :

package tvarwebu.projects.map;

import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;

public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
  private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
  private Context mContext;

  public MyItemizedOverlay(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();
  }

  public void removeLast(){
    mOverlays.remove(mOverlays.size()-1);
  }

  @Override
  protected boolean onTap(int index) {
    OverlayItem item = mOverlays.get(index);
    AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
    dialog.setTitle(item.getTitle());
    dialog.setMessage(item.getSnippet());
    dialog.show();
    return true;
  }
}

When you change names of package, you should get this screen after launch:

Hope that this article helps you with this problem :) See the comple source code of Drawing route on MapView.

by Roman Holomek,  TvářWebu

7 comments

Skip to comment form

  1. Pamela

    Amazing!

  2. Nejc

    Hi, i got one question about your post.

    I copied classes and change the package names and i got the result like this:
    http://shrani.si/f/3X/m3/1XdPKC4V/screenshot2012-05-15-14-.jpg

    On the result image there are two markers, but the path is not displayed. Do you maybe know where the problem is?

    1. Roman

      It’s really hard to say, check the response from Google, if you get correct data about route and then check drawPath method in main activity class, these are only places which could affect drawing route.

  3. longchamp online

    Nice post. I learn something more challenging on different blogs everyday. It will always be stimulating to read content from other writers and practice a little something from their store. I’d prefer to use some with the content on my blog whether you don’t mind. Natually I’ll give you a link on your web blog. Thanks for sharing.

  4. raju

    hai i copied all the code bt it is displaying the locations of 2 places. I want to display road route between those 2 points… please help me out.. thanks in advance..

  5. Shubhajit paul

    Hey Please upload the source code here.. That will be more helpful

  6. Maimoonah

    Thanks
    I want to let the road be drawn according to current gps points while trip,,how this can be?

Leave a Reply to roman Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>