rica otro esta donde dispositivo desde costa como buscar ajustes agregar activar activado iphone google-maps annotations mapkit markerclusterer

otro - Puntos de localización del clúster de iPhone Map Kit



donde esta settings en iphone (8)

En cuanto a los puntos clave del clúster de iPhone Map Kit :

Tengo miles de marcas que quiero mostrar en el mapa, pero son demasiadas para manejar, así que quiero agruparlas.

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


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


Esto puede ser un poco como usar una motosierra para cortar el césped, pero aquí hay un extracto de Algorithms in a Nutshell

Creando un árbol KD ...

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 mejor a los vecinos más cercanos: 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 sobre KD-Trees en Wikipedia


Inspirado por 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 usa 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]; }



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 muchas estrellas y pocos problemas en github:

https://github.com/infinum/FBAnnotationClustering

La aplicación en la que estaba trabajando estaba muy centrada en el mapa, por lo que tenía sentido traducir FBAnnotationClustering en 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/



OCMapView los otros sugeridos aquí, y también encontré OCMapView que funcionó mejor.

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


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

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 usa cuadribos para 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.