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