【问题标题】:Android Maps Point ClusteringAndroid 地图点聚类
【发布时间】:2011-09-16 15:52:32
【问题描述】:

android中有点聚类的代码吗?我怎样才能在没有性能问题的情况下加载千针?

【问题讨论】:

  • 这不是问题。正如我所说,这是一个常见问题解答 .. 一种教程。 “看到社区没有任何东西,所以我想分享”
  • 感谢分享。尽管鼓励发布信息性知识,但此类帖子应坚持 SOs Q&A 格式。这意味着您应该制定一个问题并回答它。见this blog post。您也可以接受自己的答案,甚至还有一个徽章。我会对此投赞成票以弥补投反对票 - 他们有点苛刻恕我直言。

标签: android maps android-mapview markerclusterer


【解决方案1】:

昨晚我在 Android MapView 上使用了 PointClustering。看到社区没有任何东西,所以我想分享。

如果它们在 mapView 中的投影太近,则对地理点进行分组。也只渲染可见点。

更新

代码从头开始重写。

现在可以在GitHub

  1. 代码从头开始重写
  2. 使用了 GVM 聚类算法(速度很快,但聚类点的定位不如我的)
  3. 即将添加以前的聚类算法

【讨论】:

  • 我认为您的帖子中缺少一些内容,例如public void draw(Canvas canvas, MapView mapView, boolean shadow) 应该去哪里?
  • OverlayItemExtended 的 getName() 返回什么?如果您提供完整的来源,您的帖子会大大改善
  • 我根据您的建议编辑了答案。 getName() 是 OverlayItem 的 getTitle() ,或者该点应该作为地图名称呈现的内容。
  • 我无法发布整个代码,因为它是当前 inDevelopment 项目正在进行的工作。
  • 把这个放到github就好了
【解决方案2】:

我对上面的代码进行了修改,并控制了地图视图中覆盖图标的数量,并将组和单点分开。

我的代码:

MMapView.java

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.impiger.maphighlight.R;

//Reference - http://stackoverflow.com/questions/7447350/android-maps-point-clustering

public class MMapView extends MapView {

    private static final String TAG = MMapView.class.getSimpleName();
    private static final int MAX_VISIBLE_POINTS = 1;
    private PMapViewOverlay itemizedOverlay;
    private List<Overlay> mapOverlays;
    private List<GeoPoint> geoPoints = new ArrayList<GeoPoint>();
    private BitmapDrawable drawable;
    private Context context;
    private Drawable emptyDrawable;
    private int count;
    private int oldZoomLevel = -1;
    ArrayList<OverlayItemExtended> mOverlays;

    public MMapView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        mapOverlays = getOverlays();
        drawable = new BitmapDrawable(
                BitmapFactory.decodeResource(getResources(),
                        R.drawable.blue_65));
        itemizedOverlay = new PMapViewOverlay(drawable, context);
        emptyDrawable = context.getResources().getDrawable(
                R.drawable.marker);
        mOverlays = new ArrayList<OverlayItemExtended>();
        init();
    }

    private GeoPoint getPoint(double lat, double lon) {
        return (new GeoPoint((int) (lat * 1000000.0), (int) (lon * 1000000.0)));
    }

    private void init(){
        putPoint(11, 77, true);
        putPoint(11.5, 76.6, false);
        putPoint(10.98383, 77.32112, false);
        putPoint(10, 77, false);
        putPoint(11, 78, false);
        putPoint(11, 77.5, false);
        putPoint(10.5, 77, false);
        putPoint(12, 77, false);
        putPoint(11.77, 77.11, false);
        putPoint(12.1, 78.33, false);
        putPoint(11.83, 77.293, false);
        putPoint(11.12, 77, false);
        putPoint(11.13, 77, false);
        putPoint(11.14, 77, false);
        putPoint(11.15, 77, false);
        putPoint(11.12, 77.2, false);
        putPoint(11.13, 77.34, false);
        putPoint(11.14, 77.4, false);
        putPoint(11.15, 77.1977, false);
        putPoint(11.347373, 77.5627783, true);
        putPoint(11.53454, 76.696645, false);
        putPoint(10.19282, 77.847373, false);
        putPoint(10.4728, 76.39388, false);
        putPoint(11.4563, 78, false);
        putPoint(11.73663, 77.5927, false);
        putPoint(10.5674, 77.6762, false);
        putPoint(12.02882, 77.672782, false);
        putPoint(11.7767876, 77.1123423, false);
        putPoint(12.18332, 78.33, false);
        putPoint(11.8393883, 77.293938783, false);
        putPoint(11.388323, 77.9478723, false);
        putPoint(11.1345645, 77.97723, false);
        putPoint(11.1423423, 77.73774, false);
        putPoint(11.1552, 77.793783, false);
        putPoint(11.127895434, 77.2944554, false);
        putPoint(11.13232345, 77.342234, false);
        putPoint(11.14456573, 77.4, false);
        putPoint(11.159765, 77.1977, false);
    }


    public void putPoint(double lat, double lon, boolean isMyPosition) {
        int latitude = (int) (lat * 1E6);
        int longitude = (int) (lon * 1E6);
        GeoPoint geo = new GeoPoint(latitude, longitude);
        geo = getPoint(lat, lon);

        /*
         * Remove doubles
         */
        Boolean alreadyExists = false;
        for (GeoPoint item : geoPoints) {
            if (item.getLatitudeE6() == geo.getLatitudeE6()
                    && item.getLongitudeE6() == geo.getLongitudeE6()) {
                alreadyExists = true;
            }
        }
        if (!alreadyExists) {
            geoPoints.add(geo);
        }
    }

    /*
     * Place the overlays
     */
    public void placeOverlays() {
        itemizedOverlay.removeAllOverlays();
        getOverlays().clear();
        mapOverlays.clear();
        mOverlays.clear();
        int i = 1;
        for (GeoPoint item : geoPoints) {
            OverlayItemExtended overlayitem = new OverlayItemExtended(item,
                    "title "+i, "snippet");
            // Here is where the magic happens
            addOverlayItemClustered(overlayitem, this,
                    geoPoints.size());
            i++;
        }

        for(int j=0;j<mOverlays.size();j++){
            OverlayItemExtended overlayItem = mOverlays.get(j);
            if(overlayItem.isMaster){
                if(overlayItem.slaves.size() > 0){
                    itemizedOverlay = new PMapViewOverlay(drawable, context);
                    itemizedOverlay.addOverlayItem(overlayItem);
                }else{
                    itemizedOverlay = new PMapViewOverlay(emptyDrawable, context);
                    itemizedOverlay.addOverlayItem(overlayItem);
                }
                mapOverlays.add(itemizedOverlay);
            }
        }
    }

    /*
     * Update the points at panned / zoom etc
     */
    public void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (getZoomLevel() != oldZoomLevel) {
            placeOverlays();
        }
        oldZoomLevel = getZoomLevel();
    }

    public void addOverlayItemClustered(OverlayItemExtended thisOverlay,
            MapView mapView, int totalPoints) {
        for (OverlayItemExtended otherOverlay : mOverlays) {
            /*
             * Thresshold for the clustering
             */
            /*
             * Zoom level >15 don't cluster If less than Max_Visible_points
             * don't cluster
             */
            if (mapView.getZoomLevel() >= 14
                    || (MAX_VISIBLE_POINTS > totalPoints)
                    && PointCluster.getOverLayItemDistance(thisOverlay,
                            otherOverlay, mapView) > 60) {
                mOverlays.add(thisOverlay);
                return;
            }
            if (PointCluster.getOverLayItemDistance(thisOverlay, otherOverlay,
                    mapView) < 90 && !thisOverlay.isClustered) {
                // Here is where the clustering actually happens
                if (otherOverlay.isMaster) {
                    thisOverlay.isMaster = false;
                    // otherOverlay.isMaster = false;
                    thisOverlay.isClustered = true;
                    otherOverlay.isClustered = true;
                    otherOverlay.slaves.push(thisOverlay);
                    thisOverlay.parent = otherOverlay;
                } else if (PointCluster.getOverLayItemDistance(thisOverlay,
                        otherOverlay.parent, mapView) < 90
                        && otherOverlay.isClustered) {
                    thisOverlay.isMaster = false;
                    thisOverlay.isClustered = true;
                    thisOverlay.parent = otherOverlay.parent;
                    otherOverlay.parent.slaves.push(thisOverlay);
                }
            }
        }
        mOverlays.add(thisOverlay);
    }
}

OverlayItemExtended.java

import java.util.Stack;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.OverlayItem;

public class OverlayItemExtended extends OverlayItem {

    public boolean isClustered = false;
    public boolean isMaster = true;
    public boolean isMe = false;
    public OverlayItemExtended parent;
    public Stack<OverlayItemExtended> slaves = new Stack<OverlayItemExtended>();

    public OverlayItemExtended(GeoPoint point, String title, String snippet) {
        super(point, title, snippet);
    }
}

PMapViewOverlay.java

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;

@SuppressWarnings("rawtypes")
public class PMapViewOverlay extends ItemizedOverlay {

    private static final String TAG = PMapViewOverlay.class.getSimpleName();
    private Context context;
    private ArrayList<OverlayItemExtended> mOverlays;

    public PMapViewOverlay(Drawable defaultMarker, Context context) {
        super(boundCenterBottom(defaultMarker));
        this.context = context;
        mOverlays = new ArrayList<OverlayItemExtended>();

        paint.setTextAlign(Paint.Align.CENTER);
        paint.setTextSize(25);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(5);
        paint.setColor(Color.WHITE);
    }

    @Override
    protected OverlayItemExtended createItem(int i) {
        return mOverlays.get(i);
    }

    @Override
    public int size() {
        return mOverlays.size();
    }

    public void addOverlayItem(OverlayItemExtended overlay) {
        mOverlays.add(overlay);
        populate();
    }

    public void removeAllOverlays() {
        mOverlays.clear();
        populate();
    }

    public void removePointsButMe() {
        for (int i = 0; i < mOverlays.size(); i++) {
            OverlayItemExtended overlay = mOverlays.get(i);
            if (overlay.isMe) {
                mOverlays.clear();
                addOverlayItem(overlay);
                break;
            }
        }
        populate();
    }

    Paint paint = new Paint();

    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
        super.draw(canvas, mapView, shadow);

        // cycle through all overlays
        for (int index = 0; index < mOverlays.size(); index++) {
            OverlayItemExtended item = mOverlays.get(index);

            // Converts lat/lng-Point to coordinates on the screen
            GeoPoint point = item.getPoint();
            Point ptScreenCoord = new Point();
            mapView.getProjection().toPixels(point, ptScreenCoord);

            if (item.isMaster) {
                if (item.slaves.size() > 0) {
                    canvas.drawText(item.slaves.size() + 1 + "",
                            ptScreenCoord.x, ptScreenCoord.y - 13, paint);
                }
            }
        }
    }

    @Override
    protected boolean onTap(int index) {
        OverlayItemExtended item = mOverlays.get(index);
        if (item.isMaster) {
            if (item.slaves.size() == 0) {
                Toast.makeText(context, "You tapped item " + item.getTitle(),
                        Toast.LENGTH_LONG).show();
            }
        }
        return super.onTap(index);
    }
}

我没有更改 PointCluster.java 中的任何代码。

我希望这会对某人有所帮助。

【讨论】:

【解决方案3】:

Google 的 Android Map Utils 对此有解决方案:Google Maps Android Marker Clustering Utility

添加依赖

implementation 'com.google.maps.android:android-maps-utils:0.5'

制作你自己的 ClusterItem

class MyItem(
        private val position: LatLng,
        val title: String,
        private val snippet: String
) : ClusterItem {
    override fun getPosition() = position
    override fun getTitle() = title
    override fun getSnippet() = snippet
}

设置集群管理器并添加项目

override fun onMapReady(googleMap: GoogleMap) {
    val clusterManager = ClusterManager<MyItem>(this, googleMap)
    googleMap.setOnCameraIdleListener(clusterManager)

    clusterManager.addItem(MyItem(LatLng(51.51, -0.12), "title", "snippet"))
}

就是这样!项目现在显示如下:

自定义图标

为了自定义图标,将val icon: BitmapDescriptor添加到您的ClusterItem并更改集群管理器的渲染器:

    clusterManager.renderer = object : DefaultClusterRenderer<MyItem>(this, googleMap, clusterManager) {
        override fun onBeforeClusterItemRendered(item: MyItem, markerOptions: MarkerOptions) {
            markerOptions.icon(item.icon)
        }
    }

使项目可点击

根据经验,与标记的任何交互都应通过集群管理器进行。这同样适用于使项目可点击。

    googleMap.setOnMarkerClickListener(clusterManager)
    clusterManager.setOnClusterItemClickListener {
        Toast.makeText(this, "Clicked on item ${it.title}", Toast.LENGTH_SHORT).show()
        true
    }

同样,您可以调用googleMap.setOnInfoWindowClickListener(clusterManager)clusterManager.setOnClusterItemInfoWindowClickListener 来处理信息窗口上的点击。

【讨论】:

    【解决方案4】:

    【讨论】:

      【解决方案5】:

      在 github 上有一个关于 Polaris 库 (https://github.com/cyrilmottier/Polaris) 的拉取请求,它添加了集群:

      https://github.com/cyrilmottier/Polaris/pull/20

      https://github.com/damianflannery/Polaris/tree/clustering

      【讨论】:

        【解决方案6】:

        还有this answer,它只需要你覆盖你的 Overlay 的 draw 方法。它将您的 mapView 分成多个部分,因此它不那么复杂。但至少它奏效了。

        【讨论】:

          【解决方案7】:

          对于 ANDROID V2,这里是集群代码

          大家好

          我查看了各种库,发现它们非常复杂,一个词都听不懂,所以我决定制作自己的聚类算法 这是我在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(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;
              }
          

          只需在此处传入包含纬度和经度的数组列表 然后显示集群 函数来了

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

          任何问题或疑虑请询问将清除它们......谢谢

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-03-02
            • 1970-01-01
            • 1970-01-01
            • 2016-09-04
            • 2010-11-28
            • 2018-12-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多