美文网首页
iOS 地图分割,四叉树数据结构C语言版

iOS 地图分割,四叉树数据结构C语言版

作者: 七维树 | 来源:发表于2018-03-23 14:55 被阅读27次

    两个类,第一个OTMQuadTree是数据结构类(4叉树),第二个是树的操作OC类,OTMCoordinateTree。仍有许多地方需要完善,暂时还没有时间,后续抽时间完成。

    OTMQuadTree.h文件

    
    #ifndef OTMQuadTree_h
    #define OTMQuadTree_h
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <math.h>
    
    typedef struct OTMCoordinate {
        double lat;
        double lng;
    }OTMCoordinate;
    OTMCoordinate OTMCoordinateMake(double lat, double lng);
    
    typedef struct OTMQuadTreeNodeData{
        OTMCoordinate coordinate;
        void* data;
    }OTMQuadTreeNodeData;
    OTMQuadTreeNodeData OTMQuadTreeNodeDataMake(double lat, double lng, void* data);
    OTMQuadTreeNodeData OTMQuadTreeNodeDataMakeWithCoord(OTMCoordinate coord, void* data);
    
    typedef struct OTMBoundingBox {
        struct OTMCoordinate leftTop;
        struct OTMCoordinate rightBottom;
    }OTMBoundingBox;
    OTMBoundingBox OTMBoundingBoxMakeWithCoordinate(OTMCoordinate leftTop , OTMCoordinate rightBottom);
    OTMBoundingBox OTMBoundingBoxMake(double leftX, double leftY, double rightX , double rightY);
    
    typedef struct OTMQuadTreeNode {
        struct OTMQuadTreeNode* leftTop;
        struct OTMQuadTreeNode* rightTop;
        struct OTMQuadTreeNode* leftBottom;
        struct OTMQuadTreeNode* rightBottom;
        OTMBoundingBox boundingBox;
        OTMQuadTreeNodeData* nodeDatas;
        int capacity;
        int count;
    }OTMQuadTreeNode;
    OTMQuadTreeNode* OTMQuadTreeNodeMake(OTMBoundingBox boundingBox, int capacity);
    
    void freeNode(OTMQuadTreeNode* node);
    bool nodeInserData(OTMQuadTreeNode* node, OTMQuadTreeNodeData data);
    
    typedef void (^OTMQuadTreeReturnDataBlock)(OTMQuadTreeNodeData data);
    typedef void (^OTMQuadTreeTraverseBlock)(OTMQuadTreeNode *node);
    
    #pragma mark -
    #pragma mark - Tree Function
    void freeNode(OTMQuadTreeNode* node);
    OTMQuadTreeNode* quadTreeBulidWithData(OTMQuadTreeNodeData *data, int count, int capacity, OTMBoundingBox box);
    void quadTreeTraverse(OTMQuadTreeNode *node,OTMQuadTreeTraverseBlock block);
    void gatherDataInBox(OTMQuadTreeNode *node, OTMBoundingBox box ,OTMQuadTreeReturnDataBlock block);
    bool nodeInserData(OTMQuadTreeNode* node, OTMQuadTreeNodeData data);
    void nodeSubDivide(OTMQuadTreeNode* node);
    
    #pragma mark -
    #pragma mark - Box Function
    bool boxIntersectsBox(OTMBoundingBox box1, OTMBoundingBox box2);
    bool boxContainData(OTMBoundingBox box, OTMQuadTreeNodeData data);
    #endif /* OTMQuadTree_h */
    

    OTMQuadTree.c文件

    #include "OTMQuadTree.h"
    #include <CoreFoundation/CoreFoundation.h>
    OTMCoordinate OTMCoordinateMake(double lat, double lng) {
        OTMCoordinate coord;
        coord.lat = lat;
        coord.lng = lng;
        return coord;
    }
    
    OTMQuadTreeNodeData OTMQuadTreeNodeDataMakeWithCoord(OTMCoordinate coord, void* data) {
        OTMQuadTreeNodeData nodeData;
        nodeData.coordinate = coord;
        nodeData.data = data;
        return nodeData;
    }
    
    OTMQuadTreeNodeData OTMQuadTreeNodeDataMake(double lat, double lng, void* data) {
        return OTMQuadTreeNodeDataMakeWithCoord(OTMCoordinateMake(lat, lng), data);
    };
    
    OTMBoundingBox OTMBoundingBoxMakeWithCoordinate(OTMCoordinate leftTop , OTMCoordinate rightBottom) {
        OTMBoundingBox box;
        box.leftTop = leftTop;
        box.rightBottom = rightBottom;
        return box;
    }
    
    OTMBoundingBox OTMBoundingBoxMake(double leftX, double leftY, double rightX , double rightY) {
        OTMCoordinate lt = OTMCoordinateMake(leftX, leftY);
        OTMCoordinate rb = OTMCoordinateMake(rightX, rightY);
        return OTMBoundingBoxMakeWithCoordinate(lt, rb);
    }
    
    OTMQuadTreeNode* OTMQuadTreeNodeMake(OTMBoundingBox boundingBox, int capacity) {
        OTMQuadTreeNode *node = malloc(sizeof(OTMQuadTreeNode));
        node->leftTop = NULL;
        node->rightTop = NULL;
        node->leftBottom = NULL;
        node->rightBottom = NULL;
        node->boundingBox = boundingBox;
        node->capacity = capacity;
        node->count = 0;
        node->nodeDatas = malloc(sizeof(OTMQuadTreeNodeData) * capacity);
        return node;
    }
    
    #pragma Box Function
    bool boxContainData(OTMBoundingBox box, OTMQuadTreeNodeData data) {
        double maxLat = box.leftTop.lat;
        double minLat = box.rightBottom.lat;
        double minLng = box.leftTop.lng;
        double maxLng = box.rightBottom.lng;
        bool latContain = data.coordinate.lat >= minLat && data.coordinate.lat <= maxLat;
        bool lngContain = data.coordinate.lng >= minLng && data.coordinate.lng <= maxLng;
        return latContain && lngContain;
    }
    
    bool boxIntersectsBox(OTMBoundingBox box1, OTMBoundingBox box2) {
        bool latInter = box1.leftTop.lat >= box2.rightBottom.lat && box1.rightBottom.lat <= box2.leftTop.lat;
        bool lngInter = box1.leftTop.lng <= box2.rightBottom.lng && box1.rightBottom.lng >= box2.leftTop.lng;
        return latInter && lngInter;
    }
    
    #pragma Tree Function
    
    void nodeSubDivide(OTMQuadTreeNode* node) {
        
        double midLat = fabs(node->boundingBox.rightBottom.lat + node->boundingBox.leftTop.lat) / 2;
        double midLng = fabs(node->boundingBox.rightBottom.lng + node->boundingBox.leftTop.lng) / 2;
        OTMCoordinate midCoord = OTMCoordinateMake(midLat, midLng);
        
        OTMBoundingBox ltBox = OTMBoundingBoxMakeWithCoordinate(node->boundingBox.leftTop, midCoord);
        node->leftTop = OTMQuadTreeNodeMake(ltBox, node->capacity);
        
        OTMBoundingBox rtBox = OTMBoundingBoxMake(midLat, node->boundingBox.leftTop.lng, node->boundingBox.rightBottom.lat, midLng);
        node->rightTop = OTMQuadTreeNodeMake(rtBox, node->capacity);
        
        OTMBoundingBox lbBox = OTMBoundingBoxMake(node->boundingBox.leftTop.lat, midLng, midLat, node->boundingBox.rightBottom.lng);
        node->leftBottom = OTMQuadTreeNodeMake(lbBox, node->capacity);
        
        OTMBoundingBox rbBox = OTMBoundingBoxMake(midLat, midLng, node->boundingBox.rightBottom.lat, node->boundingBox.rightBottom.lng);
        node->rightBottom = OTMQuadTreeNodeMake(rbBox, node->capacity);
    }
    
    bool nodeInserData(OTMQuadTreeNode* node, OTMQuadTreeNodeData data) {
        
        // box不包涵data的点则失败
        if (!boxContainData(node->boundingBox, data)) {
            return false;
        }
        
        if (node->count < node->capacity) {
            node->nodeDatas[node->count] = data;
            node->count++;
            return true;
        }
        
        //父box满了
        if (node->leftTop == NULL) { //如果没有子box
            //分割
            nodeSubDivide(node);
        }
        
        //递归查找插入位置,直到成功
        if (nodeInserData(node->leftTop, data)) return true;
        if (nodeInserData(node->rightTop, data)) return true;
        if (nodeInserData(node->leftBottom, data)) return true;
        if (nodeInserData(node->rightBottom, data)) return true;
        
        return false;
    }
    
    void gatherDataInBox(OTMQuadTreeNode *node, OTMBoundingBox box ,OTMQuadTreeReturnDataBlock block) {
        if (!boxIntersectsBox(node->boundingBox, box)) {
            //两个box不相交不合并
            return;
        }
        //遍历node中所有的数据,判断某个数据是否在目标范围呢,在则执行block
        for (int i = 0 ; i < node->count; i++) {
            if (boxContainData(box, node->nodeDatas[i])) {
                if (block) {
                    block(node->nodeDatas[i]);
                }
            }
        }
        //没有子区域了直接返回
        if (node->leftTop == NULL) {
            return;
        }
        
        //递归假如到子box中
        gatherDataInBox(node->leftTop, box, block);
        gatherDataInBox(node->rightTop, box, block);
        gatherDataInBox(node->leftBottom, box, block);
        gatherDataInBox(node->rightBottom, box, block);
    }
    
    void quadTreeTraverse(OTMQuadTreeNode *node,OTMQuadTreeTraverseBlock block){
        
        if (block) {
            block(node);
        }
        
        if (node->leftTop == NULL) {
            return;
        }
        
        quadTreeTraverse(node->leftTop,block);
        quadTreeTraverse(node->rightTop,block);
        quadTreeTraverse(node->leftBottom,block);
        quadTreeTraverse(node->rightBottom,block);
    }
    
    OTMQuadTreeNode* quadTreeBulidWithData(OTMQuadTreeNodeData *data, int count, int capacity, OTMBoundingBox box) {
        OTMQuadTreeNode *root = OTMQuadTreeNodeMake(box, capacity);
        for (int i = 0; i < count; i++) {
            nodeInserData(root, data[i]);
        }
        return root;
    }
    
    void freeNode(OTMQuadTreeNode* node) {
        if (node->leftTop != NULL) freeNode(node->leftTop);
        if (node->rightTop != NULL) freeNode(node->rightTop);
        if (node->leftBottom != NULL) freeNode(node->leftBottom);
        if (node->rightBottom != NULL) freeNode(node->rightBottom);
        
        for (int i = 0 ; i < node -> count; i++) {
            OTMQuadTreeNodeData nodeData = node->nodeDatas[i];
    //        free(nodeData.data);
            //如果data是oc对象需要用CFRelease()释放,导入头文件#include <CoreFoundation/CoreFoundation.h>
            CFRelease(nodeData.data);
        }
        free(node->nodeDatas);
        free(node);
    }
    

    OTMCoordinateTree.h文件

    #import <Foundation/Foundation.h>
    #import "OTMQuadTree.h"
    #import <MapKit/MapKit.h>
    #import "OTMValueDefinitions.h"
    
    @interface OTMCoordinateTree : NSObject
    @property (nonatomic, assign) OTMQuadTreeNode *root;
    @property (nonatomic, strong) NSArray<OTMPhotoModel *> *modelsArray;
    @property (nonatomic, assign) CLLocationCoordinate2D requestLeftTopCoord;
    @property (nonatomic, assign) CLLocationCoordinate2D requestRightBottomCoord;
    
    - (NSArray *)clusteredAnnotationsWithinMapRect:(MKMapRect)rect withZoomScale:(double)zoomScale;
    - (NSArray *)clusteredMaskAnnotationsOn:(MKMapView *)mapView;
    
    - (void)buildTree;
    - (void)rebuildTree;
    - (void)clean;
    @end
    

    OTMCoordinateTree.m文件

    #import "OTMCoordinateTree.h"
    #import <CoreLocation/CoreLocation.h>
    
    OTMBoundingBox boxFormMapRect(MKMapRect mapRect){
        CLLocationCoordinate2D topLeft = MKCoordinateForMapPoint(mapRect.origin);
        CLLocationCoordinate2D botRight = MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMaxX(mapRect), MKMapRectGetMaxY(mapRect)));
    //    CLLocationDegrees minLat = botRight.latitude;
    //    CLLocationDegrees maxLat = topLeft.latitude;
    //
    //    CLLocationDegrees minLon = topLeft.longitude;
    //    CLLocationDegrees maxLon = botRight.longitude;
        return OTMBoundingBoxMakeWithCoordinate(OTMCoordinateMake(topLeft.latitude, topLeft.longitude), OTMCoordinateMake(botRight.latitude, botRight.longitude));
    //    return OTMBoundingBoxMake(maxLat, minLon, minLat, maxLon);
    }
    
    MKMapRect mapRectFormBox(OTMBoundingBox box) {
        MKMapPoint topLeft = MKMapPointForCoordinate(CLLocationCoordinate2DMake(box.leftTop.lat, box.leftTop.lng));
        MKMapPoint botRight = MKMapPointForCoordinate(CLLocationCoordinate2DMake(box.rightBottom.lat, box.rightBottom.lng));
    
        return MKMapRectMake(topLeft.x, botRight.y, fabs(botRight.x - topLeft.x), fabs(botRight.y - topLeft.y));
    }
    
    NSInteger zoomScaleToZoomLevel(MKZoomScale scale)
    {
        double totalTilesAtMaxZoom = MKMapSizeWorld.width / 256.0;
        NSInteger zoomLevelAtMaxZoom = log2(totalTilesAtMaxZoom);
        NSInteger zoomLevel = MAX(0, zoomLevelAtMaxZoom + floor(log2f(scale) + 0.5));
        
        return zoomLevel;
    }
    
    @implementation OTMCoordinateTree
    
    - (void)buildTree {
        @autoreleasepool {
            if (self.modelsArray.count == 0) {
                return;
            }
            NSInteger count = self.modelsArray.count;
    
            OTMQuadTreeNodeData *dataArray = malloc(sizeof(OTMQuadTreeNodeData) * count);
            for (int i = 0 ; i < count; i++) {
                OTMPhotoModel *model = self.modelsArray[i];
                dataArray[i] = OTMQuadTreeNodeDataMake(model.coordinate.latitude, model.coordinate.longitude, (__bridge void *)(model));
            }
            
    //        for (int i = 0; i < count; i++) {
    //            OTMPhotoModel *model = (__bridge OTMPhotoModel *)(dataArray[i].data);
    //            NSLog(@"omt test model index:%d url = %@",i,model.preview_url);
    //        }
            
            OTMBoundingBox box = OTMBoundingBoxMake(self.requestLeftTopCoord.latitude,
                                                    self.requestLeftTopCoord.longitude,
                                                    self.requestRightBottomCoord.latitude,
                                                    self.requestRightBottomCoord.longitude);
            
            self.root = quadTreeBulidWithData(dataArray,(int)count, 4, box);
        }
    }
    
    - (void)rebuildTree {
        [self clean];
        [self buildTree];
    }
    
    - (NSArray *)clusteredMaskAnnotationsOn:(MKMapView *)mapView {
        CGFloat gridWidth = kOTMPhotoAnnotationWidth;
        //计算每个图片的对应的maprect
        __block NSMutableArray *needRemoveModels = [[NSMutableArray alloc] init];
        [self.modelsArray enumerateObjectsUsingBlock:^(OTMPhotoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            OTMPhotoModel *currentModel = obj;
            if (obj.gathered) {
                return ;
            }
    
            CGPoint centerPt = [mapView convertCoordinate:obj.coordinate toPointToView:mapView];
            CGRect objClusterRect = CGRectMake(centerPt.x -gridWidth, centerPt.y - gridWidth, gridWidth * 2, gridWidth * 2);
            MKCoordinateRegion objClusterRegion = [mapView convertRect:objClusterRect toRegionFromView:mapView];
            MKMapRect mapRect = [self mapRectForCoordinateRegion:objClusterRegion];
            [self.modelsArray enumerateObjectsUsingBlock:^(OTMPhotoModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                if (currentModel == obj || obj.gathered) {
                    return ;
                }
                MKMapPoint point = MKMapPointForCoordinate(obj.coordinate);
                if (MKMapRectContainsPoint(mapRect, point)) {
                    //有重叠的
                    obj.gathered = YES;
                    [currentModel.gatheredModels addObject:obj];
                    [needRemoveModels addObject:obj];
                }
            }];
        }];
        
        if (needRemoveModels.count > 0) {
            NSMutableArray *arr = [NSMutableArray arrayWithArray:self.modelsArray];
            [arr removeObjectsInArray:needRemoveModels];
            self.modelsArray = arr;
        }
        return self.modelsArray;
    }
    
    - (MKMapRect)mapRectForCoordinateRegion:(MKCoordinateRegion)coordinateRegion
    {
        CLLocationCoordinate2D topLeftCoordinate =
        CLLocationCoordinate2DMake(coordinateRegion.center.latitude
                                   + (coordinateRegion.span.latitudeDelta/2.0),
                                   coordinateRegion.center.longitude
                                   - (coordinateRegion.span.longitudeDelta/2.0));
        
        MKMapPoint topLeftMapPoint = MKMapPointForCoordinate(topLeftCoordinate);
        
        CLLocationCoordinate2D bottomRightCoordinate =
        CLLocationCoordinate2DMake(coordinateRegion.center.latitude
                                   - (coordinateRegion.span.latitudeDelta/2.0),
                                   coordinateRegion.center.longitude
                                   + (coordinateRegion.span.longitudeDelta/2.0));
        
        MKMapPoint bottomRightMapPoint = MKMapPointForCoordinate(bottomRightCoordinate);
        
        MKMapRect mapRect = MKMapRectMake(topLeftMapPoint.x,
                                          topLeftMapPoint.y,
                                          fabs(bottomRightMapPoint.x-topLeftMapPoint.x),
                                          fabs(bottomRightMapPoint.y-topLeftMapPoint.y));
        
        return mapRect;
    }
    
    - (NSArray *)clusteredAnnotationsWithinMapRect:(MKMapRect)rect withZoomScale:(double)zoomScale
    {
        double cellSize = 88;
        /// zoomScale = point * (1 / mapPoint)  * (1 / size) ==  point / size / mapPoint
        double scaleFactor = zoomScale / cellSize;
        
        NSInteger minX = floor(MKMapRectGetMinX(rect) * scaleFactor);
        NSInteger maxX = floor(MKMapRectGetMaxX(rect) * scaleFactor);
        NSInteger minY = floor(MKMapRectGetMinY(rect) * scaleFactor);
        NSInteger maxY = floor(MKMapRectGetMaxY(rect) * scaleFactor);
        
        NSMutableArray *clusteredAnnotations = [[NSMutableArray alloc] init];
        for (NSInteger x = minX; x <= maxX; x++) {
            for (NSInteger y = minY; y <= maxY; y++) {
                MKMapRect mapRect = MKMapRectMake(x / scaleFactor, y / scaleFactor, 1.0 / scaleFactor, 1.0 / scaleFactor);
                
                __block double totalX = 0;
                __block double totalY = 0;
                __block int count = 0;
                
                NSMutableArray *models = [[NSMutableArray alloc] init];
                
                gatherDataInBox(self.root, boxFormMapRect(mapRect), ^(OTMQuadTreeNodeData data) {
                    totalX += data.coordinate.lat;
                    totalY += data.coordinate.lng;
                    count ++;
                    
                    OTMPhotoModel *model = (__bridge OTMPhotoModel *)(data.data);
                    [models addObject:model];
                });
                
                if (count == 1) {
                    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(totalX, totalY);
                    OTMPhotoModel *model = (OTMPhotoModel *)models[0]; //默认使用第一个图片
                    model.coordinate = coordinate;//修改显示的坐标
                    [clusteredAnnotations addObject:model];
                }
                
                if (count > 1) {
                    CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(totalX / count, totalY / count);
                    OTMPhotoModel *model =(OTMPhotoModel *)models[0]; //默认使用第一个图片
                    model.coordinate = coordinate; //修改显示的坐标
                    model.count = @(count); //从新计算count
                    [clusteredAnnotations addObject:model];
                }
            }
        }
        
        return [NSArray arrayWithArray:clusteredAnnotations];
    }
    
    - (void)clean {
        if (self.root) {
            freeNode(self.root);
        }
    }
    
    - (NSArray<OTMPhotoModel *> *)modelsArray {
        if (!_modelsArray) {
            _modelsArray = [NSArray array];
        }
        return _modelsArray;
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:iOS 地图分割,四叉树数据结构C语言版

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