How can I reduce the number of annotations on a map?

后端 未结 6 1467
时光说笑
时光说笑 2020-12-13 11:03

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

相关标签:
6条回答
  • 2020-12-13 11:04

    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).

    0 讨论(0)
  • 2020-12-13 11:08

    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.
    0 讨论(0)
  • 2020-12-13 11:08

    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.

    0 讨论(0)
  • 2020-12-13 11:10

    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!

    0 讨论(0)
  • 2020-12-13 11:16

    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

    0 讨论(0)
  • 2020-12-13 11:31

    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.

    0 讨论(0)
提交回复
热议问题