Pregunta

Tengo bastante alfileres para poner en mi mapa, así que creo que sería una buena idea agrupar esas anotaciones. No estoy realmente seguro de cómo lograr esto en iPhone, pude resolver algo con Google Maps y algunos ejemplos de JavaScript. Pero iPhone usa su MKMapView y no tengo idea de cómo agrupar anotaciones allí.

¿Alguna idea o marcos que conozcas y que seas bueno? Gracias.

¿Fue útil?

Solución

Dado que este es un problema muy común y necesitaba una solución, he escrito una subclase personalizada de MKMapView que admite la agrupación. ¡Entonces lo puse a disposición de código abierto! Puedes obtenerlo aqui: https://github.com/yinkou/ocmapview.

Gestiona la agrupación de las anotaciones y puede manejar sus puntos de vista usted mismo. No tienes que hacer nada más que copiar el OCMapView carpeta a su proyecto, cree un MKMapView en tu punta y establece su clase en OCMapView. (O crearlo y delegarlo en código como un regular MKMapView)

Otros consejos

No necesariamente necesita usar un marco de terceros porque desde iOS 4.2, MKMapView tiene un método llamado - (NSSet *)annotationsInMapRect:(MKMapRect)mapRect que puedes usar para hacer tu agrupación.

Mira el video de sesión wwdc11 'Visualizar la información geográficamente con MAPKIT'. Aproximadamente a la mitad de él explica cómo hacerlo. Pero resumiré el concepto para ti:

  • Usa dos mapas (El segundo mapa nunca se agrega a la jerarquía de vista)
  • El segundo mapa contiene todas las anotaciones (De nuevo, nunca ha dibujado)
  • Divide el área del mapa en una cuadrícula de cuadrados
  • Usar -annotationsInMapRect Método para obtener datos de anotación del mapa invisible
  • El mapa visible construye sus anotaciones a partir de estos datos del mapa invisible

enter image description here

Afortunadamente, ya no necesitas marco de terceros. iOS 11 tiene soporte de agrupación nativa.

Necesitas implementar mapView:clusterAnnotationForMemberAnnotations: método.

Obtenga más detalles en el ejemplo de Apple: https://developer.apple.com/sample-code/wwdc/2017/mapkit-sample.zip

Al usar el código de demostración de Apple, es fácil implementar el concepto de agrupación en nuestro código. Link de referencia

Simplemente podemos usar el siguiente código para la agrupación

Pasos para implementar la agrupación

Paso 1 : Lo importante es para la agrupación que usamos dos mapviews (AllannotationsMapView), uno es para referencia (AllannotationsMapView).

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

En ViewDidload

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

Paso 2 : Agregue todas las anotaciones a _allannotationsMapView, a continuación _photos están la matriz de anotaciones.

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

Paso 3 : Agregue los métodos a continuación para la agrupación, en esta fotoannotación es la anotación personalizada.Métodos 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;
            }];
        }
    }
}

Métodos de manejo de agrupación

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

Al seguir los pasos anteriores, podemos lograr la agrupación en MAPVIEW, no es necesario usar ningún código o marco de terceros. Por favor, checa el Código de muestra de Apple aquí. Avíseme si tiene alguna duda sobre esto.

¿Has mirado ADCLUSTMAPVIEW? https://github.com/applidium/adclustermapview

Lo hace precisamente solo esto.

Solo quería agrupar pines, solo mostrando su número. El siguientehttps://www.cocoacontrols.com/controls/qtree-objc se ajusta a mis expectativas.

Recientemente desemboqué en ADCLUSTMAPVIEW mencionado en otra respuesta y resolví muchos, si no todos, de los problemas asociados con el proyecto. Es un algoritmo KD-Tree y anima la agrupación.

Está disponible de código abierto aquí https://github.com/ashare80/tsclustermapview

Pruebe este marco (xMapView.framework); Ahora es compatible con iOS 8.

Este marco no necesita que cambie la estructura de su proyecto actual y se puede usar directamente en su MKMapView. Hay un archivo zip. Te da un ejemplo para agrupar 200 pines a la vez. Después de probarlo en un iPod, descubrí que es muy suave.

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

Esta biblioteca admite:

  1. agrupar diferentes categorías
  2. agrupando todas las categorías
  3. configurar su propio radio de clúster y así sucesivamente
  4. esconder o mostrar un cierto de las categorías
  5. manejar y controlar individualmente cada pin en el mapa

Hay una biblioteca bastante genial y bien mantenida para Objective-C y Swift aquí: https://github.com/bigfish24/abfrealmmapview

Se agrupa muy bien y también maneja grandes cantidades de puntos debido a su integración con Reino.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top