Android Draw route path between Multiple Distance
Previous Post draw route between two distance now draw route between more the two.
Main.xml
<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
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="0vhRtRSX4ZNpVfcGL73Z9AO23GGEzrScRkAxFng" />
</LinearLayout>
balloon_overlay.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/balloon_main_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/balloon_inner_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:id="@+id/balloon_item_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="16dip" >
</TextView>
<TextView
android:id="@+id/balloon_item_snippet"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="12dip" >
</TextView>
</LinearLayout>
</LinearLayout>
BalloonItemizedOverlay.java
package com.froger.routepathexample;
import java.util.List;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
/**
* An abstract extension of ItemizedOverlay for displaying an information balloon
* upon screen-tap of each marker overlay.
*
* @author Jeff Gilfelt
*/
public abstract class BalloonItemizedOverlay<Item extends OverlayItem> extends ItemizedOverlay<Item> {
private MapView mapView;
private BalloonOverlayView<Item> balloonView;
private View clickRegion;
private int viewOffset;
final MapController mc;
private Item currentFocussedItem;
private int currentFocussedIndex;
public BalloonItemizedOverlay(Drawable defaultMarker, MapView mapView) {
super(defaultMarker);
this.mapView = mapView;
viewOffset = 0;
mc = mapView.getController();
}
public void setBalloonBottomOffset(int pixels) {
viewOffset = pixels;
}
public int getBalloonBottomOffset() {
return viewOffset;
}
protected boolean onBalloonTap(int index, Item item) {
return false;
}
@Override
protected final boolean onTap(int index) {
currentFocussedIndex = index;
currentFocussedItem = createItem(index);
boolean isRecycled;
if (balloonView == null) {
balloonView = createBalloonOverlayView();
clickRegion = (View) balloonView.findViewById(R.id.balloon_inner_layout);
clickRegion.setOnTouchListener(createBalloonTouchListener());
isRecycled = false;
} else {
isRecycled = true;
}
balloonView.setVisibility(View.GONE);
List<Overlay> mapOverlays = mapView.getOverlays();
if (mapOverlays.size() > 1) {
hideOtherBalloons(mapOverlays);
}
balloonView.setData(currentFocussedItem);
GeoPoint point = currentFocussedItem.getPoint();
MapView.LayoutParams params = new MapView.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, point,
MapView.LayoutParams.BOTTOM_CENTER);
params.mode = MapView.LayoutParams.MODE_MAP;
balloonView.setVisibility(View.VISIBLE);
if (isRecycled) {
balloonView.setLayoutParams(params);
} else {
mapView.addView(balloonView, params);
}
mc.animateTo(point);
return true;
}
/**
* Creates the balloon view. Override to create a sub-classed view that
* can populate additional sub-views.
*/
protected BalloonOverlayView<Item> createBalloonOverlayView() {
return new BalloonOverlayView<Item>(getMapView().getContext(), getBalloonBottomOffset());
}
/**
* Expose map view to subclasses.
* Helps with creation of balloon views.
*/
protected MapView getMapView() {
return mapView;
}
/**
* Sets the visibility of this overlay's balloon view to GONE.
*/
protected void hideBalloon() {
if (balloonView != null) {
balloonView.setVisibility(View.GONE);
}
}
/**
* Hides the balloon view for any other BalloonItemizedOverlay instances
* that might be present on the MapView.
*
* @param overlays - list of overlays (including this) on the MapView.
*/
private void hideOtherBalloons(List<Overlay> overlays) {
for (Overlay overlay : overlays) {
if (overlay instanceof BalloonItemizedOverlay<?> && overlay != this) {
((BalloonItemizedOverlay<?>) overlay).hideBalloon();
}
}
}
/**
* Sets the onTouchListener for the balloon being displayed, calling the
* overridden {@link #onBalloonTap} method.
*/
private OnTouchListener createBalloonTouchListener() {
return new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
View l = ((View) v.getParent()).findViewById(R.id.balloon_main_layout);
Drawable d = l.getBackground();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int[] states = {android.R.attr.state_pressed};
if (d.setState(states)) {
d.invalidateSelf();
}
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
int newStates[] = {};
if (d.setState(newStates)) {
d.invalidateSelf();
}
onBalloonTap(currentFocussedIndex, currentFocussedItem);
return true;
} else {
return false;
}
}
};
}
}
BalloonOverlayView.java
package com.froger.routepathexample;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.android.maps.OverlayItem;
/**
* A view representing a MapView marker information balloon.
* <p>
* This class has a number of Android resource dependencies:
* <ul>
* <li>drawable/balloon_overlay_bg_selector.xml</li>
* <li>drawable/balloon_overlay_close.png</li>
* <li>drawable/balloon_overlay_focused.9.png</li>
* <li>drawable/balloon_overlay_unfocused.9.png</li>
* <li>layout/balloon_map_overlay.xml</li>
* </ul>
* </p>
*
* @author Jeff Gilfelt
*
*/
public class BalloonOverlayView<Item extends OverlayItem> extends FrameLayout {
private LinearLayout layout;
private TextView title;
private TextView snippet;
/**
* Create a new BalloonOverlayView.
*
* @param context - The activity context.
* @param balloonBottomOffset - The bottom padding (in pixels) to be applied
* when rendering this view.
*/
public BalloonOverlayView(Context context, int balloonBottomOffset) {
super(context);
setPadding(10, 0, 10, balloonBottomOffset);
layout = new LinearLayout(context);
layout.setVisibility(VISIBLE);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.balloon_overlay, layout);
title = (TextView) v.findViewById(R.id.balloon_item_title);
snippet = (TextView) v.findViewById(R.id.balloon_item_snippet);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.NO_GRAVITY;
addView(layout, params);
}
/**
* Sets the view data from a given overlay item.
*
* @param item - The overlay item containing the relevant view data
* (title and snippet).
*/
public void setData(Item item) {
layout.setVisibility(VISIBLE);
if (item.getTitle() != null) {
title.setVisibility(VISIBLE);
title.setText(item.getTitle());
} else {
title.setVisibility(GONE);
}
if (item.getSnippet() != null) {
snippet.setVisibility(VISIBLE);
snippet.setText(item.getSnippet());
} else {
snippet.setVisibility(GONE);
}
}
}
GoogleParser.java
package com.froger.routepathexample;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.google.android.maps.GeoPoint;
public class GoogleParser extends XMLParser implements Parser {
/** Distance covered. **/
private int distance;
public GoogleParser(String feedUrl) {
super(feedUrl);
}
/**
* Parses a url pointing to a Google JSON object to a Route object.
* @return a Route object based on the JSON object.
*/
public Route parse() {
// turn the stream into a string
final String result = convertStreamToString(this.getInputStream());
//Create an empty route
final Route route = new Route();
//Create an empty segment
final Segment segment = new Segment();
try {
//Tranform the string into a json object
final JSONObject json = new JSONObject(result);
//Get the route object
final JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0);
//Get the leg, only one leg as we don't support waypoints
final JSONObject leg = jsonRoute.getJSONArray("legs").getJSONObject(0);
//Get the steps for this leg
final JSONArray steps = leg.getJSONArray("steps");
//Number of steps for use in for loop
final int numSteps = steps.length();
//Set the name of this route using the start & end addresses
route.setName(leg.getString("start_address") + " to " + leg.getString("end_address"));
//Get google's copyright notice (tos requirement)
route.setCopyright(jsonRoute.getString("copyrights"));
//Get the total length of the route.
route.setLength(leg.getJSONObject("distance").getInt("value"));
//Get any warnings provided (tos requirement)
if (!jsonRoute.getJSONArray("warnings").isNull(0)) {
route.setWarning(jsonRoute.getJSONArray("warnings").getString(0));
}
/* Loop through the steps, creating a segment for each one and
* decoding any polylines found as we go to add to the route object's
* map array. Using an explicit for loop because it is faster!
*/
for (int i = 0; i < numSteps; i++) {
//Get the individual step
final JSONObject step = steps.getJSONObject(i);
//Get the start position for this step and set it on the segment
final JSONObject start = step.getJSONObject("start_location");
final GeoPoint position = new GeoPoint((int) (start.getDouble("lat")*1E6),
(int) (start.getDouble("lng")*1E6));
segment.setPoint(position);
//Set the length of this segment in metres
final int length = step.getJSONObject("distance").getInt("value");
distance += length;
segment.setLength(length);
segment.setDistance(distance/1000);
//Strip html from google directions and set as turn instruction
segment.setInstruction(step.getString("html_instructions").replaceAll("<(.*?)*>", ""));
//Retrieve & decode this segment's polyline and add it to the route.
route.addPoints(decodePolyLine(step.getJSONObject("polyline").getString("points")));
//Push a copy of the segment to the route
route.addSegment(segment.copy());
}
} catch (JSONException e) {
Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
}
return route;
}
/**
* Convert an inputstream to a string.
* @param input inputstream to convert.
* @return a String of the inputstream.
*/
private static String convertStreamToString(final InputStream input) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
final StringBuilder sBuf = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sBuf.append(line);
}
} catch (IOException e) {
Log.e(e.getMessage(), "Google parser, stream2string");
} finally {
try {
input.close();
} catch (IOException e) {
Log.e(e.getMessage(), "Google parser, stream2string");
}
}
return sBuf.toString();
}
/**
* Decode a polyline string into a list of GeoPoints.
* @param poly polyline encoded string to decode.
* @return the list of GeoPoints represented by this polystring.
*/
private List<GeoPoint> decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List<GeoPoint> decoded = new ArrayList<GeoPoint>();
int lat = 0;
int lng = 0;
while (index < len) {
int b;
int shift = 0;
int result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
decoded.add(new GeoPoint(
(int) (lat*1E6 / 1E5), (int) (lng*1E6 / 1E5)));
}
return decoded;
}
}
MainActivity.java
package com.froger.routepathexample;
import java.util.List;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
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;
public class MainActivity extends MapActivity {
private MapView mapView;
GeoPoint SourceGeoPoint,DescGeoPoint,thirdGeoPoint;
Drawable drawable;
MyItemizedOverlay itemizedOverlay;
@Override
protected boolean isRouteDisplayed() { return false; }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView)findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
SourceGeoPoint = new GeoPoint((int) (13.0473735 * 1E6), (int) (80.2442964 * 1E6));// First point
DescGeoPoint= new GeoPoint((int) (13.005485 * 1E6), (int) (77.5840933 * 1E6));// Second point
thirdGeoPoint= new GeoPoint((int) (11.6523558 * 1E6), (int) (78.1566461 * 1E6));//Third Point
List<Overlay> mapOverlays = mapView.getOverlays();
drawable = getResources().getDrawable(R.drawable.first);
itemizedOverlay = new MyItemizedOverlay(drawable, mapView);
OverlayItem overlayItem = new OverlayItem(SourceGeoPoint, "","");
OverlayItem overlayItem2 = new OverlayItem(DescGeoPoint, "","");
OverlayItem overlayItem3 = new OverlayItem(thirdGeoPoint, "","");
itemizedOverlay.addOverlay(overlayItem);
itemizedOverlay.addOverlay(overlayItem2);
itemizedOverlay.addOverlay(overlayItem3);
mapOverlays.add(itemizedOverlay);
final MapController mc = mapView.getController();
mc.animateTo(SourceGeoPoint);
mc.setZoom(16);
Route route = directions(SourceGeoPoint,DescGeoPoint);
Route route1 = directions(DescGeoPoint,thirdGeoPoint);
RouteOverlay routeOverlay = new RouteOverlay(route, Color.BLUE);
RouteOverlay routeOverlay1 = new RouteOverlay(route1, Color.BLUE);
mapView.getOverlays().add(routeOverlay);
mapView.getOverlays().add(routeOverlay1);
}
private Route directions(final GeoPoint start, final GeoPoint dest) {
Parser parser;
String jsonURL = "http://maps.google.com/maps/api/directions/json?";
final StringBuffer sBuf = new StringBuffer(jsonURL);
sBuf.append("origin=");
sBuf.append(start.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(start.getLongitudeE6()/1E6);
sBuf.append("&destination=");
sBuf.append(dest.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(dest.getLongitudeE6()/1E6);
sBuf.append("&sensor=true&mode=driving");
parser = new GoogleParser(sBuf.toString());
Route r = parser.parse();
return r;
}
}
MyItemizedOverlay.java
package com.froger.routepathexample;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
public class MyItemizedOverlay extends BalloonItemizedOverlay<OverlayItem> {
private ArrayList<OverlayItem> m_overlays = new ArrayList<OverlayItem>();
private Context c;
public MyItemizedOverlay(Drawable defaultMarker, MapView mapView) {
super(boundCenter(defaultMarker), mapView);
c = mapView.getContext();
}
public void addOverlay(OverlayItem overlay) {
m_overlays.add(overlay);
populate();
}
@Override
protected OverlayItem createItem(int i) {
return m_overlays.get(i);
}
@Override
public int size() {
return m_overlays.size();
}
@Override
protected boolean onBalloonTap(int index, OverlayItem item) {
/*Toast.makeText(c, "LOCATION " + index,
Toast.LENGTH_LONG).show();*/
return true;
}
}
Parser.java
package com.froger.routepathexample;
public interface Parser {
public Route parse();
}
Route.java
package com.froger.routepathexample;
import java.util.ArrayList;
import java.util.List;
import com.google.android.maps.GeoPoint;
public class Route {
private String name;
private final List<GeoPoint> points;
private List<Segment> segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;
public Route() {
points = new ArrayList<GeoPoint>();
segments = new ArrayList<Segment>();
}
public void addPoint(final GeoPoint p) {
points.add(p);
}
public void addPoints(final List<GeoPoint> points) {
this.points.addAll(points);
}
public List<GeoPoint> getPoints() {
return points;
}
public void addSegment(final Segment s) {
segments.add(s);
}
public List<Segment> getSegments() {
return segments;
}
/**
* @param name the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param copyright the copyright to set
*/
public void setCopyright(String copyright) {
this.copyright = copyright;
}
/**
* @return the copyright
*/
public String getCopyright() {
return copyright;
}
/**
* @param warning the warning to set
*/
public void setWarning(String warning) {
this.warning = warning;
}
/**
* @return the warning
*/
public String getWarning() {
return warning;
}
/**
* @param country the country to set
*/
public void setCountry(String country) {
this.country = country;
}
/**
* @return the country
*/
public String getCountry() {
return country;
}
/**
* @param length the length to set
*/
public void setLength(int length) {
this.length = length;
}
/**
* @return the length
*/
public int getLength() {
return length;
}
/**
* @param polyline the polyline to set
*/
public void setPolyline(String polyline) {
this.polyline = polyline;
}
/**
* @return the polyline
*/
public String getPolyline() {
return polyline;
}
}
RouteOverlay.java
package com.froger.routepathexample;
import java.util.Iterator;
import java.util.List;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
class RouteOverlay extends Overlay {
/** GeoPoints representing this routePoints. **/
private final List<GeoPoint> routePoints;
/** Colour to paint routePoints. **/
private int colour;
/** Alpha setting for route overlay. **/
private static final int ALPHA = 120;
/** Stroke width. **/
private static final float STROKE = 4.5f;
/** Route path. **/
private final Path path;
/** Point to draw with. **/
private final Point p;
/** Paint for path. **/
private final Paint paint;
/**
* Public constructor.
*
* @param route Route object representing the route.
* @param defaultColour default colour to draw route in.
*/
public RouteOverlay(final Route route, final int defaultColour) {
super();
routePoints = route.getPoints();
colour = defaultColour;
path = new Path();
p = new Point();
paint = new Paint();
}
@Override
public final void draw(final Canvas c, final MapView mv,
final boolean shadow) {
super.draw(c, mv, shadow);
paint.setColor(colour);
paint.setAlpha(ALPHA);
paint.setAntiAlias(true);
paint.setStrokeWidth(STROKE);
paint.setStyle(Paint.Style.STROKE);
redrawPath(mv);
c.drawPath(path, paint);
}
/**
* Set the colour to draw this route's overlay with.
*
* @param c Int representing colour.
*/
public final void setColour(final int c) {
colour = c;
}
/**
* Clear the route overlay.
*/
public final void clear() {
routePoints.clear();
}
/**
* Recalculate the path accounting for changes to
* the projection and routePoints.
* @param mv MapView the path is drawn to.
*/
private void redrawPath(final MapView mv) {
final Projection prj = mv.getProjection();
path.rewind();
final Iterator<GeoPoint> it = routePoints.iterator();
prj.toPixels(it.next(), p);
path.moveTo(p.x, p.y);
while (it.hasNext()) {
prj.toPixels(it.next(), p);
path.lineTo(p.x, p.y);
}
path.setLastPoint(p.x, p.y);
}
}
Segment.java
package com.froger.routepathexample;
import com.google.android.maps.GeoPoint;
public class Segment {
/** Points in this segment. **/
private GeoPoint start;
/** Turn instruction to reach next segment. **/
private String instruction;
/** Length of segment. **/
private int length;
/** Distance covered. **/
private double distance;
/**
* Create an empty segment.
*/
public Segment() {
}
/**
* Set the turn instruction.
* @param turn Turn instruction string.
*/
public void setInstruction(final String turn) {
this.instruction = turn;
}
/**
* Get the turn instruction to reach next segment.
* @return a String of the turn instruction.
*/
public String getInstruction() {
return instruction;
}
/**
* Add a point to this segment.
* @param point GeoPoint to add.
*/
public void setPoint(final GeoPoint point) {
start = point;
}
/** Get the starting point of this
* segment.
* @return a GeoPoint
*/
public GeoPoint startPoint() {
return start;
}
/** Creates a segment which is a copy of this one.
* @return a Segment that is a copy of this one.
*/
public Segment copy() {
final Segment copy = new Segment();
copy.start = start;
copy.instruction = instruction;
copy.length = length;
copy.distance = distance;
return copy;
}
/**
* @param length the length to set
*/
public void setLength(final int length) {
this.length = length;
}
/**
* @return the length
*/
public int getLength() {
return length;
}
/**
* @param distance the distance to set
*/
public void setDistance(double distance) {
this.distance = distance;
}
/**
* @return the distance
*/
public double getDistance() {
return distance;
}
}
XMLParser.java
package com.froger.routepathexample;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
public class XMLParser {
// names of the XML tags
protected static final String MARKERS = "markers";
protected static final String MARKER = "marker";
protected URL feedUrl;
protected XMLParser(final String feedUrl) {
try {
this.feedUrl = new URL(feedUrl);
} catch (MalformedURLException e) {
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
}
}
protected InputStream getInputStream() {
try {
return feedUrl.openConnection().getInputStream();
} catch (IOException e) {
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
return null;
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.froger.routepathexample"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="7" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
First.png Image
Output screen.