├── .gitignore ├── ExampleApp ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib ├── IBInspectable.png ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── pin_camping.imageset │ │ ├── Contents.json │ │ ├── pin_camping.png │ │ ├── pin_camping@2x.png │ │ └── pin_camping@3x.png │ ├── pin_coffee.imageset │ │ ├── Contents.json │ │ ├── pin_coffee.png │ │ ├── pin_coffee@2x.png │ │ └── pin_coffee@3x.png │ ├── pin_default.imageset │ │ ├── Contents.json │ │ ├── pin_default.png │ │ ├── pin_default@2x.png │ │ └── pin_default@3x.png │ └── pin_museum.imageset │ │ ├── Contents.json │ │ ├── pin_museum.png │ │ ├── pin_museum@2x.png │ │ └── pin_museum@3x.png ├── Info.plist ├── MapViewController.h ├── MapViewController.m ├── MapViewController.xib ├── data.csv ├── hacclusterviewcontroller.gif └── main.m ├── HACClusterMapViewController.podspec ├── HACClusterMapViewController.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── HACClusterMapViewController.xccheckout │ └── xcuserdata │ │ ├── Lito.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ ├── harias.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── hipolitoarias.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── UserInterfaceState.xcuserstate.orig ├── xcshareddata │ └── xcschemes │ │ ├── ClusterMapViewController.xcscheme │ │ └── HACClusterMapViewController.xcscheme └── xcuserdata │ ├── Lito.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── HACClusterMapViewControllerTests.xcscheme │ │ └── xcschememanagement.plist │ ├── harias.xcuserdatad │ └── xcschemes │ │ ├── HACClusterMapViewController.xcscheme │ │ └── xcschememanagement.plist │ └── hipolitoarias.xcuserdatad │ └── xcschemes │ ├── HACClusterMapViewControllerTests.xcscheme │ └── xcschememanagement.plist ├── HACClusterMapViewController ├── HACClusterMapViewController.h ├── HACMKMapView.h ├── HACMKMapView.m ├── HACManagerQuadTree.h ├── HACManagerQuadTree.m ├── HACQuadTree.h ├── HACQuadTree.m ├── HAClusterAnnotation.h ├── HAClusterAnnotation.m ├── HAClusterAnnotationView.h ├── HAClusterAnnotationView.m └── Info.plist ├── HACClusterMapViewControllerTests ├── HACClusterMapViewControllerTests.m └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/objective-c 3 | 4 | ### Objective-C ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | # Pods/ 42 | 43 | # Carthage 44 | # 45 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 46 | # Carthage/Checkouts 47 | 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 56 | 57 | fastlane/report.xml 58 | fastlane/Preview.html 59 | fastlane/screenshots 60 | fastlane/test_output 61 | 62 | # Code Injection 63 | # 64 | # After new code Injection tools there's a generated folder /iOSInjectionProject 65 | # https://github.com/johnno1962/injectionforxcode 66 | 67 | iOSInjectionProject/ 68 | 69 | ### Objective-C Patch ### 70 | 71 | # End of https://www.gitignore.io/api/objective-c -------------------------------------------------------------------------------- /ExampleApp/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HACClusterMapViewController 4 | // 5 | // Created by Hipolito Arias on 11/8/15. 6 | // Copyright (c) 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /ExampleApp/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HACClusterMapViewController 4 | // 5 | // Created by Hipolito Arias on 11/8/15. 6 | // Copyright (c) 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "MapViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | // Override point for customization after application launch. 22 | 23 | MapViewController *mVC = [MapViewController new]; 24 | self.window.rootViewController = mVC; 25 | 26 | self.window.backgroundColor = [UIColor whiteColor]; 27 | [self.window makeKeyAndVisible]; 28 | return YES; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ExampleApp/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ExampleApp/IBInspectable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/IBInspectable.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_camping.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "pin_camping.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "pin_camping@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "pin_camping@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping@2x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_camping.imageset/pin_camping@3x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_coffee.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "pin_coffee.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "pin_coffee@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "pin_coffee@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee@2x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_coffee.imageset/pin_coffee@3x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pin_default.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "pin_default@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "pin_default@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_default.imageset/pin_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_default.imageset/pin_default.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_default.imageset/pin_default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_default.imageset/pin_default@2x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_default.imageset/pin_default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_default.imageset/pin_default@3x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_museum.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "pin_museum.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "pin_museum@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "pin_museum@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum@2x.png -------------------------------------------------------------------------------- /ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/Images.xcassets/pin_museum.imageset/pin_museum@3x.png -------------------------------------------------------------------------------- /ExampleApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ExampleApp/MapViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewController.h 3 | // HACAnnotationClustering 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 Theodore Calmes. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MapViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ExampleApp/MapViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewController.m 3 | // HACAnnotationClustering 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 Theodore Calmes. All rights reserved. 7 | // 8 | 9 | #import "MapViewController.h" 10 | #import "HACMKMapView.h" 11 | 12 | @interface MapViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet HACMKMapView *mapView; 15 | 16 | @end 17 | 18 | @implementation MapViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | self.mapView.mapDelegate = self; 24 | 25 | // 1. Example with more than 150.000 annotations 26 | [self.mapView.coordinateQuadTree buildTreeWithExample]; 27 | 28 | // 2. Custom markers example 29 | // NSArray *data = @[ 30 | // @{kLatitude:@48.47352, kLongitude:@3.87426, kTitle : @"Title 1", kSubtitle : @"", kIndex : @0}, 31 | // @{kLatitude:@52.59758, kLongitude:@-1.93061, kTitle : @"Title 2", kSubtitle : @"Subtitle 2", kIndex : @1}, 32 | // @{kLatitude:@48.41370, kLongitude:@3.43531, kTitle : @"Title 3", kSubtitle : @"Subtitle 3", kIndex : @2}, 33 | // @{kLatitude:@48.31921, kLongitude:@18.10184, kTitle : @"Title 4", kSubtitle : @"Subtitle 4", kIndex : @3}, 34 | // @{kLatitude:@47.84302, kLongitude:@22.81101, kTitle : @"Title 5", kSubtitle : @"Subtitle 5", kIndex : @4}, 35 | // @{kLatitude:@60.88622, kLongitude:@26.83792, kTitle : @"Title 6", kSubtitle : @"" , kIndex : @5} 36 | // ]; 37 | // 38 | // [self.mapView.coordinateQuadTree buildTreeWithArray:data]; 39 | // self.mapView.backgroundAnnotation = [UIColor redColor]; 40 | // self.mapView.borderAnnotation = [UIColor whiteColor]; 41 | // self.mapView.textAnnotation = [UIColor whiteColor]; 42 | self.mapView.compassFrame = CGRectMake(10, 10, 25, 25); 43 | self.mapView.legalFrame = CGRectMake(CGRectGetWidth([UIScreen mainScreen].bounds)-50, CGRectGetHeight([UIScreen mainScreen].bounds)-50, 50, 50); 44 | } 45 | 46 | # pragma mark - HACMKMapViewDelegate 47 | 48 | -(void)viewForAnnotationView:(HAClusterAnnotationView *)annotationView annotation:(HAClusterAnnotation *)annotation{ 49 | if (annotation.index % 2 == 0) { 50 | if (annotation.index == 2) { 51 | annotationView.canShowCallout = NO; 52 | } 53 | annotationView.image = [UIImage imageNamed:@"pin_museum"]; 54 | }else{ 55 | annotationView.image = [UIImage imageNamed:@"pin_coffee"]; 56 | } 57 | } 58 | 59 | -(void)didSelectAnnotationView:(HAClusterAnnotation *)annotationView{ 60 | NSLog(@"You ara select annotation index %ld", (long)annotationView.index); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /ExampleApp/MapViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /ExampleApp/hacclusterviewcontroller.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/ExampleApp/hacclusterviewcontroller.gif -------------------------------------------------------------------------------- /ExampleApp/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HACClusterMapViewController 4 | // 5 | // Created by Hipolito Arias on 11/8/15. 6 | // Copyright (c) 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /HACClusterMapViewController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "HACClusterMapViewController" 3 | s.version = "2.7" 4 | s.summary = "HACClusterMapViewController iOS 7 >." 5 | s.description = <<-DESC 6 | HACClusterMapViewController class is written in Objective-C and facilitates the use of maps when they have many pins that show. 7 | DESC 8 | s.homepage = "https://github.com/litoarias/HACClusterMapViewController" 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | s.authors = { "litoarias" => "lito.arias.cervero@gmail.com" } 11 | s.social_media_url = 'https://github.com/litoarias/HACClusterMapViewController' 12 | s.platform = :ios, "7.0" 13 | s.source = { :git => "https://github.com/litoarias/HACClusterMapViewController.git", :tag => "2.7" } 14 | s.source_files = "HACClusterMapViewController" 15 | s.requires_arc = true 16 | 17 | s.ios.frameworks = 'CoreLocation','MapKit' 18 | 19 | end 20 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 41191E5B1BDB83EA0079956B /* IBInspectable.png in Resources */ = {isa = PBXBuildFile; fileRef = 41191E5A1BDB83EA0079956B /* IBInspectable.png */; }; 11 | 4144E2191DC63AC400A3DFD8 /* HACClusterMapViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4144E2171DC63AC400A3DFD8 /* HACClusterMapViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 4144E21C1DC63AC400A3DFD8 /* HACClusterMapViewController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4144E2151DC63AC400A3DFD8 /* HACClusterMapViewController.framework */; }; 13 | 4144E21D1DC63AC400A3DFD8 /* HACClusterMapViewController.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4144E2151DC63AC400A3DFD8 /* HACClusterMapViewController.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 4144E2211DC63B2100A3DFD8 /* HACQuadTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 419521491BDAC1E0000DA89A /* HACQuadTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 4144E2221DC63B2100A3DFD8 /* HACQuadTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4195214A1BDAC1E0000DA89A /* HACQuadTree.m */; }; 16 | 4144E2231DC63B2200A3DFD8 /* HACManagerQuadTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 4195214B1BDAC1E0000DA89A /* HACManagerQuadTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 4144E2241DC63B2200A3DFD8 /* HACManagerQuadTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 4195214C1BDAC1E0000DA89A /* HACManagerQuadTree.m */; }; 18 | 4144E2251DC63B2200A3DFD8 /* HAClusterAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = 4195214D1BDAC1E0000DA89A /* HAClusterAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 4144E2261DC63B2200A3DFD8 /* HAClusterAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4195214E1BDAC1E0000DA89A /* HAClusterAnnotation.m */; }; 20 | 4144E2271DC63B2200A3DFD8 /* HAClusterAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4195214F1BDAC1E0000DA89A /* HAClusterAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | 4144E2281DC63B2200A3DFD8 /* HAClusterAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 419521501BDAC1E0000DA89A /* HAClusterAnnotationView.m */; }; 22 | 4144E2291DC63B2200A3DFD8 /* HACMKMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = 419521511BDAC1E0000DA89A /* HACMKMapView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | 4144E22A1DC63B2200A3DFD8 /* HACMKMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 419521521BDAC1E0000DA89A /* HACMKMapView.m */; }; 24 | 4195215B1BDAC1F4000DA89A /* MapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 419521591BDAC1F4000DA89A /* MapViewController.m */; }; 25 | 4195215C1BDAC1F4000DA89A /* MapViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4195215A1BDAC1F4000DA89A /* MapViewController.xib */; }; 26 | 419521601BDAC27E000DA89A /* data.csv in Resources */ = {isa = PBXBuildFile; fileRef = 4195215F1BDAC27E000DA89A /* data.csv */; }; 27 | 41DECA4C1B7A931900EBD135 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 41DECA481B7A931900EBD135 /* AppDelegate.m */; }; 28 | 41DECA531B7A932700EBD135 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 41DECA511B7A932700EBD135 /* main.m */; }; 29 | 41DECA551B7A934100EBD135 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 41DECA541B7A934100EBD135 /* Images.xcassets */; }; 30 | 41DECA591B7A935400EBD135 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 41DECA571B7A935400EBD135 /* LaunchScreen.xib */; }; 31 | 41DECA8C1B7B214B00EBD135 /* hacclusterviewcontroller.gif in Resources */ = {isa = PBXBuildFile; fileRef = 41DECA8B1B7B214B00EBD135 /* hacclusterviewcontroller.gif */; }; 32 | 41FF49C81DC5F7860064D9A8 /* HACClusterMapViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 41DECA271B7A920100EBD135 /* HACClusterMapViewControllerTests.m */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | 4144E21A1DC63AC400A3DFD8 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 41DECA061B7A920100EBD135 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 4144E2141DC63AC400A3DFD8; 41 | remoteInfo = HACClusterMapViewController; 42 | }; 43 | 41DECA221B7A920100EBD135 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 41DECA061B7A920100EBD135 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 41DECA0D1B7A920100EBD135; 48 | remoteInfo = HACClusterMapViewController; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXCopyFilesBuildPhase section */ 53 | 4113D8EA1DC4D15400F78247 /* Embed Frameworks */ = { 54 | isa = PBXCopyFilesBuildPhase; 55 | buildActionMask = 2147483647; 56 | dstPath = ""; 57 | dstSubfolderSpec = 10; 58 | files = ( 59 | 4144E21D1DC63AC400A3DFD8 /* HACClusterMapViewController.framework in Embed Frameworks */, 60 | ); 61 | name = "Embed Frameworks"; 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXCopyFilesBuildPhase section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | 41191E5A1BDB83EA0079956B /* IBInspectable.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = IBInspectable.png; sourceTree = ""; }; 68 | 4144E2151DC63AC400A3DFD8 /* HACClusterMapViewController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HACClusterMapViewController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 4144E2171DC63AC400A3DFD8 /* HACClusterMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HACClusterMapViewController.h; sourceTree = ""; }; 70 | 4144E2181DC63AC400A3DFD8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 419521491BDAC1E0000DA89A /* HACQuadTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HACQuadTree.h; sourceTree = ""; }; 72 | 4195214A1BDAC1E0000DA89A /* HACQuadTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HACQuadTree.m; sourceTree = ""; }; 73 | 4195214B1BDAC1E0000DA89A /* HACManagerQuadTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HACManagerQuadTree.h; sourceTree = ""; }; 74 | 4195214C1BDAC1E0000DA89A /* HACManagerQuadTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HACManagerQuadTree.m; sourceTree = ""; }; 75 | 4195214D1BDAC1E0000DA89A /* HAClusterAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAClusterAnnotation.h; sourceTree = ""; }; 76 | 4195214E1BDAC1E0000DA89A /* HAClusterAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAClusterAnnotation.m; sourceTree = ""; }; 77 | 4195214F1BDAC1E0000DA89A /* HAClusterAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAClusterAnnotationView.h; sourceTree = ""; }; 78 | 419521501BDAC1E0000DA89A /* HAClusterAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAClusterAnnotationView.m; sourceTree = ""; }; 79 | 419521511BDAC1E0000DA89A /* HACMKMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HACMKMapView.h; sourceTree = ""; }; 80 | 419521521BDAC1E0000DA89A /* HACMKMapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HACMKMapView.m; sourceTree = ""; }; 81 | 419521581BDAC1F4000DA89A /* MapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapViewController.h; sourceTree = ""; }; 82 | 419521591BDAC1F4000DA89A /* MapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewController.m; sourceTree = ""; }; 83 | 4195215A1BDAC1F4000DA89A /* MapViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MapViewController.xib; sourceTree = ""; }; 84 | 4195215F1BDAC27E000DA89A /* data.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = data.csv; sourceTree = ""; }; 85 | 41DECA0E1B7A920100EBD135 /* ClusterMapViewController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClusterMapViewController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | 41DECA211B7A920100EBD135 /* HACClusterMapViewControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HACClusterMapViewControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | 41DECA261B7A920100EBD135 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88 | 41DECA271B7A920100EBD135 /* HACClusterMapViewControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HACClusterMapViewControllerTests.m; sourceTree = ""; }; 89 | 41DECA471B7A931900EBD135 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 90 | 41DECA481B7A931900EBD135 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 91 | 41DECA501B7A932700EBD135 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ExampleApp/Info.plist; sourceTree = SOURCE_ROOT; }; 92 | 41DECA511B7A932700EBD135 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExampleApp/main.m; sourceTree = SOURCE_ROOT; }; 93 | 41DECA541B7A934100EBD135 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 94 | 41DECA581B7A935400EBD135 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = LaunchScreen.xib; sourceTree = ""; }; 95 | 41DECA8B1B7B214B00EBD135 /* hacclusterviewcontroller.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = hacclusterviewcontroller.gif; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 4144E2111DC63AC400A3DFD8 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 41DECA0B1B7A920100EBD135 /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | 4144E21C1DC63AC400A3DFD8 /* HACClusterMapViewController.framework in Frameworks */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | 41DECA1E1B7A920100EBD135 /* Frameworks */ = { 115 | isa = PBXFrameworksBuildPhase; 116 | buildActionMask = 2147483647; 117 | files = ( 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | 4144E2161DC63AC400A3DFD8 /* HACClusterMapViewController */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 4144E2171DC63AC400A3DFD8 /* HACClusterMapViewController.h */, 128 | 4144E2181DC63AC400A3DFD8 /* Info.plist */, 129 | ); 130 | path = HACClusterMapViewController; 131 | sourceTree = ""; 132 | }; 133 | 41DECA051B7A920100EBD135 = { 134 | isa = PBXGroup; 135 | children = ( 136 | 41DECA311B7A927200EBD135 /* ExampleApp */, 137 | 41DECA101B7A920100EBD135 /* HACClusterMapViewController */, 138 | 41DECA241B7A920100EBD135 /* HACClusterMapViewControllerTests */, 139 | 4144E2161DC63AC400A3DFD8 /* HACClusterMapViewController */, 140 | 41DECA0F1B7A920100EBD135 /* Products */, 141 | ); 142 | sourceTree = ""; 143 | }; 144 | 41DECA0F1B7A920100EBD135 /* Products */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 41DECA0E1B7A920100EBD135 /* ClusterMapViewController.app */, 148 | 41DECA211B7A920100EBD135 /* HACClusterMapViewControllerTests.xctest */, 149 | 4144E2151DC63AC400A3DFD8 /* HACClusterMapViewController.framework */, 150 | ); 151 | name = Products; 152 | sourceTree = ""; 153 | }; 154 | 41DECA101B7A920100EBD135 /* HACClusterMapViewController */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 419521491BDAC1E0000DA89A /* HACQuadTree.h */, 158 | 4195214A1BDAC1E0000DA89A /* HACQuadTree.m */, 159 | 4195214B1BDAC1E0000DA89A /* HACManagerQuadTree.h */, 160 | 4195214C1BDAC1E0000DA89A /* HACManagerQuadTree.m */, 161 | 4195214D1BDAC1E0000DA89A /* HAClusterAnnotation.h */, 162 | 4195214E1BDAC1E0000DA89A /* HAClusterAnnotation.m */, 163 | 4195214F1BDAC1E0000DA89A /* HAClusterAnnotationView.h */, 164 | 419521501BDAC1E0000DA89A /* HAClusterAnnotationView.m */, 165 | 419521511BDAC1E0000DA89A /* HACMKMapView.h */, 166 | 419521521BDAC1E0000DA89A /* HACMKMapView.m */, 167 | ); 168 | path = HACClusterMapViewController; 169 | sourceTree = ""; 170 | }; 171 | 41DECA111B7A920100EBD135 /* Supporting Files */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 41DECA501B7A932700EBD135 /* Info.plist */, 175 | 41DECA511B7A932700EBD135 /* main.m */, 176 | ); 177 | name = "Supporting Files"; 178 | path = HACClusterMapViewController; 179 | sourceTree = ""; 180 | }; 181 | 41DECA241B7A920100EBD135 /* HACClusterMapViewControllerTests */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 41DECA271B7A920100EBD135 /* HACClusterMapViewControllerTests.m */, 185 | 41DECA251B7A920100EBD135 /* Supporting Files */, 186 | ); 187 | path = HACClusterMapViewControllerTests; 188 | sourceTree = ""; 189 | }; 190 | 41DECA251B7A920100EBD135 /* Supporting Files */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 41DECA261B7A920100EBD135 /* Info.plist */, 194 | ); 195 | name = "Supporting Files"; 196 | sourceTree = ""; 197 | }; 198 | 41DECA311B7A927200EBD135 /* ExampleApp */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 41DECA471B7A931900EBD135 /* AppDelegate.h */, 202 | 41DECA481B7A931900EBD135 /* AppDelegate.m */, 203 | 419521581BDAC1F4000DA89A /* MapViewController.h */, 204 | 419521591BDAC1F4000DA89A /* MapViewController.m */, 205 | 4195215A1BDAC1F4000DA89A /* MapViewController.xib */, 206 | 41DECA571B7A935400EBD135 /* LaunchScreen.xib */, 207 | 4195215F1BDAC27E000DA89A /* data.csv */, 208 | 41DECA8B1B7B214B00EBD135 /* hacclusterviewcontroller.gif */, 209 | 41191E5A1BDB83EA0079956B /* IBInspectable.png */, 210 | 41DECA541B7A934100EBD135 /* Images.xcassets */, 211 | 41DECA111B7A920100EBD135 /* Supporting Files */, 212 | ); 213 | path = ExampleApp; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXGroup section */ 217 | 218 | /* Begin PBXHeadersBuildPhase section */ 219 | 4144E2121DC63AC400A3DFD8 /* Headers */ = { 220 | isa = PBXHeadersBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 4144E2211DC63B2100A3DFD8 /* HACQuadTree.h in Headers */, 224 | 4144E2291DC63B2200A3DFD8 /* HACMKMapView.h in Headers */, 225 | 4144E2251DC63B2200A3DFD8 /* HAClusterAnnotation.h in Headers */, 226 | 4144E2271DC63B2200A3DFD8 /* HAClusterAnnotationView.h in Headers */, 227 | 4144E2231DC63B2200A3DFD8 /* HACManagerQuadTree.h in Headers */, 228 | 4144E2191DC63AC400A3DFD8 /* HACClusterMapViewController.h in Headers */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXHeadersBuildPhase section */ 233 | 234 | /* Begin PBXNativeTarget section */ 235 | 4144E2141DC63AC400A3DFD8 /* HACClusterMapViewController */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = 4144E21E1DC63AC400A3DFD8 /* Build configuration list for PBXNativeTarget "HACClusterMapViewController" */; 238 | buildPhases = ( 239 | 4144E2101DC63AC400A3DFD8 /* Sources */, 240 | 4144E2111DC63AC400A3DFD8 /* Frameworks */, 241 | 4144E2121DC63AC400A3DFD8 /* Headers */, 242 | 4144E2131DC63AC400A3DFD8 /* Resources */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | ); 248 | name = HACClusterMapViewController; 249 | productName = HACClusterMapViewController; 250 | productReference = 4144E2151DC63AC400A3DFD8 /* HACClusterMapViewController.framework */; 251 | productType = "com.apple.product-type.framework"; 252 | }; 253 | 41DECA0D1B7A920100EBD135 /* ClusterMapViewController */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = 41DECA2B1B7A920100EBD135 /* Build configuration list for PBXNativeTarget "ClusterMapViewController" */; 256 | buildPhases = ( 257 | 41DECA0A1B7A920100EBD135 /* Sources */, 258 | 41DECA0B1B7A920100EBD135 /* Frameworks */, 259 | 41DECA0C1B7A920100EBD135 /* Resources */, 260 | 4113D8EA1DC4D15400F78247 /* Embed Frameworks */, 261 | ); 262 | buildRules = ( 263 | ); 264 | dependencies = ( 265 | 4144E21B1DC63AC400A3DFD8 /* PBXTargetDependency */, 266 | ); 267 | name = ClusterMapViewController; 268 | productName = HACClusterMapViewController; 269 | productReference = 41DECA0E1B7A920100EBD135 /* ClusterMapViewController.app */; 270 | productType = "com.apple.product-type.application"; 271 | }; 272 | 41DECA201B7A920100EBD135 /* HACClusterMapViewControllerTests */ = { 273 | isa = PBXNativeTarget; 274 | buildConfigurationList = 41DECA2E1B7A920100EBD135 /* Build configuration list for PBXNativeTarget "HACClusterMapViewControllerTests" */; 275 | buildPhases = ( 276 | 41DECA1D1B7A920100EBD135 /* Sources */, 277 | 41DECA1E1B7A920100EBD135 /* Frameworks */, 278 | 41DECA1F1B7A920100EBD135 /* Resources */, 279 | ); 280 | buildRules = ( 281 | ); 282 | dependencies = ( 283 | 41DECA231B7A920100EBD135 /* PBXTargetDependency */, 284 | ); 285 | name = HACClusterMapViewControllerTests; 286 | productName = HACClusterMapViewControllerTests; 287 | productReference = 41DECA211B7A920100EBD135 /* HACClusterMapViewControllerTests.xctest */; 288 | productType = "com.apple.product-type.bundle.unit-test"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | 41DECA061B7A920100EBD135 /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastUpgradeCheck = 0810; 297 | ORGANIZATIONNAME = MasterApp; 298 | TargetAttributes = { 299 | 4144E2141DC63AC400A3DFD8 = { 300 | CreatedOnToolsVersion = 8.1; 301 | DevelopmentTeam = 4U72882THN; 302 | ProvisioningStyle = Automatic; 303 | }; 304 | 41DECA0D1B7A920100EBD135 = { 305 | CreatedOnToolsVersion = 6.4; 306 | DevelopmentTeam = 4U72882THN; 307 | }; 308 | 41DECA201B7A920100EBD135 = { 309 | CreatedOnToolsVersion = 6.4; 310 | TestTargetID = 41DECA0D1B7A920100EBD135; 311 | }; 312 | }; 313 | }; 314 | buildConfigurationList = 41DECA091B7A920100EBD135 /* Build configuration list for PBXProject "HACClusterMapViewController" */; 315 | compatibilityVersion = "Xcode 3.2"; 316 | developmentRegion = English; 317 | hasScannedForEncodings = 0; 318 | knownRegions = ( 319 | en, 320 | Base, 321 | ); 322 | mainGroup = 41DECA051B7A920100EBD135; 323 | productRefGroup = 41DECA0F1B7A920100EBD135 /* Products */; 324 | projectDirPath = ""; 325 | projectRoot = ""; 326 | targets = ( 327 | 41DECA0D1B7A920100EBD135 /* ClusterMapViewController */, 328 | 41DECA201B7A920100EBD135 /* HACClusterMapViewControllerTests */, 329 | 4144E2141DC63AC400A3DFD8 /* HACClusterMapViewController */, 330 | ); 331 | }; 332 | /* End PBXProject section */ 333 | 334 | /* Begin PBXResourcesBuildPhase section */ 335 | 4144E2131DC63AC400A3DFD8 /* Resources */ = { 336 | isa = PBXResourcesBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | 41DECA0C1B7A920100EBD135 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | 41DECA8C1B7B214B00EBD135 /* hacclusterviewcontroller.gif in Resources */, 347 | 41DECA551B7A934100EBD135 /* Images.xcassets in Resources */, 348 | 419521601BDAC27E000DA89A /* data.csv in Resources */, 349 | 41191E5B1BDB83EA0079956B /* IBInspectable.png in Resources */, 350 | 4195215C1BDAC1F4000DA89A /* MapViewController.xib in Resources */, 351 | 41DECA591B7A935400EBD135 /* LaunchScreen.xib in Resources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | 41DECA1F1B7A920100EBD135 /* Resources */ = { 356 | isa = PBXResourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | /* End PBXResourcesBuildPhase section */ 363 | 364 | /* Begin PBXSourcesBuildPhase section */ 365 | 4144E2101DC63AC400A3DFD8 /* Sources */ = { 366 | isa = PBXSourcesBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | 4144E22A1DC63B2200A3DFD8 /* HACMKMapView.m in Sources */, 370 | 4144E2221DC63B2100A3DFD8 /* HACQuadTree.m in Sources */, 371 | 4144E2281DC63B2200A3DFD8 /* HAClusterAnnotationView.m in Sources */, 372 | 4144E2241DC63B2200A3DFD8 /* HACManagerQuadTree.m in Sources */, 373 | 4144E2261DC63B2200A3DFD8 /* HAClusterAnnotation.m in Sources */, 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | 41DECA0A1B7A920100EBD135 /* Sources */ = { 378 | isa = PBXSourcesBuildPhase; 379 | buildActionMask = 2147483647; 380 | files = ( 381 | 41DECA531B7A932700EBD135 /* main.m in Sources */, 382 | 41DECA4C1B7A931900EBD135 /* AppDelegate.m in Sources */, 383 | 4195215B1BDAC1F4000DA89A /* MapViewController.m in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | 41DECA1D1B7A920100EBD135 /* Sources */ = { 388 | isa = PBXSourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | 41FF49C81DC5F7860064D9A8 /* HACClusterMapViewControllerTests.m in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | /* End PBXSourcesBuildPhase section */ 396 | 397 | /* Begin PBXTargetDependency section */ 398 | 4144E21B1DC63AC400A3DFD8 /* PBXTargetDependency */ = { 399 | isa = PBXTargetDependency; 400 | target = 4144E2141DC63AC400A3DFD8 /* HACClusterMapViewController */; 401 | targetProxy = 4144E21A1DC63AC400A3DFD8 /* PBXContainerItemProxy */; 402 | }; 403 | 41DECA231B7A920100EBD135 /* PBXTargetDependency */ = { 404 | isa = PBXTargetDependency; 405 | target = 41DECA0D1B7A920100EBD135 /* ClusterMapViewController */; 406 | targetProxy = 41DECA221B7A920100EBD135 /* PBXContainerItemProxy */; 407 | }; 408 | /* End PBXTargetDependency section */ 409 | 410 | /* Begin PBXVariantGroup section */ 411 | 41DECA571B7A935400EBD135 /* LaunchScreen.xib */ = { 412 | isa = PBXVariantGroup; 413 | children = ( 414 | 41DECA581B7A935400EBD135 /* Base */, 415 | ); 416 | name = LaunchScreen.xib; 417 | path = Base.lproj; 418 | sourceTree = ""; 419 | }; 420 | /* End PBXVariantGroup section */ 421 | 422 | /* Begin XCBuildConfiguration section */ 423 | 4144E21F1DC63AC400A3DFD8 /* Debug */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | CLANG_ANALYZER_NONNULL = YES; 427 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 428 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 429 | CODE_SIGN_IDENTITY = ""; 430 | CURRENT_PROJECT_VERSION = 1; 431 | DEBUG_INFORMATION_FORMAT = dwarf; 432 | DEFINES_MODULE = YES; 433 | DEVELOPMENT_TEAM = 4U72882THN; 434 | DYLIB_COMPATIBILITY_VERSION = 1; 435 | DYLIB_CURRENT_VERSION = 1; 436 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 437 | INFOPLIST_FILE = HACClusterMapViewController/Info.plist; 438 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 439 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 441 | PRODUCT_BUNDLE_IDENTIFIER = MasterApps.HACClusterMapViewController; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | SKIP_INSTALL = YES; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | VERSIONING_SYSTEM = "apple-generic"; 446 | VERSION_INFO_PREFIX = ""; 447 | }; 448 | name = Debug; 449 | }; 450 | 4144E2201DC63AC400A3DFD8 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | CLANG_ANALYZER_NONNULL = YES; 454 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 455 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 456 | CODE_SIGN_IDENTITY = ""; 457 | CURRENT_PROJECT_VERSION = 1; 458 | DEFINES_MODULE = YES; 459 | DEVELOPMENT_TEAM = 4U72882THN; 460 | DYLIB_COMPATIBILITY_VERSION = 1; 461 | DYLIB_CURRENT_VERSION = 1; 462 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 463 | INFOPLIST_FILE = HACClusterMapViewController/Info.plist; 464 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 465 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 466 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 467 | PRODUCT_BUNDLE_IDENTIFIER = MasterApps.HACClusterMapViewController; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | SKIP_INSTALL = YES; 470 | TARGETED_DEVICE_FAMILY = "1,2"; 471 | VERSIONING_SYSTEM = "apple-generic"; 472 | VERSION_INFO_PREFIX = ""; 473 | }; 474 | name = Release; 475 | }; 476 | 41DECA291B7A920100EBD135 /* Debug */ = { 477 | isa = XCBuildConfiguration; 478 | buildSettings = { 479 | ALWAYS_SEARCH_USER_PATHS = NO; 480 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 481 | CLANG_CXX_LIBRARY = "libc++"; 482 | CLANG_ENABLE_MODULES = YES; 483 | CLANG_ENABLE_OBJC_ARC = YES; 484 | CLANG_WARN_BOOL_CONVERSION = YES; 485 | CLANG_WARN_CONSTANT_CONVERSION = YES; 486 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 487 | CLANG_WARN_EMPTY_BODY = YES; 488 | CLANG_WARN_ENUM_CONVERSION = YES; 489 | CLANG_WARN_INFINITE_RECURSION = YES; 490 | CLANG_WARN_INT_CONVERSION = YES; 491 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 492 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 493 | CLANG_WARN_UNREACHABLE_CODE = YES; 494 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 495 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 496 | COPY_PHASE_STRIP = NO; 497 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 498 | ENABLE_STRICT_OBJC_MSGSEND = YES; 499 | ENABLE_TESTABILITY = YES; 500 | GCC_C_LANGUAGE_STANDARD = gnu99; 501 | GCC_DYNAMIC_NO_PIC = NO; 502 | GCC_NO_COMMON_BLOCKS = YES; 503 | GCC_OPTIMIZATION_LEVEL = 0; 504 | GCC_PREPROCESSOR_DEFINITIONS = ( 505 | "DEBUG=1", 506 | "$(inherited)", 507 | ); 508 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 509 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 510 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 511 | GCC_WARN_UNDECLARED_SELECTOR = YES; 512 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 513 | GCC_WARN_UNUSED_FUNCTION = YES; 514 | GCC_WARN_UNUSED_VARIABLE = YES; 515 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 516 | MTL_ENABLE_DEBUG_INFO = YES; 517 | ONLY_ACTIVE_ARCH = YES; 518 | SDKROOT = iphoneos; 519 | }; 520 | name = Debug; 521 | }; 522 | 41DECA2A1B7A920100EBD135 /* Release */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | ALWAYS_SEARCH_USER_PATHS = NO; 526 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 527 | CLANG_CXX_LIBRARY = "libc++"; 528 | CLANG_ENABLE_MODULES = YES; 529 | CLANG_ENABLE_OBJC_ARC = YES; 530 | CLANG_WARN_BOOL_CONVERSION = YES; 531 | CLANG_WARN_CONSTANT_CONVERSION = YES; 532 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 533 | CLANG_WARN_EMPTY_BODY = YES; 534 | CLANG_WARN_ENUM_CONVERSION = YES; 535 | CLANG_WARN_INFINITE_RECURSION = YES; 536 | CLANG_WARN_INT_CONVERSION = YES; 537 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 538 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 539 | CLANG_WARN_UNREACHABLE_CODE = YES; 540 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 541 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 542 | COPY_PHASE_STRIP = NO; 543 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 544 | ENABLE_NS_ASSERTIONS = NO; 545 | ENABLE_STRICT_OBJC_MSGSEND = YES; 546 | GCC_C_LANGUAGE_STANDARD = gnu99; 547 | GCC_NO_COMMON_BLOCKS = YES; 548 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 549 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 550 | GCC_WARN_UNDECLARED_SELECTOR = YES; 551 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 552 | GCC_WARN_UNUSED_FUNCTION = YES; 553 | GCC_WARN_UNUSED_VARIABLE = YES; 554 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 555 | MTL_ENABLE_DEBUG_INFO = NO; 556 | SDKROOT = iphoneos; 557 | VALIDATE_PRODUCT = YES; 558 | }; 559 | name = Release; 560 | }; 561 | 41DECA2C1B7A920100EBD135 /* Debug */ = { 562 | isa = XCBuildConfiguration; 563 | buildSettings = { 564 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 565 | DEVELOPMENT_TEAM = 4U72882THN; 566 | INFOPLIST_FILE = ExampleApp/Info.plist; 567 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 568 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 569 | PRODUCT_BUNDLE_IDENTIFIER = "es.masterapp.$(PRODUCT_NAME:rfc1034identifier)"; 570 | PRODUCT_NAME = "$(TARGET_NAME)"; 571 | }; 572 | name = Debug; 573 | }; 574 | 41DECA2D1B7A920100EBD135 /* Release */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 578 | DEVELOPMENT_TEAM = 4U72882THN; 579 | INFOPLIST_FILE = ExampleApp/Info.plist; 580 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 581 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 582 | PRODUCT_BUNDLE_IDENTIFIER = "es.masterapp.$(PRODUCT_NAME:rfc1034identifier)"; 583 | PRODUCT_NAME = "$(TARGET_NAME)"; 584 | }; 585 | name = Release; 586 | }; 587 | 41DECA2F1B7A920100EBD135 /* Debug */ = { 588 | isa = XCBuildConfiguration; 589 | buildSettings = { 590 | BUNDLE_LOADER = "$(TEST_HOST)"; 591 | FRAMEWORK_SEARCH_PATHS = ( 592 | "$(SDKROOT)/Developer/Library/Frameworks", 593 | "$(inherited)", 594 | ); 595 | GCC_PREPROCESSOR_DEFINITIONS = ( 596 | "DEBUG=1", 597 | "$(inherited)", 598 | ); 599 | INFOPLIST_FILE = HACClusterMapViewControllerTests/Info.plist; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 601 | PRODUCT_BUNDLE_IDENTIFIER = "es.masterapp.$(PRODUCT_NAME:rfc1034identifier)"; 602 | PRODUCT_NAME = "$(TARGET_NAME)"; 603 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HACClusterMapViewController.app/HACClusterMapViewController"; 604 | }; 605 | name = Debug; 606 | }; 607 | 41DECA301B7A920100EBD135 /* Release */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | BUNDLE_LOADER = "$(TEST_HOST)"; 611 | FRAMEWORK_SEARCH_PATHS = ( 612 | "$(SDKROOT)/Developer/Library/Frameworks", 613 | "$(inherited)", 614 | ); 615 | INFOPLIST_FILE = HACClusterMapViewControllerTests/Info.plist; 616 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 617 | PRODUCT_BUNDLE_IDENTIFIER = "es.masterapp.$(PRODUCT_NAME:rfc1034identifier)"; 618 | PRODUCT_NAME = "$(TARGET_NAME)"; 619 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HACClusterMapViewController.app/HACClusterMapViewController"; 620 | }; 621 | name = Release; 622 | }; 623 | /* End XCBuildConfiguration section */ 624 | 625 | /* Begin XCConfigurationList section */ 626 | 4144E21E1DC63AC400A3DFD8 /* Build configuration list for PBXNativeTarget "HACClusterMapViewController" */ = { 627 | isa = XCConfigurationList; 628 | buildConfigurations = ( 629 | 4144E21F1DC63AC400A3DFD8 /* Debug */, 630 | 4144E2201DC63AC400A3DFD8 /* Release */, 631 | ); 632 | defaultConfigurationIsVisible = 0; 633 | defaultConfigurationName = Release; 634 | }; 635 | 41DECA091B7A920100EBD135 /* Build configuration list for PBXProject "HACClusterMapViewController" */ = { 636 | isa = XCConfigurationList; 637 | buildConfigurations = ( 638 | 41DECA291B7A920100EBD135 /* Debug */, 639 | 41DECA2A1B7A920100EBD135 /* Release */, 640 | ); 641 | defaultConfigurationIsVisible = 0; 642 | defaultConfigurationName = Release; 643 | }; 644 | 41DECA2B1B7A920100EBD135 /* Build configuration list for PBXNativeTarget "ClusterMapViewController" */ = { 645 | isa = XCConfigurationList; 646 | buildConfigurations = ( 647 | 41DECA2C1B7A920100EBD135 /* Debug */, 648 | 41DECA2D1B7A920100EBD135 /* Release */, 649 | ); 650 | defaultConfigurationIsVisible = 0; 651 | defaultConfigurationName = Release; 652 | }; 653 | 41DECA2E1B7A920100EBD135 /* Build configuration list for PBXNativeTarget "HACClusterMapViewControllerTests" */ = { 654 | isa = XCConfigurationList; 655 | buildConfigurations = ( 656 | 41DECA2F1B7A920100EBD135 /* Debug */, 657 | 41DECA301B7A920100EBD135 /* Release */, 658 | ); 659 | defaultConfigurationIsVisible = 0; 660 | defaultConfigurationName = Release; 661 | }; 662 | /* End XCConfigurationList section */ 663 | }; 664 | rootObject = 41DECA061B7A920100EBD135 /* Project object */; 665 | } 666 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/xcshareddata/HACClusterMapViewController.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | DB5E4B7B-6F72-4D44-930F-6649765DCD13 9 | IDESourceControlProjectName 10 | HACClusterMapViewController 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 3D7EA870D44BA8AF9829334C04787F4AAEB763B2 14 | https://github.com/litoarias/HACClusterMapViewController.git 15 | 16 | IDESourceControlProjectPath 17 | HACClusterMapViewController.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 3D7EA870D44BA8AF9829334C04787F4AAEB763B2 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/litoarias/HACClusterMapViewController.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 3D7EA870D44BA8AF9829334C04787F4AAEB763B2 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 3D7EA870D44BA8AF9829334C04787F4AAEB763B2 36 | IDESourceControlWCCName 37 | HACClusterMapViewController 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/Lito.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/Lito.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/harias.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/harias.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/hipolitoarias.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/hipolitoarias.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/hipolitoarias.xcuserdatad/UserInterfaceState.xcuserstate.orig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litoarias/HACClusterMapViewController/23c6a8bc97e9543c5a6442d9bf7fb0a23303fd5d/HACClusterMapViewController.xcodeproj/project.xcworkspace/xcuserdata/hipolitoarias.xcuserdatad/UserInterfaceState.xcuserstate.orig -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcshareddata/xcschemes/ClusterMapViewController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcshareddata/xcschemes/HACClusterMapViewController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/Lito.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/Lito.xcuserdatad/xcschemes/HACClusterMapViewControllerTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/Lito.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ClusterMapViewController.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | HACClusterMapVIewController.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | HACClusterMapViewController.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 1 21 | 22 | HACClusterMapViewControllerTests.xcscheme 23 | 24 | orderHint 25 | 2 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 4113D8DD1DC4D15400F78247 31 | 32 | primary 33 | 34 | 35 | 413760931DC625D800EA0799 36 | 37 | primary 38 | 39 | 40 | 4144E2141DC63AC400A3DFD8 41 | 42 | primary 43 | 44 | 45 | 41D4FB1C1DC6309C009E450F 46 | 47 | primary 48 | 49 | 50 | 41D4FB2D1DC63119009E450F 51 | 52 | primary 53 | 54 | 55 | 41DECA0D1B7A920100EBD135 56 | 57 | primary 58 | 59 | 60 | 41DECA201B7A920100EBD135 61 | 62 | primary 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/harias.xcuserdatad/xcschemes/HACClusterMapViewController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/harias.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | HACClusterMapViewController.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 41DECA0D1B7A920100EBD135 16 | 17 | primary 18 | 19 | 20 | 41DECA201B7A920100EBD135 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/hipolitoarias.xcuserdatad/xcschemes/HACClusterMapViewControllerTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /HACClusterMapViewController.xcodeproj/xcuserdata/hipolitoarias.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ClusterMapViewController.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | HACClusterMapViewController.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | HACClusterMapViewControllerTests.xcscheme 18 | 19 | orderHint 20 | 2 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 4144E2141DC63AC400A3DFD8 26 | 27 | primary 28 | 29 | 30 | 41DECA0D1B7A920100EBD135 31 | 32 | primary 33 | 34 | 35 | 41DECA201B7A920100EBD135 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACClusterMapViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HACClusterMapViewController.h 3 | // HACClusterMapViewController 4 | // 5 | // Created by Hipolito Arias on 30/10/16. 6 | // Copyright © 2016 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "HACQuadTree.h" 12 | #import "HACManagerQuadTree.h" 13 | #import "HACMKMapView.h" 14 | 15 | //! Project version number for HACClusterMapViewController. 16 | FOUNDATION_EXPORT double HACClusterMapViewControllerVersionNumber; 17 | 18 | //! Project version string for HACClusterMapViewController. 19 | FOUNDATION_EXPORT const unsigned char HACClusterMapViewControllerVersionString[]; 20 | 21 | // In this header, you should import all the public headers of your framework using statements like #import 22 | 23 | 24 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACMKMapView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HACMKMapView.h 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 23/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HACManagerQuadTree.h" 11 | #import "HAClusterAnnotation.h" 12 | #import "HAClusterAnnotationView.h" 13 | 14 | IB_DESIGNABLE 15 | @protocol HACMKMapViewDelegate 16 | 17 | @optional 18 | -(void)viewForAnnotationView:(HAClusterAnnotationView *)annotationView annotation:(HAClusterAnnotation *)annotation; 19 | -(void)viewForAnnotationView:(HAClusterAnnotationView *)annotationView clusteredAnnotation:(HAClusterAnnotation *)annotation; 20 | -(UIColor*)fillColorForAnnotation:(HAClusterAnnotation *)annotation; 21 | -(void)didSelectAnnotationView:(HAClusterAnnotation *)annotationView; 22 | -(void)didDeselectAnnotationView:(HAClusterAnnotationView *)annotationView; 23 | -(void)didFinishAddingAnnotations; 24 | -(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay; 25 | 26 | @end 27 | 28 | @interface HACMKMapView : MKMapView 29 | 30 | @property (weak, nonatomic) idmapDelegate; 31 | 32 | @property (nonatomic) IBInspectable UIColor* borderAnnotation; 33 | @property (nonatomic) IBInspectable UIColor* backgroundAnnotation; 34 | @property (nonatomic) IBInspectable UIColor* textAnnotation; 35 | @property (nonatomic) IBInspectable UIImage* defaultImage; 36 | @property (nonatomic) IBInspectable CGRect compassFrame; 37 | @property (nonatomic) IBInspectable CGRect legalFrame; 38 | 39 | @property (strong, nonatomic) HACManagerQuadTree *coordinateQuadTree; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACMKMapView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HACMKMapView.m 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 23/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "HACMKMapView.h" 10 | 11 | @implementation HACMKMapView 12 | 13 | -(id)initWithCoder:(NSCoder *)aDecoder{ 14 | if (self = [super initWithCoder:aDecoder]) { 15 | [self defaultInit]; 16 | } 17 | return self; 18 | } 19 | 20 | -(id)init{ 21 | if (self = [super init]) { 22 | [self defaultInit]; 23 | } 24 | return self; 25 | } 26 | 27 | -(void)defaultInit{ 28 | self.delegate = self; 29 | self.coordinateQuadTree = [[HACManagerQuadTree alloc] init]; 30 | self.coordinateQuadTree.mapView = self; 31 | 32 | self.backgroundAnnotation = [UIColor orangeColor]; 33 | self.borderAnnotation = [UIColor whiteColor]; 34 | self.textAnnotation = [UIColor whiteColor]; 35 | self.defaultImage = [UIImage imageNamed:@"pin_default"]; 36 | } 37 | 38 | #pragma mark - MKMapViewDelegate 39 | 40 | - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{ 41 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 42 | double scale = self.bounds.size.width / self.visibleMapRect.size.width; 43 | NSArray *annotations = [self.coordinateQuadTree clusteredAnnotationsWithinMapRect:mapView.visibleMapRect withZoomScale:scale]; 44 | //TODO: Avoid removing non clustered annotation 45 | [self updateMapViewAnnotationsWithAnnotations:annotations]; 46 | }]; 47 | } 48 | 49 | - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{ 50 | if ([annotation isKindOfClass:[MKUserLocation class]]) 51 | return nil; 52 | if (![annotation isKindOfClass:[HAClusterAnnotation class]]) 53 | return nil; 54 | static NSString *const HACAnnotatioViewReuseID = @"HACAnnotatioViewReuseID"; 55 | HAClusterAnnotationView *annotationView = (HAClusterAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:HACAnnotatioViewReuseID]; 56 | 57 | UIColor * fillColor= Nil; 58 | if (!annotationView) { 59 | 60 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(fillColorForAnnotation:)]) { 61 | fillColor = [self.mapDelegate fillColorForAnnotation:annotation]; 62 | 63 | } 64 | if (!fillColor) { 65 | fillColor= self.backgroundAnnotation; 66 | } 67 | annotationView = [[HAClusterAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:HACAnnotatioViewReuseID borderColor:self.borderAnnotation backgroundColor:fillColor textColor:self.textAnnotation]; 68 | }else{ 69 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(fillColorForAnnotation:)]) { 70 | fillColor = [self.mapDelegate fillColorForAnnotation:annotation]; 71 | 72 | } 73 | if (!fillColor) { 74 | fillColor= self.backgroundAnnotation; 75 | } 76 | annotationView.circleBackgroundColor = fillColor; 77 | 78 | } 79 | annotationView.circleBorderColor = self.borderAnnotation; 80 | annotationView.circleTextColor = self.textAnnotation; 81 | annotationView.count = [(HAClusterAnnotation *)annotation count]; 82 | annotationView.canShowCallout = YES; 83 | if (annotationView.count == 1) { 84 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(viewForAnnotationView:annotation:)]) { 85 | [self.mapDelegate viewForAnnotationView:annotationView annotation:annotation]; 86 | }else{ 87 | annotationView.image = self.defaultImage; 88 | } 89 | annotationView.centerOffset = CGPointMake(0,-annotationView.frame.size.height*0.5); 90 | }else{ 91 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(viewForAnnotationView:clusteredAnnotation:)]) { 92 | [self.mapDelegate viewForAnnotationView:annotationView clusteredAnnotation:annotation]; 93 | } 94 | } 95 | [annotationView setNeedsLayout]; 96 | return annotationView; 97 | } 98 | 99 | - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views{ 100 | for (UIView *view in views) { 101 | [self addBounceAnnimationToView:view]; 102 | } 103 | } 104 | 105 | - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{ 106 | if ([view.annotation isKindOfClass:[HAClusterAnnotation class]]){ 107 | HAClusterAnnotation *annotation = (HAClusterAnnotation *)view.annotation; 108 | if (annotation.count == 1) { 109 | [annotation updateSubtitleIfNeeded]; 110 | } 111 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(didSelectAnnotationView:)]) { 112 | [self.mapDelegate didSelectAnnotationView:annotation]; 113 | } 114 | } 115 | } 116 | 117 | - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{ 118 | if ([view.annotation isKindOfClass:[HAClusterAnnotation class]]){ 119 | HAClusterAnnotation *annotation = (HAClusterAnnotation *)view.annotation; 120 | if (annotation.count == 1) { 121 | [annotation updateSubtitleIfNeeded]; 122 | } 123 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(didDeselectAnnotationView:)]) { 124 | [self.mapDelegate didDeselectAnnotationView:(HAClusterAnnotationView *)view]; 125 | } 126 | } 127 | } 128 | 129 | - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay { 130 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(mapView:rendererForOverlay:)]) 131 | return [self.mapDelegate mapView:self rendererForOverlay:overlay]; 132 | else 133 | return nil; 134 | } 135 | 136 | - (void)addBounceAnnimationToView:(UIView *)view{ 137 | CAKeyframeAnimation *bounceAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; 138 | bounceAnimation.values = @[@(0.05), @(1.1), @(0.9), @(1)]; 139 | bounceAnimation.duration = 0.6; 140 | NSMutableArray *timingFunctions = [[NSMutableArray alloc] initWithCapacity:bounceAnimation.values.count]; 141 | for (NSUInteger i = 0; i < bounceAnimation.values.count; i++) { 142 | [timingFunctions addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; 143 | } 144 | [bounceAnimation setTimingFunctions:timingFunctions.copy]; 145 | bounceAnimation.removedOnCompletion = NO; 146 | [view.layer addAnimation:bounceAnimation forKey:@"bounce"]; 147 | } 148 | 149 | - (void)updateMapViewAnnotationsWithAnnotations:(NSArray *)annotations{ 150 | NSMutableSet *before = [NSMutableSet setWithArray:self.annotations]; 151 | [before removeObject:[self userLocation]]; 152 | NSSet *after = [NSSet setWithArray:annotations]; 153 | 154 | NSMutableSet *toKeep = [NSMutableSet setWithSet:before]; 155 | [toKeep intersectSet:after]; 156 | 157 | NSMutableSet *toAdd = [NSMutableSet setWithSet:after]; 158 | [toAdd minusSet:toKeep]; 159 | 160 | NSMutableSet *toRemove = [NSMutableSet setWithSet:before]; 161 | [toRemove minusSet:after]; 162 | 163 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 164 | [self addAnnotations:[toAdd allObjects]]; 165 | [self removeAnnotations:[toRemove allObjects]]; 166 | //Oggerschummer 167 | 168 | if (self.mapDelegate && [self.mapDelegate respondsToSelector:@selector(didFinishAddingAnnotations)]) { 169 | [self.mapDelegate didFinishAddingAnnotations]; 170 | 171 | } 172 | 173 | }]; 174 | } 175 | 176 | - (void)layoutSubviews{ 177 | [super layoutSubviews]; 178 | 179 | for(id view in self.subviews){ 180 | if([view isKindOfClass:NSClassFromString(@"MKCompassView")]) 181 | [self moveCompass:view]; 182 | 183 | if ([view isKindOfClass:[UILabel class]]) { 184 | [self moveLegal:(UILabel *)view]; 185 | } 186 | } 187 | } 188 | 189 | - (void)moveCompass:(UIView *)view{ 190 | view.frame = self.compassFrame; 191 | } 192 | 193 | -(void)moveLegal:(UILabel *)legal{ 194 | legal.frame = self.legalFrame; 195 | } 196 | 197 | @end 198 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACManagerQuadTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // HACManagerQuadTree.h 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "HACQuadTree.h" 12 | #import "HAClusterAnnotation.h" 13 | 14 | #define kLatitude @"lat" 15 | #define kLongitude @"lng" 16 | #define kTitle @"title" 17 | #define kSubtitle @"subtitle" 18 | #define kIndex @"index" 19 | 20 | IB_DESIGNABLE 21 | @protocol HACManagerQuadTreeDelegate 22 | 23 | @optional 24 | -(void) annotationAddedToCluster:(HAClusterAnnotation*) annotation; 25 | @end 26 | 27 | 28 | @interface HACManagerQuadTree : NSObject{ 29 | BOOL example; 30 | } 31 | 32 | @property (weak, nonatomic) iddelegate; 33 | @property (assign, nonatomic) HACQuadTreeNode* root; 34 | @property (strong, nonatomic) MKMapView *mapView; 35 | 36 | - (void)buildTreeWithExample; 37 | - (void)buildTreeWithArray:(NSArray *)data; 38 | - (NSArray *)clusteredAnnotationsWithinMapRect:(MKMapRect)rect withZoomScale:(double)zoomScale; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACManagerQuadTree.m: -------------------------------------------------------------------------------- 1 | // 2 | // HACManagerQuadTree.m 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "HACManagerQuadTree.h" 10 | #import "HAClusterAnnotation.h" 11 | 12 | typedef struct HACItemInfo { 13 | char* itemTitle; 14 | char* itemSubtitle; 15 | char* itemIndex; 16 | } HACHItemInfo; 17 | 18 | HACQuadTreeNodeData HACDataFromLine(NSString *line) 19 | { 20 | NSArray *components = [line componentsSeparatedByString:@","]; 21 | double latitude = [components[1] doubleValue]; 22 | double longitude = [components[0] doubleValue]; 23 | 24 | HACHItemInfo* info = malloc(sizeof(HACHItemInfo)); 25 | 26 | NSString *title = [components[2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 27 | info->itemTitle = malloc(sizeof(char) * title.length + 1); 28 | strncpy(info->itemTitle, [title UTF8String], title.length + 1); 29 | 30 | NSString *subtitle = [[components lastObject] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 31 | info->itemSubtitle = malloc(sizeof(char) * subtitle.length + 1); 32 | strncpy(info->itemSubtitle, [subtitle UTF8String], subtitle.length + 1); 33 | 34 | if (components.count > 3) { 35 | NSString *index = [components[3] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 36 | info->itemIndex = malloc(sizeof(char) * index.length + 1); 37 | strncpy(info->itemIndex, [index UTF8String], index.length + 1); 38 | } 39 | 40 | return HACQuadTreeNodeDataMake(latitude, longitude, info); 41 | } 42 | 43 | HACBoundingBox HACBoundingBoxForMapRect(MKMapRect mapRect) 44 | { 45 | CLLocationCoordinate2D topLeft = MKCoordinateForMapPoint(mapRect.origin); 46 | CLLocationCoordinate2D botRight = MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMaxX(mapRect), MKMapRectGetMaxY(mapRect))); 47 | 48 | CLLocationDegrees minLat = botRight.latitude; 49 | CLLocationDegrees maxLat = topLeft.latitude; 50 | 51 | CLLocationDegrees minLon = topLeft.longitude; 52 | CLLocationDegrees maxLon = botRight.longitude; 53 | 54 | return HACBoundingBoxMake(minLat, minLon, maxLat, maxLon); 55 | } 56 | 57 | MKMapRect HACMapRectForBoundingBox(HACBoundingBox boundingBox) 58 | { 59 | MKMapPoint topLeft = MKMapPointForCoordinate(CLLocationCoordinate2DMake(boundingBox.x0, boundingBox.y0)); 60 | MKMapPoint botRight = MKMapPointForCoordinate(CLLocationCoordinate2DMake(boundingBox.xf, boundingBox.yf)); 61 | 62 | return MKMapRectMake(topLeft.x, botRight.y, fabs(botRight.x - topLeft.x), fabs(botRight.y - topLeft.y)); 63 | } 64 | 65 | NSInteger HACZoomScaleToZoomLevel(MKZoomScale scale) 66 | { 67 | double totalTilesAtMaxZoom = MKMapSizeWorld.width / 256.0; 68 | NSInteger zoomLevelAtMaxZoom = log2(totalTilesAtMaxZoom); 69 | NSInteger zoomLevel = MAX(0, zoomLevelAtMaxZoom + floor(log2f(scale) + 0.5)); 70 | 71 | return zoomLevel; 72 | } 73 | 74 | float HACCellSizeForZoomScale(MKZoomScale zoomScale) 75 | { 76 | NSInteger zoomLevel = HACZoomScaleToZoomLevel(zoomScale); 77 | 78 | switch (zoomLevel) { 79 | case 13: 80 | case 14: 81 | case 15: 82 | return 64; 83 | case 16: 84 | case 17: 85 | case 18: 86 | return 32; 87 | case 19: 88 | return 16; 89 | 90 | default: 91 | return 88; 92 | } 93 | } 94 | 95 | @implementation HACManagerQuadTree 96 | 97 | - (void)buildTreeWithExample 98 | { 99 | @autoreleasepool { 100 | example=YES; 101 | NSArray *data = [self read]; 102 | NSInteger count = data.count - 1; 103 | HACQuadTreeNodeData *dataArray = calloc(count, sizeof(HACQuadTreeNodeData)); 104 | for (NSInteger i = 0; i < count; i++) { 105 | dataArray[i] = HACDataFromLine(data[i]); 106 | } 107 | HACBoundingBox world = HACBoundingBoxMake(-185, -185, 185, 185); 108 | _root = HACQuadTreeBuildWithData(dataArray, (int)count, world, 4); 109 | 110 | free(dataArray); 111 | dataArray = NULL; 112 | } 113 | } 114 | 115 | - (void)buildTreeWithArray:(NSArray *)data 116 | { 117 | @autoreleasepool { 118 | data = [self dropPinsWithData:data]; 119 | NSInteger count = data.count; 120 | HACQuadTreeNodeData *dataArray = malloc(sizeof(HACQuadTreeNodeData) * count); 121 | for (NSInteger i = 0; i < count; i++) { 122 | dataArray[i] = HACDataFromLine(data[i]); 123 | } 124 | HACBoundingBox world = HACBoundingBoxMake(-185, -185, 185, 185); 125 | _root = HACQuadTreeBuildWithData(dataArray, (int)count, world, 4); 126 | } 127 | } 128 | 129 | -(NSArray *)dropPinsWithData:(NSArray *)data { 130 | NSMutableArray*annotationArray= [NSMutableArray new]; 131 | for (int i = 0; i < data.count; i++) { 132 | NSDictionary *d = data[i]; 133 | NSMutableString *line = [NSMutableString new]; 134 | [line appendString:[NSString stringWithFormat:@"%@, ", [d valueForKey:kLongitude]]]; 135 | [line appendString:[NSString stringWithFormat:@"%@, ", [d valueForKey:kLatitude]]]; 136 | [line appendString:[NSString stringWithFormat:@"%@, ", [d valueForKey:kTitle]]]; 137 | [line appendString:[NSString stringWithFormat:@"%@, ", [d valueForKey:kIndex]]]; 138 | [line appendString:[NSString stringWithFormat:@"%@", [d valueForKey:kSubtitle]]]; 139 | [annotationArray addObject:line]; 140 | } 141 | return [[NSArray alloc]initWithArray:annotationArray]; 142 | } 143 | 144 | -(NSArray *)read { 145 | NSString *data = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"csv"] encoding:NSASCIIStringEncoding error:nil]; 146 | NSArray *lines = [data componentsSeparatedByString:@"\n"]; 147 | return lines; 148 | } 149 | 150 | - (NSArray *)clusteredAnnotationsWithinMapRect:(MKMapRect)rect withZoomScale:(double)zoomScale 151 | { 152 | double HACCellSize = HACCellSizeForZoomScale(zoomScale); 153 | double scaleFactor = zoomScale / HACCellSize; 154 | 155 | NSInteger minX = floor(MKMapRectGetMinX(rect) * scaleFactor); 156 | NSInteger maxX = floor(MKMapRectGetMaxX(rect) * scaleFactor); 157 | NSInteger minY = floor(MKMapRectGetMinY(rect) * scaleFactor); 158 | NSInteger maxY = floor(MKMapRectGetMaxY(rect) * scaleFactor); 159 | 160 | NSMutableArray *clusteredAnnotations = [[NSMutableArray alloc] init]; 161 | for (NSInteger x = minX; x <= maxX; x++) { 162 | int cont = 0; 163 | for (NSInteger y = minY; y <= maxY; y++) { 164 | MKMapRect mapRect = MKMapRectMake(x / scaleFactor, y / scaleFactor, 1.0 / scaleFactor, 1.0 / scaleFactor); 165 | 166 | __block double totalX = 0; 167 | __block double totalY = 0; 168 | __block int count = 0; 169 | 170 | NSMutableArray *titles = [[NSMutableArray alloc] init]; 171 | NSMutableArray *subtitles = [[NSMutableArray alloc] init]; 172 | NSMutableArray *indexes = [[NSMutableArray alloc] init]; 173 | 174 | HACQuadTreeGatherDataInRange(self.root, HACBoundingBoxForMapRect(mapRect), ^(HACQuadTreeNodeData data) { 175 | totalX += data.x; 176 | totalY += data.y; 177 | count++; 178 | 179 | HACHItemInfo info = *(HACHItemInfo *)data.data; 180 | [titles addObject:[NSString stringWithFormat:@"%s", info.itemTitle]]; 181 | [subtitles addObject:[NSString stringWithFormat:@"%s", info.itemSubtitle]]; 182 | if (!example) { 183 | [indexes addObject:[NSString stringWithFormat:@"%s", info.itemIndex]]; 184 | } 185 | }); 186 | 187 | cont++; 188 | if (count == 1) { 189 | CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(totalX, totalY); 190 | HAClusterAnnotation *annotation = [[HAClusterAnnotation alloc] initWithCoordinate:coordinate count:count index:[[indexes lastObject] integerValue]]; 191 | annotation.indexes = [[NSMutableArray alloc]initWithArray:indexes]; 192 | annotation.title = [titles lastObject]; 193 | ![[subtitles lastObject]isEqualToString:@""] ? (annotation.subtitle = [subtitles lastObject]) : (annotation.subtitle = nil); 194 | [clusteredAnnotations addObject:annotation]; 195 | 196 | if (self.delegate && [self.delegate respondsToSelector:@selector(annotationAddedToCluster:)]) { 197 | [self.delegate annotationAddedToCluster:annotation]; 198 | } 199 | } 200 | 201 | if (count > 1) { 202 | CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(totalX / count, totalY / count); 203 | HAClusterAnnotation *annotation = [[HAClusterAnnotation alloc] initWithCoordinate:coordinate count:count index:[[indexes lastObject] integerValue]]; 204 | annotation.indexes = [[NSMutableArray alloc]initWithArray:indexes]; 205 | //DLog (@" %@, %i", annotation.title, annotation.indexes.count); 206 | [clusteredAnnotations addObject:annotation]; 207 | if (self.delegate && [self.delegate respondsToSelector:@selector(annotationAddedToCluster:)]) { 208 | [self.delegate annotationAddedToCluster:annotation]; 209 | } 210 | } 211 | } 212 | } 213 | 214 | return [NSArray arrayWithArray:clusteredAnnotations]; 215 | } 216 | 217 | @end 218 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACQuadTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // HACQuadTree.h 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef struct HACQuadTreeNodeData { 12 | double x; 13 | double y; 14 | void* data; 15 | } HACQuadTreeNodeData; 16 | HACQuadTreeNodeData HACQuadTreeNodeDataMake(double x, double y, void* data); 17 | 18 | typedef struct HACBoundingBox { 19 | double x0; double y0; 20 | double xf; double yf; 21 | } HACBoundingBox; 22 | HACBoundingBox HACBoundingBoxMake(double x0, double y0, double xf, double yf); 23 | 24 | typedef struct quadTreeNode { 25 | struct quadTreeNode* northWest; 26 | struct quadTreeNode* northEast; 27 | struct quadTreeNode* southWest; 28 | struct quadTreeNode* southEast; 29 | HACBoundingBox boundingBox; 30 | int bucketCapacity; 31 | HACQuadTreeNodeData *points; 32 | int count; 33 | } HACQuadTreeNode; 34 | HACQuadTreeNode* HACQuadTreeNodeMake(HACBoundingBox boundary, int bucketCapacity); 35 | 36 | void HACFreeQuadTreeNode(HACQuadTreeNode* node); 37 | 38 | bool HACBoundingBoxContainsData(HACBoundingBox box, HACQuadTreeNodeData data); 39 | bool HACBoundingBoxIntersectsBoundingBox(HACBoundingBox b1, HACBoundingBox b2); 40 | 41 | typedef void(^HACQuadTreeTraverseBlock)(HACQuadTreeNode* currentNode); 42 | void HACQuadTreeTraverse(HACQuadTreeNode* node, HACQuadTreeTraverseBlock block); 43 | 44 | typedef void(^HACDataReturnBlock)(HACQuadTreeNodeData data); 45 | void HACQuadTreeGatherDataInRange(HACQuadTreeNode* node, HACBoundingBox range, HACDataReturnBlock block); 46 | 47 | bool HACQuadTreeNodeInsertData(HACQuadTreeNode* node, HACQuadTreeNodeData data); 48 | HACQuadTreeNode* HACQuadTreeBuildWithData(HACQuadTreeNodeData *data, int count, HACBoundingBox boundingBox, int capacity); 49 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HACQuadTree.m: -------------------------------------------------------------------------------- 1 | // 2 | // HACQuadTree.m 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "HACQuadTree.h" 10 | 11 | #pragma mark - Constructors 12 | 13 | HACQuadTreeNodeData HACQuadTreeNodeDataMake(double x, double y, void* data) 14 | { 15 | HACQuadTreeNodeData d; d.x = x; d.y = y; d.data = data; 16 | return d; 17 | } 18 | 19 | HACBoundingBox HACBoundingBoxMake(double x0, double y0, double xf, double yf) 20 | { 21 | HACBoundingBox bb; bb.x0 = x0; bb.y0 = y0; bb.xf = xf; bb.yf = yf; 22 | return bb; 23 | } 24 | 25 | HACQuadTreeNode* HACQuadTreeNodeMake(HACBoundingBox boundary, int bucketCapacity) 26 | { 27 | HACQuadTreeNode* node = malloc(sizeof(HACQuadTreeNode)); 28 | node->northWest = NULL; 29 | node->northEast = NULL; 30 | node->southWest = NULL; 31 | node->southEast = NULL; 32 | 33 | node->boundingBox = boundary; 34 | node->bucketCapacity = bucketCapacity; 35 | node->count = 0; 36 | node->points = malloc(sizeof(HACQuadTreeNodeData) * bucketCapacity); 37 | 38 | return node; 39 | } 40 | 41 | #pragma mark - Bounding Box Functions 42 | 43 | bool HACBoundingBoxContainsData(HACBoundingBox box, HACQuadTreeNodeData data) 44 | { 45 | bool containsX = box.x0 <= data.x && data.x <= box.xf; 46 | bool containsY = box.y0 <= data.y && data.y <= box.yf; 47 | 48 | return containsX && containsY; 49 | } 50 | 51 | bool HACBoundingBoxIntersectsBoundingBox(HACBoundingBox b1, HACBoundingBox b2) 52 | { 53 | return (b1.x0 <= b2.xf && b1.xf >= b2.x0 && b1.y0 <= b2.yf && b1.yf >= b2.y0); 54 | } 55 | 56 | #pragma mark - Quad Tree Functions 57 | 58 | void HACQuadTreeNodeSubdivide(HACQuadTreeNode* node) 59 | { 60 | HACBoundingBox box = node->boundingBox; 61 | 62 | double xMid = (box.xf + box.x0) / 2.0; 63 | double yMid = (box.yf + box.y0) / 2.0; 64 | 65 | HACBoundingBox northWest = HACBoundingBoxMake(box.x0, box.y0, xMid, yMid); 66 | node->northWest = HACQuadTreeNodeMake(northWest, node->bucketCapacity); 67 | 68 | HACBoundingBox northEast = HACBoundingBoxMake(xMid, box.y0, box.xf, yMid); 69 | node->northEast = HACQuadTreeNodeMake(northEast, node->bucketCapacity); 70 | 71 | HACBoundingBox southWest = HACBoundingBoxMake(box.x0, yMid, xMid, box.yf); 72 | node->southWest = HACQuadTreeNodeMake(southWest, node->bucketCapacity); 73 | 74 | HACBoundingBox southEast = HACBoundingBoxMake(xMid, yMid, box.xf, box.yf); 75 | node->southEast = HACQuadTreeNodeMake(southEast, node->bucketCapacity); 76 | } 77 | 78 | bool HACQuadTreeNodeInsertData(HACQuadTreeNode* node, HACQuadTreeNodeData data) 79 | { 80 | if (!HACBoundingBoxContainsData(node->boundingBox, data)) { 81 | return false; 82 | } 83 | 84 | if (node->count < node->bucketCapacity) { 85 | node->points[node->count++] = data; 86 | return true; 87 | } 88 | 89 | if (node->northWest == NULL) { 90 | HACQuadTreeNodeSubdivide(node); 91 | } 92 | 93 | if (HACQuadTreeNodeInsertData(node->northWest, data)) return true; 94 | if (HACQuadTreeNodeInsertData(node->northEast, data)) return true; 95 | if (HACQuadTreeNodeInsertData(node->southWest, data)) return true; 96 | if (HACQuadTreeNodeInsertData(node->southEast, data)) return true; 97 | 98 | return false; 99 | } 100 | 101 | void HACQuadTreeGatherDataInRange(HACQuadTreeNode* node, HACBoundingBox range, HACDataReturnBlock block) 102 | { 103 | if (!node) return; 104 | 105 | if (!HACBoundingBoxIntersectsBoundingBox(node->boundingBox, range)) return; 106 | 107 | 108 | for (int i = 0; i < node->count; i++) { 109 | if (HACBoundingBoxContainsData(range, node->points[i])) { 110 | block(node->points[i]); 111 | } 112 | } 113 | 114 | if (node->northWest == NULL) { 115 | return; 116 | } 117 | 118 | HACQuadTreeGatherDataInRange(node->northWest, range, block); 119 | HACQuadTreeGatherDataInRange(node->northEast, range, block); 120 | HACQuadTreeGatherDataInRange(node->southWest, range, block); 121 | HACQuadTreeGatherDataInRange(node->southEast, range, block); 122 | } 123 | 124 | void HACQuadTreeTraverse(HACQuadTreeNode* node, HACQuadTreeTraverseBlock block) 125 | { 126 | block(node); 127 | 128 | if (node->northWest == NULL) { 129 | return; 130 | } 131 | 132 | HACQuadTreeTraverse(node->northWest, block); 133 | HACQuadTreeTraverse(node->northEast, block); 134 | HACQuadTreeTraverse(node->southWest, block); 135 | HACQuadTreeTraverse(node->southEast, block); 136 | } 137 | 138 | HACQuadTreeNode* HACQuadTreeBuildWithData(HACQuadTreeNodeData *data, int count, HACBoundingBox boundingBox, int capacity) 139 | { 140 | HACQuadTreeNode* root = HACQuadTreeNodeMake(boundingBox, capacity); 141 | for (int i = 0; i < count; i++) { 142 | HACQuadTreeNodeInsertData(root, data[i]); 143 | } 144 | 145 | return root; 146 | } 147 | 148 | void HACFreeQuadTreeNode(HACQuadTreeNode* node) 149 | { 150 | if (node->northWest != NULL) HACFreeQuadTreeNode(node->northWest); 151 | if (node->northEast != NULL) HACFreeQuadTreeNode(node->northEast); 152 | if (node->southWest != NULL) HACFreeQuadTreeNode(node->southWest); 153 | if (node->southEast != NULL) HACFreeQuadTreeNode(node->southEast); 154 | 155 | for (int i=0; i < node->count; i++) { 156 | free(node->points[i].data); 157 | } 158 | free(node->points); 159 | free(node); 160 | } 161 | 162 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HAClusterAnnotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // HAClusterAnnotation.h 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HAClusterAnnotation : NSObject 13 | 14 | @property (assign, nonatomic) CLLocationCoordinate2D coordinate; 15 | @property (copy, nonatomic) NSString *title; 16 | @property (copy, nonatomic) NSString *subtitle; 17 | @property (assign, nonatomic) NSInteger count; 18 | @property (assign, nonatomic) UIColor* fillColor; 19 | @property (assign, nonatomic) NSInteger index; 20 | @property (copy, nonatomic) NSMutableArray *indexes; 21 | 22 | - (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate count:(NSInteger)count index:(NSInteger)index; 23 | 24 | - (void)updateSubtitleIfNeeded; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HAClusterAnnotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // HAClusterAnnotation.m 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "HAClusterAnnotation.h" 10 | 11 | @implementation HAClusterAnnotation 12 | 13 | - (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate count:(NSInteger)count index:(NSInteger)index 14 | { 15 | self = [super init]; 16 | if (self) { 17 | _coordinate = coordinate; 18 | _title = [NSString stringWithFormat:@"%ld items in this area", (long)count]; 19 | _count = count; 20 | _index = index; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSUInteger)hash 26 | { 27 | NSString *toHash = [NSString stringWithFormat:@"%.5F%.5F", self.coordinate.latitude, self.coordinate.longitude]; 28 | return [toHash hash]; 29 | } 30 | 31 | - (BOOL)isEqual:(id)object 32 | { 33 | return [self hash] == [object hash]; 34 | } 35 | 36 | - (NSString *)stringForPlacemark:(CLPlacemark *)placemark { 37 | 38 | NSMutableString *string = [[NSMutableString alloc] init]; 39 | if (placemark.locality) { 40 | [string appendString:placemark.locality]; 41 | } 42 | 43 | if (placemark.administrativeArea) { 44 | if (string.length > 0) 45 | [string appendString:@", "]; 46 | [string appendString:placemark.administrativeArea]; 47 | } 48 | 49 | if (string.length == 0 && placemark.name) 50 | [string appendString:placemark.name]; 51 | 52 | return string; 53 | } 54 | 55 | - (void)updateSubtitleIfNeeded { 56 | 57 | if (self.subtitle == nil) { 58 | // for the subtitle, we reverse geocode the lat/long for a proper location string name 59 | CLLocation *location = [[CLLocation alloc] initWithLatitude:self.coordinate.latitude longitude:self.coordinate.longitude]; 60 | CLGeocoder *geocoder = [[CLGeocoder alloc] init]; 61 | [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { 62 | if (placemarks.count > 0) { 63 | CLPlacemark *placemark = placemarks[0]; 64 | self.subtitle = [NSString stringWithFormat:@"Near %@", [self stringForPlacemark:placemark]]; 65 | } 66 | }]; 67 | } 68 | } 69 | 70 | 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HAClusterAnnotationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HAClusterAnnotationView.h 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HAClusterAnnotation.h" 11 | 12 | @interface HAClusterAnnotationView : MKAnnotationView 13 | 14 | @property (assign, nonatomic) UIColor *circleBackgroundColor; 15 | @property (assign, nonatomic) UIColor *circleBorderColor; 16 | @property (assign, nonatomic) UIColor *circleTextColor; 17 | @property (assign, nonatomic) NSUInteger count; 18 | 19 | - (id)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier borderColor:(UIColor *)borderColor backgroundColor:(UIColor *)backgroundColor textColor:(UIColor *)textColor; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /HACClusterMapViewController/HAClusterAnnotationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HAClusterAnnotationView.m 3 | // HAClusterMapView 4 | // 5 | // Created by Hipolito Arias on 14/10/15. 6 | // Copyright © 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import "HAClusterAnnotationView.h" 10 | 11 | CGPoint HACRectCenter(CGRect rect) 12 | { 13 | return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 14 | } 15 | 16 | CGRect HACenterRect(CGRect rect, CGPoint center) 17 | { 18 | CGRect r = CGRectMake(center.x - rect.size.width/2.0, 19 | center.y - rect.size.height/2.0, 20 | rect.size.width, 21 | rect.size.height); 22 | return r; 23 | } 24 | 25 | static CGFloat const HACScaleFactorAlpha = 0.3; 26 | static CGFloat const HACScaleFactorBeta = 0.4; 27 | 28 | CGFloat HACScaledValueForValue(CGFloat value) 29 | { 30 | return 1.0 / (1.0 + expf(-1 * HACScaleFactorAlpha * powf(value, HACScaleFactorBeta))); 31 | } 32 | 33 | @interface HAClusterAnnotationView () 34 | @property (strong, nonatomic) UILabel *countLabel; 35 | @end 36 | 37 | @implementation HAClusterAnnotationView 38 | 39 | 40 | - (id)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier borderColor:(UIColor *)borderColor backgroundColor:(UIColor *)backgroundColor textColor:(UIColor *)textColor 41 | { 42 | self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; 43 | if (self) { 44 | self.frame = CGRectMake(0,0,44,44); 45 | self.backgroundColor = [UIColor clearColor]; 46 | self.circleBackgroundColor = backgroundColor; 47 | self.circleBorderColor = borderColor; 48 | self.circleTextColor = textColor; 49 | [self setupLabel]; 50 | [self setCount:1]; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)setupLabel 56 | { 57 | _countLabel = [[UILabel alloc] initWithFrame:self.frame]; 58 | _countLabel.backgroundColor = [UIColor clearColor]; 59 | _countLabel.textColor = self.circleTextColor; 60 | _countLabel.textAlignment = NSTextAlignmentCenter; 61 | _countLabel.shadowColor = [UIColor clearColor]; 62 | _countLabel.shadowOffset = CGSizeMake(0, 0); 63 | _countLabel.adjustsFontSizeToFitWidth = YES; 64 | _countLabel.numberOfLines = 1; 65 | _countLabel.font = [UIFont systemFontOfSize:12]; 66 | _countLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; 67 | [self addSubview:_countLabel]; 68 | } 69 | 70 | - (void)setCount:(NSUInteger)count 71 | { 72 | _count = count; 73 | if (_count != 1) { 74 | _countLabel.hidden = NO; 75 | CGRect newBounds = CGRectMake(0, 0, roundf(44 * HACScaledValueForValue(count)), roundf(44 * HACScaledValueForValue(count))); 76 | self.frame = HACenterRect(newBounds, self.center); 77 | 78 | CGRect newLabelBounds = CGRectMake(0, 0, newBounds.size.width / 1.3, newBounds.size.height / 1.3); 79 | self.countLabel.frame = HACenterRect(newLabelBounds, HACRectCenter(newBounds)); 80 | self.countLabel.text = [@(_count) stringValue]; 81 | 82 | 83 | }else{ 84 | CGRect newBounds = CGRectMake(0, 0, roundf(44 * HACScaledValueForValue(count)), roundf(44 * HACScaledValueForValue(count))); 85 | self.frame = HACenterRect(newBounds, self.center); 86 | 87 | CGRect newLabelBounds = CGRectMake(0, 0, newBounds.size.width / 1.3, newBounds.size.height / 1.3); 88 | self.countLabel.frame = HACenterRect(newLabelBounds, HACRectCenter(newBounds)); 89 | 90 | _countLabel.text=@""; 91 | _countLabel.hidden = YES; 92 | } 93 | [self setNeedsLayout]; 94 | [self setNeedsDisplay]; 95 | } 96 | 97 | - (void)drawRect:(CGRect)rect 98 | { 99 | CGContextRef context = UIGraphicsGetCurrentContext(); 100 | 101 | CGContextSetAllowsAntialiasing(context, true); 102 | 103 | UIColor *outerCircleStrokeColor = [UIColor colorWithWhite:0 alpha:0.25]; 104 | UIColor *innerCircleStrokeColor = self.circleBorderColor; 105 | 106 | UIColor *innerCircleFillColor; 107 | 108 | if ([self.annotation isKindOfClass:[HAClusterAnnotation class]]){ 109 | if (((HAClusterAnnotation*)self.annotation).count==1){ 110 | 111 | innerCircleFillColor= ((HAClusterAnnotation*)self.annotation).fillColor ; 112 | }else{ 113 | innerCircleFillColor= self.circleBackgroundColor; 114 | } 115 | }else{ 116 | innerCircleFillColor= self.circleBackgroundColor; 117 | } 118 | 119 | 120 | 121 | 122 | 123 | CGRect circleFrame = CGRectInset(rect, 4, 4); 124 | 125 | [outerCircleStrokeColor setStroke]; 126 | CGContextSetLineWidth(context, 5.0); 127 | CGContextStrokeEllipseInRect(context, circleFrame); 128 | 129 | [innerCircleStrokeColor setStroke]; 130 | CGContextSetLineWidth(context, 4); 131 | CGContextStrokeEllipseInRect(context, circleFrame); 132 | 133 | [innerCircleFillColor setFill]; 134 | CGContextFillEllipseInRect(context, circleFrame); 135 | } 136 | @end 137 | 138 | -------------------------------------------------------------------------------- /HACClusterMapViewController/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /HACClusterMapViewControllerTests/HACClusterMapViewControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HACClusterMapViewControllerTests.m 3 | // HACClusterMapViewControllerTests 4 | // 5 | // Created by Hipolito Arias on 11/8/15. 6 | // Copyright (c) 2015 MasterApp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HACClusterMapViewControllerTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation HACClusterMapViewControllerTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /HACClusterMapViewControllerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HACClusterMapViewController 2 |
3 | 4 | 5 | 6 | 7 | 8 | HACClusterMapViewController class is written in Objective-C and facilitates the use of maps when they have many pins that show. 9 | Will require us to come up with an ultra quick data structure built for the task. We will need to build it in C for it to be performant. 10 | This version 2.2 has been derived from the original authors version 2.0 , adding more flexibility in the delegates and the display of the annotations as well as correcting some issues. 11 | 12 | ![Preview](https://github.com/litoarias/HACClusterMapViewController/blob/master/ExampleApp/hacclusterviewcontroller.gif) 13 | 14 | ##Requirements and Dependencies 15 | - iOS >= 7.0 16 | - ARC enabled 17 | - CoreLocation Framework 18 | - MKMapKit Framework 19 | 20 | ##Installation 21 | 22 | ####CocoaPods: 23 | 24 | pod 'HACClusterMapViewController' 25 | 26 | ####Carthage: 27 | 28 | github "litoarias/HACClusterMapViewController" 29 | 30 | ####Manual install: 31 | - Copy `HACQuadTree.h` `HACQuadTree.m` `HACManagerQuadTree.h` `HACManagerQuadTree.m` `HAClusterAnnotation.h` `HAClusterAnnotation.m` `HAClusterAnnotationView.h` `HAClusterAnnotationView.m` and `HACMKMapView.h` `HACMKMapView.m` to your project 32 | - Manual install [HACClusterMapViewController](https://github.com/litoarias/HACClusterMapViewController/#manual-install) 33 | 34 | ##Usage 35 | 36 | Import in your .h HACMKMapView 37 | ```objective-c 38 | #import "HACMKMapView.h" 39 | ``` 40 | 41 | #### Map `IBOutlet` 42 | For using Interface Builder, set class in your MKMapView `HACMKMapView` and make `IBOutlet` of your map, and put name to IBOutlet for example `mapView`. 43 | 44 | #### Delegate for using methods 45 | ```objective-c 46 | @interface MapViewController () 47 | - (void)viewDidLoad { 48 | [super viewDidLoad]; 49 | self.mapView.mapDelegate = self; 50 | } 51 | ``` 52 | 53 | #### Creat tree struct 54 | It is easy to use , you must create the following structure in a loop. 55 | ```objective-c 56 | 57 | NSArray *data = @[ 58 | @{kLatitude:@48.47352, kLongitude:@3.87426, kTitle : @"Title 1", kSubtitle : @"", kIndex : @0}, 59 | @{kLatitude:@52.59758, kLongitude:@-1.93061, kTitle : @"Title 2", kSubtitle : @"Subtitle 2", kIndex : @1}, 60 | @{kLatitude:@48.41370, kLongitude:@3.43531, kTitle : @"Title 3", kSubtitle : @"Subtitle 3", kIndex : @2}, 61 | @{kLatitude:@48.31921, kLongitude:@18.10184, kTitle : @"Title 4", kSubtitle : @"Subtitle 4", kIndex : @3}, 62 | @{kLatitude:@47.84302, kLongitude:@22.81101, kTitle : @"Title 5", kSubtitle : @"Subtitle 5", kIndex : @4}, 63 | @{kLatitude:@60.88622, kLongitude:@26.83792, kTitle : @"Title 6", kSubtitle : @"" , kIndex : @5} 64 | ]; 65 | 66 | ``` 67 | 68 | #### We send build 69 | The last step would be to call the driver father and pass the array as a parameter to start the process 70 | ```objective-c 71 | [self.mapView.coordinateQuadTree buildTreeWithArray:data]; 72 | ``` 73 | #### Delegate methods 74 | With delegate methods we can set custom images of annotations or know what index of object be reference. 75 | ```objective-c 76 | 77 | -(void)viewForAnnotationView:(HAClusterAnnotationView *)annotationView annotation:(HAClusterAnnotation *)annotation{ 78 | if (annotation.index % 2 == 0) { 79 | annotationView.image = [UIImage imageNamed:@"pin_museum"]; 80 | }else{ 81 | annotationView.image = [UIImage imageNamed:@"pin_coffee"]; 82 | } 83 | } 84 | 85 | -(void)didSelectAnnotationView:(HAClusterAnnotation *)annotation{ 86 | NSLog(@"You ara select annotation index %ld", (long)annotation.index); 87 | } 88 | 89 | ``` 90 | #### Customize 91 | ### Programmatically 92 | ```objective-c 93 | self.mapView.backgroundAnnotation = [UIColor redColor]; 94 | self.mapView.borderAnnotation = [UIColor whiteColor]; 95 | self.mapView.textAnnotation = [UIColor whiteColor]; 96 | self.mapView.compassFrame = CGRectMake(10, 10, 25, 25); 97 | self.mapView.legalFrame = CGRectMake(CGRectGetWidth([UIScreen mainScreen].bounds)-50, CGRectGetHeight([UIScreen mainScreen].bounds)-50, 50, 50); 98 | ``` 99 | 100 | ### Interface Builder 101 | ![Preview](https://github.com/litoarias/HACClusterMapViewController/blob/master/ExampleApp/IBInspectable.png) 102 | 103 | Enjoy :D 104 | 105 | ## Contributing 106 | 107 | 1. Fork it ( https://github.com/[my-github-username]/HACClusterMapViewController/fork ) 108 | 2. Create your feature branch (`git checkout -b my-new-feature`) 109 | 3. Commit your changes (`git commit -am 'Add some feature'`) 110 | 4. Push to the branch (`git push origin my-new-feature`) 111 | 5. Create a new Pull Request 112 | --------------------------------------------------------------------------------