Google changed its map API for Android and introduced API V2. The previous codes for drawing path are not working with API V2.
I have managed to draw a path with API V2. I had searched a lot for the solution but did not find any answer. So I am sharing its answer.
This question is related to
google-maps
path
google-maps-markers
routes
google-maps-android-api-2
Dont know whether I should put this as answer or not...
I used @Zeeshan0026's solution to draw the path...and the problem was that if I draw path once, and then I do try to draw path once again, both two paths show and this continues...paths showing even when markers were deleted... while, ideally, old paths' shouldn't be there once new path is drawn / markers are deleted..
going through some other question over SO, I had the following solution
I add the following function in Zeeshan's class
public void clearRoute(){
for(Polyline line1 : polylines)
{
line1.remove();
}
polylines.clear();
}
in my map activity, before drawing the path, I called this function.. example usage as per my app is
private Route rt;
rt.clearRoute();
if (src == null) {
Toast.makeText(getApplicationContext(), "Please select your Source", Toast.LENGTH_LONG).show();
}else if (Destination == null) {
Toast.makeText(getApplicationContext(), "Please select your Destination", Toast.LENGTH_LONG).show();
}else if (src.equals(Destination)) {
Toast.makeText(getApplicationContext(), "Source and Destinatin can not be the same..", Toast.LENGTH_LONG).show();
}else{
rt.drawRoute(mMap, MapsMainActivity.this, src,
Destination, false, "en");
}
you can use rt.clearRoute();
as per your requirements..
Hoping that it will save a few minutes of someone else and will help some beginner in solving this issue..
Complete Class Code
see on github
Edit: here is part of code from mainactivity..
case R.id.mkrbtn_set_dest:
Destination = selmarker.getPosition();
destmarker = selmarker;
desShape = createRouteCircle(Destination, false);
if (src == null) {
Toast.makeText(getApplicationContext(),
"Please select your Source first...",
Toast.LENGTH_LONG).show();
} else if (src.equals(Destination)) {
Toast.makeText(getApplicationContext(),
"Source and Destinatin can not be the same..",
Toast.LENGTH_LONG).show();
} else {
if (isNetworkAvailable()) {
rt.drawRoute(mMap, MapsMainActivity.this, src,
Destination, false, "en");
src = null;
Destination = null;
} else {
Toast.makeText(
getApplicationContext(),
"Internet Connection seems to be OFFLINE...!",
Toast.LENGTH_LONG).show();
}
}
break;
Edit 2 as per comments
usage :
//variables as data members
GoogleMap mMap;
private Route rt;
static LatLng src;
static LatLng Destination;
//MapsMainActivity is my activity
//false for interim stops for traffic, google
// en language for html description returned
rt.drawRoute(mMap, MapsMainActivity.this, src,
Destination, false, "en");
in below code midpointsList is an ArrayList of waypoints
private String getMapsApiDirectionsUrl(GoogleMap googleMap, LatLng startLatLng, LatLng endLatLng, ArrayList<LatLng> midpointsList) {
String origin = "origin=" + startLatLng.latitude + "," + startLatLng.longitude;
String midpoints = "";
for (int mid = 0; mid < midpointsList.size(); mid++) {
midpoints += "|" + midpointsList.get(mid).latitude + "," + midpointsList.get(mid).longitude;
}
String waypoints = "waypoints=optimize:true" + midpoints + "|";
String destination = "destination=" + endLatLng.latitude + "," + endLatLng.longitude;
String key = "key=AIzaSyCV1sOa_7vASRBs6S3S6t1KofFvDhjohvI";
String sensor = "sensor=false";
String params = origin + "&" + waypoints + "&" + destination + "&" + sensor + "&" + key;
String output = "json";
String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + params;
Log.e("url", url);
parseDirectionApidata(url, googleMap);
return url;
}
Then copy and paste this url in your browser to check And the below code is to parse the url
private void parseDirectionApidata(String url, final GoogleMap googleMap) {
final JSONObject jsonObject = new JSONObject();
try {
AppUtill.getJsonWithHTTPPost(ViewMapActivity.this, 1, new ServiceCallBack() {
@Override
public void serviceCallBack(int id, JSONObject jsonResult) throws JSONException {
if (jsonResult != null) {
Log.e("jsonRes", jsonResult.toString());
String status = jsonResult.optString("status");
if (status.equalsIgnoreCase("ok")) {
drawPath(jsonResult, googleMap);
}
} else {
Toast.makeText(ViewMapActivity.this, "Unable to parse Directions Data", Toast.LENGTH_LONG).show();
}
}
}, url, jsonObject);
} catch (Exception e) {
e.printStackTrace();
}
}
And then pass the result to the drawPath method
public void drawPath(JSONObject jObject, GoogleMap googleMap) {
List<List<HashMap<String, String>>> routes = new ArrayList<List<HashMap<String, String>>>();
JSONArray jRoutes = null;
JSONArray jLegs = null;
JSONArray jSteps = null;
List<LatLng> list = null;
try {
Toast.makeText(ViewMapActivity.this, "Drawing Path...", Toast.LENGTH_SHORT).show();
jRoutes = jObject.getJSONArray("routes");
/** Traversing all routes */
for (int i = 0; i < jRoutes.length(); i++) {
jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
List path = new ArrayList<HashMap<String, String>>();
/** Traversing all legs */
for (int j = 0; j < jLegs.length(); j++) {
jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");
/** Traversing all steps */
for (int k = 0; k < jSteps.length(); k++) {
String polyline = "";
polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
list = decodePoly(polyline);
}
Log.e("list", list.toString());
routes.add(path);
Log.e("routes", routes.toString());
if (list != null) {
Polyline line = googleMap.addPolyline(new PolylineOptions()
.addAll(list)
.width(12)
.color(Color.parseColor("#FF0000"))//Google maps blue color #05b1fb
.geodesic(true)
);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
private List<LatLng> decodePoly(String encoded) {
List<LatLng> poly = new ArrayList<LatLng>();
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.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 = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
LatLng p = new LatLng((((double) lat / 1E5)),
(((double) lng / 1E5)));
poly.add(p);
}
return poly;
}
decode poly function is to decode the points(lat and long) provided by Directions API in encoded form
Try below solution to draw path with animation and also get time and distance between two points.
DirectionHelper.java
public class DirectionHelper {
public List<List<HashMap<String, String>>> parse(JSONObject jObject) {
List<List<HashMap<String, String>>> routes = new ArrayList<>();
JSONArray jRoutes;
JSONArray jLegs;
JSONArray jSteps;
JSONObject jDistance = null;
JSONObject jDuration = null;
try {
jRoutes = jObject.getJSONArray("routes");
/** Traversing all routes */
for (int i = 0; i < jRoutes.length(); i++) {
jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
List path = new ArrayList<>();
/** Traversing all legs */
for (int j = 0; j < jLegs.length(); j++) {
/** Getting distance from the json data */
jDistance = ((JSONObject) jLegs.get(j)).getJSONObject("distance");
HashMap<String, String> hmDistance = new HashMap<String, String>();
hmDistance.put("distance", jDistance.getString("text"));
/** Getting duration from the json data */
jDuration = ((JSONObject) jLegs.get(j)).getJSONObject("duration");
HashMap<String, String> hmDuration = new HashMap<String, String>();
hmDuration.put("duration", jDuration.getString("text"));
/** Adding distance object to the path */
path.add(hmDistance);
/** Adding duration object to the path */
path.add(hmDuration);
jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");
/** Traversing all steps */
for (int k = 0; k < jSteps.length(); k++) {
String polyline = "";
polyline = (String) ((JSONObject) ((JSONObject) jSteps.get(k)).get("polyline")).get("points");
List<LatLng> list = decodePoly(polyline);
/** Traversing all points */
for (int l = 0; l < list.size(); l++) {
HashMap<String, String> hm = new HashMap<>();
hm.put("lat", Double.toString((list.get(l)).latitude));
hm.put("lng", Double.toString((list.get(l)).longitude));
path.add(hm);
}
}
routes.add(path);
}
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
}
return routes;
}
//Method to decode polyline points
private List<LatLng> decodePoly(String encoded) {
List<LatLng> poly = new ArrayList<>();
int index = 0, len = encoded.length();
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = encoded.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 = encoded.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
LatLng p = new LatLng((((double) lat / 1E5)),
(((double) lng / 1E5)));
poly.add(p);
}
return poly;
}
}
GetPathFromLocation.java
public class GetPathFromLocation extends AsyncTask<String, Void, List<List<HashMap<String, String>>>> {
private Context context;
private String TAG = "GetPathFromLocation";
private LatLng source, destination;
private ArrayList<LatLng> wayPoint;
private GoogleMap mMap;
private boolean animatePath, repeatDrawingPath;
private DirectionPointListener resultCallback;
private ProgressDialog progressDialog;
//https://www.mytrendin.com/draw-route-two-locations-google-maps-android/
//https://www.androidtutorialpoint.com/intermediate/google-maps-draw-path-two-points-using-google-directions-google-map-android-api-v2/
public GetPathFromLocation(Context context, LatLng source, LatLng destination, ArrayList<LatLng> wayPoint, GoogleMap mMap, boolean animatePath, boolean repeatDrawingPath, DirectionPointListener resultCallback) {
this.context = context;
this.source = source;
this.destination = destination;
this.wayPoint = wayPoint;
this.mMap = mMap;
this.animatePath = animatePath;
this.repeatDrawingPath = repeatDrawingPath;
this.resultCallback = resultCallback;
}
synchronized public String getUrl(LatLng source, LatLng dest, ArrayList<LatLng> wayPoint) {
String url = "https://maps.googleapis.com/maps/api/directions/json?sensor=false&mode=driving&origin="
+ source.latitude + "," + source.longitude + "&destination=" + dest.latitude + "," + dest.longitude;
for (int centerPoint = 0; centerPoint < wayPoint.size(); centerPoint++) {
if (centerPoint == 0) {
url = url + "&waypoints=optimize:true|" + wayPoint.get(centerPoint).latitude + "," + wayPoint.get(centerPoint).longitude;
} else {
url = url + "|" + wayPoint.get(centerPoint).latitude + "," + wayPoint.get(centerPoint).longitude;
}
}
url = url + "&key=" + context.getResources().getString(R.string.google_api_key);
return url;
}
public int getRandomColor() {
Random rnd = new Random();
return Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
}
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Please wait...");
progressDialog.setIndeterminate(false);
progressDialog.setCancelable(false);
progressDialog.show();
}
@Override
protected List<List<HashMap<String, String>>> doInBackground(String... url) {
String data;
try {
InputStream inputStream = null;
HttpURLConnection connection = null;
try {
URL directionUrl = new URL(getUrl(source, destination, wayPoint));
connection = (HttpURLConnection) directionUrl.openConnection();
connection.connect();
inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer stringBuffer = new StringBuffer();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
data = stringBuffer.toString();
bufferedReader.close();
} catch (Exception e) {
Log.e(TAG, "Exception : " + e.toString());
return null;
} finally {
inputStream.close();
connection.disconnect();
}
Log.e(TAG, "Background Task data : " + data);
//Second AsyncTask
JSONObject jsonObject;
List<List<HashMap<String, String>>> routes = null;
try {
jsonObject = new JSONObject(data);
// Starts parsing data
DirectionHelper helper = new DirectionHelper();
routes = helper.parse(jsonObject);
Log.e(TAG, "Executing Routes : "/*, routes.toString()*/);
return routes;
} catch (Exception e) {
Log.e(TAG, "Exception in Executing Routes : " + e.toString());
return null;
}
} catch (Exception e) {
Log.e(TAG, "Background Task Exception : " + e.toString());
return null;
}
}
@Override
protected void onPostExecute(List<List<HashMap<String, String>>> result) {
super.onPostExecute(result);
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
ArrayList<LatLng> points;
PolylineOptions lineOptions = null;
String distance = "";
String duration = "";
// Traversing through all the routes
for (int i = 0; i < result.size(); i++) {
points = new ArrayList<>();
lineOptions = new PolylineOptions();
// Fetching i-th route
List<HashMap<String, String>> path = result.get(i);
// Fetching all the points in i-th route
for (int j = 0; j < path.size(); j++) {
HashMap<String, String> point = path.get(j);
if (j == 0) { // Get distance from the list
distance = (String) point.get("distance");
continue;
} else if (j == 1) { // Get duration from the list
duration = (String) point.get("duration");
continue;
}
double lat = Double.parseDouble(point.get("lat"));
double lng = Double.parseDouble(point.get("lng"));
LatLng position = new LatLng(lat, lng);
points.add(position);
}
// Adding all the points in the route to LineOptions
lineOptions.addAll(points);
lineOptions.width(8);
lineOptions.color(Color.RED);
//lineOptions.color(getRandomColor());
if (animatePath) {
final ArrayList<LatLng> finalPoints = points;
((AppCompatActivity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
PolylineOptions polylineOptions;
final Polyline greyPolyLine, blackPolyline;
final ValueAnimator polylineAnimator;
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (LatLng latLng : finalPoints) {
builder.include(latLng);
}
polylineOptions = new PolylineOptions();
polylineOptions.color(Color.RED);
polylineOptions.width(8);
polylineOptions.startCap(new SquareCap());
polylineOptions.endCap(new SquareCap());
polylineOptions.jointType(ROUND);
polylineOptions.addAll(finalPoints);
greyPolyLine = mMap.addPolyline(polylineOptions);
polylineOptions = new PolylineOptions();
polylineOptions.width(8);
polylineOptions.color(Color.WHITE);
polylineOptions.startCap(new SquareCap());
polylineOptions.endCap(new SquareCap());
polylineOptions.zIndex(5f);
polylineOptions.jointType(ROUND);
blackPolyline = mMap.addPolyline(polylineOptions);
polylineAnimator = ValueAnimator.ofInt(0, 100);
polylineAnimator.setDuration(5000);
polylineAnimator.setInterpolator(new LinearInterpolator());
polylineAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
List<LatLng> points = greyPolyLine.getPoints();
int percentValue = (int) valueAnimator.getAnimatedValue();
int size = points.size();
int newPoints = (int) (size * (percentValue / 100.0f));
List<LatLng> p = points.subList(0, newPoints);
blackPolyline.setPoints(p);
}
});
polylineAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (repeatDrawingPath) {
List<LatLng> greyLatLng = greyPolyLine.getPoints();
if (greyLatLng != null) {
greyLatLng.clear();
}
polylineAnimator.start();
}
}
@Override
public void onAnimationCancel(Animator animation) {
polylineAnimator.cancel();
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
polylineAnimator.start();
}
});
}
Log.e(TAG, "PolylineOptions Decoded");
}
// Drawing polyline in the Google Map for the i-th route
if (resultCallback != null && lineOptions != null)
resultCallback.onPath(lineOptions, distance, duration);
}
}
DirectionPointListener
public interface DirectionPointListener {
public void onPath(PolylineOptions polyLine,String distance,String duration);
}
Now draw path using below code in your Activity
private GoogleMap mMap;
private ArrayList<LatLng> wayPoint = new ArrayList<>();
private SupportMapFragment mapFragment;
mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
/*Add Source Marker*/
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(source);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
mMap.addMarker(markerOptions);
builder.include(source);
/*Add Destination Marker*/
markerOptions = new MarkerOptions();
markerOptions.position(destination);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
mMap.addMarker(markerOptions);
builder.include(destination);
LatLngBounds bounds = builder.build();
int width = mapFragment.getView().getMeasuredWidth();
int height = mapFragment.getView().getMeasuredHeight();
int padding = (int) (width * 0.15); // offset from edges of the map 10% of screen
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);
mMap.animateCamera(cu);
new GetPathFromLocation(context, source, destination, wayPoint, mMap, true, false, new DirectionPointListener() {
@Override
public void onPath(PolylineOptions polyLine, String distance, String duration) {
mMap.addPolyline(polyLine);
Log.e(TAG, "onPath :: Distance :: " + distance + " Duration :: " + duration);
binding.txtDistance.setText(String.format(" %s", distance));
binding.txtDuration.setText(String.format(" %s", duration));
}
}).execute();
}
});
}
OutPut
I hope this can help you!
Thank You.
Source: Stackoverflow.com