Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

adding configuration #135

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 47 additions & 32 deletions Sources/Cluster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,13 @@
import CoreLocation
import MapKit

open class ClusterManager {

var tree = QuadTree(rect: MKMapRectWorld)

/**
The size of each cell on the grid (The larger the size, the better the performance).

If nil, automatically adjusts the cell size to zoom level. The default is nil.
*/
open var cellSize: Double?

/**
The current zoom level of the visible map region.

Min value is 0 (max zoom out), max is 20 (max zoom in).
*/
open internal(set) var zoomLevel: Double = 0

open class Configuration {
/**
The maximum zoom level before disabling clustering.

Min value is 0 (max zoom out), max is 20 (max zoom in). The default is 20.
*/
open var maxZoomLevel: Double = .maxZoomLevel
open var maxZoomLevel: Double = 20

/**
The minimum number of annotations for a cluster.
Expand All @@ -55,6 +38,8 @@ open class ClusterManager {
*/
open var shouldDistributeAnnotationsOnSameCoordinate: Bool = true

open var marginFactor: Double = -1

/**
The position of the cluster annotation.
*/
Expand Down Expand Up @@ -85,6 +70,33 @@ open class ClusterManager {
*/
open var clusterPosition: ClusterPosition = .nearCenter

/**
The size of each cell on the grid (The larger the size, the better the performance).

If nil, automatically adjusts the cell size to zoom level. The default is nil.
*/
open var cellSize: (_ zoomScale: Double) -> CGSize = { CGSize(width: $0.cellSize, height: $0.cellSize) }
}

public protocol ClusterManagerDelegate: class {
func clusterManager(_ manager: ClusterManager, cellSizeFor zoomScale: Double) -> CGSize
}

open class ClusterManager {

var tree = QuadTree(rect: MKMapRectWorld)

open let configuration = Configuration()

open weak var delegate: ClusterManagerDelegate?

/**
The current zoom level of the visible map region.

Min value is 0 (max zoom out), max is 20 (max zoom in).
*/
open internal(set) var zoomLevel: Double = 0

/**
The list of annotations associated.

Expand Down Expand Up @@ -181,6 +193,7 @@ open class ClusterManager {
let visibleMapRect = mapView.visibleMapRect
let visibleMapRectWidth = visibleMapRect.size.width
let zoomScale = Double(mapBounds.width) / visibleMapRectWidth
print(mapView.zoomLevel)
queue.cancelAllOperations()
queue.addBlockOperation { [weak self, weak mapView] operation in
guard let `self` = self, let mapView = mapView else { return }
Expand All @@ -199,15 +212,17 @@ open class ClusterManager {
open func clusteredAnnotations(zoomScale: Double, visibleMapRect: MKMapRect, operation: Operation? = nil) -> (toAdd: [MKAnnotation], toRemove: [MKAnnotation]) {
var isCancelled: Bool { return operation?.isCancelled ?? false }

guard !zoomScale.isInfinite, !zoomScale.isNaN else { return (toAdd: [], toRemove: []) }
guard !MKMapRectIsNull(visibleMapRect), !MKMapRectIsEmpty(visibleMapRect) else { return (toAdd: [], toRemove: []) }

zoomLevel = zoomScale.zoomLevel
let scaleFactor = zoomScale / (cellSize ?? zoomScale.cellSize)

let minX = Int(floor(visibleMapRect.minX * scaleFactor))
let maxX = Int(floor(visibleMapRect.maxX * scaleFactor))
let minY = Int(floor(visibleMapRect.minY * scaleFactor))
let maxY = Int(floor(visibleMapRect.maxY * scaleFactor))
// zoomLevel = zoomScale.zoomLevel
zoomLevel = 0
let scaleFactorX = zoomScale / Double(configuration.cellSize(zoomScale).width)
let scaleFactorY = zoomScale / Double(configuration.cellSize(zoomScale).height)

let minX = Int(floor(visibleMapRect.minX * scaleFactorX))
let maxX = Int(floor(visibleMapRect.maxX * scaleFactorX))
let minY = Int(floor(visibleMapRect.minY * scaleFactorY))
let maxY = Int(floor(visibleMapRect.maxY * scaleFactorY))

var allAnnotations = [MKAnnotation]()

Expand All @@ -216,7 +231,7 @@ open class ClusterManager {

for x in minX...maxX {
for y in minY...maxY {
var mapRect = MKMapRect(x: Double(x) / scaleFactor, y: Double(y) / scaleFactor, width: 1 / scaleFactor, height: 1 / scaleFactor)
var mapRect = MKMapRect(x: Double(x) / scaleFactorX, y: Double(y) / scaleFactorY, width: 1 / scaleFactorX, height: 1 / scaleFactorY)
if mapRect.origin.x > MKMapPointMax.x {
mapRect.origin.x -= MKMapPointMax.x
}
Expand All @@ -237,7 +252,7 @@ open class ClusterManager {
}

// handle annotations on the same coordinate
for value in hash.values where shouldDistributeAnnotationsOnSameCoordinate && value.count > 1 {
for value in hash.values where configuration.shouldDistributeAnnotationsOnSameCoordinate && value.count > 1 {
for (index, node) in value.enumerated() {
let distanceFromContestedLocation = 3 * Double(value.count) / 2
let radiansBetweenAnnotations = (.pi * 2) / Double(value.count)
Expand All @@ -248,9 +263,9 @@ open class ClusterManager {

// handle clustering
let count = annotations.count
if count >= minCountForClustering, zoomLevel <= maxZoomLevel {
if count >= configuration.minCountForClustering, zoomLevel <= configuration.maxZoomLevel {
let cluster = ClusterAnnotation()
switch clusterPosition {
switch configuration.clusterPosition {
case .center:
cluster.coordinate = MKCoordinateForMapPoint(MKMapPoint(x: mapRect.midX, y: mapRect.midY))
case .nearCenter:
Expand Down Expand Up @@ -285,7 +300,7 @@ open class ClusterManager {
var toRemove = before.subtracted(after)
let toAdd = after.subtracted(before)

if !shouldRemoveInvisibleAnnotations {
if !configuration.shouldRemoveInvisibleAnnotations {
let nonRemoving = toRemove.filter { !visibleMapRect.contains($0.coordinate) }
toRemove.subtract(nonRemoving)
}
Expand Down
31 changes: 31 additions & 0 deletions Sources/ClusterMap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// ClusterMap.swift
// Cluster
//
// Created by Lasha Efremidze on 7/30/18.
// Copyright © 2018 efremidze. All rights reserved.
//

import MapKit

public struct Animation {
let annotations: [ClusterAnnotation]
var from: CLLocationCoordinate2D
var to: CLLocationCoordinate2D
}

public protocol ClusterMap {
var manager: ClusterManager { get }
var visibleMapRect: MKMapRect { get }
var zoom: Double { get }
func selectCluster(annotation: MKAnnotation, animated: Bool)
func deselectCluster(annotation: MKAnnotation, animated: Bool)
func addClusters(annotations: [MKAnnotation])
func removeClusters(annotations: [MKAnnotation])
func performAnimations(_ animations: [Animation], completion: (Bool) -> Void)
}

//extension MKMapView: ClusterMap {
// public var zoom: Double {
// }
//}
10 changes: 5 additions & 5 deletions Sources/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ public func ==(lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool
return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
}

extension Double {
static let maxZoomLevel: Double = 20
extension MKMapView {
var zoomLevel: Double {
let maxZoomLevel = log2(MKMapSizeWorld.width / 256) // 20
let zoomLevel = floor(log2(self) + 0.5) // negative
return max(0, maxZoomLevel + zoomLevel) // max - current
return floor(log2(360 * Double(frame.width / 256) / region.span.longitudeDelta))
}
}

extension Double {
var cellSize: Double {
switch self {
case 13...15:
Expand Down