web-dev-qa-db-fra.com

Marqueurs de cluster dans Google Maps Android v2

Je dois placer différents marqueurs sur GoogleMap à partir de Google Maps Android v2 API. Le problème est que plusieurs marqueurs sont définis sur la même position (lat/lng), de sorte que l'utilisateur ne voit que le marqueur "le plus récent".

Existe-t-il une possibilité (dans le meilleur des cas: une bibliothèque) qui regroupe différents marqueurs de la même zone (par rapport au niveau de zoom)?

J'ai déjà lu sur MarkerClusterer, mais cela est conçu pour l'API JavaScript.

15
Greeny

Google a fourni un utilitaire pour ce faire dans le cadre de leur Google Maps Android Bibliothèque d'utilitaires API:

https://developers.google.com/maps/documentation/Android/utility/marker-clustering

avec la source à:

https://github.com/googlemaps/Android-maps-utils

23
Intrications

Je n'ai pas encore eu l'occasion de l'essayer, mais cela semble très prometteur:

http://code.google.com/p/Android-maps-extensions/

De ce message

Voici une autre bibliothèque avec des animations de cluster assez sympas:

https://github.com/twotoasters/clusterkraf

20
DiscDev

Je suis un peu en retard, mais Clusterkraf est une excellente bibliothèque, consultez-la:

https://github.com/twotoasters/clusterkraf

7
Androiderson

Pour ceux qui cherchent à implémenter leur propre code de clustering. Veuillez vérifier mon clustering algo très rapidement et fonctionne incroyablement bien.

J'ai regardé différentes bibliothèques et je les ai trouvées si complexes qu'elles ne pouvaient pas comprendre un mot, j'ai donc décidé de créer mon propre algorithme de clustering. Voilà mon code en Java.

    static int OFFSET = 268435456;
        static double RADIUS = 85445659.4471;
        static double pi = 3.1444;

    public static double lonToX(double lon) {
        return Math.round(OFFSET + RADIUS * lon * pi / 180);
    }

    public static double latToY(double lat) {
        return Math.round(OFFSET
                - RADIUS
                * Math.log((1 + Math.sin(lat * pi / 180))
                        / (1 - Math.sin(lat * pi / 180))) / 2);
    }

    public static int pixelDistance(double lat1, double lon1, double lat2,
            double lon2, int zoom) {
        double x1 = lonToX(lon1);
        double y1 = latToY(lat1);

        double x2 = lonToX(lon2);
        double y2 = latToY(lat2);

        return (int) (Math
                .sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2))) >> (21 - zoom);
    }

    static ArrayList<Cluster> cluster(ArrayList<Marker> markers, int zoom) {

        ArrayList<Cluster> clusterList = new ArrayList<Cluster>();

        ArrayList<Marker> originalListCopy = new ArrayList<Marker>();

        for (Marker marker : markers) {
            originalListCopy.add(marker);
        }

        /* Loop until all markers have been compared. */
        for (int i = 0; i < originalListCopy.size();) {

            /* Compare against all markers which are left. */

            ArrayList<Marker> markerList = new ArrayList<Marker>();
            for (int j = i + 1; j < markers.size();) {
                int pixelDistance = pixelDistance(markers.get(i).getLatitude(),
                        markers.get(i).getLongitude(), markers.get(j)
                                .getLatitude(), markers.get(j).getLongitude(),
                        zoom);

                if (pixelDistance < 40) {


                    markerList.add(markers.get(j));

                    markers.remove(j);

                    originalListCopy.remove(j);
                    j = i + 1;
                } else {
                    j++;
                }

            }

            if (markerList.size() > 0) {
 markerList.add(markers.get(i));                
Cluster cluster = new Cluster(clusterList.size(), markerList,
                        markerList.size() + 1, originalListCopy.get(i)
                                .getLatitude(), originalListCopy.get(i)
                                .getLongitude());
                clusterList.add(cluster);
                originalListCopy.remove(i);
                markers.remove(i);
                i = 0;

            } else {
                i++;
            }

            /* If a marker has been added to cluster, add also the one */
            /* we were comparing to and remove the original from array. */

        }
        return clusterList;
    }

Just pass in your array list here containing latitude and longitude then to display clusters. Here goes the function.


    @Override
    public void onTaskCompleted(ArrayList<FlatDetails> flatDetailsList) {

        LatLngBounds.Builder builder = new LatLngBounds.Builder();

        originalListCopy = new ArrayList<FlatDetails>();
        ArrayList<Marker> markersList = new ArrayList<Marker>();
        for (FlatDetails detailList : flatDetailsList) {

            markersList.add(new Marker(detailList.getLatitude(), detailList
                    .getLongitude(), detailList.getApartmentTypeString()));

            originalListCopy.add(detailList);

            builder.include(new LatLng(detailList.getLatitude(), detailList
                    .getLongitude()));

        }

        LatLngBounds bounds = builder.build();
        int padding = 0; // offset from edges of the map in pixels
        CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);

        googleMap.moveCamera(cu);

        ArrayList<Cluster> clusterList = Utils.cluster(markersList,
                (int) googleMap.getCameraPosition().zoom);

        // Removes all markers, overlays, and polylines from the map.
        googleMap.clear();

        // Zoom in, animating the camera.
        googleMap.animateCamera(CameraUpdateFactory.zoomTo(previousZoomLevel),
                2000, null);

        CircleOptions circleOptions = new CircleOptions().center(point) //
                // setcenter
                .radius(3000) // set radius in meters
                .fillColor(Color.TRANSPARENT) // default
                .strokeColor(Color.BLUE).strokeWidth(5);

        googleMap.addCircle(circleOptions);

        for (Marker detail : markersList) {

            if (detail.getBhkTypeString().equalsIgnoreCase("1 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk1)));
            } else if (detail.getBhkTypeString().equalsIgnoreCase("2 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk_2)));

            }

            else if (detail.getBhkTypeString().equalsIgnoreCase("3 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk_3)));

            } else if (detail.getBhkTypeString().equalsIgnoreCase("2.5 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk2)));

            } else if (detail.getBhkTypeString().equalsIgnoreCase("4 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk_4)));

            } else if (detail.getBhkTypeString().equalsIgnoreCase("5 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk5)));

            } else if (detail.getBhkTypeString().equalsIgnoreCase("5+ BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk_5)));

            }

            else if (detail.getBhkTypeString().equalsIgnoreCase("2 BHK")) {
                googleMap.addMarker(new MarkerOptions()
                        .position(
                                new LatLng(detail.getLatitude(), detail
                                        .getLongitude()))
                        .snippet(String.valueOf(""))
                        .title("Flat" + flatDetailsList.indexOf(detail))
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.bhk_2)));

            }
        }

        for (Cluster cluster : clusterList) {

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inMutable = true;
            options.inPurgeable = true;
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.cluster_marker, options);

            Canvas canvas = new Canvas(bitmap);

            Paint paint = new Paint();
            Paint.setColor(getResources().getColor(R.color.white));
            Paint.setTextSize(30);

            canvas.drawText(String.valueOf(cluster.getMarkerList().size()), 10,
                    40, Paint);

            googleMap.addMarker(new MarkerOptions()
                    .position(
                            new LatLng(cluster.getClusterLatitude(), cluster
                                    .getClusterLongitude()))
                    .snippet(String.valueOf(cluster.getMarkerList().size()))
                    .title("Cluster")
                    .icon(BitmapDescriptorFactory.fromBitmap(bitmap)));

        }

    }
6
user1530779

Vous devez suivre ces étapes:

  1. Nous devons implémenter/installer Google Maps Android bibliothèque d'utilitaires API

Studio Android/Gradle:

dependencies {
    compile 'com.google.maps.Android:android-maps-utils:0.3+'
}
  1. Deuxièmement, vous devez lire la documentation officielle de Google -> Google Maps Android Marker Clustering Utility

Ajouter un simple cluster de marqueurs

Suivez les étapes ci-dessous pour créer un cluster simple de dix marqueurs. Le résultat ressemblera à ceci, bien que le nombre de marqueurs affichés/groupés changera en fonction du niveau de zoom:

Add a simple marker clusterer

Voici un résumé des étapes requises:

  1. Implémentez ClusterItem pour représenter un marqueur sur la carte. L'élément de cluster renvoie la position du marqueur en tant qu'objet LatLng.
  2. Ajoutez un nouveau ClusterManager pour regrouper les éléments du cluster (marqueurs) en fonction du niveau de zoom.
  3. Définissez la OnCameraChangeListener() de la carte sur ClusterManager, car ClusterManager implémente l'écouteur.
  4. Si vous souhaitez ajouter des fonctionnalités spécifiques en réponse à un événement de clic de marqueur, définissez OnMarkerClickListener() de la carte sur ClusterManager, car ClusterManager implémente l'écouteur.
  5. Insérez les marqueurs dans le ClusterManager.

Examen des étapes plus en détail: Pour créer notre cluster simple de dix marqueurs, créez d'abord une classe MyItem qui implémente ClusterItem.

public class MyItem implements ClusterItem {
    private final LatLng mPosition;

    public MyItem(double lat, double lng) {
        mPosition = new LatLng(lat, lng);
    }

    @Override
    public LatLng getPosition() {
        return mPosition;
    }
}

Dans votre activité de carte, ajoutez le ClusterManager et alimentez-le en éléments de cluster. Notez l'argument de type <MyItem>, Qui déclare que le ClusterManager est de type MyItem.

private void setUpClusterer() {
    // Declare a variable for the cluster manager.
    private ClusterManager<MyItem> mClusterManager;

    // Position the map.
    getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));

    // Initialize the manager with the context and the map.
    // (Activity extends context, so we can pass 'this' in the constructor.)
    mClusterManager = new ClusterManager<MyItem>(this, getMap());

    // Point the map's listeners at the listeners implemented by the cluster
    // manager.
    getMap().setOnCameraChangeListener(mClusterManager);
    getMap().setOnMarkerClickListener(mClusterManager);

    // Add cluster items (markers) to the cluster manager.
    addItems();
}

private void addItems() {

    // Set some lat/lng coordinates to start with.
    double lat = 51.5145160;
    double lng = -0.1270060;

    // Add ten cluster items in close proximity, for purposes of this example.
    for (int i = 0; i < 10; i++) {
        double offset = i / 60d;
        lat = lat + offset;
        lng = lng + offset;
        MyItem offsetItem = new MyItem(lat, lng);
        mClusterManager.addItem(offsetItem);
    }
}

Démo

https://www.youtube.com/watch?v=5ZnVraO1mT4

Github

Si vous avez besoin de plus d'informations/projet terminé, veuillez visiter: https://github.com/dimitardanailov/googlemapsclustermarkers

Si vous avez d'autres questions, contactez-moi.

5
d.danailov

Pour qui a des problèmes avec Clusterkraf (je ne peux pas le faire fonctionner!) Et pour qui ne peut pas utiliser Android-maps-extension parce qu'il est expédié avec une version personnalisée de Play Services lib, je veux suggérer cette autre bibliothèque, qui est minuscule, bien écrite et fonctionne prête à l'emploi:

Clusterer : https://github.com/mrmans0n/clusterer

Après une matinée entière à essayer d'utiliser les deux autres bibliothèques, celle-ci m'a sauvé la journée!

4
lorenzo-s

si vous n'obtenez que le dernier marqueur, vous devez regrouper tous les marqueurs

> private ClusterManager<MyItem> mClusterManager;

après avoir ajouté vos articles

mClusterManager.addItem("your item");
mClusterManager.cluster();
2
Mohammad

Pour ceux d'entre vous qui ont des problèmes avec les performances de Google Maps Android API Utility Library, nous avons créé notre propre bibliothèque de clustering rapide: https://github.com/sharewire/google-maps-clustering . Il peut facilement gérer 20000 marqueurs sur la carte.

0
makovkastar