Lou Zar Lou Zar - 5 months ago 133
Android Question

Google Maps API and custom polyline route between Markers

I'd like to make a custom route for an android app, I'm not sure which API should I use and if it is compatible with Java.

As far as I know I need to use waypoints to make a route (I don't need to know the distance between the two points, just to make a route).

The objective is to choose an option from a menu on the side of the map and show one of the custom routes between two Markers.

Answer

You can do this using the Google Maps API v2 for Android, and the Google Maps Directions webservice API

For getting started with the Google Maps API, there are plenty of other good answers already. See here for a complete working example of a simple map Activity. Note that you'll also need to get an API key set up to work with your project.

As for using the Google Maps Directions webservice API, you should first read the documentation. You can use an API key and enable the API in your developer console, but it still works currently without using an API key.

Here is the basic code you'll need in order to use the Google Maps API to draw a Polyline between two points, note that the points returned from the API are encoded in a base 64 encoded String that needs to be decoded.

First, here is the AsyncTask, that you should give two LatLng points to when calling it.

You would call the AsyncTask with two LatLng objects, for example between two Markers:

new GetDirectionsAsync().execute(markerOne.getPosition(), markerTwo.getPosition());

Here is the AsyncTask code:

class GetDirectionsAsync extends AsyncTask<LatLng, Void, List<LatLng>> {

    JSONParser jsonParser;
    String DIRECTIONS_URL = "https://maps.googleapis.com/maps/api/directions/json";


    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected List<LatLng> doInBackground(LatLng... params) {
        LatLng start = params[0];
        LatLng end = params[1];

        HashMap<String, String> points = new HashMap<>();
        points.put("origin", start.latitude + "," + start.longitude);
        points.put("destination", end.latitude + "," + end.longitude);

        jsonParser = new JSONParser();

        JSONObject obj = jsonParser.makeHttpRequest(DIRECTIONS_URL, "GET", points, true);

        if (obj == null) return null;

        try {
            List<LatLng> list = null;

            JSONArray routeArray = obj.getJSONArray("routes");
            JSONObject routes = routeArray.getJSONObject(0);
            JSONObject overviewPolylines = routes.getJSONObject("overview_polyline");
            String encodedString = overviewPolylines.getString("points");
            list = decodePoly(encodedString);

            return list;

        } catch (JSONException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(List<LatLng> pointsList) {

        if (pointsList == null) return;

        if (line != null){
            line.remove();
        }

        PolylineOptions options = new PolylineOptions().width(5).color(Color.MAGENTA).geodesic(true);
        for (int i = 0; i < pointsList.size(); i++) {
            LatLng point = pointsList.get(i);
            options.add(point);
        }
        line = mMap.addPolyline(options);

    }
}

The AsyncTask references some member variables of the Activity, namely the Polyline and the GoogleMap, the Activity definition would look like this:

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback{

    GoogleMap mMap;
    Polyline line;
    //.....

Here's the decodePoly() method used to decode the points:

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;
}

Here's the JSONParser class used in this example, note that this is a modified version updated for android-23 that I wrote a blog post about:

public class JSONParser {

    String charset = "UTF-8";
    HttpURLConnection conn;
    DataOutputStream wr;
    StringBuilder result;
    URL urlObj;
    JSONObject jObj = null;
    StringBuilder sbParams;
    String paramsString;

    public JSONObject makeHttpRequest(String url, String method,
                                      HashMap<String, String> params, boolean encode) {

        sbParams = new StringBuilder();
        int i = 0;
        for (String key : params.keySet()) {
            try {
                if (i != 0){
                    sbParams.append("&");
                }
                if (encode) {
                    sbParams.append(key).append("=")
                            .append(URLEncoder.encode(params.get(key), charset));
                }
                else{
                    sbParams.append(key).append("=")
                            .append(params.get(key));
                }

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            i++;
        }

        if (method.equals("POST")) {
            // request method is POST
            try {
                urlObj = new URL(url);

                conn = (HttpURLConnection) urlObj.openConnection();

                conn.setDoOutput(true);

                conn.setRequestMethod("POST");

                conn.setRequestProperty("Accept-Charset", charset);

                conn.setReadTimeout(10000);
                conn.setConnectTimeout(15000);

                conn.connect();

                paramsString = sbParams.toString();

                wr = new DataOutputStream(conn.getOutputStream());
                wr.writeBytes(paramsString);
                wr.flush();
                wr.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        else if(method.equals("GET")){
            // request method is GET

            if (sbParams.length() != 0) {
                url += "?" + sbParams.toString();
            }

            Log.d("JSONParser", "full GET url: " + url);

            try {
                urlObj = new URL(url);

                conn = (HttpURLConnection) urlObj.openConnection();

                conn.setDoOutput(false);

                conn.setRequestMethod("GET");

                conn.setRequestProperty("Accept-Charset", charset);

                conn.setConnectTimeout(15000);

                conn.connect();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        try {
            //Receive the response from the server
            InputStream in = new BufferedInputStream(conn.getInputStream());
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            String line;
            result = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }

            Log.d("JSON Parser", "result: " + result.toString());

        } catch (IOException e) {
            e.printStackTrace();
        }

        conn.disconnect();

        // try parse the string to a JSON object
        try {
            jObj = new JSONObject(result.toString());
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }

        // return JSON Object
        return jObj;
    }
}

Result of drawing a route between two Markers:

enter image description here