Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ClusterMethodDistanceBased #83

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

amishjake
Copy link

This is a drop-in replacement for CCHMapClusterOperation that addresses issue #82, the limitations of the grid-based algorithm. Now, when the operation is created in the controller, you can specify:
operation.clusterMethod = ClusterMethodDistanceBased; (defaults to grid based)
The algorithm is mostly translated from Google Maps Utilities, that I use in an Android app that handles map clustering nicely.
It appears to work correctly here in iOS with some amount of performance hit with larger 100+ datasets. I think it's worth it to not see the pins jump around illogically when making small incremental zoom motions.
I must admit I don't really understand the "unique locations" part of the original method, and I may have gotten some of the "reusing" existing annotations wrong, so let me know.

BOOL respondsToSelector = [_clusterControllerDelegate respondsToSelector:@selector(mapClusterController:willReuseMapClusterAnnotation:)];

double zoomLevel = CCHMapClusterControllerZoomLevelForRegion(self.mapViewRegion.center.longitude, self.mapViewRegion.span.longitudeDelta, self.mapViewWidth);
BOOL disableClustering = (zoomLevel > self.maxZoomLevelForClustering);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never read in your code. We should not ignore this property.

@mikrobi
Copy link

mikrobi commented Apr 16, 2015

When trying out the code I got an exception:

  2015-04-16 09:14:48.261 Roundhere[29815:1984234] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSSetM addObject:]: object cannot be nil'
  *** First throw call stack:
  (
    0   CoreFoundation                      0x01a69746 __exceptionPreprocess + 182
    1   libobjc.A.dylib                     0x045d9a97 objc_exception_throw + 44
    2   CoreFoundation                      0x0197e01b -[__NSSetM addObject:] + 699
    3   MapKit                              0x02b056df __30-[MKQuadTrie _itemsInMapRect:]_block_invoke + 694
    4   MapKit                              0x02b04859 _Z17__depthFirstApplyP14MKQuadTrieNodeU13block_pointerF28MKQuadTrieTraversalDirectiveS0_E + 79
    5   MapKit                              0x02b053fc -[MKQuadTrie _itemsInMapRect:] + 438
    6   MapKit                              0x02b058d2 -[MKQuadTrie itemsInMapRect:] + 393
    7   MapKit                              0x02b060e9 -[MKQuadTrie allItems] + 88
    8   MapKit                              0x02af1d4d -[MKAnnotationManager annotations] + 80
    9   MapKit                              0x02a9b7b8 -[MKMapView annotations] + 42
    10  Roundhere                           0x0020875d -[CCHMapClusterOperation initWithMapView:cellSize:marginFactor:reuseExistingClusterAnnotations:maxZoomLevelForClustering:minUniqueLocationsForClustering:] + 973
    11  Roundhere                           0x00202007 -[CCHMapClusterController updateAnnotationsWithCompletionHandler:] + 471
    12  Roundhere                           0x0020400d -[CCHMapClusterController mapView:regionDidChangeAnimated:] + 589
    13  CoreFoundation                      0x0194a84d __invoking___ + 29
    14  CoreFoundation                      0x0194a6f8 -[NSInvocation invoke] + 360
    15  CoreFoundation                      0x019e332a -[NSInvocation invokeWithTarget:] + 74
    16  Roundhere                           0x0020fa64 -[CCHMapViewDelegateProxy forwardInvocation:] + 468
    17  CoreFoundation                      0x019b804e ___forwarding___ + 478
    18  CoreFoundation                      0x019b7e4e _CF_forwarding_prep_0 + 14
    19  MapKit                              0x02a8fd98 -[MKMapView _didChangeRegionMidstream:] + 791
    20  MapKit                              0x02a98e51 -[MKMapView mapLayer:didChangeRegionAnimated:] + 77
    21  VectorKit                           0x0f524879 -[VKMapView map:didChangeRegionAnimated:] + 105
    22  VectorKit                           0x0f52ca87 -[VKMapCanvas cameraController:didChangeRegionAnimated:] + 55
    23  VectorKit                           0x0f5776fa -[VKCameraController endRegionChange] + 90
    24  VectorKit                           0x0f580385 -[VKMapCameraController stopPinchingWithFocusPoint:] + 101
    25  VectorKit                           0x0f52ce2f -[VKMapCanvas stopPinchingWithFocusPoint:] + 63
    26  VectorKit                           0x0f525779 -[VKMapView stopPinchingWithFocusPoint:] + 73
    27  MapKit                              0x02ad539d -[MKMapGestureController handlePinch:] + 1550
    28  UIKit                               0x0364d677 _UIGestureRecognizerSendActions + 327
    29  UIKit                               0x0364bef4 -[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 561
    30  UIKit                               0x0364df3d -[UIGestureRecognizer _delayedUpdateGesture] + 60
    31  UIKit                               0x036518ba ___UIGestureRecognizerUpdate_block_invoke661 + 57
    32  UIKit                               0x0365177d _UIGestureRecognizerRemoveObjectsFromArrayAndApplyBlocks + 317
    33  UIKit                               0x03645686 _UIGestureRecognizerUpdate + 3720
    34  UIKit                               0x0326595b -[UIWindow _sendGesturesForEvent:] + 1356
    35  UIKit                               0x032667c0 -[UIWindow sendEvent:] + 770
    36  UIKit                               0x032246d1 -[UIApplication sendEvent:] + 242
    37  UIKit                               0x03234b08 _UIApplicationHandleEventFromQueueEvent + 21484
    38  UIKit                               0x03208337 _UIApplicationHandleEventQueue + 2300
    39  CoreFoundation                      0x0198b06f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    40  CoreFoundation                      0x01980b7d __CFRunLoopDoSources0 + 253
    41  CoreFoundation                      0x019800d8 __CFRunLoopRun + 952
    42  CoreFoundation                      0x0197fa5b CFRunLoopRunSpecific + 443
    43  CoreFoundation                      0x0197f88b CFRunLoopRunInMode + 123
    44  GraphicsServices                    0x067a92c9 GSEventRunModal + 192
    45  GraphicsServices                    0x067a9106 GSEventRun + 104
    46  UIKit                               0x0320c106 UIApplicationMain + 1526
    47  Roundhere                           0x0008fe1a main + 138
    48  libdyld.dylib                       0x054e4ac9 start + 1

@mikrobi
Copy link

mikrobi commented Apr 16, 2015

@amishjake I'd like to work on this algorithm, too. Do you have more resources to read about this algorithm in detail? Thx.

@mikrobi
Copy link

mikrobi commented Apr 16, 2015

In general I am wondering if it makes sense to offer different cluster algorithms to chose from. Why would you want to use the grid based algorithm when it obviously comes with some issues? I guess the library should just provide the best clustering possible in terms of performance and UX.

mikrobi added a commit to mikrobi/CCHMapClusterController that referenced this pull request Apr 16, 2015
This is a work in progress commit with a first working version of a
distance based clustering algorithm.

It’s based on ideas from
choefele#83
@mikrobi
Copy link

mikrobi commented Apr 16, 2015

Hey @amishjake I went through your code in more detail and I like the idea of the algorithm. As I've said before, I guess it doesn't make too much sense to ship the library with two algorithms (especially if one of them provides bad UX).

Based on your PR I've changed the existing algorithm. I've changed everything to make the test suit pass. It's still work in progress but it's already usable. The only thing the is still missing is considering the minUniqueLocationsForClustering property and some specs for testing the performance with the new algorithm.

As we have an urgent deadline in our project, I will first go with this solution and create an actual PR later on when the I have more time to add some performance tests and the missing support for minUniqueLocationsForClustering

You can have a look at the current state in my fork:

https://github.com/mikrobi/CCHMapClusterController/tree/distance_clustering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants