I'm developing an app where i need to know the path between the current user posistion and a point of interest.
I'm using android 2.3.3, google maps android v2 and direction api. My problem is that all the code i have found is for the old version of the maps, and I also tried to adapt the code but i failed. I try to change GeoPoint (not supported in this new versione) in LatLng. the problem is that i can't display the path, to do it i create a new polyline and i add it to the map.
i post my code:
Parser:
public interface Parser {
public Route parse();
}
XMLParser
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;
}
}
}
JsonParser (parse the google direction json)
public class JsonParser extends XMLParser implements Parser {
/** Distance covered. **/
private int distance;
public JsonParser(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 LatLng position = new LatLng(start.getDouble("lat"), start.getDouble("lng"));
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 LatLng.
* @param poly polyline encoded string to decode.
* @return the list of GeoPoints represented by this polystring.
*/
private List<LatLng> decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List<LatLng> decoded = new LinkedList<LatLng>();
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 LatLng((lat / 1E5),(lng / 1E5)));
}
return decoded;
}
}
Route (to save json info)
public class Route {
private String name;
private final List<LatLng> points;
private List<Segment> segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;
public Route() {
points = new LinkedList<LatLng>();
segments = new LinkedList<Segment>();
}
public void addPoint(final LatLng p) {
points.add(p);
}
public void addPoints(final List<LatLng> points) {
this.points.addAll(points);
}
public List<LatLng> 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;
}
}
Segment:
public class Segment {
/** Points in this segment. **/
private LatLng 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 LatLng to add.
*/
public void setPoint(LatLng point) {
start = point;
}
/** Get the starting point of this
* segment.
* @return a LatLng
*/
public LatLng 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;
}
}
My MainActivity (there are 326 code line because of user localization, you can find it on google developer site, so we can just suppose to have two static point A and B and I want to go from A to B):
public class MainActivity extends FragmentActivity{
private GoogleMap map;
private Marker currentLocation;
private PolylineOptions pathLine;
private LatLng imhere = new LatLng(41.8549038,12.4618208);
private LatLng poi = new LatLng(41.89000,12.49324);
private LocationManager mLocationManager;
private Handler mHandler;
private boolean mUseBoth;
private Context context;
// Keys for maintaining UI states after rotation.
private static final String KEY_BOTH = "use_both";
// UI handler codes.
private static final int UPDATE_LATLNG = 2;
private static final int FIVE_SECONDS = 5000;
private static final int THREE_METERS = 3;
private static final int TWO_MINUTES = 1000 * 60 * 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
Marker colosseoMarker = map.addMarker(new MarkerOptions()
.position(colosseo)
.title("Start")
.snippet("poi")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
Marker current pos = map.addMarker(new MarkerOptions()
.position(imhere)
.title("i'm here")
.snippet("here!")
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));
context =this ;
map.setOnMarkerClickListener(new OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
final String[] options = {"Calcola il Percorso"};
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Ottieni Informazioni aggiuntive");
builder.setPositiveButton("Calcola Percorso",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
LatLng start = new LatLng(imhere.latitude,imhere.longitude);
LatLng dest = new LatLng(poi.latitude, poi.longitude);
Route route = drawPath(start, dest);
List<LatLng> list= route.getPoints();
if(pathLine!= null) pathline =null;
pathLine = new PolylineOptions();
pathLine.addAll(list);
pathLine.color(Color.rgb(0,191,255));
map.addPolyline(pathLine);
}
});
AlertDialog alert = builder.create();
alert.show();
Toast.makeText(MainActivity.this, marker.getSnippet(),Toast.LENGTH_SHORT).show();
return true;
}
});
map.animateCamera(CameraUpdateFactory.newLatLngZoom(imhere, 12));
}
}
};
// Get a reference to the LocationManager object.
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
private Route drawPath(LatLng start, LatLng 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.latitude);
sBuf.append(',');
sBuf.append(start.longitude);
sBuf.append("&destination=");
sBuf.append(dest.latitude);
sBuf.append(',');
sBuf.append(dest.longitude);
sBuf.append("&sensor=true&mode=walking");
parser = new JsonParser(sBuf.toString());
Route r = parser.parse();
return r;
}
}
every suggestion is well accepted
Activity
. The JSON call usingdrawPath
should be done in anAsyncTask
. Before working with themap
, make sure it's ready to use, see the sample code in the SDK forsetUpMapIfNeeded
advised to be run inonResume()
.map.clear()
to clear the map before redrawing. – MiserereAsyncTask
and now it's better. Now I'm trying to usesetUpMapIfNeeded
andmap.clear()
– SeraphdecodePolyLine
works well. – Cenozoic