近期公司需求在地图上展示点部坐标信息,结果后台一次性返回了1200多个点部,加载到地图上连6S都巨卡,所以自己去弄了一个百度API上的一个聚合功能,文档写的相关内容很少,Demo也没有对数据量过多的时候的优化,所以就自己研究了一下,希望能和大家学习一下,编程道路上还是个新人,望多指教。
大概的实现效果界面就是这样
相关的百度静态库(BMKClusterManager)导入就不做说明了,API里面有很详细的说明。
首先创一个百度大头针BMKPointAnnotation的子类,在里面对大头针的样式(这里用的Label)和自定义多少个大头针聚合为一体(size)和它的显示样式颜色等
/* *点聚合Annotation */
@interface ClusterAnnotation : BMKPointAnnotation
///所包含annotation个数
@property (nonatomic, assign) NSInteger size;
@property (nonatomic ,strong) NSString * AnnotationAddress;
@end
@implementation ClusterAnnotation
@synthesize size = _size;
@end
/* *点聚合AnnotationView */
@interface ClusterAnnotationView : BMKPinAnnotationView {
}
@property (nonatomic, assign) NSInteger size;
@property (nonatomic, strong) UILabel *label;
@end
@implementation ClusterAnnotationView
@synthesize size = _size;
@synthesize label = _label;- (id)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self) {
[self setBounds:CGRectMake(0.f, 0.f, 22.f, 22.f)];
_label = [[UILabel alloc] initWithFrame:CGRectMake(0.f, 0.f, 40.f, 40.f)];
_label.layer.masksToBounds = YES;
_label.layer.cornerRadius =20;
_label.layer.borderWidth = 3;
_label.layer.borderColor = [UIColor whiteColor].CGColor;
_label.textColor = [UIColor whiteColor];
_label.font = [UIFont systemFontOfSize:15];
_label.textAlignment = NSTextAlignmentCenter;
[self addSubview:_label];
self.alpha = 0.85;
}
return self;
}
- (void)setSize:(NSInteger)size {
_size = size;
if (_size == 1) {
self.label.hidden = YES;
self.pinColor = BMKPinAnnotationColorPurple;
return;
}
self.label.hidden = NO;
if (size > 200) {
self.label.backgroundColor =[UIColor colorWithHexString:@"#5b78c9"];
} else if (size > 100) {
self.label.backgroundColor = [UIColor colorWithHexString:@"#9388ff"];
} else if (size > 50){
self.label.backgroundColor = [UIColor colorWithHexString:@"#62a5d3"];
} else if (size > 20){
self.label.backgroundColor = [UIColor colorWithHexString:@"#5688e9"];
} else {
self.label.backgroundColor = [UIColor colorWithHexString:@"#889cff"];
}
_label.text = [NSString stringWithFormat:@"%ld", size];
}
@end
之后需要在使用的控制器中导入几个全局管理类
@interface ClusterDemoViewController() {
BMKClusterManager *_clusterManager;//点聚合管理类
NSInteger _clusterZoom;//聚合级别
NSMutableArray *_clusterCaches;//点聚合缓存标注
}
接下来对没个管理类进行设置,这里的_clusterCaches是将百度地图的每个等级的比例(比例尺等级为3-22)所需要展现的大头针个数进行了一个缓存处理。_clusterManager存储所有的标注,它会将所有标注进行处理,比如说在什么时候需要聚合什么时候需要分离。需要注意的是导入的坐标点是否为百度经纬度,如果为GPS或者其他的需要转换。
//#pragma mark - 设置百度地图大头针和代理方法
-(void)didSetAnnotations{
//适配ios7
if( ([[[UIDevice currentDevice] systemVersion] doubleValue]>=7.0)) {
self.navigationController.navigationBar.translucent = NO;
}
_clusterCaches = [[NSMutableArray alloc] init];
for (NSInteger i = 3; i < 22; i++) {
[_clusterCaches addObject:[NSMutableArray array]];
}
_clusterManager = [[BMKClusterManager alloc] init];
for (NSDictionary * tempDic in self.pointArr) {
CLLocationCoordinate2D coor = CLLocationCoordinate2DMake([tempDic[@"Latitude"] doubleValue], [tempDic[@"Longitude"] doubleValue]);
NSDictionary *locDic = BMKConvertBaiduCoorFrom(coor,BMK_COORDTYPE_GPS);
CLLocationCoordinate2D newCoor =BMKCoorDictionaryDecode(locDic);
if (!(coor.longitude ==0 && coor.longitude == 0)) {
ClusterAnnotation* annotation = [[ClusterAnnotation alloc]init];
annotation.coordinate = newCoor;
annotation.title = tempDic[@"Company"];
annotation.AnnotationAddress = tempDic[@"Address"];
[self.annotationArr addObject:annotation];
//添加大头针到管理类
BMKClusterItem *clusterItem = [[BMKClusterItem alloc] init];
clusterItem.coor = newCoor;
[_clusterManager addClusterItem:clusterItem];
}
}
}
接下来是实现聚合最麻烦的地方,Demo里面给的案例因为只有20个点,所以完全不需要做优化,我照着写就很卡,我的处理是点部在当前屏幕上加载
#pragma mark - 更新聚合状态
//更新聚合状态
- (void)updateClusters {
//当前屏幕中心经纬度
CLLocationDegrees centerLatitude= _baiduMapView.centerCoordinate.latitude;
CLLocationDegrees centerLongitude = _baiduMapView.centerCoordinate.longitude;
//当前地图经纬度跨度范围
__block CLLocationDegrees smallLatitude =centerLatitude - (_baiduMapView.region.span.latitudeDelta/2.f);
__block CLLocationDegrees bigLatitude = centerLatitude + (_baiduMapView.region.span.latitudeDelta/2.f);
__block CLLocationDegrees smallLongitude =centerLongitude - (_baiduMapView.region.span.longitudeDelta/2.f);
__block CLLocationDegrees bigLongitude = centerLongitude + (_baiduMapView.region.span.longitudeDelta/2.f);
_clusterZoom = (NSInteger)_baiduMapView.zoomLevel;
@synchronized(_clusterCaches) {
__block NSMutableArray *clusters = [_clusterCaches objectAtIndex:(_clusterZoom-3)];
if (clusters.count > 0) {
[_baiduMapView removeAnnotations:_baiduMapView.annotations];
NSMutableArray *tmp = [[NSMutableArray alloc] init];
//获取聚合后的标注
__block NSArray *array = [_clusterManager getClusters:_clusterZoom];
for (BMKCluster *item in array) {
//创建大头针
ClusterAnnotation *annotation = [[ClusterAnnotation alloc] init];
annotation.coordinate = item.coordinate;
//创建大头
if ( (smallLatitude <= annotation.coordinate.latitude)
&& (bigLatitude >= annotation.coordinate.latitude)
&& (smallLongitude<= annotation.coordinate.longitude)
&& (bigLongitude >= annotation.coordinate.longitude)) {
annotation.size = item.size;
for (ClusterAnnotation * tempAnn in self.annotationArr) {
// CLLocationCoordinate2D coor = CLLocationCoordinate2DMake([tempDic[@"Latitude"] doubleValue], [tempDic[@"Longitude"] doubleValue]);
//判断是否为大头针
if (annotation.coordinate.latitude == tempAnn.coordinate.latitude
&& annotation.coordinate.longitude == tempAnn.coordinate.longitude && annotation.size == 1) {
annotation.title =tempAnn.title;
annotation.AnnotationAddress = tempAnn.AnnotationAddress;
break;
}else{
annotation.title = [NSString stringWithFormat:@"共%ld个点部", item.size];
}
}
[tmp addObject:annotation];
}
}
//满足在指定范围内则添加
[_baiduMapView addAnnotations:tmp];
} else {
// WS(weakSelf);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
///获取聚合后的标注
__block NSArray *array = [_clusterManager getClusters:_clusterZoom];
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *tmp = [[NSMutableArray alloc] init];
for (BMKCluster *item in array) {
//创建大头针
ClusterAnnotation *annotation = [[ClusterAnnotation alloc] init];
annotation.coordinate = item.coordinate;
[clusters addObject:annotation];
//满足在指定范围内则添加
if ( (smallLatitude <= annotation.coordinate.latitude)
&& (bigLatitude >= annotation.coordinate.latitude)
&& (smallLongitude<= annotation.coordinate.longitude)
&& (bigLongitude >= annotation.coordinate.longitude)) {
annotation.size = item.size;
//遍历给大头针标题赋值
for (ClusterAnnotation * tempAnn in self.annotationArr){
// CLLocationCoordinate2D coor = CLLocationCoordinate2DMake([tempDic[@"Latitude"] doubleValue], [tempDic[@"Longitude"] doubleValue]);
//判断是否为大头针
if (annotation.coordinate.latitude == tempAnn.coordinate.latitude
&& annotation.coordinate.longitude == tempAnn.coordinate.longitude && annotation.size == 1) {
annotation.title =tempAnn.title;
annotation.AnnotationAddress = tempAnn.AnnotationAddress;
break;
}else{
annotation.title = [NSString stringWithFormat:@"共%ld个点部", item.size];
}
}
[tmp addObject:annotation];
}
}
[_baiduMapView removeAnnotations:_baiduMapView.annotations];
[_baiduMapView addAnnotations:tmp];
});
});
}
}
}
上面的能理解写完以后基本就完成了,之后就是在合适的地方调用那个方法就可以了,这里的代码我放上来时没有封装,写的也比较粗糙,相关注释也写在上面了,聚合最复杂的地方也是这部分
#pragma mark - BMKMapViewDelegate
// 根据anntation生成对应的View
- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id)annotation
{
//普通annotation
NSString *AnnotationViewID = @"ClusterMark";
ClusterAnnotation *cluster = (ClusterAnnotation*)annotation;
ClusterAnnotationView *annotationView = [[ClusterAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
annotationView.size = cluster.size;
annotationView.draggable = YES;
annotationView.annotation = cluster;
return annotationView;
}
/**
*当点击annotation view弹出的泡泡时,调用此接口
*@param mapView 地图View
*@param view 泡泡所属的annotation view
*/
- (void)mapView:(BMKMapView *)mapView annotationViewForBubble:(BMKAnnotationView *)view {
if ([view isKindOfClass:[ClusterAnnotationView class]]) {
ClusterAnnotation *clusterAnnotation = (ClusterAnnotation*)view.annotation;
if (clusterAnnotation.size > 1) {
[mapView setCenterCoordinate:view.annotation.coordinate];
[mapView zoomIn];
}
}
}
/**
*地图初始化完毕时会调用此接口
*@param mapview 地图View
*/
- (void)mapViewDidFinishLoading:(BMKMapView *)mapView {
[self updateClusters];
}
/**
*地图渲染每一帧画面过程中,以及每次需要重绘地图时(例如添加覆盖物)都会调用此接口
*@param mapview 地图View
*@param status 此时地图的状态
*/
- (void)mapView:(BMKMapView *)mapView onDrawMapFrame:(BMKMapStatus *)status {
if (_clusterZoom != 0 && _clusterZoom != (NSInteger)mapView.zoomLevel) {
[self updateClusters];
}
}