Кластер Google map с направлением

У нас есть Google map library, чтобы показать кратные маркеры в кластере.

У меня есть два вопроса:

1. Можем ли мы показать несколько направлений на карте Google, как показано ниже?

2. Можем ли мы показать детали нескольких направлений в кластерных маркерах?

enter image description here

Кластер

выглядит следующим образом:

Я приведу вам примеры: из страны Индия, я сохраняю разные направления в своем db.

как

ahmedabad to delhi

Дели в Агру

ahmedabad to bombay

jaypur to delhi

Я должен показать, что кластер из вышеуказанных направлений зависит от уровня масштабирования, когда пользователь масштабирует карту Google, вместо кластеров на карте Google отображается несколько указаний.

Я хочу знать, возможно ли это? Если да, то как?

Ответ 1

Вы можете достичь своей цели, используя API-интерфейс Directions. Вы указываете начальную точку и конечную точку (это может быть либо lat/lon, либо имя места). Еще одно обязательное поле - режим перемещения (по умолчанию - движение).

Теперь Directions возвращают вершины, которые могут быть преобразованы в полилинию, а затем нарисованы на карте. Этот ответ делает это довольно хорошо, и я буду использовать его код.

package com.example.simon.maps;

import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import android.graphics.Color;
import android.os.Bundle;
import android.os.StrictMode;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.util.SparseArray;

/**
 * Created by Simon on 2014 Jul 25.
 */

public class MainActivity extends FragmentActivity {

    final static String TAG = "MainActivity";
    GoogleMap mMap;
    GMapV2Direction md;
    int mZoomLevel;
    final float STARTING_ZOOM = 5.0f;
    // List of polylines for each zoom level
    SparseArray<List<Polyline>> mPolylines = new SparseArray<List<Polyline>>();

    public class direction {
        String start, end;
        int zoomLevel;

        direction(String pStart, String pEnd, int pZoomLevel) {
            start = pStart;
            end = pEnd;
            zoomLevel = pZoomLevel;
        }
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy p = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(p);
        }

        md = new GMapV2Direction();
        mMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();

        List<direction> directions = new ArrayList<direction>();
        directions.add(new direction("Ahmedabad,Gujarat,India", "delhi,India", 4));
        directions.add(new direction("Ahmedabad,Gujarat,India","Bombay,Maharashtra,India", 4));
        directions.add(new direction("Jeypore,Odisha,India", "delhi,India", 5));
        for (MainActivity.direction direction : directions) {
            // Query
            Document doc = md.getDocument(direction.start, direction.end);
            // Parse the xml
            if (doc == null) {
                Log.e(TAG, "Failed to get the route from " + direction.start
                        + " to " + direction.end);
                continue;
            }
            // Get points
            ArrayList<LatLng> directionPoint = md.getDirection(doc);
            // Convert vertexes to a polyline
            PolylineOptions rectLine = new PolylineOptions().width(3).color(Color.RED);
            for (LatLng aDirectionPoint : directionPoint) {
                rectLine.add(aDirectionPoint);
            }
            // Add poly to the map
            addPolyline(rectLine, direction.zoomLevel);
        }
        // Get the starting point of the first direction
        LatLng start = mPolylines.valueAt(0).get(0).getPoints().get(0);
        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(start, STARTING_ZOOM), 1000, null);
        // Set the initial zoom level and show the necessary polylines
        mZoomLevel = (int) STARTING_ZOOM;
        initPolylines(mZoomLevel);

        // Listen for the camera zoom level changes
        mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                // Note that because we are casting the zoomLevel to int,
                // it will be considered as changed only when it reaches
                // a new integer when rounded (e.g. 5.0, 6.0 etc.)
                int newZoomLevel = (int) cameraPosition.zoom;
                if (newZoomLevel != mZoomLevel) {
                    Log.d(TAG, "New zoom level: " + newZoomLevel);
                    // Loop all the changed zoom levels
                    // E.g. zoomed-out from 15 to 13, then hide [14, 15]
                    if (newZoomLevel < mZoomLevel) { // Zoomed out
                        for (int i=1; i<=mZoomLevel-newZoomLevel; i++)
                            hidePolylines(mPolylines.get(newZoomLevel+i));
                    } else { // Zoomed-in
                        for (int i=1; i<=newZoomLevel-mZoomLevel; i++)
                            showPolylines(mPolylines.get(mZoomLevel + i));
                    }
                    mZoomLevel = newZoomLevel;
                }
            }
        });
    }

    private void addPolyline(PolylineOptions polyOpts, int zoomLevel) {
        List<Polyline> polylines = mPolylines.get(zoomLevel);
        // Create polyline list for this zoom level, if it still doesn't exist
        if (polylines == null) {
            polylines = new ArrayList<Polyline>();
            mPolylines.put(zoomLevel, polylines);
        }
        // Append a new item to this poly list
        Polyline polyline = mMap.addPolyline(polyOpts);
        polyline.setVisible(false);
        polylines.add(polyline);
    }

    private void initPolylines(int zoomLevel) {
        for(int i=0; i<mPolylines.size(); i++) {
            // Loop until zoom level is reached
            if  (mPolylines.keyAt(i) > zoomLevel) break;

            showPolylines(mPolylines.get(mPolylines.keyAt(i)));
        }
    }

    private void showPolylines(List<Polyline> polylines) {
        if (polylines != null)
            for (Polyline polyline : polylines) polyline.setVisible(true);
    }

    private void hidePolylines(List<Polyline> polylines) {
        if (polylines != null)
            for (Polyline polyline : polylines) polyline.setVisible(false);
    }

}

Этот код добавляет полилинии к SparseArray, который содержит полилинии для каждого уровня масштабирования. Все поля скрыты по умолчанию и отображаются только при достижении определенного уровня масштабирования (также в initPolylines).

Здесь есть несколько вещей:

  • Поскольку масштаб может меняться на несколько уровней одновременно, мы зацикливаем каждый уровень, чтобы скрыть/показать конкретные направления.
  • Знаки местоположения должны быть точными, или они не будут найдены, и вы можете получить кучу ошибок в коде (я только добавил Log.e)
  • Если вы хотите создать маркеры, скажем, начальную и конечную точку направления, вы можете использовать следующий код для получения координат:

    List<LatLng> points = mPolylines.valueAt(0).get(0).getPoints();
    LatLng start = points.get(0);
    LatLng end = points.get(points.size()-1);
    
  • Маркеры могут быть добавлены/удалены при определенных уровнях масштабирования, как это сделано здесь с помощью полилиний.

И вот код анализатора xml (GMapV2Direction):

package com.example.simon.maps;

import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.google.android.gms.maps.model.LatLng;

public class GMapV2Direction {

    public GMapV2Direction() { }

    public Document getDocument(String start, String end) {
        String url = "http://maps.googleapis.com/maps/api/directions/xml?"
                + "origin="+start+"&destination="+end+"&units=metric&mode=driving";
        try {
            HttpClient httpClient = new DefaultHttpClient();
            HttpContext localContext = new BasicHttpContext();
            HttpPost httpPost = new HttpPost(url);
            HttpResponse response = httpClient.execute(httpPost, localContext);
            InputStream in = response.getEntity().getContent();
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public ArrayList<LatLng> getDirection (Document doc) {
        NodeList nl1, nl2, nl3;
        ArrayList<LatLng> listGeopoints = new ArrayList<LatLng>();
        nl1 = doc.getElementsByTagName("step");
        if (nl1.getLength() > 0) {
            for (int i = 0; i < nl1.getLength(); i++) {
                Node node1 = nl1.item(i);
                nl2 = node1.getChildNodes();

                Node locationNode = nl2.item(getNodeIndex(nl2, "start_location"));
                nl3 = locationNode.getChildNodes();
                Node latNode = nl3.item(getNodeIndex(nl3, "lat"));
                double lat = Double.parseDouble(latNode.getTextContent());
                Node lngNode = nl3.item(getNodeIndex(nl3, "lng"));
                double lng = Double.parseDouble(lngNode.getTextContent());
                listGeopoints.add(new LatLng(lat, lng));

                locationNode = nl2.item(getNodeIndex(nl2, "polyline"));
                nl3 = locationNode.getChildNodes();
                latNode = nl3.item(getNodeIndex(nl3, "points"));
                ArrayList<LatLng> arr = decodePoly(latNode.getTextContent());
                for (LatLng anArr : arr) {
                    listGeopoints.add(new LatLng(anArr.latitude, anArr.longitude));
                }

                locationNode = nl2.item(getNodeIndex(nl2, "end_location"));
                nl3 = locationNode.getChildNodes();
                latNode = nl3.item(getNodeIndex(nl3, "lat"));
                lat = Double.parseDouble(latNode.getTextContent());
                lngNode = nl3.item(getNodeIndex(nl3, "lng"));
                lng = Double.parseDouble(lngNode.getTextContent());
                listGeopoints.add(new LatLng(lat, lng));
            }
        }

        return listGeopoints;
    }

    private int getNodeIndex(NodeList nl, String nodename) {
        for(int i = 0 ; i < nl.getLength() ; i++) {
            if(nl.item(i).getNodeName().equals(nodename))
                return i;
        }
        return -1;
    }

    private ArrayList<LatLng> decodePoly(String encoded) {
        ArrayList<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 position = new LatLng((double) lat / 1E5, (double) lng / 1E5);
            poly.add(position);
        }
        return poly;
    }
}

Некоторые окончательные примечания:

  • Если у вас много направлений, загрузите их внутри initPolylines и showPolylines и выгрузите их в hidePolylines
  • Если направления, которые вы хотите показать на карте, являются постоянными, лучшим способом было бы использовать плитки с полилиниями на определенных уровнях масштабирования. Хорошим бесплатным инструментом для этого является Maperitive, он может экспортировать плитки на столько уровней масштабирования, сколько вы хотите. Затем вы будете хранить плитки онлайн и использовать UrlTileProvider, чтобы отображать их на своей карте.

Ответ 2

Я не знаю, правильно ли понял ваш вопрос, но я все равно попытаюсь ответить. Вы можете создать несколько полилиний и добавить к карте из ответа JSON от google maps API направления. Таким образом, вы можете добавить несколько маршрутов на одной карте.

Насколько я понял второй вопрос, вы хотите, чтобы маркер кластера указывал детали маршрутов, которые вы рисуете. Для этого я предполагаю, что вы можете скопировать точки, возвращаемые API, и использовать настраиваемые кластеры маркеров, чтобы показать нужную информацию.

Надеюсь, что это поможет

-Edit- Если вы хотите нарисовать маршруты в приложении для Android, вы не получите ни один DirectionRenderer, который вы можете использовать. Вам необходимо отобразить маршруты с помощью Polylines. Вот почему я упомянул о создании полилиний в ответе выше. В ответ на запрос направления вы получаете JSON, содержащий несколько координатных точек, которые могут соединяться путем рисования через них полилинии. Вы можете иметь несколько таких полилиний на карте (от любой точки до любой точки). Случай 1: вы хотите 2 маршрута от Бомбея до Гоа, установите для параметра альтернативы значение true. Случай 2: вы хотите, чтобы два разных маршрута, Бангалор в Бомбей, Гоа в Дели, делали два разных запроса API. Вы получите два разных маршрута и нарисуйте их на карте.

Для вашего второго вопроса, поскольку нет встроенной кластеризации для маршрутов (полилиний), вам нужно закодировать логику значков кластера, отображаемых на карте. Для получения текущего уровня масштабирования используйте следующее.

GoogleMap map;    
float zoom = map.getCameraPosition().zoom;

Для отображения и скрытия полилиний

Polyline line = map.addPolyline(new PolylineOptions()
 .add(new LatLng(51.5, -0.1), new LatLng(40.7, -74.0))

 // multiple points that you get from the response of directions API

 .width(5)
 .color(Color.RED));

//your logic for visibility
if(zoom<=5){
  line.setVisible(true);
}

Я не мог найти никакого способа в API, чтобы скрыть значок кластера с помощью кода. Я думаю, он должен скрыться после определенного уровня масштабирования.