关键词:
【中文标题】iPhone Map Kit 集群定位【英文标题】:iPhone Map Kit cluster pinpoints 【发布时间】:2011-01-04 02:20:32 【问题描述】:关于 :
我想在地图上显示 1000 个标记,但数量太多而无法处理,因此我想将它们聚集在一起。
是否有可用的框架或概念证明?这是可能的还是已经完成了?
【问题讨论】:
你看过地震演示吗? (我认为)他们有一个定制的别针,它有一个聚集的圆圈,圆圈的大小根据集群内的别针数量以及它们的组合里氏标度而增加。 这是演示吗? switchonthecode.com/tutorials/… 【参考方案1】:可以使用REVClusterMap进行集群
【讨论】:
在我集成 awakeFromNib 后为我工作:从该博客文章的 cmets 修复。集群可能会更快 IMO,但它可以工作。 链接不再有效【参考方案2】:注意:这是我所属的商业产品,但它解决了这个问题。
我在几个应用程序中解决了这个问题,并决定将其提取到一个可重用的框架中。它被称为Superpin,它是一个(商业,许可费用为 149 美元)iOS 框架,内部使用四叉树进行注释存储并执行基于网格的聚类。该算法非常快,包含的示例应用程序显示了世界各地的机场(超过 30k+ 注释),并且在 3G iPhone 上运行非常流畅。
【讨论】:
Superpin 绝对是通往 - 如果你能负担得起的话。简单,干净,良好的实施。在我们的一款包含大约 500 条注释的应用中对地图产生了巨大影响。 嘿@esad 你从哪里得到的世界机场列表?谢谢! @Andres ourairports.com 在公共领域提供有关世界机场的数据【参考方案3】:这可能有点像使用电锯修剪草坪,但这里摘录自Algorithms in a Nutshell
创建 KD 树...
public class KDFactory
// Known comparators for partitioning points along dimensional axes.
private static Comparator<IMultiPoint> comparators[ ] ;
// Recursively construct KDTree using median method on input points.
public static KDTree generate (IMultiPoint [ ] points)
if (points. length == 0) return null;
// median will be the root.
int maxD = points[ 0] . dimensionality( );
KDTree tree = new KDTree(maxD) ;
// Make dimensional comparators that compare points by ith dimension
comparators = new Comparator[ maxD+1] ;
for (int i = 1; i <= maxD; i++)
comparators[ i] = new DimensionalComparator(i) ;
tree. setRoot(generate (1, maxD, points, 0, points. length-1) ) ;
return tree;
// generate the node for the d-th dimension (1 <= d <= maxD)
// for points[ left, right]
private static DimensionalNode generate (int d, int maxD,
IMultiPoint points[ ] ,
int left, int right)
// Handle the easy cases first
if (right < left) return null;
if (right == left) return new DimensionalNode (d, points[ left] ) ;
// Order the array[ left, right] so the mth element will be the median
// and the elements prior to it will all be <=, though they won' t
// necessarily be sorted; similarly, the elements after will all be >=
int m = 1+(right-left) /2;
Selection. select(points, m, left, right, comparators[ d] ) ;
// Median point on this dimension becomes the parent
DimensionalNode dm = new DimensionalNode (d, points[ left+m-1] ) ;
// update to the next dimension, or reset back to 1
if (++d > maxD) d = 1;
// recursively compute left and right sub-trees, which translate
// into ' below' and ' above' for n-dimensions.
dm. setBelow(maxD, generate (d, maxD, points, left, left+m-2) ) ;
dm. setAbove(maxD, generate (d, maxD, points, left+m, right) ) ;
return dm;
找到最好的最近邻:O(log n) 最差的 O(n)
// method in KDTree
public IMultiPoint nearest (IMultiPoint target)
if (root == null) return null;
// find parent node to which target would have been inserted. This is our
// best shot at locating closest point; compute best distance guess so far
DimensionalNode parent = parent(target) ;
IMultiPoint result = parent. point;
double smallest = target. distance(result) ;
// now start back at the root, and check all rectangles that potentially
// overlap this smallest distance. If better one is found, return it.
double best[ ] = new double[ ] smallest ;
double raw[ ] = target. raw( );
IMultiPoint betterOne = root. nearest (raw, best) ;
if (betterOne ! = null) return betterOne;
return result;
// method in DimensionalNode. min[ 0] contains best computed shortest distance.
IMultiPoint nearest (double[ ] rawTarget, double min[ ] )
// Update minimum if we are closer.
IMultiPoint result = null;
// If shorter, update minimum
double d = shorter(rawTarget, min[ 0] ) ;
if (d >= 0 && d < min[ 0] )
min[ 0] = d;
result = point;
// determine if we must dive into the subtrees by computing direct
// perpendicular distance to the axis along which node separates
// the plane. If d is smaller than the current smallest distance,
// we could "bleed" over the plane so we must check both.
double dp = Math. abs(coord - rawTarget[ dimension-1] ) ;
IMultiPoint newResult = null;
if (dp < min[ 0] )
// must dive into both. Return closest one.
if (above ! = null)
newResult = above. nearest (rawTarget, min) ;
if (newResult ! = null) result = newResult;
if (below ! = null)
newResult = below. nearest(rawTarget, min) ;
if (newResult ! = null) result = newResult;
else
// only need to go in one! Determine which one now.
if (rawTarget[ dimension-1] < coord)
if (below ! = null)
newResult = below. nearest (rawTarget, min) ;
else
if (above ! = null)
newResult = above. nearest (rawTarget, min) ;
// Use smaller result, if found.
if (newResult ! = null) return newResult;
return result;
更多关于KD-Trees at Wikipedia
【讨论】:
【参考方案4】:我尝试了这里建议的其他人,我还发现OCMapView 效果最好。
它是免费的,并且可以轻松地对注释进行分组,这正是我所需要的。它比 Revolver 更新更多,对我来说更容易实现。
【讨论】:
【参考方案5】:离线地图应用“OffMaps”是一个概念证明;)
http://itunes.apple.com/us/app/offmaps/id313854422?mt=8
【讨论】:
【参考方案6】:我最近不得不使用 MapKit 实现注释聚类。上面提到的解决方案很好,具体取决于您的用例。我最终选择了 FBAnnotationClustering (Objective-C),因为它是免费的,并且在 github 上有很多星星和很少的问题:
https://github.com/infinum/FBAnnotationClustering
我正在开发的应用程序非常以地图为中心,因此将 FBAnnotationClustering 翻译成 Swift 是有意义的。这是有关该方法的博客文章,其中包含指向 github 上示例项目的链接。
http://ribl.co/blog/2015/05/28/map-clustering-with-swift-how-we-implemented-it-into-the-ribl-ios-app/
【讨论】:
我正在使用你的端口来 swift,你能看看这个问题吗,谢谢:***.com/questions/37747381/…【参考方案7】:受 WWDC 2011 视频的启发,这段代码非常适合我。也许不是这里提出的最快的,但它是免费的,而且绝对是最简单的。
它基本上使用2张地图。一个是隐藏的并保存每个注释(我的代码中的 allAnnotationMapView)。一个是可见的,并且仅显示集群或单个注释(我的代码中的 mapView)。
- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer
if (gestureRecognizer.state == UIGestureRecognizerStateEnded)
[self updateVisibleAnnotations];
- (void)updateVisibleAnnotations
static float marginFactor = 2.0f;
static float bucketSize = 50.0f;
MKMapRect visibleMapRect = [self.mapView visibleMapRect];
MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);
CLLocationCoordinate2D leftCoordinate = [self.mapView convertPoint:CGPointZero toCoordinateFromView:self.view];
CLLocationCoordinate2D rightCoordinate = [self.mapView convertPoint:CGPointMake(bucketSize, 0) toCoordinateFromView:self.view];
double gridSize = MKMapPointForCoordinate(rightCoordinate).x - MKMapPointForCoordinate(leftCoordinate).x;
MKMapRect gridMapRect = MKMapRectMake(0, 0, gridSize, gridSize);
double startX = floor(MKMapRectGetMinX(adjustedVisibleMapRect) / gridSize) * gridSize;
double startY = floor(MKMapRectGetMinY(adjustedVisibleMapRect) / gridSize) * gridSize;
double endX = floor(MKMapRectGetMaxX(adjustedVisibleMapRect) / gridSize) * gridSize;
double endY = floor(MKMapRectGetMaxY(adjustedVisibleMapRect) / gridSize) * gridSize;
gridMapRect.origin.y = startY;
while(MKMapRectGetMinY(gridMapRect) <= endY)
gridMapRect.origin.x = startX;
while (MKMapRectGetMinX(gridMapRect) <= endX)
NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect];
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop)
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem)
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
return shouldBeMerged;
] mutableCopy];
NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop)
BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]];
BOOL shouldBeMerged = NO;
if (isPointMapItem)
PointMapItem *pointItem = (PointMapItem *)obj;
shouldBeMerged = pointItem.shouldBeMerged;
return isPointMapItem && !shouldBeMerged;
];
for (PointMapItem *item in notMergedAnnotationsInBucket)
[self.mapView addAnnotation:item];
if(filteredAnnotationsInBucket.count > 0)
PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];
[filteredAnnotationsInBucket removeObject:annotationForGrid];
annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];
[self.mapView addAnnotation:annotationForGrid];
//force reload of the image because it's not done if annotationForGrid is already present in the bucket!!
MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid];
NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)];
[countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]];
[countLabel setTextColor:[UIColor whiteColor]];
[annotationView addSubview:countLabel];
imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO];
annotationView.image = [UIImage imageNamed:imageName];
if (filteredAnnotationsInBucket.count > 0)
[self.mapView deselectAnnotation:annotationForGrid animated:NO];
for (PointMapItem *annotation in filteredAnnotationsInBucket)
[self.mapView deselectAnnotation:annotation animated:NO];
annotation.clusterAnnotation = annotationForGrid;
annotation.containedAnnotations = nil;
if ([visibleAnnotationsInBucket containsObject:annotation])
CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
[UIView animateWithDuration:0.3 animations:^
annotation.coordinate = annotation.clusterAnnotation.coordinate;
completion:^(BOOL finished)
annotation.coordinate = actualCoordinate;
[self.mapView removeAnnotation:annotation];
];
gridMapRect.origin.x += gridSize;
gridMapRect.origin.y += gridSize;
- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
NSSet *annotationsForGridSet = [annotations objectsPassingTest:^BOOL(id obj, BOOL *stop)
BOOL returnValue = ([visibleAnnotationsInBucket containsObject:obj]);
if (returnValue)
*stop = YES;
return returnValue;
];
if (annotationsForGridSet.count != 0)
return [annotationsForGridSet anyObject];
MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(gridMapRect), MKMapRectGetMidY(gridMapRect));
NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2)
MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id<MKAnnotation>)obj1).coordinate);
MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id<MKAnnotation>)obj2).coordinate);
CLLocationDistance distance1 = MKMetersBetweenMapPoints(mapPoint1, centerMapPoint);
CLLocationDistance distance2 = MKMetersBetweenMapPoints(mapPoint2, centerMapPoint);
if (distance1 < distance2)
return NSOrderedAscending;
else if (distance1 > distance2)
return NSOrderedDescending;
return NSOrderedSame;
];
return [sortedAnnotations objectAtIndex:0];
【讨论】:
【参考方案8】:我认为Foto Brisko(iTunes 链接)可以做到这一点。 我认为没有 Cocoa Touch 框架。
【讨论】:
ios中map-kit中的默认位置
】ios中map-kit中的默认位置【英文标题】:Defaultlocationinmap-kitinios【发布时间】:2016-05-0506:34:08【问题描述】:我是IOS新手,我需要用我的纬度和经度显示默认位置(12.940358、80.208647)。带注释针。.h文件:#import<UIKit/UIKit.h>#im... 查看详情
使用 Map Kit 平铺注释
】使用MapKit平铺注释【英文标题】:TilingannotationswithMapKit【发布时间】:2011-04-0119:26:41【问题描述】:在MapKit中使用注释,您可以在地图上放置图钉,甚至自定义图像。很酷。但是,我使用的数据有数百万个位置,分布在世界各... 查看详情
华为 Map Kit React Native 不渲染地图
】华为MapKitReactNative不渲染地图【英文标题】:HuaweiMapKitReactNativenotrenderthemap【发布时间】:2021-12-1621:34:16【问题描述】:按照华为设置文档https://developer.huawei.com/consumer/en/doc/development/HMS-Plugin-Guides-V1/preparedevenv-0000001050032222-V 查看详情
使用 Map Kit 如何找到我的位置?
】使用MapKit如何找到我的位置?【英文标题】:UsingMapKithowcanIfindmylocation?【发布时间】:2011-06-1408:28:25【问题描述】:我需要在MapKit中显示我的当前位置,我该怎么做?self.map.showsUserLocation=TRUE;没有显示我的位置,而是显示地图... 查看详情
Sprite Kit 和 Pathfinding,iPad 和 iPhone 的区别?
】SpriteKit和Pathfinding,iPad和iPhone的区别?【英文标题】:SpriteKitandPathfinding,differencesbetweeniPadandiPhone?【发布时间】:2014-10-2820:05:07【问题描述】:我想知道iPad和iPhone之间是否存在一些显着差异的设置?我正在使用SpriteKit、SKD8.1和... 查看详情
iPhone Store Kit 返回无效产品 ID 错误
】iPhoneStoreKit返回无效产品ID错误【英文标题】:iPhoneStoreKitreturninginvalidproductIDerrors【发布时间】:2009-10-2719:18:30【问题描述】:我正在尝试在我的iPhone上测试应用内购买,但遇到了一个问题,即我请求信息的产品ID最终在“didRec... 查看详情
Map Kit 引脚下的字符串(mkannotationview 图像)
】MapKit引脚下的字符串(mkannotationview图像)【英文标题】:stringunderMapKitpin(mkannotationviewimage)【发布时间】:2015-03-2818:58:56【问题描述】:我正在尝试在地图上的图钉下设置名称。每个名称都应该是具体的。我没有使用默认引脚... 查看详情
iPhone 'Event Kit' - 如何为获取指定额外的搜索过滤器(超过开始/结束日期)?
】iPhone\\\'EventKit\\\'-如何为获取指定额外的搜索过滤器(超过开始/结束日期)?【英文标题】:iPhone\'EventKit\'-howcanIspecifyadditionalsearchfiltersforafetch(beyondstart/enddate)?iPhone\'EventKit\'-如何为获取指定额外的搜索过滤器(超过开始/结束... 查看详情
在 android studio 中无法获取华为 Map Kit 的 com.huawei.hms.location.LocationServices
】在androidstudio中无法获取华为MapKit的com.huawei.hms.location.LocationServices【英文标题】:Cannotgetcom.huawei.hms.location.LocationServicesforHuaweiMapKitinandroidstudio【发布时间】:2021-04-0722:52:41【问题描述】:我目前正在尝试将GoogleMaps迁移到我的an... 查看详情
iPhone In-App Purchase Store Kit 错误 -1003“无法连接到 iTunes Store”
】iPhoneIn-AppPurchaseStoreKit错误-1003“无法连接到iTunesStore”【英文标题】:iPhoneIn-AppPurchaseStoreKiterror-1003"CannotconnecttoiTunesStore"【发布时间】:2009-11-1120:02:35【问题描述】:我一直致力于添加应用内购买,并且能够使用StoreKit... 查看详情
来自 IOS App 的 Watch Kit 通知
...我想要一个简单的行为:当我在配对手表应用程序上按下iPhone上的按钮时,我想出现一个通知。这可能吗?我使用watchOS2,我的手表应用程序与iPhone应用程序完美通信,反之亦然,通过WatchConectivity 查看详情
iPhone如何进行地理定位?
】iPhone如何进行地理定位?【英文标题】:HowdoesiPhonedogeolocating?【发布时间】:2009-12-0707:37:23【问题描述】:既然有3代iPhone,它们的地理定位机制是什么(WiFi、GPS或其他方式?)?最近在做iPhone开发,想深入了解一下。(我要... 查看详情
PhoneGap + Event Kit 框架?
...想知道使用这些基于JavaScript的工具通过EventKitFramework访问iPhone日历的可能性/困难程度。感谢您的帮助。【 查看详情
如何从 iOS 的 Health Kit 中实时获取附近的心率数据?
...会话,并在HealthKit中更新了心率数据。现在,我想在我的iPhone屏幕上显示当前的心率。手表传感器更新Healthkit中的心率数据,但iPhone应用程序无法 查看详情
在远程集群上使用 Hbase 运行 Map Reduce
】在远程集群上使用Hbase运行MapReduce【英文标题】:RunningaMapReducewithHbaseonaremotecluster【发布时间】:2014-03-1707:09:46【问题描述】:我有一个包含某些数据的HBase集群。我还有一个hadoop集群,它也有某些数据。现在是否可以使用来... 查看详情
iPhone 4 与 iPhone 5 定位服务的区别?
】iPhone4与iPhone5定位服务的区别?【英文标题】:iPhone4v.iPhone5LocationServicesDifference?【发布时间】:2014-04-2620:37:36【问题描述】:我有一个使用核心位置的应用。它主要适用于iPad,但也适用于iPhone。我想使用GPS,但不想频繁更新... 查看详情
iPhone GPS 定位
】iPhoneGPS定位【英文标题】:iPhoneGPSLocation【发布时间】:2009-07-0805:53:23【问题描述】:我想在目标C中获取iPhone上的经度和纬度。谁能指导我如何以编程方式获取这些坐标?#import<Foundation/Foundation.h>@classCLLocationManager;@interface... 查看详情
iPhone 室内定位应用
】iPhone室内定位应用【英文标题】:iPhoneindoorlocationbasedapp【发布时间】:2011-10-2005:18:06【问题描述】:我正在研究如何为我的工作创建一个应用程序,允许客户下载该应用程序(最好通过应用程序商店),并使用某种wifi三角测... 查看详情