2011-08-20 5 views
11

Tengo una vista de mapa que tiene muchas anotaciones, la mayoría de ellas muy cercanas entre sí. Lo que quiero hacer es similar a la aplicación de fotos en iOS, donde las anotaciones se agrupan cuando están demasiado cerca unas de otras y cada vez que se aleja, se agrupan si están demasiado separadas.Agrupado/Desagrupar MKAnnotación según el nivel de zoom (y mantenerlo rápido)

Ya he visto this question pero la respuesta dada no es realmente lo que estaba buscando.

Estoy buscando una biblioteca o un algoritmo que pueda implementar yo mismo.

Respuesta

5

Eche un vistazo a here para ver la respuesta completa. Contiene ambas implementaciones para MapKit y Google Maps. El código está inspirado en el video WWDC 2011 y funciona muy bien en mi aplicación.

Publiqué el código para MapKit aquí de todos modos, pero hay algunas observaciones útiles en mi otra respuesta.

- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer { 
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){ 
     [self updateVisibleAnnotations]; 
    } 
} 
- (void)updateVisibleAnnotations { 
    static float marginFactor = 2.0f; 
    static float bucketSize = 50.0f; 
    MKMapRect visibleMapRect = [self.mapView visibleMapRect]; 
    MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height); 

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

    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; 

    gridMapRect.origin.y = startY; 
    while(MKMapRectGetMinY(gridMapRect) <= endY) { 
     gridMapRect.origin.x = startX; 
     while (MKMapRectGetMinX(gridMapRect) <= endX) { 
      NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect]; 
      NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect]; 

      NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) { 
       BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]]; 
       BOOL shouldBeMerged = NO; 
       if (isPointMapItem) { 
        PointMapItem *pointItem = (PointMapItem *)obj; 
        shouldBeMerged = pointItem.shouldBeMerged; 
       } 
       return shouldBeMerged; 
      }] mutableCopy]; 
      NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) { 
       BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]]; 
       BOOL shouldBeMerged = NO; 
       if (isPointMapItem) { 
        PointMapItem *pointItem = (PointMapItem *)obj; 
        shouldBeMerged = pointItem.shouldBeMerged; 
       } 
       return isPointMapItem && !shouldBeMerged; 
      }]; 
      for (PointMapItem *item in notMergedAnnotationsInBucket) { 
       [self.mapView addAnnotation:item]; 
      } 

      if(filteredAnnotationsInBucket.count > 0) { 
       PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket]; 
       [filteredAnnotationsInBucket removeObject:annotationForGrid]; 
       annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects]; 
       [self.mapView addAnnotation:annotationForGrid]; 
       //force reload of the image because it's not done if annotationForGrid is already present in the bucket!! 
       MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid]; 
       NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO]; 
       UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)]; 
       [countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]]; 
       [countLabel setTextColor:[UIColor whiteColor]]; 
       [annotationView addSubview:countLabel]; 
       imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO]; 
       annotationView.image = [UIImage imageNamed:imageName]; 

       if (filteredAnnotationsInBucket.count > 0){ 
        [self.mapView deselectAnnotation:annotationForGrid animated:NO]; 
       } 
       for (PointMapItem *annotation in filteredAnnotationsInBucket) { 
        [self.mapView deselectAnnotation:annotation animated:NO]; 
        annotation.clusterAnnotation = annotationForGrid; 
        annotation.containedAnnotations = nil; 
        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; 
    } 
} 

- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations { 
    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]; 
    } 
    MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(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; 
    }]; 
    return [sortedAnnotations objectAtIndex:0]; 
} 
+0

¿tiene muestras para esto? – karthikeyan

10

Uno de los videos de la sesión de la WWDC 2011 de Apple muestra cómo hacer exactamente esto. Vaya a https://developer.apple.com/videos/wwdc/2011/ (debe estar registrado como desarrollador) y vaya al video titulado "Visualizando información geográficamente con MapKit". La idea básica es usar una vista de mapa fuera de pantalla para guardar todas sus anotaciones y copiarlas en la vista de mapa en pantalla según sea necesario, asegurándose de que no esté tratando de mostrar demasiadas a la vez. Incluso hace la animación ingeniosa con las anotaciones a medida que haces zoom.

+1

¿Dónde puedo obtener el código fuente de la demostración en el video? –

+0

Eso no lo sé. – shawkinaw

Cuestiones relacionadas