2010-01-12 9 views
23

En cuanto iPhone Map Kit puntitos de racimo:cúmulo iPhone Mapa Kit señala

tengo 1000 de marcas que quiero mostrar en el mapa, pero es sólo demasiados para manejar por lo que quiero agruparlas.

¿Hay marcos disponibles o prueba de conceptos? ¿Que esto es posible o ya se ha hecho?

+0

Ha visto la demo terremoto? (Creo) Tenían un pin personalizado en el que tenía un círculo que estaba agrupado y el tamaño del círculo aumentaba en función de la cantidad de pines dentro del clúster, así como de sus escalas combinadas de Richter. –

+1

es esta la demostración? http://www.switchonthecode.com/tutorials/building-an-earthquake-monitor-for-iphone-using-mapkit –

Respuesta

13

Puede utilizar REVClusterMap a agruparse

+1

Funcionó para mí después de integrar el awakeFromNib: solución de los comentarios de esa publicación de blog. La agrupación podría ser un OMI un poco más rápido, pero funciona. –

+0

El enlace ya no es válido –

+0

... pero se puede encontrar aquí en su lugar: https://github.com/RVLVR/REVClusterMap –

-1

Creo que Foto Brisko (enlace de iTunes) hace esto.
No creo que haya un marco Cocoa Touch para él.

6

Esto podría ser un poco como el uso de una sierra eléctrica para cortar el césped, pero aquí es un extracto de Algorithms in a Nutshell

Creación de una KD-árbol ...

public class KDFactory { 
    // Known comparators for partitioning points along dimensional axes. 
    private static Comparator<IMultiPoint> comparators[ ] ; 
    // Recursively construct KDTree using median method on input points. 
    public static KDTree generate (IMultiPoint [ ] points) { 
    if (points. length == 0) { return null; } 
    // median will be the root. 
    int maxD = points[ 0] . dimensionality(); 
    KDTree tree = new KDTree(maxD) ; 
    // Make dimensional comparators that compare points by ith dimension 
    comparators = new Comparator[ maxD+1] ; 
    for (int i = 1; i <= maxD; i++) { 
     comparators[ i] = new DimensionalComparator(i) ; 
    } 
    tree. setRoot(generate (1, maxD, points, 0, points. length-1)) ; 
    return tree; 
    } 

    // generate the node for the d-th dimension (1 <= d <= maxD) 
    // for points[ left, right] 
    private static DimensionalNode generate (int d, int maxD, 
              IMultiPoint points[ ] , 
              int left, int right) { 
    // Handle the easy cases first 
    if (right < left) { return null; } 
    if (right == left) { return new DimensionalNode (d, points[ left]) ; } 
    // Order the array[ left, right] so the mth element will be the median 
    // and the elements prior to it will all be <=, though they won' t 
    // necessarily be sorted; similarly, the elements after will all be >= 
    int m = 1+(right-left) /2; 
    Selection. select(points, m, left, right, comparators[ d]) ; 
    // Median point on this dimension becomes the parent 
    DimensionalNode dm = new DimensionalNode (d, points[ left+m-1]) ; 
    // update to the next dimension, or reset back to 1 
    if (++d > maxD) { d = 1; } 
    // recursively compute left and right sub-trees, which translate 
    // into ' below' and ' above' for n-dimensions. 
    dm. setBelow(maxD, generate (d, maxD, points, left, left+m-2)) ; 
    dm. setAbove(maxD, generate (d, maxD, points, left+m, right)) ; 
    return dm; 
    } 
} 

Encontrar vecinos más cercanos mejor: O (log n) peor O (n)

// method in KDTree 
public IMultiPoint nearest (IMultiPoint target) { 
    if (root == null) return null; 
    // find parent node to which target would have been inserted. This is our 
    // best shot at locating closest point; compute best distance guess so far 
    DimensionalNode parent = parent(target) ; 
    IMultiPoint result = parent. point; 
    double smallest = target. distance(result) ; 
    // now start back at the root, and check all rectangles that potentially 
    // overlap this smallest distance. If better one is found, return it. 
    double best[ ] = new double[ ] { smallest }; 
    double raw[ ] = target. raw(); 
    IMultiPoint betterOne = root. nearest (raw, best) ; 
    if (betterOne ! = null) { return betterOne; } 
    return result; 
} 

// method in DimensionalNode. min[ 0] contains best computed shortest distance. 
IMultiPoint nearest (double[ ] rawTarget, double min[ ]) { 
    // Update minimum if we are closer. 
    IMultiPoint result = null; 
    // If shorter, update minimum 
    double d = shorter(rawTarget, min[ 0]) ; 
    if (d >= 0 && d < min[ 0]) { 
     min[ 0] = d; 
     result = point; 
    } 
    // determine if we must dive into the subtrees by computing direct 
    // perpendicular distance to the axis along which node separates 
    // the plane. If d is smaller than the current smallest distance, 
    // we could "bleed" over the plane so we must check both. 
    double dp = Math. abs(coord - rawTarget[ dimension-1]) ; 
    IMultiPoint newResult = null; 
    if (dp < min[ 0]) { 
     // must dive into both. Return closest one. 
     if (above ! = null) { 
     newResult = above. nearest (rawTarget, min) ; 
     if (newResult ! = null) { result = newResult; } 
     } 
     if (below ! = null) { 
     newResult = below. nearest(rawTarget, min) ; 
     if (newResult ! = null) { result = newResult; } 
     } 
    } else { 
     // only need to go in one! Determine which one now. 
     if (rawTarget[ dimension-1] < coord) { 
     if (below ! = null) { 
      newResult = below. nearest (rawTarget, min) ; 
     } 
     } else { 
     if (above ! = null) { 
      newResult = above. nearest (rawTarget, min) ; 
     } 
     } 
     // Use smaller result, if found. 
     if (newResult ! = null) { return newResult; } 
    } 
    return result; 
    } 

Más en KD-Trees at Wikipedia

8

Nota: Este es un producto comercial que estoy afiliado con, pero resuelve este problema mismo.

Resolví este problema en algunas de mis aplicaciones y decidí extraerlo en un marco reutilizable. Se llama Superpin y es un Marco de iOS (comercial, con licencia cuesta $ 149) que internamente utiliza cuadrículas para el almacenamiento de anotaciones y realiza clustering basado en grillas. El algoritmo es bastante rápido, la aplicación de muestra incluida muestra los aeropuertos del mundo (más de 30k + anotaciones) y funciona sin problemas en un iPhone 3G.

+1

Superpin es definitivamente el camino a seguir, si puede pagarlo. Implementación simple, limpia y buena. Hizo una gran diferencia en los mapas en una de nuestras aplicaciones que contienen alrededor de 500 anotaciones. – theTRON

+0

hey @esad ¿de dónde sacaste esa increíble lista de aeropuertos del mundo? ¡Gracias! – Andres

+0

@Andres http://www.ourairports.com/ proporciona datos sobre los aeropuertos del mundo bajo dominio público – esad

6

Probé los otros sugirieron aquí, y también he encontrado OCMapView que ha trabajado el mejor.

Es gratis y permite agrupar anotaciones fácilmente, que es lo que necesitaba. Es un poco más nuevo & más actualizado que Revolver y para mí es más fácil de implementar.

0

Inspirado en el video WWDC 2011, este código funciona muy bien para mí. Quizás no sea el más rápido de todos propuesto aquí, pero es gratis y definitivamente es el más simple.

Básicamente utiliza 2 mapas. Uno está oculto y contiene todas las anotaciones (allAnotationMapView en mi código). Uno es visible y muestra solo los clústeres o las anotaciones si son únicos (mapView en mi código).

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

Recientemente tuve que implementar la agrupación de anotaciones con MapKit. Las soluciones mencionadas anteriormente son buenas, dependiendo de su caso de uso.Terminé yendo con FBAnnotationClustering (Objective-C) porque era gratis, y tenía un montón de estrellas y algunos problemas en github:

https://github.com/infinum/FBAnnotationClustering

La aplicación que estaba trabajando estaba muy centrada en un mapa, por lo que tenía sentido traducir FBAnnotationClustering a Swift. Aquí hay una publicación de blog sobre el enfoque, que incluye un enlace al proyecto de muestra en github.

http://ribl.co/blog/2015/05/28/map-clustering-with-swift-how-we-implemented-it-into-the-ribl-ios-app/

+0

Estoy utilizando su puerto para navegar rápidamente, podría echarle un vistazo a esta pregunta, gracias: http: //stackoverflow.com/questions/37747381/mapkit-display-annotation-clusters-and-along-with-non-clustered-annotations – Eduardo

Cuestiones relacionadas