[ios3-地图] 如何在iOS地图上高效的显示大量数据 [转]

- typedef struct TBQuadTreeNodeData {
- double x;
- double y;
- void* data;
- } TBQuadTreeNodeData;
- TBQuadTreeNodeData TBQuadTreeNodeDataMake(double x, double y, void* data);
- typedef struct TBBoundingBox {
- double x0; double y0;
- double xf; double yf;
- } TBBoundingBox;
- TBBoundingBox TBBoundingBoxMake(double x0, double y0, double xf, double yf);
- typedef struct quadTreeNode {
- struct quadTreeNode* northWest;
- struct quadTreeNode* northEast;
- struct quadTreeNode* southWest;
- struct quadTreeNode* southEast;
- TBBoundingBox boundingBox;
- int bucketCapacity;
- TBQuadTreeNodeData *points;
- int count;
- } TBQuadTreeNode;
- TBQuadTreeNode* TBQuadTreeNodeMake(TBBoundingBox boundary, int bucketCapacity);

- void TBQuadTreeNodeSubdivide(TBQuadTreeNode* node)
- {
- TBBoundingBox box = node->boundingBox;
- double xMid = (box.xf + box.x0) / 2.0;
- double yMid = (box.yf + box.y0) / 2.0;
- TBBoundingBox northWest = TBBoundingBoxMake(box.x0, box.y0, xMid, yMid);
- node->northWest = TBQuadTreeNodeMake(northWest, node->bucketCapacity);
- TBBoundingBox northEast = TBBoundingBoxMake(xMid, box.y0, box.xf, yMid);
- node->northEast = TBQuadTreeNodeMake(northEast, node->bucketCapacity);
- TBBoundingBox southWest = TBBoundingBoxMake(box.x0, yMid, xMid, box.yf);
- node->southWest = TBQuadTreeNodeMake(southWest, node->bucketCapacity);
- TBBoundingBox southEast = TBBoundingBoxMake(xMid, yMid, box.xf, box.yf);
- node->southEast = TBQuadTreeNodeMake(southEast, node->bucketCapacity);
- }
- bool TBQuadTreeNodeInsertData(TBQuadTreeNode* node, TBQuadTreeNodeData data)
- {
- // Bail if our coordinate is not in the boundingBox
- if (!TBBoundingBoxContainsData(node->boundingBox, data)) {
- return false;
- }
- // Add the coordinate to the points array
- if (node->count < node->bucketCapacity) {
- node->points[node->count++] = data;
- return true;
- }
- // Check to see if the current node is a leaf, if it is, split
- if (node->northWest == NULL) {
- TBQuadTreeNodeSubdivide(node);
- }
- // Traverse the tree
- if (TBQuadTreeNodeInsertData(node->northWest, data)) return true;
- if (TBQuadTreeNodeInsertData(node->northEast, data)) return true;
- if (TBQuadTreeNodeInsertData(node->southWest, data)) return true;
- if (TBQuadTreeNodeInsertData(node->southEast, data)) return true;
- return false;
- }

- typedef void(^TBDataReturnBlock)(TBQuadTreeNodeData data);
- void TBQuadTreeGatherDataInRange(TBQuadTreeNode* node, TBBoundingBox range, TBDataReturnBlock block)
- {
- // If range is not contained in the node's boundingBox then bail
- if (!TBBoundingBoxIntersectsBoundingBox(node->boundingBox, range)) {
- return;
- }
- for (int i = 0; i < node->count; i++) {
- // Gather points contained in range
- if (TBBoundingBoxContainsData(range, node->points[i])) {
- block(node->points[i]);
- }
- }
- // Bail if node is leaf
- if (node->northWest == NULL) {
- return;
- }
- // Otherwise traverse down the tree
- TBQuadTreeGatherDataInRange(node->northWest, range, block);
- TBQuadTreeGatherDataInRange(node->northEast, range, block);
- TBQuadTreeGatherDataInRange(node->southWest, range, block);
- TBQuadTreeGatherDataInRange(node->southEast, range, block);
- }
- typedef struct TBHotelInfo {
- char* hotelName;
- char* hotelPhoneNumber;
- } TBHotelInfo;
- TBQuadTreeNodeData TBDataFromLine(NSString *line)
- {
- // Example line:
- // -80.26262, 25.81015, Everglades Motel, USA-United States, +1 305-888-8797
- NSArray *components = [line componentsSeparatedByString:@","];
- double latitude = [components[1] doubleValue];
- double longitude = [components[0] doubleValue];
- TBHotelInfo* hotelInfo = malloc(sizeof(TBHotelInfo));
- NSString *hotelName = [components[2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- hotelInfo->hotelName = malloc(sizeof(char) * hotelName.length + 1);
- strncpy(hotelInfo->hotelName, [hotelName UTF8String], hotelName.length + 1);
- NSString *hotelPhoneNumber = [[components lastObject] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- hotelInfo->hotelPhoneNumber = malloc(sizeof(char) * hotelPhoneNumber.length + 1);
- strncpy(hotelInfo->hotelPhoneNumber, [hotelPhoneNumber UTF8String], hotelPhoneNumber.length + 1);
- return TBQuadTreeNodeDataMake(latitude, longitude, hotelInfo);
- }
- - (void)buildTree
- {
- NSString *data = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"USA-HotelMotel" ofType:@"csv"] encoding:NSASCIIStringEncoding error:nil];
- NSArray *lines = [data componentsSeparatedByString:@"\n"];
- NSInteger count = lines.count - 1;
- TBQuadTreeNodeData *dataArray = malloc(sizeof(TBQuadTreeNodeData) * count);
- for (NSInteger i = 0; i < count; i++) {
- dataArray[i] = TBDataFromLine(lines[i]);
- }
- TBBoundingBox world = TBBoundingBoxMake(19, -166, 72, -53);
- _root = TBQuadTreeBuildWithData(dataArray, count, world, 4);
- }

- - (NSArray *)clusteredAnnotationsWithinMapRect:(MKMapRect)rect withZoomScale:(double)zoomScale
- {
- double TBCellSize = TBCellSizeForZoomScale(zoomScale);
- double scaleFactor = zoomScale / TBCellSize;
- 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;
- TBQuadTreeGatherDataInRange(self.root, TBBoundingBoxForMapRect(mapRect), ^(TBQuadTreeNodeData data) {
- totalX += data.x;
- totalY += data.y;
- count++;
- });
- if (count >= 1) {
- CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(totalX / count, totalY / count);
- TBClusterAnnotation *annotation = [[TBClusterAnnotation alloc] initWithCoordinate:coordinate count:count];
- [clusteredAnnotations addObject:annotation];
- }
- }
- }
- return [NSArray arrayWithArray:clusteredAnnotations];
- }
- - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
- {
- [[NSOperationQueue new] addOperationWithBlock:^{
- double zoomScale = self.mapView.bounds.size.width / self.mapView.visibleMapRect.size.width;
- NSArray *annotations = [self.coordinateQuadTree clusteredAnnotationsWithinMapRect:mapView.visibleMapRect withZoomScale:zoomScale];
- [self updateMapViewAnnotationsWithAnnotations:annotations];
- }];
- }

- - (void)updateMapViewAnnotationsWithAnnotations:(NSArray *)annotations
- {
- NSMutableSet *before = [NSMutableSet setWithArray:self.mapView.annotations];
- NSSet *after = [NSSet setWithArray:annotations];
- // Annotations circled in blue shared by both sets
- NSMutableSet *toKeep = [NSMutableSet setWithSet:before];
- [toKeep intersectSet:after];
- // Annotations circled in green
- NSMutableSet *toAdd = [NSMutableSet setWithSet:after];
- [toAdd minusSet:toKeep];
- // Annotations circled in red
- NSMutableSet *toRemove = [NSMutableSet setWithSet:before];
- [toRemove minusSet:after];
- // These two methods must be called on the main thread
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- [self.mapView addAnnotations:[toAdd allObjects]];
- [self.mapView removeAnnotations:[toRemove allObjects]];
- }];
- }

- static CGFloat const TBScaleFactorAlpha = 0.3;
- static CGFloat const TBScaleFactorBeta = 0.4;
- CGFloat TBScaledValueForValue(CGFloat value)
- {
- return 1.0 / (1.0 + expf(-1 * TBScaleFactorAlpha * powf(value, TBScaleFactorBeta)));
- }
- - (void)setCount:(NSUInteger)count
- {
- _count = count;
- // Our max size is (44,44)
- CGRect newBounds = CGRectMake(0, 0, roundf(44 * TBScaledValueForValue(count)), roundf(44 * TBScaledValueForValue(count)));
- self.frame = TBCenterRect(newBounds, self.center);
- CGRect newLabelBounds = CGRectMake(0, 0, newBounds.size.width / 1.3, newBounds.size.height / 1.3);
- self.countLabel.frame = TBCenterRect(newLabelBounds, TBRectCenter(newBounds));
- self.countLabel.text = [@(_count) stringValue];
- [self setNeedsDisplay];
- }
- - (void)setupLabel
- {
- _countLabel = [[UILabel alloc] initWithFrame:self.frame];
- _countLabel.backgroundColor = [UIColor clearColor];
- _countLabel.textColor = [UIColor whiteColor];
- _countLabel.textAlignment = NSTextAlignmentCenter;
- _countLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.75];
- _countLabel.shadowOffset = CGSizeMake(0, -1);
- _countLabel.adjustsFontSizeToFitWidth = YES;
- _countLabel.numberOfLines = 1;
- _countLabel.font = [UIFont boldSystemFontOfSize:12];
- _countLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
- [self addSubview:_countLabel];
- }
- - (void)drawRect:(CGRect)rect
- {
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSetAllowsAntialiasing(context, true);
- UIColor *outerCircleStrokeColor = [UIColor colorWithWhite:0 alpha:0.25];
- UIColor *innerCircleStrokeColor = [UIColor whiteColor];
- UIColor *innerCircleFillColor = [UIColor colorWithRed:(255.0 / 255.0) green:(95 / 255.0) blue:(42 / 255.0) alpha:1.0];
- CGRect circleFrame = CGRectInset(rect, 4, 4);
- [outerCircleStrokeColor setStroke];
- CGContextSetLineWidth(context, 5.0);
- CGContextStrokeEllipseInRect(context, circleFrame);
- [innerCircleStrokeColor setStroke];
- CGContextSetLineWidth(context, 4);
- CGContextStrokeEllipseInRect(context, circleFrame);
- [innerCircleFillColor setFill];
- CGContextFillEllipseInRect(context, circleFrame);
- }
- - (void)addBounceAnnimationToView:(UIView *)view
- {
- CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
- bounceAnimation.values = @[@(0.05), @(1.1), @(0.9), @(1)];
- bounceAnimation.duration = 0.6;
- NSMutableArray *timingFunctions = [[NSMutableArray alloc] init];
- for (NSInteger i = 0; i < 4; i++) {
- [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
- }
- [bounceAnimation setTimingFunctions:timingFunctions.copy];
- bounceAnimation.removedOnCompletion = NO;
- [view.layer addAnimation:bounceAnimation forKey:@"bounce"];
- }
- - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
- {
- for (UIView *view in views) {
- [self addBounceAnnimationToView:view];
- }
- }
- NSInteger TBZoomScaleToZoomLevel(MKZoomScale scale)
- {
- double totalTilesAtMaxZoom = MKMapSizeWorld.width / 256.0;
- NSInteger zoomLevelAtMaxZoom = log2(totalTilesAtMaxZoom);
- NSInteger zoomLevel = MAX(0, zoomLevelAtMaxZoom + floor(log2f(scale) + 0.5));
- return zoomLevel;
- }
- float TBCellSizeForZoomScale(MKZoomScale zoomScale)
- {
- NSInteger zoomLevel = TBZoomScaleToZoomLevel(zoomScale);
- switch (zoomLevel) {
- case 13:
- case 14:
- case 15:
- return 64;
- case 16:
- case 17:
- case 18:
- return 32;
- case 19:
- return 16;
- default:
- return 88;
- }
- }
[ios3-地图] 如何在iOS地图上高效的显示大量数据 [转]的更多相关文章
- 如何在iOS地图上高效的显示大量数据
2016-01-13 / 23:02:13 刚才在微信上看到这篇由cocoachina翻译小组成员翻译的文章,觉得还是挺值得参考的,因此转载至此,原文请移步:http://robots.thought ...
- 如何在iOS手机上进行自动化测试
版权声明:允许转载,但转载必须保留原链接:请勿用作商业或者非法用途 Airtest支持iOS自动化测试,在Mac上为iOS手机部署iOS-Tagent之后,就可以使用AirtestIDE连接设备,像连 ...
- fir.im Weekly - 如何在 iOS 上构建 TensorFlow 应用
本期 fir.im Weekly 收集了最近新鲜出炉的 iOS /Android 技术分享,包括 iOS 系统开发 TensorFlow 教程.iOS 新架构.iOS Notifications 推送 ...
- iOS 地图定位及大头针的基本使用
地图 Part1 - 定位及大头针的基本使用 一.MapKit 作用 : 用于地图展示 如大头针,路线,覆盖层展示等(着重界面展示) 使用步骤 导入头文件 #import <MapKit/Map ...
- 【高德API】如何利用MapKit开发全英文检索的iOS地图
原文:[高德API]如何利用MapKit开发全英文检索的iOS地图 制作全英文地图的展示并不困难,但是要制作全英文的数据检索列表,全英文的信息窗口,你就没办法了吧.告诉你,我有妙招!使用iOS自带的M ...
- 【iOS地图开发】巧妙打造中英文全球地图
地图开发的同学们经常遇到这样的问题,国内版地图开发,用高德或者百度就行了.但是,国外的地图怎么办?这里告诉大家,如果利用iOS地图,打造中英文的,国内国外都能用的,全球地图. 制作全英文地图的展示并不 ...
- iOS 地图相关
参考博文:https://blog.csdn.net/zhengang007/article/details/52858198?utm_source=blogxgwz7 1.坐标系 目前常见的坐标系有 ...
- Swift - 使用MapKit显示地图,并在地图上做标记
通过使用MapKit可以将地图嵌入到视图中,MapKit框架除了可以显示地图,还支持在地图上做标记. 1,通过mapType属性,可以设置地图的显示类型 MKMapType.Standard :标准地 ...
- 从底层谈WebGIS 原理设计与实现(六):WebGIS中地图瓦片在Canvas上的拼接显示原理
从底层谈WebGIS 原理设计与实现(六):WebGIS中地图瓦片在Canvas上的拼接显示原理 作者:naaoveGI… 文章来源:naaoveGIS 点击数:1145 更新时间: ...
随机推荐
- PHP 7: PHP 变量和常量的定义
原文:PHP 7: PHP 变量和常量的定义 本章说说变量的定义.如果对于变量和常量的定义,你会注意几个方面呢?你可能会想到: 如何定义变量,它和C# 等语言有什么不同呢? 变量区分大小写吗? PHP ...
- 大约Android 3.0后AsyncTask默认的单线程分析
在Android下了很大的后台操作在需要的情况下.通常用于AsyncTask这个类.比方说,网络负载形象.访问server接口.一般的情况是使用一个的一例AsyncTask对象mTask,复制Asyn ...
- Crystal Report 在 VS 2010 中的使用和发布
原文:Crystal Report 在 VS 2010 中的使用和发布 使用: 打开CrystalReport官网下载页 目前最新版本为13.0.4 选择“SAP Crystal Reports, v ...
- jquery 触屏滑动+定时滚动
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...
- javascript this指向
this对象是什么: this对象是与运行时函数执行的上下文绑定的.这句话其实已经很好的解释了this对象,为我们确定this指明了方向!但是需要注意的是:由于javascript具有动态性(解释执行 ...
- 关于如何惟一地标识一台Android设备的综合性讨论
想必大家在开发Android项目的时候,多多少少会遇到“如何惟一地标识一台Android设备”等类似的问题.不只是以前,即使是现在乃至可以预见的将来,这个问题都将一直存在. 如果大家使用搜索工具搜索的 ...
- 对中级 Linux 用户有用的 20 个命令
也许你已经发现第一篇文章非常的有用,这篇文章是继对初级Linux用户非常有用的20个命令的一个延伸. 第一篇文章的目的是为新手准备的而这篇文章则是为了Linux的中高级用户.在这里你将学会如何进行自定 ...
- 《C++游戏开发》笔记十三 平滑过渡的战争迷雾(一) 原理:Warcraft3地形拼接算法
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9611887 作者:七十一雾央 新浪微博:http:/ ...
- cocos2d(CCSprite 用贝塞尔做抛物线,足球精灵并且同时做旋转放大效果)
今天刚学到Cocos2d中的动作哪一张,自己做了一个用贝塞尔曲线足球精灵实现同时放大旋转和抛物线动作. 使用 [CCSpawn actions:,,]链接这几个动作,同时做.与CCSequence(一 ...
- C#制作高仿360安全卫士窗体3
C#制作高仿360安全卫士窗体(三) 距上篇C#制作高仿360安全卫士窗体(二)也将近一个多月了,这个月事情还是像往常一样的多.不多我也乐在其中,毕竟我做的是我喜欢做的东西.今天特地抽空把怎么制作 ...