### Implement CoordinateIdentifiable Protocol for Custom Annotations Source: https://context7.com/vospennikov/clustermap/llms.txt Demonstrates implementing the CoordinateIdentifiable protocol for custom annotations, including controlling clustering behavior with the `shouldCluster` property and integrating with `MKMapItem`. Basic and selective annotations are shown, along with an example of adding annotations to a ClusterManager. ```swift import ClusterMap import CoreLocation import MapKit // Basic annotation for SwiftUI struct BasicAnnotation: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D } // Annotation with clustering control struct SelectiveAnnotation: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D let isPermanent: Bool // Override default clustering behavior var shouldCluster: Bool { return !isPermanent } } // MapKit integration with MKMapItem extension MKMapItem: @unchecked Sendable {} extension MKMapItem: CoordinateIdentifiable { public var coordinate: CLLocationCoordinate2D { get { placemark.coordinate } set { /* MKMapItem coordinate is read-only */ } } } // Usage example let annotation1 = BasicAnnotation( coordinate: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060) ) let annotation2 = SelectiveAnnotation( coordinate: CLLocationCoordinate2D(latitude: 40.7580, longitude: -73.9855), isPermanent: true // This annotation will never be clustered ) let clusterManager = ClusterManager() await clusterManager.add([annotation2]) ``` -------------------------------- ### Swift: Define Identifiable Annotations for ClusterMap Source: https://github.com/vospennikov/clustermap/blob/main/README.md Defines custom annotation types that conform to `CoordinateIdentifiable`, `Identifiable`, and `Hashable` protocols, enabling them to be used with the ClusterMap library. This includes examples for SwiftUI annotations and extensions for MapKit's `MKMapItem` and `MKPointAnnotation`. ```swift // SwiftUI annotation struct ExampleAnnotation: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D } // MapKit (MKMapItem) integration extension MKMapItem: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D { get { placemark.coordinate } set(newValue) { } } } // MapKit (MKPointAnnotation) integration class ExampleAnnotation: MKPointAnnotation, CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() } ``` -------------------------------- ### Initialize Cluster Manager with Default or Custom Configuration (Swift) Source: https://context7.com/vospennikov/clustermap/llms.txt Demonstrates how to create an instance of ClusterManager for managing and clustering map annotations. It shows initialization with default settings and with a custom configuration object that allows fine-tuning parameters like max zoom level, minimum cluster size, and cell size based on zoom. ```swift import ClusterMap import CoreLocation import MapKit // Define your annotation type conforming to required protocols struct CityAnnotation: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D let name: String } // Create manager with default configuration let clusterManager = ClusterManager() // Or create with custom configuration let customConfig = ClusterManager.Configuration( maxZoomLevel: 18, minCountForClustering: 3, shouldRemoveInvisibleAnnotations: true, shouldDistributeAnnotationsOnSameCoordinate: true, distanceFromContestedLocation: 3, clusterPosition: .nearCenter, cellSizeForZoomLevel: { zoom in switch zoom { case 13...15: return CGSize(width: 64, height: 64) case 16...18: return CGSize(width: 32, height: 32) case 19...: return CGSize(width: 16, height: 16) default: return CGSize(width: 88, height: 88) } } ) let customClusterManager = ClusterManager(configuration: customConfig) ``` -------------------------------- ### Fetch Visible and Nested Annotations with ClusterMap Source: https://context7.com/vospennikov/clustermap/llms.txt Shows how to retrieve annotations visible on the map or nested within visible clusters using the ClusterMap library. It includes adding sample data, triggering clustering computation, and then fetching visible annotations, nested annotations, and all annotations. ```swift import ClusterMap import CoreLocation let clusterManager = ClusterManager() // Add sample data let cities = [ CityAnnotation(coordinate: CLLocationCoordinate2D(latitude: 64.1466, longitude: -21.9426), name: "Reykjavik"), CityAnnotation(coordinate: CLLocationCoordinate2D(latitude: 65.6835, longitude: -18.1002), name: "Akureyri"), CityAnnotation(coordinate: CLLocationCoordinate2D(latitude: 66.0449, longitude: -17.3389), name: "Husavik") ] await clusterManager.add(cities) // Trigger clustering computation let mapSize = CGSize(width: 400, height: 600) let region = MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 65.0, longitude: -19.0), span: MKCoordinateSpan(latitudeDelta: 5.0, longitudeDelta: 5.0) ) await clusterManager.reload(mapViewSize: mapSize, coordinateRegion: region) // Get all visible annotations (both individual and clusters) let visibleAnnotations = await clusterManager.visibleAnnotations for annotation in visibleAnnotations { switch annotation { case .annotation(let city): print("Visible city: (city.name)") case .cluster(let cluster): print("Cluster with (cluster.memberAnnotations.count) cities") } } // Get all nested annotations within visible clusters let nestedAnnotations = await clusterManager.fetchVisibleNestedAnnotations() print("Total visible cities (including clustered): (nestedAnnotations.count)") // Get all annotations regardless of visibility let allAnnotations = await clusterManager.fetchAllAnnotations() print("Total annotations in manager: (allAnnotations.count)") ``` -------------------------------- ### Custom Cluster Alignment Strategies in Swift Source: https://context7.com/vospennikov/clustermap/llms.txt Demonstrates how to use built-in cluster alignment strategies (.center, .nearCenter, .average, .first) and how to implement a custom strategy for positioning cluster annotations. Requires ClusterMap, CoreLocation, and MapKit frameworks. ```swift import ClusterMap import CoreLocation import MapKit // Using built-in alignment strategies let centerConfig = ClusterManager.Configuration( clusterPosition: .center // Cluster at the center of the grid cell ) let nearCenterConfig = ClusterManager.Configuration( clusterPosition: .nearCenter // Cluster at the annotation nearest to center ) let averageConfig = ClusterManager.Configuration( clusterPosition: .average // Cluster at the average position of all annotations ) let firstConfig = ClusterManager.Configuration( clusterPosition: .first // Cluster at the first annotation's position ) // Custom alignment strategy struct WeightedAverageStrategy: ClusterAlignmentStrategy { func calculatePosition( for coordinates: [CLLocationCoordinate2D], within mapRect: MKMapRect ) -> CLLocationCoordinate2D { guard !coordinates.isEmpty else { return MKMapRect.world.center.coordinate } // Weight coordinates toward the center of the cluster let centerCoordinate = coordinates.reduce(CLLocationCoordinate2D(latitude: 0, longitude: 0)) { result, coord in CLLocationCoordinate2D( latitude: result.latitude + coord.latitude, longitude: result.longitude + coord.longitude ) } let count = Double(coordinates.count) return CLLocationCoordinate2D( latitude: centerCoordinate.latitude / count, longitude: centerCoordinate.longitude / count ) } } // Use custom strategy let customAlignment = ClusterAlignment(alignmentStrategy: WeightedAverageStrategy()) let customConfig = ClusterManager.Configuration( clusterPosition: customAlignment ) let manager = ClusterManager(configuration: customConfig) ``` -------------------------------- ### Swift: Initialize and Manage Points with ClusterManager Source: https://github.com/vospennikov/clustermap/blob/main/README.md Demonstrates the initialization of the `ClusterManager` with a specific annotation type and how to add, remove, and clear points using `async/await` operations. This is the core usage pattern for managing map annotations with ClusterMap. ```swift let clusterManager = ClusterManager() let reykjavik = ExampleAnnotation(coordinates: CLLocationCoordinate2D(latitude: 64.1466, longitude: -21.9426)) let akureyri = ExampleAnnotation(coordinates: CLLocationCoordinate2D(latitude: 65.6835, longitude: -18.1002)) let husavik = ExampleAnnotation(coordinates: CLLocationCoordinate2D(latitude: 66.0449, longitude: -17.3389)) let cities = [reykjavik, akureyri, husavik] await clusterManager.add(cities) await clusterManager.remove(husavik) await clusterManager.removeAll() ``` -------------------------------- ### Live Implementation of ClusterMapClient for TCA Source: https://github.com/vospennikov/clustermap/blob/main/README.md Provides a live implementation for the ClusterMapClient dependency key in TCA. This implementation uses the ClusterManager to add, reload, and retrieve visible annotations (objects and clusters) based on map region and size. ```swift extension ClusterMapClient: DependencyKey { static var liveValue = ClusterMapClient( clusterObjects: { inputObjects, mapRegion, mapSize in let clusterManager = ClusterManager() await clusterManager.add(inputObjects) await clusterManager.reload(mapViewSize: mapSize, coordinateRegion: mapRegion) var objects: [ExampleAnnotation] = [] var clusters: [ExampleClusterAnnotation] = [] await clusterManager.visibleAnnotations.forEach { switch $0 { case .annotation(let annotation): objects.append(annotation) case .cluster(let cluster): clusters.append(.init(coordinate: cluster.coordinate)) } } return .init(objects: objects, clusters: clusters) } ) } ``` -------------------------------- ### Integrate ClusterMap with MKMapView using Swift Source: https://context7.com/vospennikov/clustermap/llms.txt This Swift code demonstrates how to integrate ClusterMap with MKMapView. It sets up a MapViewController, initializes ClusterManager, loads sample points, and reloads the map with annotation differences. It also includes MapViewDelegate methods for handling region changes and custom annotation views. ```swift import ClusterMap import MapKit import UIKit class MapViewController: UIViewController { private let mapView = MKMapView() private let clusterManager = ClusterManager() private var annotations: [PointAnnotation] = [] override func viewDidLoad() { super.viewDidLoad() view.addSubview(mapView) mapView.frame = view.bounds mapView.delegate = self // Load initial data Task { let points = generateSamplePoints() await clusterManager.add(points) await reloadMap() } } func reloadMap() async { let difference = await clusterManager.reload( mapViewSize: mapView.bounds.size, coordinateRegion: mapView.region ) await applyChanges(difference) } func applyChanges(_ difference: ClusterManager.Difference) async { await MainActor.run { // Remove annotations for annotationType in difference.removals { switch annotationType { case .annotation(let annotation): annotations.removeAll(where: { $0.id == annotation.id }) mapView.removeAnnotation(annotation) case .cluster(let clusterAnnotation): if let result = annotations.enumerated().first(where: { $0.element.id == clusterAnnotation.id }) { annotations.remove(at: result.offset) mapView.removeAnnotation(result.element) } } } // Add annotations for annotationType in difference.insertions { switch annotationType { case .annotation(let annotation): annotations.append(annotation) mapView.addAnnotation(annotation) case .cluster(let clusterAnnotation): let cluster = ClusterMKAnnotation() cluster.id = clusterAnnotation.id cluster.coordinate = clusterAnnotation.coordinate cluster.memberAnnotations = clusterAnnotation.memberAnnotations annotations.append(cluster) mapView.addAnnotation(cluster) } } } } func generateSamplePoints() -> [PointAnnotation] { // Generate sample data... return [] } } extension MapViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { Task { await reloadMap() } } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if let cluster = annotation as? ClusterMKAnnotation { let identifier = "ClusterAnnotation" let view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) ?? MKAnnotationView(annotation: annotation, reuseIdentifier: identifier) view.annotation = cluster // Customize cluster view... return view } return nil } } class PointAnnotation: NSObject, MKAnnotation, CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() dynamic var coordinate: CLLocationCoordinate2D init(coordinate: CLLocationCoordinate2D) { self.coordinate = coordinate super.init() } static func == (lhs: PointAnnotation, rhs: PointAnnotation) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } class ClusterMKAnnotation: PointAnnotation { var memberAnnotations: [PointAnnotation] = [] } ``` -------------------------------- ### Swift Package Manager: Add ClusterMap Dependency Source: https://github.com/vospennikov/clustermap/blob/main/README.md Provides instructions for integrating the ClusterMap library into an Xcode project using Swift Package Manager (SPM). It includes adding the package repository URL and specifying the product dependency in the `Package.swift` manifest. ```swift // In Package.swift .package(url: "https://github.com/vospennikov/ClusterMap.git", from: "2.1.0") // In target dependencies .target( name: "MyTarget", dependencies: [ .product(name: "ClusterMap", package: "ClusterMap"), ] ) ``` -------------------------------- ### SwiftUI Map Annotation Clustering Source: https://context7.com/vospennikov/clustermap/llms.txt Demonstrates how to integrate cluster management with SwiftUI's Map view. It computes clusters based on map view size and camera position, applying differences to displayed annotations. This requires iOS 17+ and the ClusterMap library. ```swift import ClusterMap import MapKit import SwiftUI // SwiftUI iOS 17+ integration struct MapView: View { @State private var mapSize: CGSize = .zero @State private var annotations: [CityAnnotation] = [] @State private var clusters: [ClusterDisplayAnnotation] = [] let clusterManager = ClusterManager() var body: some View { Map { ForEach(annotations) { annotation in Annotation(annotation.name, coordinate: annotation.coordinate) { Circle() .fill(.blue) .frame(width: 20, height: 20) } } ForEach(clusters) { cluster in Annotation("\(cluster.count)", coordinate: cluster.coordinate) { Circle() .fill(.red) .frame(width: 40, height: 40) .overlay(Text("\(cluster.count)").foregroundColor(.white)) } } } .readSize { newSize in mapSize = newSize } .onMapCameraChange(frequency: .onEnd) { context in Task.detached { let difference = await clusterManager.reload( mapViewSize: mapSize, coordinateRegion: context.region ) await applyChanges(difference) } } .task { // Add sample data let sampleCities = generateSampleCities() await clusterManager.add(sampleCities) } } func applyChanges(_ difference: ClusterManager.Difference) async { for removal in difference.removals { switch removal { case .annotation(let annotation): annotations.removeAll { $0.id == annotation.id } case .cluster(let clusterAnnotation): clusters.removeAll { $0.id == clusterAnnotation.id } } } for insertion in difference.insertions { switch insertion { case .annotation(let annotation): annotations.append(annotation) case .cluster(let clusterAnnotation): clusters.append(ClusterDisplayAnnotation( id: clusterAnnotation.id, coordinate: clusterAnnotation.coordinate, count: clusterAnnotation.memberAnnotations.count )) } } } func generateSampleCities() -> [CityAnnotation] { // Generate sample data... return [] } } struct ClusterDisplayAnnotation: Identifiable { let id: UUID let coordinate: CLLocationCoordinate2D let count: Int } ``` -------------------------------- ### SwiftUI: Apply ClusterMap Difference to Annotations Source: https://github.com/vospennikov/clustermap/blob/main/README.md Processes the `ClusterManager.Difference` returned by the reload method to update the displayed annotations and clusters. It handles both removals and insertions of individual annotations and cluster annotations. ```swift private var annotations: [ExampleAnnotation] = [] private var clusters: [ExampleClusterAnnotation] = [] func applyChanges(_ difference: ClusterManager.Difference) { for removal in difference.removals { switch removal { case .annotation(let annotation): annotations.removeAll { $0 == annotation } case .cluster(let clusterAnnotation): clusters.removeAll { $0.id == clusterAnnotation.id } } } for insertion in difference.insertions { switch insertion { case .annotation(let newItem): annotations.append(newItem) case .cluster(let newItem): clusters.append(ExampleClusterAnnotation( id: newItem.id, coordinate: newItem.coordinate, count: newItem.memberAnnotations.count )) } } } ``` -------------------------------- ### SwiftUI Size Reading Helper for Map Calculations Source: https://context7.com/vospennikov/clustermap/llms.txt Illustrates using the `.readSize` view modifier from ClusterMapSwiftUI to automatically read and update view dimensions, simplifying map size calculations. An alternative manual approach using GeometryReader is also shown. Requires ClusterMapSwiftUI, MapKit, and SwiftUI. ```swift import ClusterMapSwiftUI import MapKit import SwiftUI struct ContentView: View { @State private var mapSize: CGSize = .zero @State private var position: MapCameraPosition = .automatic let clusterManager = ClusterManager() var body: some View { Map(position: $position) .readSize { newSize in mapSize = newSize print("Map size changed: \(newSize)") } .onMapCameraChange(frequency: .onEnd) { context in Task.detached { await clusterManager.reload( mapViewSize: mapSize, coordinateRegion: context.region ) } } } } // Alternative manual approach without ClusterMapSwiftUI struct ManualSizeReadingView: View { @State private var mapSize: CGSize = .zero var body: some View { GeometryReader { geometryProxy in Map() .onAppear { mapSize = geometryProxy.size } .onChange(of: geometryProxy.size) { oldValue, newValue in mapSize = newValue } } } } struct LocationAnnotation: CoordinateIdentifiable, Identifiable, Hashable { let id = UUID() var coordinate: CLLocationCoordinate2D } ``` -------------------------------- ### Add, Remove, and Retrieve Annotations with ClusterMap (Swift) Source: https://context7.com/vospennikov/clustermap/llms.txt Illustrates how to manage annotations within the ClusterManager. This includes adding single or multiple annotations, removing specific annotations or all of them, and retrieving all currently managed annotations. All operations are asynchronous and thread-safe. ```swift import ClusterMap import CoreLocation // Create sample annotations let reykjavik = CityAnnotation( coordinate: CLLocationCoordinate2D(latitude: 64.1466, longitude: -21.9426), name: "Reykjavik" ) let akureyri = CityAnnotation( coordinate: CLLocationCoordinate2D(latitude: 65.6835, longitude: -18.1002), name: "Akureyri" ) let husavik = CityAnnotation( coordinate: CLLocationCoordinate2D(latitude: 66.0449, longitude: -17.3389), name: "Husavik" ) // Add single annotation await clusterManager.add(reykjavik) // Add multiple annotations let cities = [akureyri, husavik] await clusterManager.add(cities) // Remove single annotation await clusterManager.remove(husavik) // Remove multiple annotations await clusterManager.remove([reykjavik, akureyri]) // Remove all annotations await clusterManager.removeAll() // Retrieve all stored annotations let allAnnotations = await clusterManager.fetchAllAnnotations() print("Total annotations: \(allAnnotations.count)") ``` -------------------------------- ### TCA Dependency for ClusterMap Client Source: https://github.com/vospennikov/clustermap/blob/main/README.md Defines the ClusterMapClient structure and extends DependencyValues to make it accessible as a TCA dependency. This involves defining the `clusterObjects` function signature for clustering annotations. ```swift struct ClusterMapClient { struct ClusterClientResult { let objects: [ExampleAnnotation] let clusters: [ExampleClusterAnnotation] } var clusterObjects: @Sendable ([ExampleAnnotation], MKCoordinateRegion, CGSize) async -> ClusterClientResult } extension DependencyValues { var clusterMapClient: ClusterMapClient { get { self[ClusterMapClient.self] } set { self[ClusterMapClient.self] = newValue } } } ``` -------------------------------- ### UIKit: Apply ClusterMap Difference to MKMapView Source: https://github.com/vospennikov/clustermap/blob/main/README.md Applies the `ClusterManager.Difference` to an `MKMapView` in a UIKit environment. This involves removing and adding `MKAnnotation` objects, including custom cluster annotations, to the map view based on the calculated differences. ```swift var annotations: [ExampleAnnotation] = [] private func applyChanges(_ difference: ClusterManager.Difference) { for annotationType in difference.removals { switch annotationType { case .annotation(let annotation): annotations.removeAll(where: { $0 == annotation }) mapView.removeAnnotation(annotation) case .cluster(let clusterAnnotation): if let result = annotations.enumerated().first(where: { $0.element.id == clusterAnnotation.id }) { annotations.remove(at: result.offset) mapView.removeAnnotation(result.element) } } } for annotationType in difference.insertions { switch annotationType { case .annotation(let annotation): annotations.append(annotation) mapView.addAnnotation(annotation) case .cluster(let clusterAnnotation): let cluster = ClusterAnnotation() cluster.id = clusterAnnotation.id cluster.coordinate = clusterAnnotation.coordinate cluster.memberAnnotations = clusterAnnotation.memberAnnotations annotations.append(cluster) mapView.addAnnotation(cluster) } } } ``` -------------------------------- ### SwiftUI: Read Map Size with .readSize Modifier Source: https://github.com/vospennikov/clustermap/blob/main/README.md An alternative method to read map size in SwiftUI using the prebuilt `.readSize` modifier from the ClusterMapSwiftUI package. This simplifies the process of obtaining and updating the map's dimensions. ```swift import ClusterMapSwiftUI @State private var mapSize: CGSize = .zero var body: some View { Map() .readSize(onChange: { newValue in mapSize = newValue }) } ``` -------------------------------- ### SwiftUI: Read Map Size with GeometryReader Source: https://github.com/vospennikov/clustermap/blob/main/README.md Reads the map size using SwiftUI's GeometryReader and updates a state variable. This is necessary for ClusterMap to perform correct cluster calculations. The size is updated on appearance and when the geometry changes. ```swift @State private var mapSize: CGSize = .zero var body: some View { GeometryReader(content: { geometryProxy in Map() .onAppear(perform: { mapSize = geometryProxy.size }) .onChange(of: geometryProxy.size) { oldValue, newValue in mapSize = newValue } }) } ``` -------------------------------- ### UIKit: Reload ClusterMap with MKMapView Size Source: https://github.com/vospennikov/clustermap/blob/main/README.md Reloads the ClusterMap by directly accessing the size of the `MKMapView`. This method is used in UIKit integration to provide the necessary map dimensions for cluster calculations. ```swift var mapView = MKMapView(frame: .zero) func reloadMap() async { await clusterManager.reload(mapViewSize: mapView.bounds.size, coordinateRegion: mapView.region) } ``` -------------------------------- ### SwiftUI: Reload ClusterMap on Camera Change Source: https://github.com/vospennikov/clustermap/blob/main/README.md Reloads the ClusterMap when the map camera position changes, specifically using the .onMapCameraChange modifier for iOS 17+. It passes the map view size and the current coordinate region to the cluster manager for recalculation. ```swift Map() .onMapCameraChange(frequency: .onEnd) { context in Task.detached { await clusterManager.reload(mapViewSize: mapSize, coordinateRegion: context.region) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.