当前位置:   article > 正文

android德地图点聚合,说一说Android的地图聚合

地图mark点聚合算法

最近遇到一个需求,其中涉及到一些聚合的东西,给大家说说我的不成熟的小想法。国际惯例,先上黄图:

7d1c7aa19e1a

cluster.gif

首先说说什么是聚合,如果你不怎么用地图的话,可能对聚合这个东西几乎没什么概念,聚合呢,其实就是将地图上过于密集的覆盖物集合到一块,当地图舒展开了,集合中的覆盖物又会分布开,就是这么个效果。

再来说说为什么要聚合,说到底就是让交互变得更友善,没聚合之前,图上总共1400多个点,不能想象密集恐惧症的人看了会有什么感觉,反正我自己看着也毛毛的;再一个呢,这么多的点,图片加载渲染的时候难免会卡顿,聚合之后的话,会有效减少卡顿的现象。

其实在我用过的地图中,官方实现了聚合功能的只有百度地图,其他都得自己来实现了,OK,进入我们的正题,到底如何实现聚合呢?

说下我写的两种聚合的方法:

第一种:以地图上的某个点作为聚合点,以这个点的坐标为中心点,创建出一个Rect,再去计算在这个Rect中是否包含了其他的点,如果包含了,这些个点就合体成为了一个聚合点。

7d1c7aa19e1a

98ED363C-B79E-4B2D-ADCF-8B0EA0F15D24.png

好了,我们来写一个聚合类:

public class Cluster {

//聚合大小控制,就是控制Rect的宽高

private int bounds;

private PointF f;

private MapView mapView;

private List ps = new ArrayList();//用来存储该聚合内有多少个覆盖物,就是地图上的Overlay

public Cluster() {}

//新建的时候会扔一个覆盖物进来,如果没有聚合产生那么这个聚合就是原来的覆盖物

public Cluster(PointF f, MapView mapView, int bounds) {

this.f = f;

this.mapView = mapView;

this.bounds = bounds;

ps.add(f);

}

//返回一个方形的范围区域,用作判定聚合

public Rect getRect() {

float x = f.x;

float y = f.y;

//将地图的坐标转换成屏幕的坐标

float[] floats = mapView.convertMapXYToScreenXY1(x, y);

Rect rect = new Rect((int) floats[0], (int) floats[1], (int) (floats[0] + bounds), (int) (floats[1] + bounds));

return rect;

}

//如果被判定在聚合内,那么就将这个点加入聚合类中的集合

public void addPoints(PointF p) {

ps.add(p);

}

//当所有的覆盖物聚合计算完成后,次方法返回聚合的坐标

public PointF getPosition(){

if (ps.size() == 1) {

return f;

}

float x = 0;

float y = 0;

for (PointF p : ps) {

x += p.x;

y += p.y;

}

x = x / ps.size();

y = y / ps.size();

return new PointF(x,y);

}

}

接下来聚合的算法:

public void getCluster() {

clusters.clear();

newPoints.clear();

//遍历地图上所有的覆盖物进行聚合操作

for (PointF mark : marks) {

float[] floats1 = mapView.convertMapXYToScreenXY1(mark.x, mark.y);//地图上的点转换成屏幕坐标点

int width = mapView.getWidth();

int height = mapView.getHeight();

//计算出屏幕中的点,不在屏幕中的不用聚合

if (floats1[0] < 0 || floats1[1] < 0 || floats1[0] > width || floats1[1] > height) {

continue;

}

boolean isIn = false;//是否已经聚合

//如果没有的话就先创建一个聚合类扔进去

if (clusters.size() == 0) {

clusters.add(new Cluster(mark, mapView, 100));

} else {//有了聚合类就开始计算点是否在聚合内

for (Cluster cluster : clusters) {

float[] floats = mapView.convertMapXYToScreenXY1(mark.x, mark.y);

boolean isContian = cluster.getRect().contains((int) floats[0], (int) floats[1]);//是否在聚合内

if (isContian) {

cluster.addPoints(mark);

isIn = true;

break;

}

}

//如果不在那几个聚合点内的话,重新添加到一个新的聚合类中去

if (!isIn) {

clusters.add(new Cluster(mark, mapView, bounds));

}

}

}

//将聚合中的重新计算取出

for (Cluster cluster : clusters) {

newPoints.add(new PointF(cluster.getPosition().x,cluster.getPosition().y));

}

}

注释应该写的挺清楚的,还是那句话,写代码之前多想想你要什么,就往这个聚合类中添加什么,慢慢的这个类就会越来越健壮。

接下来第二种方法:

将整个屏幕分成N个Rect,分别计算在某个Rect中有多少个覆盖物,如果多于一个覆盖物的话,那么这个就是聚合,否则,就是一个覆盖物。

7d1c7aa19e1a

203BDB66-3DD2-4288-8456-EFEF14AA58B2.png

再来个聚合类:

public class MCluster {

public boolean isClick() {

return isClick;

}

public void setClick(boolean click) {

isClick = click;

}

private boolean isClick;//是否可以点击

private String PntName;

private String Unit;

private float Value;

public String getPntName() {

return PntName;

}

public void setPntName(String pntName) {

PntName = pntName;

}

public String getUnit() {

return Unit;

}

public void setUnit(String unit) {

Unit = unit;

}

public float getValue() {

return Value;

}

public void setValue(float value) {

Value = value;

}

//覆盖物集合

private List ps = new ArrayList();

private Rect rect;

public MCluster() {

}

public MCluster(Rect rect) {

this.rect = rect;

}

public void addPoint(PointF pointF){

ps.add(pointF);

}

//将集合清空

public void clear(){

ps.clear();

}

public Rect getRect(){

return rect;

}

//看这个聚合内是否有覆盖物

public boolean hasPoint(){

if (ps.size() == 0) {

return false;

}

return true;

}

//判断是否是聚合,如果集合中点数大于1说明是聚合了,否则不聚合

public boolean isCluster(){

if (ps.size() == 1) {

return false;

}

return true;

}

//计算坐标

public MyPoint getPosition(){

float x = 0;

float y = 0;

for (PointF p : ps) {

x += p.x;

y += p.y;

}

x = x / ps.size();

y = y / ps.size();

MyPoint myPoint = new MyPoint(x, y,isCluster(),PntName,Unit,Value,ps.size(),isClick());

return myPoint;

}

//得到聚合的数量

public int getSize(){

return ps.size();

}

}

其实大同小异。

划分聚合:

private void makeCluster() {

//以320像素密度为基础设置Rect的宽高为50像素

float base = 320;

int width1 = (int) SPUtils.get(mapView.getContext(), "width", -1);//屏幕的宽

int height1 = (int) SPUtils.get(mapView.getContext(), "height", -1);//屏幕的高

int density = (int) SPUtils.get(mapView.getContext(), "density", -1);//屏幕的像素密度

float scale = (density/base);

final float width = 50*scale;//Rect的宽高

int round = Math.round(width);

final int h = height1/round;

final int w = width1 / round;

//将屏幕划分成N个聚合区

for (int j = 0; j < h+1; j++) {

for (int i = 0; i < (w + 1); i++) {

mClusters.add(new MCluster( new Rect(i * round,j * round,i * round + round,j * round + round)));

}

}

}

接着看聚合的算法:

public void getNewCluster(){

//遍历所有的覆盖物

for (Points mark : marks) {

PointF pointF = mark.getPointF();

if (pointF == null) {

return;

}

float[] floats1 = mapView.convertMapXYToScreenXY1(pointF.x, pointF.y);//地图上的点转换成屏幕坐标点

int x = (int) floats1[0];

int y = (int) floats1[1];

int width = mapView.getWidth();

int height = mapView.getHeight();

//计算出屏幕中的点,不在屏幕中的不用聚合

if (x< 0 || y < 0 || x > width || y > height) {

continue;

}

//遍历所有的聚合

for (MCluster mCluster : mClusters) {

Rect rect = mCluster.getRect();

//在聚合内

if (rect.contains(x, y)) {

mCluster.addPoint(pointF);

mCluster.setClick(mark.isClick());

mCluster.setPntName(mark.getPntName());

mCluster.setUnit(mark.getUnit());

mCluster.setValue(mark.getValue());

break;

}

}

}

newPoints.clear();

for (MCluster mCluster : mClusters) {

if (mCluster.hasPoint()) {

newPoints.add(mCluster.getPosition());

}

}

//将聚合中的数据清除

for (MCluster mCluster : mClusters) {

mCluster.clear();

}

}

以上,哪里说的不对欢迎指正

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/529848
推荐阅读
相关标签
  

闽ICP备14008679号