如何减少地图上的注释数量?

发布于 2024-08-20 10:17:19 字数 488 浏览 2 评论 0原文

我正在编写一个带有大约 900 个注释的地图视图。地图上有这么多注释会影响性能,因此我想将其减少到一次约 300 个。这些注释代表一个国家/地区的商店,因此它们往往集中在主要城市周围,然后在较小的城镇中以 2 或 3 个为一组的小群体聚集。我想减少人数,这样 2 或 3 人的团体就不会受到影响,但城市中的人数会减少(它们距离太近,无法提供有用的信息)。

在图像中,您可以看到有几个我想精简的大组(东京、名古屋和大阪)。但对于单独的引脚或成小组的引脚,我想确保它们不会被过滤。放大后,我想显示丢失的引脚。

有谁知道我可以使用一些好的代码,以便消除靠近的点,但更分散的点则保持不变?

替代文本 http://img.skitch.com/20100204-jpde6wugc94nn692k7m36gmqf1.jpg

I'm coding a map view with around 900 annotations. Having this many annotations on a map make the performance suffer, so I'd like to reduce it to about 300 at a time. The annotations are representing shops in a country, so they tend to cluster a lot around major cities, then in small groups of 2 or 3 in smaller towns. I want to reduce the numbers so that the groups of 2 or 3 are left alone, but the numbers in the city are thinned (they're so close together that they offer no useful information).

In the image you can see that there are a couple of big groups (Tokyo, Nagoya and Osaka) which I want to thin out. But with the pins on their own or in small groups, I want to make sure they don't get filtered. Once I zoom in, I want to show the missing pins.

Does anyone know of some good code that I can use, so that points which are close together are eliminated, but ones more spread out are left alone?

alt text http://img.skitch.com/20100204-jpde6wugc94nn692k7m36gmqf1.jpg

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

泅人 2024-08-27 10:17:19

一种方法是在放置新引脚之前,检查是否已在新引脚的距离 d 内放置了另一个引脚。如果有,请勿放置新引脚。您需要根据当前的缩放级别改变 d 。

您可以通过仅考虑以新引脚为中心的边界框中的引脚来减少检查的引脚数量。该框的一侧可以是 dxd 度(d 根据缩放级别而变化)。

One approach is that before placing a new pin, check if there's another pin already placed within distance d of the new pin. If there is, don't place the new pin. You need to vary d based on the current zoom level.

You can reduce the number of pins you check against by considering only pins in a bounding box centered on the new pin. The box could be d x d degrees on a side (with d varying based on zoom level).

一瞬间的火花 2024-08-27 10:17:19

如果可以选择商业第三方库,请查看 Superpin(许可证费用为 199 美元)。它是一个 iOS 框架,内部使用四叉树进行注释存储并执行基于网格的聚类。该算法非常快,附带的示例应用程序显示了世界各地的机场(超过 30k+ 注释),并且在 3G iPhone 上运行得非常流畅。

您可能还想检查 http://revolver.be/blog/mapkit-clustering- with-ios/,另一个现成的解决方案,对于非商业项目是免费的。

免责声明:我是 Superpin 开发者之一

If a commercial, third party library is an option, check out Superpin (license costs $199). It is an iOS Framework that internally uses quadtrees for annotation storage and performs grid-based clustering. The algorithm is quite fast, the included sample app is showing airports of the world (more than 30k+ annotations) and it's running quite smooth on an 3G iPhone.

You may also want to check http://revolver.be/blog/mapkit-clustering-with-ios/, another ready-made solution, which is free for non-commerical projects.

Disclaimer: I'm one of the Superpin developers

书间行客 2024-08-27 10:17:19

我能想到的两个选项:

  • 如果您有兴趣点(例如城市),您可以简单地将所有图钉按它们最接近的 POI 以较低的缩放级别进行分组。
  • 您可以使用 K-means 聚类 将引脚分组为簇并用中点引脚表示它们。

Two options I can think of:

  • If you have points of interest to work with (Eg, cities), you can simply group all pins by the POI they're closest to, in lower zoom levels.
  • You can use K-means clustering to group pins into clusters and represent them with a midpoint pin.
娇柔作态 2024-08-27 10:17:19

下面是一个代码片段,它获取 MKAnnotation 的坐标,将其转换为相对于 MKMapView 的 CGPoint,并记录该 CGPoint 处的基础视图。

CGPoint pinPoint = [mapView convertCoordinate:pinView.annotation.coordinate toPointToView:mapView];
NSLog(@"pointing to %@", [[mapView hitTest:pinPoint withEvent:nil] description]);

将其放入循环中以迭代所有引脚。如果底层视图是另一个 MKAnnotation 实例,则隐藏该引脚。

if([[mapView hitTest:pinPoint withEvent:nil] isKindOfClass:[FFMapPinView class]])
    pinView.hidden = YES;

为了使其正常工作,您需要对pinsArray进行排序,以便索引0是最前面的引脚。

Here's a code snippet that takes an MKAnnotation's coordinates, convert it to a CGPoint relative to the MKMapView, and logs what's the underlying view at that CGPoint.

CGPoint pinPoint = [mapView convertCoordinate:pinView.annotation.coordinate toPointToView:mapView];
NSLog(@"pointing to %@", [[mapView hitTest:pinPoint withEvent:nil] description]);

Put that inside a loop that iterates through all your pins. If the underlying view is another MKAnnotation instance, then hide that pin.

if([[mapView hitTest:pinPoint withEvent:nil] isKindOfClass:[FFMapPinView class]])
    pinView.hidden = YES;

For this to work properly, you need the pinsArray to be ordered so that index 0 is the frontmost pin.

心碎无痕… 2024-08-27 10:17:19

考虑到人口稠密地区的许多图钉将位于同一条街道上,您可以考虑制作“超级图钉”,列出特定街道上的图钉,而不是每个单独的地址。

-S!

Considering that many pins in densely-populated areas will be on the same street, you could consider making "super pins" that list the pins on a particular street, instead of on each individual address.

-S!

孤独难免 2024-08-27 10:17:19

我知道,聚会迟到了,但你可能会发现这个例程很有用。它来自此文件,它是FOSS 项目的一部分。

/**************************************************************//**
 \brief This function looks for meetings in close proximity to each
        other, and collects them into "red markers."
 \returns an NSArray of BMLT_Results_MapPointAnnotation objects.
 *****************************************************************/
- (NSArray *)mapMeetingAnnotations:(NSArray *)inResults ///< This is an NSArray of BMLT_Meeting objects. Each one represents a meeting.
{
#ifdef DEBUG
    NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Checking %d Meetings.", [inResults count]);
#endif
    NSMutableArray  *ret = nil;

    NSInteger   displayIndex = 1;

    if ( [inResults count] )
        {
        NSMutableArray  *points = [[NSMutableArray alloc] init];
        for ( BMLT_Meeting *meeting in inResults )
            {
#ifdef DEBUG
            NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Checking Meeting \"%@\".", [meeting getBMLTName]);
#endif
            CLLocationCoordinate2D  meetingLocation = [meeting getMeetingLocationCoords].coordinate;
            CGPoint meetingPoint = [(MKMapView *)[self view] convertCoordinate:meetingLocation toPointToView:nil];
            CGRect  hitTestRect = CGRectMake(meetingPoint.x - BMLT_Meeting_Distance_Threshold_In_Pixels,
                                             meetingPoint.y - BMLT_Meeting_Distance_Threshold_In_Pixels,
                                             BMLT_Meeting_Distance_Threshold_In_Pixels * 2,
                                             BMLT_Meeting_Distance_Threshold_In_Pixels * 2);

            BMLT_Results_MapPointAnnotation *annotation = nil;
#ifdef DEBUG
            NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Meeting \"%@\" Has the Following Hit Test Rect: (%f, %f), (%f, %f).", [meeting getBMLTName], hitTestRect.origin.x, hitTestRect.origin.y, hitTestRect.size.width, hitTestRect.size.height);
#endif

            for ( BMLT_Results_MapPointAnnotation *annotationTemp in points )
                {
                CGPoint annotationPoint = [(MKMapView *)[self view] convertCoordinate:annotationTemp.coordinate toPointToView:nil];
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Comparing the Following Annotation Point: (%f, %f).", annotationPoint.x, annotationPoint.y);
#endif

                if ( !([[annotationTemp getMyMeetings] containsObject:meeting]) && CGRectContainsPoint(hitTestRect, annotationPoint) )
                    {
#ifdef DEBUG
                    for ( BMLT_Meeting *t_meeting in [annotationTemp getMyMeetings] )
                        {
                        NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Meeting \"%@\" Is Close to \"%@\".", [meeting getBMLTName], [t_meeting getBMLTName]);
                        }
#endif
                    annotation = annotationTemp;
                    }
                }

            if ( !annotation )
                {
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations -This meeting gets its own annotation.");
#endif
                NSArray *meetingsAr = [[NSArray alloc] initWithObjects:meeting, nil];  
                annotation = [[BMLT_Results_MapPointAnnotation alloc] initWithCoordinate:[meeting getMeetingLocationCoords].coordinate andMeetings:meetingsAr andIndex:0];
                [annotation setDisplayIndex:displayIndex++];
                [points addObject:annotation];
                }
            else
                {
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations -This meeting gets lumped in with others.");
#endif
                [annotation addMeeting:meeting];
                }

            if ( annotation )
                {
                if ( !ret )
                    {
                    ret = [[NSMutableArray alloc] init];
                    }

                if ( ![ret containsObject:annotation] )
                    {
                    [ret addObject:annotation];
                    }
                }
            }
        }

    // This is the black marker.
    BMLT_Results_MapPointAnnotation *annotation = [[BMLT_Results_MapPointAnnotation alloc] initWithCoordinate:[[BMLTAppDelegate getBMLTAppDelegate] searchMapMarkerLoc] andMeetings:nil andIndex:0];

    if ( annotation )
        {
        [annotation setTitle:NSLocalizedString(@"BLACK-MARKER-TITLE", nil)];
        [ret addObject:annotation];
        }

    return ret;
}

您可以在该应用的已发布版本中查看其实际效果。

邻近的会议被收集到红色注释中,这会打开一个列表。

Late to the party, I know, but you may find this routine useful. It comes from this file, which is part of a FOSS project.

/**************************************************************//**
 \brief This function looks for meetings in close proximity to each
        other, and collects them into "red markers."
 \returns an NSArray of BMLT_Results_MapPointAnnotation objects.
 *****************************************************************/
- (NSArray *)mapMeetingAnnotations:(NSArray *)inResults ///< This is an NSArray of BMLT_Meeting objects. Each one represents a meeting.
{
#ifdef DEBUG
    NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Checking %d Meetings.", [inResults count]);
#endif
    NSMutableArray  *ret = nil;

    NSInteger   displayIndex = 1;

    if ( [inResults count] )
        {
        NSMutableArray  *points = [[NSMutableArray alloc] init];
        for ( BMLT_Meeting *meeting in inResults )
            {
#ifdef DEBUG
            NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Checking Meeting \"%@\".", [meeting getBMLTName]);
#endif
            CLLocationCoordinate2D  meetingLocation = [meeting getMeetingLocationCoords].coordinate;
            CGPoint meetingPoint = [(MKMapView *)[self view] convertCoordinate:meetingLocation toPointToView:nil];
            CGRect  hitTestRect = CGRectMake(meetingPoint.x - BMLT_Meeting_Distance_Threshold_In_Pixels,
                                             meetingPoint.y - BMLT_Meeting_Distance_Threshold_In_Pixels,
                                             BMLT_Meeting_Distance_Threshold_In_Pixels * 2,
                                             BMLT_Meeting_Distance_Threshold_In_Pixels * 2);

            BMLT_Results_MapPointAnnotation *annotation = nil;
#ifdef DEBUG
            NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Meeting \"%@\" Has the Following Hit Test Rect: (%f, %f), (%f, %f).", [meeting getBMLTName], hitTestRect.origin.x, hitTestRect.origin.y, hitTestRect.size.width, hitTestRect.size.height);
#endif

            for ( BMLT_Results_MapPointAnnotation *annotationTemp in points )
                {
                CGPoint annotationPoint = [(MKMapView *)[self view] convertCoordinate:annotationTemp.coordinate toPointToView:nil];
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Comparing the Following Annotation Point: (%f, %f).", annotationPoint.x, annotationPoint.y);
#endif

                if ( !([[annotationTemp getMyMeetings] containsObject:meeting]) && CGRectContainsPoint(hitTestRect, annotationPoint) )
                    {
#ifdef DEBUG
                    for ( BMLT_Meeting *t_meeting in [annotationTemp getMyMeetings] )
                        {
                        NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations - Meeting \"%@\" Is Close to \"%@\".", [meeting getBMLTName], [t_meeting getBMLTName]);
                        }
#endif
                    annotation = annotationTemp;
                    }
                }

            if ( !annotation )
                {
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations -This meeting gets its own annotation.");
#endif
                NSArray *meetingsAr = [[NSArray alloc] initWithObjects:meeting, nil];  
                annotation = [[BMLT_Results_MapPointAnnotation alloc] initWithCoordinate:[meeting getMeetingLocationCoords].coordinate andMeetings:meetingsAr andIndex:0];
                [annotation setDisplayIndex:displayIndex++];
                [points addObject:annotation];
                }
            else
                {
#ifdef DEBUG
                NSLog(@"BMLTMapResultsViewController mapMeetingAnnotations -This meeting gets lumped in with others.");
#endif
                [annotation addMeeting:meeting];
                }

            if ( annotation )
                {
                if ( !ret )
                    {
                    ret = [[NSMutableArray alloc] init];
                    }

                if ( ![ret containsObject:annotation] )
                    {
                    [ret addObject:annotation];
                    }
                }
            }
        }

    // This is the black marker.
    BMLT_Results_MapPointAnnotation *annotation = [[BMLT_Results_MapPointAnnotation alloc] initWithCoordinate:[[BMLTAppDelegate getBMLTAppDelegate] searchMapMarkerLoc] andMeetings:nil andIndex:0];

    if ( annotation )
        {
        [annotation setTitle:NSLocalizedString(@"BLACK-MARKER-TITLE", nil)];
        [ret addObject:annotation];
        }

    return ret;
}

You can see it in action in the released version of the app.

Meetings in close proximity are gathered into red annotations, which open a list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文