Устранить наложение контактов в MKMapView - PullRequest
2 голосов
/ 12 ноября 2010

Я работаю с приложением MKMapView. Мне нужно уточнить, можно ли устранить контакт OverLap в MKMapView? Потому что в некоторых местах отображается большое количество выводов. Мне трудно идентифицировать булавки.

Ответы [ 4 ]

4 голосов
/ 30 июля 2011

Если у вас есть учетная запись разработчика Apple, я настоятельно рекомендую получить видео Session 111 с 2011 WWDC Conference Sessions под названием «Географическая визуализация информации с помощью MapKit». Один из сегментов, в частности, посвящен тому, как кластеризовать контент из больших наборов данных, чтобы позволить вам группировать или разгруппировать выводы на основе плотности при различных уровнях масштабирования.

Их пример элегантно прост, но суть проблемы заключается в том, что вы хотите заменить группу перекрывающихся выводов одним выводом, а при увеличении масштаба один вывод снова разделится на отдельные выводы.

То, как и когда вы решаете группировать вещи, может значительно различаться. Решение Apple просто подразделяет карту на сетку, и любое поле с более чем одним контактом приводит к группе. Вы также можете использовать алгоритмический подход, такой как использование алгоритма kMeansCluster, который невероятно прост, и вы можете передать все свои аннотации через алгоритм и получить массив групп с другой стороны, логически организованных.

Оттуда вопрос отслеживания всех отдельных выводов и того, как они сгруппированы при увеличении и уменьшении масштаба. Вы будете отображать только одну аннотацию для каждой группы или отдельных оставшихся штифтов. Также возможно анимировать переходы по мере увеличения и уменьшения карты, чтобы вы могли визуально усилить происходящее.

Моя собственная техника слишком тесно связана с подходом Apple, чтобы я мог публиковать здесь, поэтому я надеюсь, что вы сможете получить доступ к вышеупомянутому видео, которое охватывает почти все эти моменты.

1 голос
/ 19 августа 2015

Довольно просто реализовать собственную структуру кластеризации аннотаций.Вот пример основного, который вы можете сослаться здесь .

1 голос
/ 22 июня 2015

Для этого вам необходимо реализовать концепцию кластеризации на вашей карте. Используя демонстрационный код Apple, легко внедрить концепцию кластеризации в нашем коде. Ссылочная ссылка

Просто мы можем использовать следующий код для кластеризации

Шаги по внедрению кластеризации

Шаг 1: Важно, чтобы для кластеризации мы использовали два вида карты (allAnnotationsMapView,), один для справки (allAnnotationsMapView).

@property (nonatomic, strong) MKMapView *allAnnotationsMapView;
@property (nonatomic, strong) IBOutlet MKMapView *mapView;

В viewDidLoad

    _allAnnotationsMapView = [[MKMapView alloc] initWithFrame:CGRectZero];

Шаг 2: Добавьте все аннотации в _allAnnotationsMapView, ниже _photos - массив аннотаций.

        [_allAnnotationsMapView addAnnotations:_photos];
        [self updateVisibleAnnotations];

Шаг 3: Добавьте ниже методы кластеризации, в этой PhotoAnnotation есть пользовательская аннотация. Методы MapViewDelegate

 - (void)mapView:(MKMapView *)aMapView regionDidChangeAnimated:(BOOL)animated {

    [self updateVisibleAnnotations];
}

- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views {

    for (MKAnnotationView *annotationView in views) {
        if (![annotationView.annotation isKindOfClass:[PhotoAnnotation class]]) {
            continue;
        }

        PhotoAnnotation *annotation = (PhotoAnnotation *)annotationView.annotation;

        if (annotation.clusterAnnotation != nil) {
            // animate the annotation from it's old container's coordinate, to its actual coordinate
            CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
            CLLocationCoordinate2D containerCoordinate = annotation.clusterAnnotation.coordinate;

            // since it's displayed on the map, it is no longer contained by another annotation,
            // (We couldn't reset this in -updateVisibleAnnotations because we needed the reference to it here
            // to get the containerCoordinate)
            annotation.clusterAnnotation = nil;

            annotation.coordinate = containerCoordinate;

            [UIView animateWithDuration:0.3 animations:^{
                annotation.coordinate = actualCoordinate;
            }];
        }
    }
}

методы кластеризации

    - (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations {

    // first, see if one of the annotations we were already showing is in this mapRect
    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];
    }

    // otherwise, sort the annotations based on their distance from the center of the grid square,
    // then choose the one closest to the center to show
    MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMidX(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;
    }];

    PhotoAnnotation *photoAnn = sortedAnnotations[0];
    NSLog(@"lat long %f %f", photoAnn.coordinate.latitude, photoAnn.coordinate.longitude);

    return sortedAnnotations[0];
}

- (void)updateVisibleAnnotations {

    // This value to controls the number of off screen annotations are displayed.
    // A bigger number means more annotations, less chance of seeing annotation views pop in but decreased performance.
    // A smaller number means fewer annotations, more chance of seeing annotation views pop in but better performance.
    static float marginFactor = 2.0;

    // Adjust this roughly based on the dimensions of your annotations views.
    // Bigger numbers more aggressively coalesce annotations (fewer annotations displayed but better performance).
    // Numbers too small result in overlapping annotations views and too many annotations on screen.
    static float bucketSize = 60.0;

    // find all the annotations in the visible area + a wide margin to avoid popping annotation views in and out while panning the map.
    MKMapRect visibleMapRect = [self.mapView visibleMapRect];
    MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);

    // determine how wide each bucket will be, as a MKMapRect square
    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);

    // condense annotations, with a padding of two squares, around the visibleMapRect
    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;

    // for each square in our grid, pick one annotation to show
    gridMapRect.origin.y = startY;
    while (MKMapRectGetMinY(gridMapRect) <= endY) {
        gridMapRect.origin.x = startX;

        while (MKMapRectGetMinX(gridMapRect) <= endX) {
            NSSet *allAnnotationsInBucket = [self.allAnnotationsMapView annotationsInMapRect:gridMapRect];
            NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];

            // we only care about PhotoAnnotations
            NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
                return ([obj isKindOfClass:[PhotoAnnotation class]]);
            }] mutableCopy];

            if (filteredAnnotationsInBucket.count > 0) {
                PhotoAnnotation *annotationForGrid = (PhotoAnnotation *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];

                [filteredAnnotationsInBucket removeObject:annotationForGrid];

                // give the annotationForGrid a reference to all the annotations it will represent
                annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];

                [self.mapView addAnnotation:annotationForGrid];

                for (PhotoAnnotation *annotation in filteredAnnotationsInBucket) {
                    // give all the other annotations a reference to the one which is representing them
                    annotation.clusterAnnotation = annotationForGrid;
                    annotation.containedAnnotations = nil;

                    // remove annotations which we've decided to cluster
                    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;
    }
}

Следуя вышеперечисленным шагам, мы можем добиться кластеризации на карте, нет необходимости использовать какой-либо сторонний код или инфраструктуру. Пожалуйста, проверьте образец кода Apple здесь. Пожалуйста, дайте мне знать, если у вас есть какие-либо сомнения по этому поводу.

0 голосов
/ 18 ноября 2010

Если ваши контакты наложены друг на друга, значит, для вашего места высокий уровень масштабирования. Можно подумать об удалении некоторых аннотаций на этом уровне масштабирования до тех пор, пока у вас не будет перекрытия аннотаций, и при масштабировании вы можете добавить аннотации, чтобы между аннотациями было достаточно места.

...