diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md index bc19cb4..f271c01 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,8 @@ If you want to grab the quad tree you can get it from my [GitHub](https://github or just install using cocoapods ```pod 'TBQuadTree', '~> 0.0'``` + +Swift Version +-------------- +Added a Swift version of the code as an attempt at playing with Swift and figuring out Objective-C code. +Note: this is not an exemplary Swift code, I'm still trying to figure out the best way past all this explict casting hell and might missed some memory management problems. diff --git a/TBAnnotationClustering-Swift.xcodeproj/project.pbxproj b/TBAnnotationClustering-Swift.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6873d76 --- /dev/null +++ b/TBAnnotationClustering-Swift.xcodeproj/project.pbxproj @@ -0,0 +1,355 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2557EE9C1C3801B5001A8883 /* TBHotelInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2557EE9B1C3801B5001A8883 /* TBHotelInfo.swift */; }; + 2557EEA21C3801DC001A8883 /* TBBoundingBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2557EE9F1C3801DC001A8883 /* TBBoundingBox.swift */; }; + 2557EEA31C3801DC001A8883 /* TBQuadTreeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2557EEA01C3801DC001A8883 /* TBQuadTreeNode.swift */; }; + 2557EEA41C3801DC001A8883 /* TBQuadTreeNodeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2557EEA11C3801DC001A8883 /* TBQuadTreeNodeData.swift */; }; + 257D9E021C36DA57004B0025 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E011C36DA57004B0025 /* AppDelegate.swift */; }; + 257D9E041C36DA57004B0025 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E031C36DA57004B0025 /* ViewController.swift */; }; + 257D9E071C36DA57004B0025 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 257D9E051C36DA57004B0025 /* Main.storyboard */; }; + 257D9E091C36DA57004B0025 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 257D9E081C36DA57004B0025 /* Assets.xcassets */; }; + 257D9E0C1C36DA57004B0025 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 257D9E0A1C36DA57004B0025 /* LaunchScreen.storyboard */; }; + 257D9E1C1C36F59D004B0025 /* USA-HotelMotel.csv in Resources */ = {isa = PBXBuildFile; fileRef = 257D9E1B1C36F59D004B0025 /* USA-HotelMotel.csv */; }; + 257D9E211C36F72A004B0025 /* TBHotelCSVTreeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E201C36F72A004B0025 /* TBHotelCSVTreeBuilder.swift */; }; + 257D9E261C370377004B0025 /* TBCoordinateQuadTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E251C370377004B0025 /* TBCoordinateQuadTree.swift */; }; + 257D9E281C371038004B0025 /* TBClusterAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E271C371038004B0025 /* TBClusterAnnotation.swift */; }; + 257D9E2A1C371CD6004B0025 /* TBClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257D9E291C371CD6004B0025 /* TBClusterAnnotationView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2557EE9B1C3801B5001A8883 /* TBHotelInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBHotelInfo.swift; path = "TBAnnotationClustering-Swift/TBHotelInfo.swift"; sourceTree = ""; }; + 2557EE9F1C3801DC001A8883 /* TBBoundingBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBBoundingBox.swift; path = "TBAnnotationClustering-Swift/TBBoundingBox.swift"; sourceTree = ""; }; + 2557EEA01C3801DC001A8883 /* TBQuadTreeNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBQuadTreeNode.swift; path = "TBAnnotationClustering-Swift/TBQuadTreeNode.swift"; sourceTree = ""; }; + 2557EEA11C3801DC001A8883 /* TBQuadTreeNodeData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBQuadTreeNodeData.swift; path = "TBAnnotationClustering-Swift/TBQuadTreeNodeData.swift"; sourceTree = ""; }; + 257D9DFE1C36DA57004B0025 /* TBAnnotationClustering-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TBAnnotationClustering-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 257D9E011C36DA57004B0025 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 257D9E031C36DA57004B0025 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 257D9E061C36DA57004B0025 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 257D9E081C36DA57004B0025 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = "TBAnnotationClustering-Swift/Assets.xcassets"; sourceTree = ""; }; + 257D9E0B1C36DA57004B0025 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 257D9E0D1C36DA57004B0025 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "TBAnnotationClustering-Swift/Info.plist"; sourceTree = ""; }; + 257D9E1B1C36F59D004B0025 /* USA-HotelMotel.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "USA-HotelMotel.csv"; sourceTree = ""; }; + 257D9E201C36F72A004B0025 /* TBHotelCSVTreeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TBHotelCSVTreeBuilder.swift; path = "TBAnnotationClustering-Swift/TBHotelCSVTreeBuilder.swift"; sourceTree = ""; }; + 257D9E251C370377004B0025 /* TBCoordinateQuadTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TBCoordinateQuadTree.swift; sourceTree = ""; }; + 257D9E271C371038004B0025 /* TBClusterAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TBClusterAnnotation.swift; sourceTree = ""; }; + 257D9E291C371CD6004B0025 /* TBClusterAnnotationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TBClusterAnnotationView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 257D9DFB1C36DA57004B0025 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 257D9DF51C36DA57004B0025 = { + isa = PBXGroup; + children = ( + 257D9E1A1C36F56E004B0025 /* Supporting Files */, + 257D9E131C36E048004B0025 /* Data Structures */, + 257D9E221C36F742004B0025 /* Hotel Data */, + 257D9E001C36DA57004B0025 /* TBAnnotationClustering-Swift */, + 257D9DFF1C36DA57004B0025 /* Products */, + ); + sourceTree = ""; + }; + 257D9DFF1C36DA57004B0025 /* Products */ = { + isa = PBXGroup; + children = ( + 257D9DFE1C36DA57004B0025 /* TBAnnotationClustering-Swift.app */, + ); + name = Products; + sourceTree = ""; + }; + 257D9E001C36DA57004B0025 /* TBAnnotationClustering-Swift */ = { + isa = PBXGroup; + children = ( + 257D9E011C36DA57004B0025 /* AppDelegate.swift */, + 257D9E031C36DA57004B0025 /* ViewController.swift */, + 257D9E251C370377004B0025 /* TBCoordinateQuadTree.swift */, + 257D9E271C371038004B0025 /* TBClusterAnnotation.swift */, + 257D9E291C371CD6004B0025 /* TBClusterAnnotationView.swift */, + 257D9E051C36DA57004B0025 /* Main.storyboard */, + 257D9E0A1C36DA57004B0025 /* LaunchScreen.storyboard */, + ); + path = "TBAnnotationClustering-Swift"; + sourceTree = ""; + }; + 257D9E131C36E048004B0025 /* Data Structures */ = { + isa = PBXGroup; + children = ( + 2557EE9F1C3801DC001A8883 /* TBBoundingBox.swift */, + 2557EEA01C3801DC001A8883 /* TBQuadTreeNode.swift */, + 2557EEA11C3801DC001A8883 /* TBQuadTreeNodeData.swift */, + ); + name = "Data Structures"; + sourceTree = ""; + }; + 257D9E1A1C36F56E004B0025 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 257D9E1B1C36F59D004B0025 /* USA-HotelMotel.csv */, + 257D9E081C36DA57004B0025 /* Assets.xcassets */, + 257D9E0D1C36DA57004B0025 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 257D9E221C36F742004B0025 /* Hotel Data */ = { + isa = PBXGroup; + children = ( + 2557EE9B1C3801B5001A8883 /* TBHotelInfo.swift */, + 257D9E201C36F72A004B0025 /* TBHotelCSVTreeBuilder.swift */, + ); + name = "Hotel Data"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 257D9DFD1C36DA57004B0025 /* TBAnnotationClustering-Swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 257D9E101C36DA57004B0025 /* Build configuration list for PBXNativeTarget "TBAnnotationClustering-Swift" */; + buildPhases = ( + 257D9DFA1C36DA57004B0025 /* Sources */, + 257D9DFB1C36DA57004B0025 /* Frameworks */, + 257D9DFC1C36DA57004B0025 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "TBAnnotationClustering-Swift"; + productName = "TBAnnotationClustering-Swift"; + productReference = 257D9DFE1C36DA57004B0025 /* TBAnnotationClustering-Swift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 257D9DF61C36DA57004B0025 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = eyaldar; + TargetAttributes = { + 257D9DFD1C36DA57004B0025 = { + CreatedOnToolsVersion = 7.2; + DevelopmentTeam = CYF3A39EVW; + }; + }; + }; + buildConfigurationList = 257D9DF91C36DA57004B0025 /* Build configuration list for PBXProject "TBAnnotationClustering-Swift" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 257D9DF51C36DA57004B0025; + productRefGroup = 257D9DFF1C36DA57004B0025 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 257D9DFD1C36DA57004B0025 /* TBAnnotationClustering-Swift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 257D9DFC1C36DA57004B0025 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 257D9E0C1C36DA57004B0025 /* LaunchScreen.storyboard in Resources */, + 257D9E091C36DA57004B0025 /* Assets.xcassets in Resources */, + 257D9E071C36DA57004B0025 /* Main.storyboard in Resources */, + 257D9E1C1C36F59D004B0025 /* USA-HotelMotel.csv in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 257D9DFA1C36DA57004B0025 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 257D9E211C36F72A004B0025 /* TBHotelCSVTreeBuilder.swift in Sources */, + 257D9E2A1C371CD6004B0025 /* TBClusterAnnotationView.swift in Sources */, + 257D9E041C36DA57004B0025 /* ViewController.swift in Sources */, + 257D9E021C36DA57004B0025 /* AppDelegate.swift in Sources */, + 2557EE9C1C3801B5001A8883 /* TBHotelInfo.swift in Sources */, + 2557EEA31C3801DC001A8883 /* TBQuadTreeNode.swift in Sources */, + 2557EEA21C3801DC001A8883 /* TBBoundingBox.swift in Sources */, + 257D9E261C370377004B0025 /* TBCoordinateQuadTree.swift in Sources */, + 2557EEA41C3801DC001A8883 /* TBQuadTreeNodeData.swift in Sources */, + 257D9E281C371038004B0025 /* TBClusterAnnotation.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 257D9E051C36DA57004B0025 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 257D9E061C36DA57004B0025 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 257D9E0A1C36DA57004B0025 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 257D9E0B1C36DA57004B0025 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 257D9E0E1C36DA57004B0025 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 257D9E0F1C36DA57004B0025 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 257D9E111C36DA57004B0025 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "TBAnnotationClustering-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.eyaldar.TBAnnotationClustering-Swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 257D9E121C36DA57004B0025 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = "TBAnnotationClustering-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.eyaldar.TBAnnotationClustering-Swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 257D9DF91C36DA57004B0025 /* Build configuration list for PBXProject "TBAnnotationClustering-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 257D9E0E1C36DA57004B0025 /* Debug */, + 257D9E0F1C36DA57004B0025 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 257D9E101C36DA57004B0025 /* Build configuration list for PBXNativeTarget "TBAnnotationClustering-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 257D9E111C36DA57004B0025 /* Debug */, + 257D9E121C36DA57004B0025 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 257D9DF61C36DA57004B0025 /* Project object */; +} diff --git a/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..a3084cf --- /dev/null +++ b/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate b/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a03a3c7 Binary files /dev/null and b/TBAnnotationClustering-Swift.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering-Swift.xcscheme b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering-Swift.xcscheme new file mode 100644 index 0000000..d204d0d --- /dev/null +++ b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering-Swift.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..aa5a1b2 --- /dev/null +++ b/TBAnnotationClustering-Swift.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + TBAnnotationClustering-Swift.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 257D9DFD1C36DA57004B0025 + + primary + + + + + diff --git a/TBAnnotationClustering-Swift/AppDelegate.swift b/TBAnnotationClustering-Swift/AppDelegate.swift new file mode 100644 index 0000000..1b3c646 --- /dev/null +++ b/TBAnnotationClustering-Swift/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/TBAnnotationClustering-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json b/TBAnnotationClustering-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/TBAnnotationClustering-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TBAnnotationClustering-Swift/Base.lproj/LaunchScreen.storyboard b/TBAnnotationClustering-Swift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..342c1ee --- /dev/null +++ b/TBAnnotationClustering-Swift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TBAnnotationClustering-Swift/Base.lproj/Main.storyboard b/TBAnnotationClustering-Swift/Base.lproj/Main.storyboard new file mode 100644 index 0000000..e4f8e63 --- /dev/null +++ b/TBAnnotationClustering-Swift/Base.lproj/Main.storyboard @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TBAnnotationClustering-Swift/Info.plist b/TBAnnotationClustering-Swift/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/TBAnnotationClustering-Swift/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/TBAnnotationClustering-Swift/TBBoundingBox.swift b/TBAnnotationClustering-Swift/TBBoundingBox.swift new file mode 100644 index 0000000..ce10c3b --- /dev/null +++ b/TBAnnotationClustering-Swift/TBBoundingBox.swift @@ -0,0 +1,44 @@ +// +// TBBoundingBox.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +struct TBBoundingBox { + let x0:Double + let y0:Double + let xf:Double + let yf:Double + + private let height:Double + private let width:Double + private let centerX:Double + private let centerY:Double + + init(x:Double, y:Double, xf:Double, yf:Double) { + self.x0 = x + self.y0 = y + self.xf = xf + self.yf = yf + + height = abs(yf - y0) + width = abs(xf - x0) + centerX = x0 + (width / 2) + centerY = y0 + (height / 2) + } + + func containsData(data:TBQuadTreeNodeData) -> Bool { + let containsX = x0 <= data.x && data.x <= xf + let containsY = y0 <= data.y && data.y <= yf + + return containsX && containsY + } + + func intersectWith(other:TBBoundingBox) -> Bool { + let intersectsX = abs(centerX - other.centerX) * 2 < (width + other.width) + let intersectsY = abs(centerY - other.centerY) * 2 < (height + other.height) + return intersectsX && intersectsY + } +} diff --git a/TBAnnotationClustering-Swift/TBClusterAnnotation.swift b/TBAnnotationClustering-Swift/TBClusterAnnotation.swift new file mode 100644 index 0000000..dd98245 --- /dev/null +++ b/TBAnnotationClustering-Swift/TBClusterAnnotation.swift @@ -0,0 +1,35 @@ +// +// TBClusterAllocation.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import MapKit + +class TBClusterAnnotation: NSObject, MKAnnotation { + + let coordinate: CLLocationCoordinate2D + let count:Int + + var title:String? + var subtitle:String? + + init(coordinate:CLLocationCoordinate2D, count:Int) { + self.coordinate = coordinate + self.title = "\(count) hotels in this area" + self.count = count + + super.init() + } + + override func isEqual(object: AnyObject?) -> Bool { + if let other = object as? TBClusterAnnotation { + return coordinate.longitude == other.coordinate.longitude && + coordinate.latitude == other.coordinate.latitude + } + + return false + } +} diff --git a/TBAnnotationClustering-Swift/TBClusterAnnotationView.swift b/TBAnnotationClustering-Swift/TBClusterAnnotationView.swift new file mode 100644 index 0000000..02ef5ce --- /dev/null +++ b/TBAnnotationClustering-Swift/TBClusterAnnotationView.swift @@ -0,0 +1,103 @@ +// +// TBClusterAnnotationView.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import MapKit + +class TBClusterAnnotationView: MKAnnotationView { + + let TBScaleFactorAlpha:Float = 0.3 + let TBScaleFactorBeta:Float = 0.4 + + private var count:Int? + private var countLabel:UILabel? + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override init(frame: CGRect) { + super.init(frame: frame) + } + + override init(annotation: MKAnnotation?, reuseIdentifier: String?) { + super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) + + self.backgroundColor = UIColor.clearColor() + setupLabel() + setCount(1) + } + + func TBScaledValueForValue(value:Float, multiplier:Float) -> Float { + // Multiplier * (1/e^(-Alpha * X^(Beta))) + return multiplier * (1.0 / (1.0 + expf(-1 * TBScaleFactorAlpha * powf(value, TBScaleFactorBeta)))) + } + + func TBCenterRect(rect:CGRect, center: CGPoint) -> CGRect { + let r = CGRect(x: center.x - rect.size.width/2.0, y: center.y - rect.size.height/2.0, width: rect.size.width, height: rect.size.height) + return r + } + + func TBRectCenter(rect: CGRect) -> CGPoint { + return CGPoint(x: CGRectGetMidX(rect), y: CGRectGetMidY(rect)); + } + + func setupLabel() { + countLabel = UILabel(frame: self.frame) + countLabel?.backgroundColor = UIColor.clearColor() + countLabel?.textColor = UIColor.whiteColor() + countLabel?.textAlignment = NSTextAlignment.Center + countLabel?.shadowColor = UIColor(white: 0, alpha: 0.75) + countLabel?.shadowOffset = CGSize(width: 0, height: -1) + countLabel?.adjustsFontSizeToFitWidth = true + countLabel?.numberOfLines = 1 + countLabel?.font = UIFont.boldSystemFontOfSize(12) + countLabel?.baselineAdjustment = UIBaselineAdjustment.AlignCenters + + addSubview(countLabel!) + } + + func setCount(count:Int) { + self.count = count + let size = CGFloat(roundf(TBScaledValueForValue(Float(count), multiplier: 44.0))) + + let newBounds = CGRect(x: 0, y: 0, width: size, height: size) + frame = TBCenterRect(newBounds, center: self.center) + + let newLabelBounds:CGRect = CGRect(x: 0, y: 0, width: newBounds.size.width, height: newBounds.size.height) + countLabel?.frame = TBCenterRect(newLabelBounds, center: TBRectCenter(newBounds)) + countLabel?.text = String(count) + + self.setNeedsDisplay() + } + + override func drawRect(rect: CGRect) { + let context = UIGraphicsGetCurrentContext() + + CGContextSetAllowsAntialiasing(context, true) + + let outerCircleStrokeColor = UIColor(white: 0, alpha: 0.25) + let innerCircleStrokeColor = UIColor.whiteColor() + var innerCircleFillColor = UIColor(red: (255.0 / 255.0), green: (95.0 / 255.0), blue: (42.0 / 255.0), alpha: 1.0) + if(count == 1) { + innerCircleFillColor = UIColor(red: (95.0 / 255.0), green: (150.0 / 255.0), blue: (42.0 / 255.0), alpha: 1.0) + } + + let circleFrame = CGRectInset(rect, 4, 4) + + outerCircleStrokeColor.setStroke() + CGContextSetLineWidth(context, 5.0) + CGContextStrokeEllipseInRect(context, circleFrame) + + innerCircleStrokeColor.setStroke() + CGContextSetLineWidth(context, 4.0) + CGContextStrokeEllipseInRect(context, circleFrame) + + innerCircleFillColor.setFill() + CGContextFillEllipseInRect(context, circleFrame) + } +} diff --git a/TBAnnotationClustering-Swift/TBCoordinateQuadTree.swift b/TBAnnotationClustering-Swift/TBCoordinateQuadTree.swift new file mode 100644 index 0000000..cccfd5c --- /dev/null +++ b/TBAnnotationClustering-Swift/TBCoordinateQuadTree.swift @@ -0,0 +1,115 @@ +// +// File.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import Foundation +import MapKit + +class TBCoordinateQuadTree : NSObject { + + weak var mapView: MKMapView! + let hotelTreeBuilder:TBHotelCSVTreeBuilder + private var root:TBQuadTreeNode? + + init(builder:TBHotelCSVTreeBuilder, mapView: MKMapView) { + self.hotelTreeBuilder = builder + self.mapView = mapView + } + + func buildTree(dataFileName:String, worldBounds:TBBoundingBox) { + root = hotelTreeBuilder.buildTree(dataFileName, worldBounds: worldBounds) + } + + func clusteredAnnotationWithinMapRect(rect:MKMapRect, zoomScale:MKZoomScale) -> [TBClusterAnnotation] { + let tbCellSize = TBCellSizeForZoomScale(zoomScale) + let zoomScaleDouble = Double(zoomScale) + let scaleFactor = zoomScaleDouble / tbCellSize + + let minX = floor(MKMapRectGetMinX(rect) * scaleFactor) + let maxX = floor(MKMapRectGetMaxX(rect) * scaleFactor) + let minY = floor(MKMapRectGetMinY(rect) * scaleFactor) + let maxY = floor(MKMapRectGetMaxY(rect) * scaleFactor) + + var clusteredAnnotations = [TBClusterAnnotation]() + + for var x:Double = minX; x <= maxX; x++ { + for var y:Double = minY; y <= maxY; y++ { + let mapRect = MKMapRectMake(x/scaleFactor, y/scaleFactor, 1.0/scaleFactor, 1.0/scaleFactor) + + var totalX = 0.0 + var totalY = 0.0 + + var names = [String]() + var phoneNumbers = [String]() + + root?.gatherDataInRange(getBoundingBox(mapRect), action: { (data) -> Void in + totalX += data.x + totalY += data.y + + if let hotelInfo = data.data as? TBHotelInfo { + names.append(hotelInfo.hotelName) + phoneNumbers.append(hotelInfo.hotelPhoneNumber) + } + }) + + let count = names.count + + if count > 1 { + let coordinate = CLLocationCoordinate2D(latitude: totalX / Double(count), longitude: totalY / Double(count)) + let annotation = TBClusterAnnotation(coordinate: coordinate, count: count) + clusteredAnnotations.append(annotation) + } + + if count == 1 { + let coordinate = CLLocationCoordinate2D(latitude: totalX, longitude: totalY) + let annotation = TBClusterAnnotation(coordinate: coordinate, count: count) + annotation.title = names.last! + annotation.subtitle = phoneNumbers.last! + clusteredAnnotations.append(annotation) + } + } + } + + return clusteredAnnotations + } + + func getBoundingBox(mapRect:MKMapRect) -> TBBoundingBox { + let topLeft = MKCoordinateForMapPoint(mapRect.origin) + let bottomRight = MKCoordinateForMapPoint(MKMapPoint(x: MKMapRectGetMaxX(mapRect), y: MKMapRectGetMaxY(mapRect))) + + let minLat = bottomRight.latitude + let maxLat = topLeft.latitude + + let minLong = topLeft.longitude + let maxLong = bottomRight.longitude + + return TBBoundingBox(x: minLat, y: minLong, xf: maxLat, yf: maxLong) + } + + func TBZoomScaleToZoomLevel(scale:MKZoomScale) -> Int { + let totalTilesAtMaxZoom = MKMapSizeWorld.width / 256.0 + let zoomLevelAtMaxZoom = Int(log2(totalTilesAtMaxZoom)) + let zoomLevel = Int(max(0, Double(zoomLevelAtMaxZoom) + floor(log2(Double(scale)) + 0.5))); + + return zoomLevel; + } + + func TBCellSizeForZoomScale(zoomScale:MKZoomScale) -> Double { + let zoomLevel = TBZoomScaleToZoomLevel(zoomScale); + + switch (zoomLevel) { + case 13, 14, 15: + return 64 + case 16, 17, 18: + return 32 + case 19: + return 16 + default: + return 88 + } + } +} diff --git a/TBAnnotationClustering-Swift/TBHotelCSVTreeBuilder.swift b/TBAnnotationClustering-Swift/TBHotelCSVTreeBuilder.swift new file mode 100644 index 0000000..d87b1f5 --- /dev/null +++ b/TBAnnotationClustering-Swift/TBHotelCSVTreeBuilder.swift @@ -0,0 +1,53 @@ +// +// TBCoordinateQuadTreeBuilder.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import Foundation + +class TBHotelCSVTreeBuilder { + + func buildTree(dataFileName:String, worldBounds:TBBoundingBox) -> TBQuadTreeNode { + let data = getFileContent(dataFileName) + let lines = data.componentsSeparatedByString("\n") + + var dataArray = [TBQuadTreeNodeData]() + + for line in lines { + if line != "" { + dataArray.append(dataFromLine(line)) + } + } + + return TBQuadTreeNode(boundary: worldBounds, capacity: 4, dataArr: dataArray) + } + + private func dataFromLine(line: NSString) -> TBQuadTreeNodeData { + let components = line.componentsSeparatedByString(",") + + let latitude = Double(components[1].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()))! + let longitude = Double(components[0].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()))! + + let hotelName = components[2].stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + let hotelPhoneNumber = components.last!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + + let hotelInfo = TBHotelInfo(hotelName: hotelName, hotelPhoneNumber: hotelPhoneNumber) + + return TBQuadTreeNodeData(x: latitude, y: longitude, data: hotelInfo) + } + + private func getFileContent(fileName:String) -> String { + let bundle = NSBundle.mainBundle() + let path = bundle.pathForResource(fileName, ofType: "csv")! + var data = "" + + do { + data = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding) + } catch {} + + return data + } +} \ No newline at end of file diff --git a/TBAnnotationClustering-Swift/TBHotelInfo.swift b/TBAnnotationClustering-Swift/TBHotelInfo.swift new file mode 100644 index 0000000..e49cec2 --- /dev/null +++ b/TBAnnotationClustering-Swift/TBHotelInfo.swift @@ -0,0 +1,19 @@ +// +// TBHotelInfo.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import Foundation + +class TBHotelInfo : NSObject{ + let hotelName:String + let hotelPhoneNumber:String + + init(hotelName:String, hotelPhoneNumber:String) { + self.hotelName = hotelName + self.hotelPhoneNumber = hotelPhoneNumber + } +} \ No newline at end of file diff --git a/TBAnnotationClustering-Swift/TBQuadTreeNode.swift b/TBAnnotationClustering-Swift/TBQuadTreeNode.swift new file mode 100644 index 0000000..d6120f8 --- /dev/null +++ b/TBAnnotationClustering-Swift/TBQuadTreeNode.swift @@ -0,0 +1,113 @@ +// +// TBQuadTreeNode.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import Foundation + +class TBQuadTreeNode { + var northWest:TBQuadTreeNode? + var northEast:TBQuadTreeNode? + var southWest:TBQuadTreeNode? + var southEast:TBQuadTreeNode? + var boundingBox:TBBoundingBox + var bucketCapacity:Int + var points = [TBQuadTreeNodeData]() + + init(boundary:TBBoundingBox, capacity:Int, dataArr:[TBQuadTreeNodeData]? = nil) { + self.boundingBox = boundary + self.bucketCapacity = capacity + + if let dataArr = dataArr { + for data in dataArr { + insertData(data) + } + } + } + + var isLeaf: Bool { + return (northWest == nil) + } + + func subdivide() { + let xMid = (boundingBox.xf + boundingBox.x0) / 2.0 + let yMid = (boundingBox.yf + boundingBox.y0) / 2.0 + + let northWestBox = TBBoundingBox(x: boundingBox.x0, y: boundingBox.y0, xf: xMid, yf: yMid) + northWest = TBQuadTreeNode(boundary: northWestBox, capacity: bucketCapacity) + + let northEastBox = TBBoundingBox(x: xMid, y: boundingBox.y0, xf: boundingBox.xf, yf: yMid) + northEast = TBQuadTreeNode(boundary: northEastBox, capacity: bucketCapacity) + + let southWestBox = TBBoundingBox(x: boundingBox.x0, y: yMid, xf: xMid, yf: boundingBox.yf) + southWest = TBQuadTreeNode(boundary: southWestBox, capacity: bucketCapacity) + + let southEastBox = TBBoundingBox(x: xMid, y: yMid, xf: boundingBox.xf, yf: boundingBox.yf) + southEast = TBQuadTreeNode(boundary: southEastBox, capacity: bucketCapacity) + } + + func insertData(data:TBQuadTreeNodeData) -> Bool { + // Bail if our coordinate is not inside the boundingBox + if !boundingBox.containsData(data) { + return false + } + + // Add the coordinate to the points array + if points.count < bucketCapacity { + points.append(data) + return true + } + + // Check to see if the current node is a leaf, in case it is, split + if isLeaf { + subdivide() + } + + // Traverse the tree + if northWest!.insertData(data) { return true } + if northEast!.insertData(data) { return true } + if southWest!.insertData(data) { return true } + if southEast!.insertData(data) { return true } + + return false + } + + func gatherDataInRange(range: TBBoundingBox, action: (TBQuadTreeNodeData) -> Void) { + // If range is not contained in the node's boundingBox then bail + if !boundingBox.intersectWith(range) { + return + } + + for point in points { + if range.containsData(point) { + action(point) + } + } + + // If node isn't leaf traverse down the tree + if !isLeaf { + northWest!.gatherDataInRange(range, action: action) + northEast!.gatherDataInRange(range, action: action) + southWest!.gatherDataInRange(range, action: action) + southEast!.gatherDataInRange(range, action: action) + } + } + + func traverse(action: (TBQuadTreeNode) -> Void) { + action(self) + + if !isLeaf { + northWest!.traverse(action) + northEast!.traverse(action) + southWest!.traverse(action) + southEast!.traverse(action) + } + } + + class func buildWithData(boundary:TBBoundingBox, bucketCapacity:Int) { + + } +} diff --git a/TBAnnotationClustering-Swift/TBQuadTreeNodeData.swift b/TBAnnotationClustering-Swift/TBQuadTreeNodeData.swift new file mode 100644 index 0000000..22ff3f3 --- /dev/null +++ b/TBAnnotationClustering-Swift/TBQuadTreeNodeData.swift @@ -0,0 +1,19 @@ +// +// TBQuadTreeNodeData.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +struct TBQuadTreeNodeData { + let x:Double + let y:Double + let data:AnyObject + + init(x:Double, y:Double, data:AnyObject) { + self.x = x + self.y = y + self.data = data + } +} diff --git a/TBAnnotationClustering-Swift/ViewController.swift b/TBAnnotationClustering-Swift/ViewController.swift new file mode 100644 index 0000000..200cae4 --- /dev/null +++ b/TBAnnotationClustering-Swift/ViewController.swift @@ -0,0 +1,105 @@ +// +// ViewController.swift +// TBAnnotationClustering-Swift +// +// Created by Eyal Darshan on 1/1/16. +// Copyright © 2016 eyaldar. All rights reserved. +// + +import UIKit +import Foundation +import MapKit + +class ViewController: UIViewController, MKMapViewDelegate { + @IBOutlet weak var mapView: MKMapView! + + let TBAnnotatioViewReuseID = "TBAnnotatioViewReuseID" + + // DEFINE THE TREE'S BOUNDS: + let world = TBBoundingBox(x: -90, y: -180, xf: 90, yf: 180) + + let hotelTreeBuilder = TBHotelCSVTreeBuilder() + var tbCoordinateQuadTree:TBCoordinateQuadTree? + + override func viewDidLoad() { + super.viewDidLoad() + + mapView.delegate = self + + tbCoordinateQuadTree = TBCoordinateQuadTree(builder: hotelTreeBuilder, mapView: mapView) + tbCoordinateQuadTree!.buildTree("USA-HotelMotel", worldBounds: world) + } + + func updateMapViewAnnotations(annotations: [MKAnnotation]) { + let before = NSMutableSet(array: self.mapView.annotations) + let after = NSMutableSet(array: annotations) + + let toKeep = NSMutableSet(set: before) + toKeep.intersectsSet(after as Set) + + let toAdd = NSMutableSet(set: after) + toAdd.minusSet(toKeep as Set) + + let toRemove = NSMutableSet(set: before) + toRemove.minusSet(after as Set) + + NSOperationQueue.mainQueue().addOperationWithBlock() { + self.mapView.addAnnotations(toAdd.allObjects as! [MKAnnotation]) + self.mapView.removeAnnotations(toRemove.allObjects as! [MKAnnotation]) + } + } + + func addBounceAnimationToView(view:UIView?) { + let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale") + + bounceAnimation.values = [0.05, 1.1, 0.9, 1] + bounceAnimation.duration = 0.6 + + var timingFunctions = [CAMediaTimingFunction]() + + for _ in 0..<4 { + timingFunctions.append(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)) + } + + bounceAnimation.timingFunctions = timingFunctions + bounceAnimation.removedOnCompletion = false + + view!.layer.addAnimation(bounceAnimation, forKey: "bounce") + } + + // MARK: MKMapViewDelegate + + func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView]) { + for view in views { + addBounceAnimationToView(view) + } + } + + func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) { + NSOperationQueue().addOperationWithBlock() { + let zoomScale = Double(self.mapView.bounds.size.width) / self.mapView.visibleMapRect.size.width + let annotations = self.tbCoordinateQuadTree!.clusteredAnnotationWithinMapRect(mapView.visibleMapRect, zoomScale: MKZoomScale(zoomScale)) + + self.updateMapViewAnnotations(annotations) + } + } + + func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { + + var view:TBClusterAnnotationView? + if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(TBAnnotatioViewReuseID) as? TBClusterAnnotationView { + view = dequeuedView + } else { + view = TBClusterAnnotationView(annotation: annotation, reuseIdentifier: TBAnnotatioViewReuseID) + } + + view?.canShowCallout = true + + if let annotation = annotation as? TBClusterAnnotation { + view?.setCount(annotation.count) + } + + return view + } +} + diff --git a/TBAnnotationClustering.xcodeproj/project.pbxproj b/TBAnnotationClustering.xcodeproj/project.pbxproj index fc0803a..3e1b758 100644 --- a/TBAnnotationClustering.xcodeproj/project.pbxproj +++ b/TBAnnotationClustering.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 257D9E1E1C36F5D7004B0025 /* USA-HotelMotel.csv in Resources */ = {isa = PBXBuildFile; fileRef = 257D9E1D1C36F5D7004B0025 /* USA-HotelMotel.csv */; }; E44063B617FF44A500E3A194 /* TBClusterAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = E44063B517FF44A500E3A194 /* TBClusterAnnotationView.m */; }; E47A6E8D17F62C0700A468D1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E47A6E8C17F62C0700A468D1 /* Foundation.framework */; }; E47A6E8F17F62C0700A468D1 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E47A6E8E17F62C0700A468D1 /* CoreGraphics.framework */; }; @@ -18,9 +19,6 @@ E47A6EA617F62C0700A468D1 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E47A6EA517F62C0700A468D1 /* XCTest.framework */; }; E47A6EA717F62C0700A468D1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E47A6E8C17F62C0700A468D1 /* Foundation.framework */; }; E47A6EA817F62C0700A468D1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E47A6E9017F62C0700A468D1 /* UIKit.framework */; }; - E47A6EB017F62C0700A468D1 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E47A6EAE17F62C0700A468D1 /* InfoPlist.strings */; }; - E47A6EB217F62C0700A468D1 /* TBAnnotationClusteringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E47A6EB117F62C0700A468D1 /* TBAnnotationClusteringTests.m */; }; - E47A6EBC17F62C1000A468D1 /* USA-HotelMotel.csv in Resources */ = {isa = PBXBuildFile; fileRef = E47A6EBB17F62C1000A468D1 /* USA-HotelMotel.csv */; }; E480B06C1804F047006247FA /* TBClusterAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = E480B06B1804F047006247FA /* TBClusterAnnotation.m */; }; E4FB04C417F6302000FC288B /* TBQuadTree.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FB04C317F6302000FC288B /* TBQuadTree.m */; }; E4FB04CD17F6320B00FC288B /* TBCoordinateQuadTree.m in Sources */ = {isa = PBXBuildFile; fileRef = E4FB04CC17F6320B00FC288B /* TBCoordinateQuadTree.m */; }; @@ -40,6 +38,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 257D9E1D1C36F5D7004B0025 /* USA-HotelMotel.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "USA-HotelMotel.csv"; sourceTree = SOURCE_ROOT; }; E44063B417FF44A500E3A194 /* TBClusterAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBClusterAnnotationView.h; sourceTree = ""; }; E44063B517FF44A500E3A194 /* TBClusterAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBClusterAnnotationView.m; sourceTree = ""; }; E47A6E8917F62C0700A468D1 /* TBAnnotationClustering.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TBAnnotationClustering.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,10 +54,6 @@ E47A6E9E17F62C0700A468D1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; E47A6EA417F62C0700A468D1 /* TBAnnotationClusteringTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TBAnnotationClusteringTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E47A6EA517F62C0700A468D1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - E47A6EAD17F62C0700A468D1 /* TBAnnotationClusteringTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TBAnnotationClusteringTests-Info.plist"; sourceTree = ""; }; - E47A6EAF17F62C0700A468D1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - E47A6EB117F62C0700A468D1 /* TBAnnotationClusteringTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TBAnnotationClusteringTests.m; sourceTree = ""; }; - E47A6EBB17F62C1000A468D1 /* USA-HotelMotel.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "USA-HotelMotel.csv"; sourceTree = ""; }; E480B06A1804F047006247FA /* TBClusterAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBClusterAnnotation.h; sourceTree = ""; }; E480B06B1804F047006247FA /* TBClusterAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBClusterAnnotation.m; sourceTree = ""; }; E4FB04C217F6302000FC288B /* TBQuadTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBQuadTree.h; sourceTree = ""; }; @@ -101,7 +96,6 @@ isa = PBXGroup; children = ( E47A6E9217F62C0700A468D1 /* TBAnnotationClustering */, - E47A6EAB17F62C0700A468D1 /* TBAnnotationClusteringTests */, E47A6E8B17F62C0700A468D1 /* Frameworks */, E47A6E8A17F62C0700A468D1 /* Products */, ); @@ -153,7 +147,7 @@ E47A6E9317F62C0700A468D1 /* Supporting Files */ = { isa = PBXGroup; children = ( - E47A6EBB17F62C1000A468D1 /* USA-HotelMotel.csv */, + 257D9E1D1C36F5D7004B0025 /* USA-HotelMotel.csv */, E47A6E9417F62C0700A468D1 /* TBAnnotationClustering-Info.plist */, E47A6E9517F62C0700A468D1 /* InfoPlist.strings */, E47A6E9817F62C0700A468D1 /* main.m */, @@ -162,24 +156,6 @@ name = "Supporting Files"; sourceTree = ""; }; - E47A6EAB17F62C0700A468D1 /* TBAnnotationClusteringTests */ = { - isa = PBXGroup; - children = ( - E47A6EB117F62C0700A468D1 /* TBAnnotationClusteringTests.m */, - E47A6EAC17F62C0700A468D1 /* Supporting Files */, - ); - path = TBAnnotationClusteringTests; - sourceTree = ""; - }; - E47A6EAC17F62C0700A468D1 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - E47A6EAD17F62C0700A468D1 /* TBAnnotationClusteringTests-Info.plist */, - E47A6EAE17F62C0700A468D1 /* InfoPlist.strings */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -225,7 +201,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = TB; - LastUpgradeCheck = 0500; + LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Theodore Calmes"; TargetAttributes = { E47A6EA317F62C0700A468D1 = { @@ -256,7 +232,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E47A6EBC17F62C1000A468D1 /* USA-HotelMotel.csv in Resources */, + 257D9E1E1C36F5D7004B0025 /* USA-HotelMotel.csv in Resources */, E47A6E9717F62C0700A468D1 /* InfoPlist.strings in Resources */, E47A6E9F17F62C0700A468D1 /* Images.xcassets in Resources */, ); @@ -266,7 +242,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E47A6EB017F62C0700A468D1 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -291,7 +266,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E47A6EB217F62C0700A468D1 /* TBAnnotationClusteringTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -314,14 +288,6 @@ name = InfoPlist.strings; sourceTree = ""; }; - E47A6EAE17F62C0700A468D1 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - E47A6EAF17F62C0700A468D1 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -329,7 +295,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -344,6 +309,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -368,7 +334,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -405,6 +370,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TBAnnotationClustering/TBAnnotationClustering-Prefix.pch"; INFOPLIST_FILE = "TBAnnotationClustering/TBAnnotationClustering-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "thoughtbot.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -418,6 +384,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "TBAnnotationClustering/TBAnnotationClustering-Prefix.pch"; INFOPLIST_FILE = "TBAnnotationClustering/TBAnnotationClustering-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "thoughtbot.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -426,7 +393,6 @@ E47A6EB917F62C0700A468D1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TBAnnotationClustering.app/TBAnnotationClustering"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", @@ -449,7 +415,6 @@ E47A6EBA17F62C0700A468D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TBAnnotationClustering.app/TBAnnotationClustering"; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", diff --git a/TBAnnotationClustering.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate b/TBAnnotationClustering.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..7f379f5 Binary files /dev/null and b/TBAnnotationClustering.xcodeproj/project.xcworkspace/xcuserdata/Ran.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering.xcscheme b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering.xcscheme new file mode 100644 index 0000000..95091d1 --- /dev/null +++ b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/TBAnnotationClustering.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..341f744 --- /dev/null +++ b/TBAnnotationClustering.xcodeproj/xcuserdata/Ran.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + TBAnnotationClustering.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + E47A6E8817F62C0700A468D1 + + primary + + + E47A6EA317F62C0700A468D1 + + primary + + + + + diff --git a/TBAnnotationClustering/.DS_Store b/TBAnnotationClustering/.DS_Store new file mode 100644 index 0000000..0b08b17 Binary files /dev/null and b/TBAnnotationClustering/.DS_Store differ diff --git a/TBAnnotationClustering/TBAnnotationClustering-Info.plist b/TBAnnotationClustering/TBAnnotationClustering-Info.plist index 763c73b..5860c3f 100644 --- a/TBAnnotationClustering/TBAnnotationClustering-Info.plist +++ b/TBAnnotationClustering/TBAnnotationClustering-Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - thoughtbot.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/TBAnnotationClustering/TBClusterAnnotation.m b/TBAnnotationClustering/TBClusterAnnotation.m index e9d86c2..c41f6d2 100644 --- a/TBAnnotationClustering/TBClusterAnnotation.m +++ b/TBAnnotationClustering/TBClusterAnnotation.m @@ -15,7 +15,7 @@ - (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate count:(NSInteger)cou self = [super init]; if (self) { _coordinate = coordinate; - _title = [NSString stringWithFormat:@"%d hotels in this area", count]; + _title = [NSString stringWithFormat:@"%ld hotels in this area", (long)count]; _count = count; } return self; diff --git a/TBAnnotationClustering/TBCoordinateQuadTree.m b/TBAnnotationClustering/TBCoordinateQuadTree.m index 7028980..81b65af 100644 --- a/TBAnnotationClustering/TBCoordinateQuadTree.m +++ b/TBAnnotationClustering/TBCoordinateQuadTree.m @@ -90,7 +90,7 @@ @implementation TBCoordinateQuadTree - (void)buildTree { @autoreleasepool { - NSString *data = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"USA-HotelMotel" ofType:@"csv"] encoding:NSASCIIStringEncoding error:nil]; + NSString *data = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"USA-HotelMotel" ofType:@"csv"] encoding:NSUTF8StringEncoding error:nil]; NSArray *lines = [data componentsSeparatedByString:@"\n"]; NSInteger count = lines.count - 1; @@ -100,8 +100,8 @@ - (void)buildTree dataArray[i] = TBDataFromLine(lines[i]); } - TBBoundingBox world = TBBoundingBoxMake(19, -166, 72, -53); - _root = TBQuadTreeBuildWithData(dataArray, count, world, 4); + TBBoundingBox world = TBBoundingBoxMake(-90, -180, 90, 180); + _root = TBQuadTreeBuildWithData(dataArray, (int)count, world, 4); } } diff --git a/TBAnnotationClustering/TBQuadTree.h b/TBAnnotationClustering/TBQuadTree.h index a738ff3..f48cbde 100644 --- a/TBAnnotationClustering/TBQuadTree.h +++ b/TBAnnotationClustering/TBQuadTree.h @@ -18,6 +18,8 @@ TBQuadTreeNodeData TBQuadTreeNodeDataMake(double x, double y, void* data); typedef struct TBBoundingBox { double x0; double y0; double xf; double yf; + double centerX; double centerY; + double width; double height; } TBBoundingBox; TBBoundingBox TBBoundingBoxMake(double x0, double y0, double xf, double yf); diff --git a/TBAnnotationClustering/TBQuadTree.m b/TBAnnotationClustering/TBQuadTree.m index 842d443..ed4d508 100644 --- a/TBAnnotationClustering/TBQuadTree.m +++ b/TBAnnotationClustering/TBQuadTree.m @@ -19,6 +19,8 @@ TBQuadTreeNodeData TBQuadTreeNodeDataMake(double x, double y, void* data) TBBoundingBox TBBoundingBoxMake(double x0, double y0, double xf, double yf) { TBBoundingBox bb; bb.x0 = x0; bb.y0 = y0; bb.xf = xf; bb.yf = yf; + bb.width = fabs(xf - x0); bb.height = fabs(yf - y0); + bb.centerX = x0 + (bb.width / 2); bb.centerY = x0 + (bb.height / 2); return bb; } @@ -50,7 +52,9 @@ bool TBBoundingBoxContainsData(TBBoundingBox box, TBQuadTreeNodeData data) bool TBBoundingBoxIntersectsBoundingBox(TBBoundingBox b1, TBBoundingBox b2) { - return (b1.x0 <= b2.xf && b1.xf >= b2.x0 && b1.y0 <= b2.yf && b1.yf >= b2.y0); + bool intersectsX = fabs(b1.centerX - b2.centerX) * 2 < (b1.width + b2.width); + bool intersectsY = fabs(b1.centerY - b2.centerY) * 2 < (b1.height + b2.height); + return intersectsX && intersectsY; } #pragma mark - Quad Tree Functions diff --git a/TBAnnotationClusteringTests/TBAnnotationClusteringTests-Info.plist b/TBAnnotationClusteringTests/TBAnnotationClusteringTests-Info.plist deleted file mode 100644 index 7e7af8d..0000000 --- a/TBAnnotationClusteringTests/TBAnnotationClusteringTests-Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - thoughtbot.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/TBAnnotationClusteringTests/TBAnnotationClusteringTests.m b/TBAnnotationClusteringTests/TBAnnotationClusteringTests.m deleted file mode 100644 index 6c5e0a2..0000000 --- a/TBAnnotationClusteringTests/TBAnnotationClusteringTests.m +++ /dev/null @@ -1,34 +0,0 @@ -// -// TBAnnotationClusteringTests.m -// TBAnnotationClusteringTests -// -// Created by Theodore Calmes on 9/27/13. -// Copyright (c) 2013 Theodore Calmes. All rights reserved. -// - -#import - -@interface TBAnnotationClusteringTests : XCTestCase - -@end - -@implementation TBAnnotationClusteringTests - -- (void)setUp -{ - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testExample -{ - XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); -} - -@end diff --git a/TBAnnotationClusteringTests/en.lproj/InfoPlist.strings b/TBAnnotationClusteringTests/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28f..0000000 --- a/TBAnnotationClusteringTests/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/TBAnnotationClustering/USA-HotelMotel.csv b/USA-HotelMotel.csv similarity index 100% rename from TBAnnotationClustering/USA-HotelMotel.csv rename to USA-HotelMotel.csv