Android百度地图点聚合开发
自v3.3.0版本起,SDK提供了给Marker增加动画的能力,具体实现方法如下:// 通过marker的icons设置一组图片,再通过period设置多少帧刷新一次图片资源这是官方的介绍,下面看看具体实现方法。
代码结构
主要包括java,manifest,drawable,layout
如图:
首先java包括MainActivity,MyApplication等等。
第一个类是Cluster代码如下:
public class Cluster {
private static final String TAG = "Cluster";
private static final String TAG_ADD_Cluster = "AddCluster_method";
private Context mContext;
private MapView mMapView;
private Boolean isAverageCenter;
private int mGridSize;
private double mDistance;
private List<ClusterMarker> mClusterMarkers;
public Cluster(Context context, MapView mapView
, Boolean isAverageCenter
, int mGridSize, double mDistance) {
this.mContext = context;
this.mMapView = mapView;
this.isAverageCenter = isAverageCenter;
this.mGridSize = mGridSize;
this.mDistance = mDistance;
mClusterMarkers = new ArrayList<>();
}
public ArrayList<MarkerOptions> createCluster(List<MarkerOptions> markerList) {
this.mClusterMarkers.clear();
ArrayList<MarkerOptions> itemList = new ArrayList<MarkerOptions>();
for (int i = 0; i < markerList.size(); i++) {
addCluster(markerList.get(i));
}
for (int i = 0; i < mClusterMarkers.size(); i++) {
ClusterMarker cm = mClusterMarkers.get(i);
setClusterDrawable(cm);
MarkerOptions oi = new MarkerOptions().position(cm.getmCenter()).icon(cm.getMarkerOptions().getIcon());
itemList.add(oi);
}
Log.e(TAG, "itemList.size:" + itemList.size());
return itemList;
}
/**
* 添加标注点,如果第一次添加,直接新建,否则与地图上原有的点进行判断,如果距离小于mDistance,则进行聚合
*
* @param marker
*/
private void addCluster(MarkerOptions marker) {
LatLng markGeo = marker.getPosition();
// 没有ClusterMarkers
if (mClusterMarkers.size() == 0) {
ClusterMarker clusterMarker = new ClusterMarker(marker.getPosition());
clusterMarker.getMarkerOptions().icon(marker.getIcon());
clusterMarker.AddMarker(marker, isAverageCenter);
MBound bound = new MBound(markGeo.latitude, markGeo.longitude, markGeo.latitude, markGeo.longitude);
bound = MapUtils.getExtendedBounds(mMapView, bound, mGridSize);
clusterMarker.setmGridBounds(bound);
mClusterMarkers.add(clusterMarker);
} else {
ClusterMarker clusterContain = null;
double distance = mDistance;
for (int i = 0; i < mClusterMarkers.size(); i++) {
ClusterMarker clusterMarker = mClusterMarkers.get(i);
Log.e(TAG_ADD_Cluster, "in mClusterMarker.size size = = " + mClusterMarkers.size());
LatLng center = clusterMarker.getmCenter();
double d = DistanceUtil.getDistance(center, marker.getPosition());
//[]--------选择clusterMarker 中最近的,clusterMarker-------双重循环-----------[]
if (d < distance) {
distance = d;
clusterContain = clusterMarker;
} else {
// Log.d(TAG_ADD_Cluster, "d>distence,不满足聚合距离");
}
}
// 现存的clusterMarker 没有符合条件的
if (clusterContain == null || !isMarkersInCluster(markGeo, clusterContain.getmGridBounds())) {
// Log.e(TAG_ADD_Cluster, "======clusterContain=======================--------------");
ClusterMarker clusterMarker = new ClusterMarker(marker.getPosition());
clusterMarker.getMarkerOptions().icon(marker.getIcon());
clusterMarker.AddMarker(marker, isAverageCenter);
MBound bound = new MBound(markGeo.latitude, markGeo.longitude, markGeo.latitude, markGeo.longitude);
bound = MapUtils.getExtendedBounds(mMapView, bound, mGridSize);
clusterMarker.setmGridBounds(bound);
mClusterMarkers.add(clusterMarker);
} else {
clusterContain.AddMarker(marker, isAverageCenter);
Log.e(TAG_ADD_Cluster, "添加到选中 clusterMarker:--->clusterContain.size:---->" + clusterContain.getmMarkers().size());
}
}
}
/**
* 设置聚合点的颜色与中间数字
*
* @param clusterMarker
*/
private void setClusterDrawable(ClusterMarker clusterMarker) {
View drawableView = LayoutInflater.from(mContext).inflate(
R.layout.drawable_mark, null);
TextView text = (TextView) drawableView.findViewById(R.id.drawble_mark);
text.setPadding(3, 3, 3, 3);
int markNum = clusterMarker.getmMarkers().size();
Log.e("setClusterDrawable", "!!!!!!!!!!!!!!!!!!!!!!!" + markNum);
if (markNum >= 2) {
text.setText(markNum + "");
if (markNum < 11) {
text.setBackgroundResource(R.drawable.m0);
} else if (markNum > 10 && markNum < 21) {
text.setBackgroundResource(R.drawable.m1);
} else if (markNum > 20 && markNum < 31) {
text.setBackgroundResource(R.drawable.m2);
} else if (markNum > 30 && markNum < 41) {
text.setBackgroundResource(R.drawable.m3);
} else {
text.setBackgroundResource(R.drawable.m4);
}
clusterMarker.getMarkerOptions().icon(BitmapDescriptorFactory.fromView(drawableView));
} else {
clusterMarker.getMarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.nav_turn_via_1));
}
}
/**
* 判断坐标点是否在MBound 覆盖区域内
*
* @param markerGeo
* @param bound
* @return
*/
private Boolean isMarkersInCluster(LatLng markerGeo, MBound bound) {
Log.e(TAG, "rightTopLat:" + bound.getRightTopLat());
Log.e(TAG, "rightTopLng:" + bound.getRightTopLng());
Log.e(TAG, "leftBottomLat:" + bound.getLeftBottomLat());
Log.e(TAG, "leftBottomlng:" + bound.getLeftBottomLng());
if (markerGeo.latitude > bound.getLeftBottomLat()
&& markerGeo.latitude < bound.getRightTopLat()
&& markerGeo.longitude > bound.getLeftBottomLng()
&& markerGeo.longitude < bound.getRightTopLng()) {
return true;
}
return false;
}
}
接着是ClusterMarker类:
public class ClusterMarker {
private LatLng mCenter;
private List<MarkerOptions> mMarkers;
private MBound mGridBounds;
public MarkerOptions getMarkerOptions() {
return mMarkerOptions;
}
private MarkerOptions mMarkerOptions;
public ClusterMarker(LatLng geoPoint) {
mMarkers = new ArrayList<>();
mMarkerOptions = new MarkerOptions().position(geoPoint);
}
/**
* 计算平均中心点
*
* @return
*/
private LatLng calAverageCenter() {
double latitude = 0, longitude = 0;
int len = mMarkers.size() == 0 ? 1 : mMarkers.size();
Log.e("calAverageCenter:", "calAverageCenter:------>" + len);
for (int i = 0; i < len; i++) {
latitude = latitude + mMarkers.get(i).getPosition().latitude;
longitude = longitude + mMarkers.get(i).getPosition().longitude;
}
return new LatLng((int) (latitude / len), (int) (longitude / len));
}
/**
* ClusterMarker 中添加marker
*
* @param marker
* @param isAverageCenter
*/
public void AddMarker(MarkerOptions marker, Boolean isAverageCenter) {
mMarkers.add(marker);
if (!isAverageCenter) {
if (mCenter == null)
this.mCenter = mMarkers.get(0).getPosition();
} else {
this.mCenter = calAverageCenter();
}
}
public LatLng getmCenter() {
return this.mCenter;
}
public void setmCenter(LatLng mCenter) {
this.mCenter = mCenter;
}
public List<MarkerOptions> getmMarkers() {
return mMarkers;
}
public void setmMarkers(List<MarkerOptions> mMarkers, Boolean isAverageCenter) {
this.mMarkers.addAll(mMarkers);
if (!isAverageCenter) {
if (mCenter == null) {
this.mCenter = mMarkers.get(0).getPosition();
}
} else
this.mCenter = calAverageCenter();
}
public MBound getmGridBounds() {
return mGridBounds;
}
public void setmGridBounds(MBound mGridBounds) {
this.mGridBounds = mGridBounds;
}
}
然后MainActivity类,
public class MainActivity extends Activity {
private String TAG = "MainActivity";
private MapView mMapView;
private Boolean isAverageCenter = false;
private Integer mMaxZoom = 12;
private Integer mGridSize = 60;
private ArrayList<MarkerOptions> mMarkers;
private Cluster mCluster;
private double mDistance = 600000;
private BaiduMap mBaiduMap;
public LocationClient mLocationClient = null;
public BDLocationListener myLocationListener = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
// 生产随机经纬度坐标
addFakeDate();
this.isAverageCenter = false;
// 监听mapview
setListener();
LocationOrientate();
}
private void init() {
mMapView = (MapView) findViewById(R.id.bmapView);
mBaiduMap = mMapView.getMap();
mBaiduMap.setMapStatus(MapStatusUpdateFactory.zoomTo(5.0f));
mCluster = new Cluster(this, mMapView, isAverageCenter, mGridSize, mDistance);
mBaiduMap.clear();
}
/**
* 创建虚拟数据
*/
private void addFakeDate() {
mMarkers = new ArrayList<>();
for (int i = 0; i < 200; i++) {
LatLng pt = new LatLng(((Math.random() * 30 + 15)), ((Math.random() * 25 + 95)));
MarkerOptions item = new MarkerOptions().position(pt)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.nav_turn_via_1));
mMarkers.add(item);
}
}
/**
* 在地图上显示标注点
* @param list 所有标注点
*/
private void pinMarkers(ArrayList<MarkerOptions> list) {
this.mBaiduMap.clear();
Log.e(TAG, "pinMarkers: size:" + list.size());
for (int i = 0; i < list.size(); i++) {
this.mBaiduMap.addOverlay(list.get(i));
}
mMapView.refreshDrawableState();
}
private void setListener() {
//点击标注点放大
mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
if (mBaiduMap.getMapStatus().zoom < mBaiduMap.getMaxZoomLevel()) {
float newZoom = mBaiduMap.getMapStatus().zoom + 2.0f;
mBaiduMap.setMapStatus(MapStatusUpdateFactory.zoomTo(newZoom));
}
return false;
}
});
//地图加载结束时显示标注点
mBaiduMap.setOnMapLoadedCallback(new BaiduMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
refreshMarks();
}
});
//地图状态改变时重新绘制标注点
mBaiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {
@Override
public void onMapStatusChangeStart(MapStatus mapStatus) {
}
@Override
public void onMapStatusChange(MapStatus mapStatus) {
}
@Override
public void onMapStatusChangeFinish(MapStatus mapStatus) {
refreshMarks();
}
});
}
/**
* 如果当前地图缩放程度超过设置的最大值,则不改变点坐标,否则重新进行聚合运算
*/
private void refreshMarks() {
if (mBaiduMap.getMapStatus().zoom >= MainActivity.this.mMaxZoom) {
mBaiduMap.clear();
pinMarkers(refreshVersionClusterMarker(mMarkers));
} else {
ArrayList<MarkerOptions> clusters = mCluster.createCluster(refreshVersionClusterMarker(mMarkers));
mBaiduMap.clear();
pinMarkers(clusters);
}
}
private void LocationOrientate() {
mLocationClient = new LocationClient(MainActivity.this);
myLocationListener = new MyLocationListener();
mLocationClient.registerLocationListener(myLocationListener);
LocationClientOption option = new LocationClientOption();
option.setAddrType("all");
option.setProdName("定位GPS");
option.setOpenGps(true);
option.setCoorType("bd09ll");
mLocationClient.setLocOption(option);
mLocationClient.start();
mMapView.refreshDrawableState();
}
/**
* 初始时定位到当前位置
*/
public class MyLocationListener implements BDLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
LatLng currentPosition = new LatLng(location.getLatitude(), location.getLongitude());
mBaiduMap.setMapStatus(MapStatusUpdateFactory.newLatLng(currentPosition));
}
}
private ArrayList<MarkerOptions> refreshVersionClusterMarker(ArrayList<MarkerOptions> list) {
MapStatus mapStatus = mBaiduMap.getMapStatus();
LatLngBounds latLngBounds = mapStatus.bound;
ArrayList<MarkerOptions> result = new ArrayList<MarkerOptions>();
for (int i = 0; i < list.size(); i++) {
if (latLngBounds.contains(list.get(i).getPosition())) {
result.add(list.get(i));
}
}
Log.e(TAG, "可见点:" + result.size());
return result;
}
}
MapUtils类
public class MapUtils {
static double DEF_PI = 3.14159265359; // PI
static double DEF_2PI = 6.28318530712; // 2*PI
static double DEF_PI180 = 0.01745329252; // PI/180.0
static double DEF_R = 6370693.5; // radius of earth
public static MBound getExtendedBounds(MapView map, MBound bound,
Integer gridSize) {
MBound tbounds = cutBoundsInRange(bound);
Projection projection = map.getMap().getProjection();
Point pixelNE = projection.toScreenLocation(tbounds.getRightTop());
Point pixelSW = projection.toScreenLocation(tbounds.getLeftBottom());
pixelNE.x += gridSize;
pixelNE.y -= gridSize;
pixelSW.x -= gridSize;
pixelSW.y += gridSize;
LatLng rightTop = projection.fromScreenLocation(new Point(pixelNE.x, pixelNE.y));
LatLng leftBottom = projection.fromScreenLocation(new Point(pixelSW.x, pixelSW.y));
return new MBound(rightTop.latitude, rightTop.longitude, leftBottom.latitude, leftBottom.longitude);
}
public static MBound cutBoundsInRange(MBound bounds) {
double maxX = getRange(bounds.getRightTopLat(),
-74, 74);
double minX = getRange(bounds.getRightTopLat(),
-74, 740);
double maxY = getRange(bounds.getRightTopLng(),
-180, 180);
double minY = getRange(bounds.getLeftBottomLng(),
-180, 180);
return new MBound(minX, minY, maxX, maxY);
}
public static double getRange(double i, double mix, double max) {
i = Math.max(i, mix);
i = Math.min(i, max);
return i;
}
public static double GetShortDistance(double lon1, double lat1, double lon2,
double lat2) {
double ew1, ns1, ew2, ns2;
double dx, dy, dew;
double distance;
ew1 = lon1 * DEF_PI180;
ns1 = lat1 * DEF_PI180;
ew2 = lon2 * DEF_PI180;
ns2 = lat2 * DEF_PI180;
dew = ew1 - ew2;
if (dew > DEF_PI)
dew = DEF_2PI - dew;
else if (dew < -DEF_PI)
dew = DEF_2PI + dew;
dx = DEF_R * Math.cos(ns1) * dew;
dy = DEF_R * (ns1 - ns2);
distance = Math.sqrt(dx * dx + dy * dy);
return distance;
}
public static double GetLongDistance(double lon1, double lat1, double lon2,
double lat2) {
double ew1, ns1, ew2, ns2;
double distance;
ew1 = lon1 * DEF_PI180;
ns1 = lat1 * DEF_PI180;
ew2 = lon2 * DEF_PI180;
ns2 = lat2 * DEF_PI180;
distance = Math.sin(ns1) * Math.sin(ns2) + Math.cos(ns1)
* Math.cos(ns2) * Math.cos(ew1 - ew2);
if (distance > 1.0)
distance = 1.0;
else if (distance < -1.0)
distance = -1.0;
distance = DEF_R * Math.acos(distance);
return distance;
}
public static Bitmap convertViewToBitmap(View view) {
view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
}
MBound类
public class MBound {
private double rightTopLat;
private double rightTopLng;
private double leftBottomLat;
private double leftBottomLng;
private LatLng rightTop;
private LatLng leftBottom;
public MBound(LatLng rightTop, LatLng leftBottom) {
super();
this.rightTop = rightTop;
this.leftBottom = leftBottom;
rightTopLat = rightTop.latitude;
rightTopLng = rightTop.longitude;
leftBottomLat = leftBottom.latitude;
leftBottomLng = leftBottom.longitude;
}
public MBound(double rightTopLat, double rightTopLng, double leftBottomLat, double leftBottomLng) {
this.rightTopLat = rightTopLat;
this.rightTopLng = rightTopLng;
this.leftBottomLat = leftBottomLat;
this.leftBottomLng = leftBottomLng;
rightTop = new LatLng(rightTopLat, rightTopLng);
leftBottom = new LatLng(leftBottomLat, leftBottomLng);
}
public double getRightTopLat() {
return rightTopLat;
}
public void setRightTopLat(int rightTopLat) {
this.rightTopLat = rightTopLat;
}
public double getRightTopLng() {
return rightTopLng;
}
public void setRightTopLng(int rightTopLng) {
this.rightTopLng = rightTopLng;
}
public double getLeftBottomLat() {
return leftBottomLat;
}
public void setLeftBottomLat(int leftBottomLat) {
this.leftBottomLat = leftBottomLat;
}
public double getLeftBottomLng() {
return leftBottomLng;
}
public void setLeftBottomLng(int leftBottomLng) {
this.leftBottomLng = leftBottomLng;
}
public LatLng getRightTop() {
return rightTop;
}
public void setRightTop(LatLng rightTop) {
this.rightTop = rightTop;
}
public LatLng getLeftBottom() {
return leftBottom;
}
public void setLeftBottom(LatLng leftBottom) {
this.leftBottom = leftBottom;
}
}
MyApplication类
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SDKInitializer.initialize(getApplicationContext());
}
}
界面代码:
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
drawable_mark.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@null">
<TextView
android:id="@+id/drawble_mark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2"
android:textSize="15sp"
android:gravity="center"
android:textColor="@android:color/white"
android:background="@drawable/m0"
/>
</LinearLayout>
另外就是图片了,这里就不上传了,manifest配置和百度地图官方配置一样
最后加上demo链接
Android百度地图点集合MarkerClusterdemo