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

本文介绍了使用OTMQuadTree数据结构实现的地图坐标树,用于高效地存储和检索地理坐标数据。OTMQuadTree分为四叉树节点类和操作类,支持插入数据、遍历及按矩形区域收集数据等功能。代码实现包括四叉树节点的创建、分割、插入数据等操作,以及在指定矩形区域内查找包含的数据。此外,还提供了清理、重建树的方法。适用于地图上的数据聚集和检索场景。
摘要由CSDN通过智能技术生成

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

OTMQuadTree.h文件

#ifndef OTMQuadTree_h

#define OTMQuadTree_h

#include

#include

#include

#include

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

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

CFRelease(nodeData.data);

}

free(node->nodeDatas);

free(node);

}

OTMCoordinateTree.h文件

#import

#import "OTMQuadTree.h"

#import

#import "OTMValueDefinitions.h"

@interface OTMCoordinateTree : NSObject

@property (nonatomic, assign) OTMQuadTreeNode *root;

@property (nonatomic, strong) NSArray *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

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 *)modelsArray {

if (!_modelsArray) {

_modelsArray = [NSArray array];

}

return _modelsArray;

}

@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值