美文网首页iOS地图对移动开发有帮助iOS Developer
高德地图点聚合算法实现与思考

高德地图点聚合算法实现与思考

作者: 无忌不悔 | 来源:发表于2017-04-13 14:11 被阅读2422次

    说在前面


    为了实现在地图上对大量的标注点进行聚合显示,使都有标注点根据中心点以及缩放比例的变化动态排列并完成点聚合的功能。现提供基于高德地图SDK的点聚合实现方式。

    以下分别为两种放缩比例下标注点的聚合情况效果图:

    整体效果 局部效果

    思路


    前期思路

    服务端计算标注统计数据,在客户端加载绘制

    在预研前期,我考虑了数据从后台获取的方式,在地图上添加overlay的方式,但是考虑到用户的操作会要求所有数据必须实时生成并展现,这意味着每次缩放比例和中心点的改变都要访问服务器以重载数据,实现起来难度较大,并且对服务器和客户端都造成了较大的压力。

    一次性加载所有数据,客户端实现算法在需要时计算重载

    在阅读高德开放平台提供的API时发现高德地图示例中心在2017年2月10日更新了点聚合效果示例

    其应用场景为:当地图上需要展示的marker过多,可能会导致界面上marker压盖、性能变差。使用点聚合功能,则可以解决该问题。

    原理与实现


    架构

    Controllers

    • <UIViewController>
      • BaseMapViewController 地图基类
        • AnnotationClusterViewController poi点聚合
      • PoiDetailViewController 显示poi详细信息列表

    View

    • MAAnnotationView
      • ClusterAnnotationView 自定义的聚合annotationView

    Models

    • Conform to <MAAnnotation>
      • ClusterAnnotation 记录annotation的信息,如其代表的poi数组、poi的个数、poi平均坐标,并提供两个annotation是否Equal的判断
    • CoordinateQuadTree 封装的四叉树类
    • QuadTree 四叉树基本算法

    核心代码

    点聚合算法高德官方代码(iOS)见iOS_3D_ClusterAnnotation于Github。

    在项目中实现

    引入AMap3DMap(当前使用版本为4.3.0)

    pod 'AMap3DMap','~>4.3.0'
    
    

    下载并添加MAAnnotationCluster

    在地图上添加标注点

    创建MAClusteringManagerMAClusterAnimator

    @property(nonatomic,strong)MAClusteringManager * clusteringManager ;
    @property(nonatomic,strong)MAClusterAnimator * animator;
    

    初始化标注点数组

    NSMutableArray *pointAnnotations = [NSMutableArray array];
    

    将含有位置信息的点添加到数组中

    for (int i = 0; i < [receivedData count]; i ++) {
                        NSDictionary *annotationDict = receivedData[i];
                        CGFloat lat = [[annotationDict objectForKey:@"latitude"] floatValue];
                        CGFloat lon = [[annotationDict objectForKey:@"longitude"] floatValue];
                        NSNumber *pID = [annotationDict objectForKey:@"id"];
                        MAPointAnnotation *pointAnnotation = [[MAPointAnnotation alloc] init];
                        pointAnnotation.coordinate = CLLocationCoordinate2DMake(lat,  lon);
                        pointAnnotation.title = [pID stringValue];
                        [pointAnnotations addObject:pointAnnotation];
                    }
                    self.animator = [[MAClusterAnimator alloc] init];
                    self.clusteringManager = [[MAClusteringManager alloc] initWithAnnotations:pointAnnotations];
                    self.clusteringManager.delegate = self;
                    [self mapView:_mapView regionDidChangeAnimated:YES];
                    
    

    其中 receivedData可以通过后台获取或本地生成,包含经度、纬度以及pID,即每一个标注点的唯一ID。mapView regionDidChangeAnimated:方法在mapView区域变化时自动调用,重算annotation,初始化时手动调用一次,使标注点以聚合的形式加载到地图上。

    实现mapView regionDidChangeAnimated:方法体

    double scale = mapView.bounds.size.width / mapView.visibleMapRect.size.width;
        [[NSOperationQueue new] addOperationWithBlock:^{
            // Get the main queue and do the UI stuff .
            NSArray *annatations = [NSArray array];
            annatations = [self.clusteringManager clusteredAnnotationsWithinMapRect:mapView.visibleMapRect withZoomScale:scale];
            [self.clusteringManager displayAnnotations:annatations onMapView:mapView];
        }];
        
    

    注意:此处必须要使用多线程

    根据MAClusteringManager计算的数量修改Annotation样式。

    mapView viewForAnnotation:方法中

    - (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id<MAAnnotation>)annotation {
        static NSString *const AnnotatioViewReuseID = @"AnnotatioViewReuseID";
        static NSString *const ClusterAnnotationReuseID = @"ClusterAnnotationReuseID";
        static NSString *const SingleClusterAnnotationRauseID = @"SingleClusterAnnotationRauseID";
        if ([annotation isKindOfClass:[MAAnnotationCluster class]]) {
            //聚合点
            MAAnnotationCluster *cluster = (MAAnnotationCluster *)annotation;
            cluster.title = [NSString stringWithFormat:@"%zd items",[cluster.annotations count]];
            MAClusterAnnotationView *clusterView = (MAClusterAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ClusterAnnotationReuseID];
            if (!clusterView) {
                clusterView = [[MAClusterAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ClusterAnnotationReuseID];
                clusterView.canShowCallout = NO;
                clusterView.clusterColor = ClusterColor;
            }
            
            clusterView.count = [((MAAnnotationCluster *)annotation).annotations count];
            return clusterView;
        }else {
            if ([annotation isKindOfClass:[MAPointAnnotation class]]) {
                MAAnnotationView *poiAnnotationView = (MAAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotatioViewReuseID];
                if ([[annotation title] isEqualToString:(NSString*)RoutePlanningViewControllerStartTitle]) {
                    poiAnnotationView.image = [UIImage imageNamed:@""];
                    return poiAnnotationView;
                }else if([[annotation title] isEqualToString:(NSString *)RoutePlanningViewControllerDestinationTitle]) {
                    poiAnnotationView.image = [UIImage imageNamed:@"ani"];
                    return poiAnnotationView;
                }else {
                    MAClusterAnnotationView *clusterView = (MAClusterAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ClusterAnnotationReuseID];
                    if (!clusterView) {
                        clusterView = [[MAClusterAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:SingleClusterAnnotationRauseID];
                        clusterView.canShowCallout = NO;
                        clusterView.clusterColor = ClusterColor;
                    }
                    clusterView.count = 1;
                    return clusterView;
                }
            }else {
                return nil;
            }
        }
    }
    
    

    这样,就在四叉树算法的基础上完成了点聚合功能的实现。

    相关文章

      网友评论

      • 无忌不悔:MACluster下载地址已附于文末,需要请自取~
      • 无厌_:好厉害,看不懂。。。
      • z益达:求demo,感激不尽,445190979@qq.com
        无忌不悔:@z益达 已发,请查收!
        z益达:@无忌不悔 那可以发MAClusteringManager和MAClusterAnimator"这两个文件给我么
        无忌不悔:@z益达 所有的细节都在文中贴出来了,只需要引入高德的相关库就可以了~
      • 不换帅质:楼主可以给我发一份吗 MAClusteringManager和MAClusterAnimator 谢谢啦 944120988@qq.com
      • 张小牛_邪:楼主可以给我发一份吗 MAClusteringManager和MAClusterAnimator 谢谢啦 1203008460@qq.com
        无忌不悔:@张小牛_邪 已发
      • 每天刷两次牙:450742103@qq.com,同求文中的"创建MAClusteringManager和MAClusterAnimator"这两个文件.
        每天刷两次牙:@无忌不悔 ,,三扣
        无忌不悔:@Ming_en_long 已发~
      • e1cc5bb87d8c:请教一下,按照您的思路,一次读取所有数据,但是单次从服务器读取大量数据,等待时间长,您是怎么解决的?
        无忌不悔:@焕焕_f243 也许还有更好的处理方式:smile:
        e1cc5bb87d8c:@无忌不悔 如果产品要求不能分次加载,是不是就只有“等待”一条路可以走啦?
        无忌不悔:@焕焕_f243 可以分次加载,按当前放缩比例加载半径等于一定量的范围内标注点,根据实际情况控制半径
      • 不负时光_:你好 我想问下官方的 /* 建立四叉树. */
        [coordinateQuadTree buildTreeWithPOIs:annotations]; 是要导入POI,而我想实现导入自己的MAPointAnnotation,发现很难修改,而你这边MAClusteringManager,MAClusterAnimator 我不知道内部是怎么实现的,能详细的介绍下么
        无忌不悔:@不负时光_0518 👌🏻不客气
        不负时光_:@无忌不悔 我从您的github 里找到了,谢谢了
        无忌不悔:@不负时光_a442 留下邮箱,我可以发给你
      • acc36d69a26b:每个集合点怎么动态的从外面传图片进去?谢谢
        acc36d69a26b:@无忌不悔 我试了下没有成功
        无忌不悔:@614WHF 可以在经纬度信息基础上增加属性进行初始化传值
      • 40816892dcb0:文中的"创建MAClusteringManager和MAClusterAnimator"这两个文件. 能贴一下这两文件的代码,或者发邮箱给我吗? 谢谢 511953544@qq.com
        无忌不悔:@coderxjw 抱歉,上次回去忘记了,邮件已发,请查收
        40816892dcb0:@无忌不悔 你好,有空的话,能再发给我一次吗? 谢谢
        无忌不悔:@coderxjw 好的,等我回去发到你邮箱

      本文标题:高德地图点聚合算法实现与思考

      本文链接:https://www.haomeiwen.com/subject/nmrxattx.html