Question

J'ai beaucoup de broches pour mettre sur ma carte, donc je pense que ce serait une bonne idée de regrouper ces annotations. Je ne suis pas vraiment sûr de savoir comment atteindre cet objectif sur l'iPhone, j'ai pu en tirer quelque chose avec google maps et quelques exemples de javascript. Mais l'iPhone utilise son mkmapview et je ne sais pas comment les annotations de cluster là-dedans.

Toutes les idées ou les cadres que vous connaissez et sont bons? Merci.

Était-ce utile?

La solution

Puisque c'est un problème très commun et je besoin d'une solution que j'ai écrit une sous-classe personnalisée de MKMapView qui prend en charge les clusters. Ensuite, je l'ai fait open source disponible! Vous pouvez l'obtenir ici: https://github.com/yinkou/OCMapView .

Il gère le regroupement des annotations et vous pouvez gérer leurs vues par vous-même. Vous ne devez pas faire quoi que ce soit, mais pour copier le dossier OCMapView à votre projet, créez un MKMapView dans votre plume et définissez sa classe à OCMapView. (Ou créer et déléguer dans le code comme un MKMapView régulier)

Autres conseils

Vous ne doit pas nécessairement utiliser un cadre 3ème partie parce que depuis iOS 4.2, MKMapView a une méthode appelée - (NSSet *)annotationsInMapRect:(MKMapRect)mapRect que vous pouvez utiliser pour faire votre cluster.

Découvrez la session WWDC11 vidéo « Informations Visualizing Géographiquement avec MapKit ». À mi-chemin à travers elle explique comment le faire. Mais je vais résumer le concept pour vous:

  • Utilisez deux cartes ( seconde carte est jamais ajouté à la hiérarchie de la vue )
  • Deuxième carte contient toutes les annotations ( à nouveau, il n'a jamais tiré )
  • fracture de la zone de la carte dans une grille de carrés
  • Utilisez la méthode de -annotationsInMapRect pour obtenir des données d'annotation de carte invisible
  • Visible carte construit ses annotations à partir de ces données de carte invisible

entrer image description ici

Heureusement, vous n'avez pas besoin cadre 3ème partie est plus. iOS 11 a un support natif de clustering.

Vous devez implémenter mapView:clusterAnnotationForMemberAnnotations: méthode.

Obtenir plus de détails dans l'exemple d'Apple: https: // developer.apple.com/sample-code/wwdc/2017/MapKit-Sample.zip

En utilisant le code d'Apple de démonstration, il est facile à mettre en œuvre notre concept de regroupement dans le code.

Il suffit que nous pouvons utiliser le code suivant pour le clustering

Les étapes pour mettre en œuvre le regroupement

Etape 1: La chose importante est pour le regroupement, nous utilisons deux mapviews (allAnnotationsMapView,), on est pour référence (allAnnotationsMapView)

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

viewDidLoad

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

Etape 2:. Ajouter toutes les annotations au _allAnnotationsMapView, en dessous _photos sont le tableau annotations

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

Etape 3: Ajouter ci-dessous les méthodes de regroupement, dans ce PhotoAnnotation est l'annotation personnalisée. méthodes de 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;
            }];
        }
    }
}

regroupement des méthodes de manipulation

    - (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;
    }
}

En suivant les étapes ci-dessus, nous pouvons réaliser le regroupement sur MapView, il est nécessaire d'utiliser un code tiers ou d'un cadre. S'il vous plaît vérifier Code Apple échantillon ici. S'il vous plaît laissez-moi savoir si vous avez des doutes à ce sujet.

Avez-vous regardé ADClusterMapView? https://github.com/applidium/ADClusterMapView

Il fait exactement tout cela.

Je voulais juste broches clustering, juste montrer son numéro. La suivante https://www.cocoacontrols.com/controls/qtree-objc correspond à mes attentes.

J'ai récemment bifurquait de ADClusterMapView mentionné dans une autre réponse et résolu beaucoup, sinon la totalité, des problèmes associés au projet. Il est un algorithme kd-tree et anime le regroupement.

Il est open source ici https://github.com/ashare80/TSClusterMapView

Essayez ce cadre (XMapView.framework); il prend en charge maintenant iOS 8.

Ce cadre n'a pas besoin de vous pour changer votre structure actuelle du projet et il peut être utilisé directement à votre MKMapView. Il y a un fichier zip. Il vous donne un exemple pour regrouper 200 broches à la fois. Après avoir testé dans un iPod Je l'ai trouvé est très lisse.

http://www.xuliu.info/xMapView.html

Ce soutien de la bibliothèque:

  1. regroupement différentes catégories
  2. regroupement de toutes les catégories
  3. la configuration de votre propre rayon de cluster et ainsi de suite
  4. cacher ou montrer une certaine catégories
  5. individuellement manipuler et contrôler chaque broche dans la carte

Il y a un assez cool et d'une bibliothèque bien entretenue pour les Objective-C et Swift ici: https: // GitHub. com / bigfish24 / ABFRealmMapView

Il ne regroupement vraiment bien et gère également de grandes quantités de points en raison de son intégration avec royaume .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top