Pergunta

Estou analisando pontos de agrupamento em um mapa (latitude/longitude).Há alguma recomendação sobre um algoritmo adequado que seja rápido e escalável?

Mais especificamente, tenho uma série de coordenadas de latitude/longitude e uma janela de visualização de mapa.Estou tentando agrupar os pontos próximos para remover a desordem.

Já tenho uma solução para o problema (Veja aqui), só estou me perguntando se existe algum algoritmo formal que resolva o problema de forma eficiente.

Foi útil?

Solução

Para um aplicativo virtual Earth, usei o clustering descritoaqui.É extremamente rápido e facilmente extensível.

Outras dicas

Você poderia indexar todos os seus pontos usando um QuadTile esquema e, em seguida, com base na escala, quanto mais abaixo você desce nas divisões quádruplas.Todos os pontos localizados de forma semelhante estarão próximos uns dos outros no seu índice, permitindo que o agrupamento aconteça de forma eficiente.

QuadTiles são um exemplo de Códigos Morton, e há um exemplo de python vinculado a esse artigo da Wikipedia que pode ajudar.

Eu olhei para várias bibliotecas e as achei tão complexas que não conseguia entender uma palavra, então decidi fazer meu próprio algoritmo de cluster

Aqui vai meu código em 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);
    }

// Isso calcula a distância em pixels entre dois pontos longos em um determinado nível de zoom

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

// A principal função que realmente calcula os clusters 1.ArrayList de pontos longos é iterado para length .2.Loop interno Uma cópia da mesma lista de Array é iterada da posição I+1, ou seja, deixando o índice 3 do loop superior.O elemento 0 é o centro do centróide e todos os outros pontos são comparados se a distância do pixel for muito menos adicione -a ao cluster 4.Remova todos os elementos de Top ArrayList e copie ArrayList que formaram o cluster 5 reiniciar o processo reinicializando o índice de 0;6 se o centróide selecionado não tiver clusters, esse elemento não será excluído

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(i));
                    markerList.add(markers.get(j));

                    markers.remove(j);

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

            }

            if (markerList.size() > 0) {
                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)));

        }

    }




ANY QUESTIONS OR DOUBTS PLEASE ASK WILL CLEAR THEM ALL ...........THANKS
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top