-
Notifications
You must be signed in to change notification settings - Fork 96
/
OCMapView.m
203 lines (166 loc) · 5.84 KB
/
OCMapView.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//
// OCMapView.m
// openClusterMapView
//
// Created by Botond Kis on 14.07.11.
//
#import "OCMapView.h"
@interface OCMapView (private)
- (void)initSetUp;
@end
@implementation OCMapView
@synthesize clusteringEnabled;
@synthesize annotationsToIgnore;
@synthesize clusteringMethod;
@synthesize clusterSize;
@synthesize clusterByGroupTag;
@synthesize minLongitudeDeltaToCluster;
- (id)init
{
self = [super init];
if (self) {
// call actual initializer
[self initSetUp];
}
return self;
}
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
// call actual initializer
[self initSetUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
// call actual initializer
[self initSetUp];
}
return self;
}
- (void)initSetUp{
allAnnotations = [[NSMutableSet alloc] init];
annotationsToIgnore = [[NSMutableSet alloc] init];
clusteringMethod = OCClusteringMethodBubble;
clusterSize = 0.2;
minLongitudeDeltaToCluster = 0.0;
clusteringEnabled = YES;
clusterByGroupTag = NO;
backgroundClusterQueue = dispatch_queue_create("com.OCMapView.clustering", NULL);
}
- (void)dealloc{
[allAnnotations release];
[annotationsToIgnore release];
dispatch_release(backgroundClusterQueue);
[super dealloc];
}
// ======================================
#pragma mark MKMapView implementation
- (void)addAnnotation:(id < MKAnnotation >)annotation{
[allAnnotations addObject:annotation];
[self doClustering];
}
- (void)addAnnotations:(NSArray *)annotations{
[allAnnotations addObjectsFromArray:annotations];
[self doClustering];
}
- (void)removeAnnotation:(id < MKAnnotation >)annotation{
[allAnnotations removeObject:annotation];
[self doClustering];
}
- (void)removeAnnotations:(NSArray *)annotations{
[annotations retain];
for (id<MKAnnotation> annotation in annotations) {
[allAnnotations removeObject:annotation];
}
[annotations release];
[self doClustering];
}
// ======================================
#pragma mark - Properties
//
// Returns, like the original method,
// all annotations in the map unclustered.
- (NSArray *)annotations{
return [allAnnotations allObjects];
}
//
// Returns all annotations which are actually displayed on the map. (clusters)
- (NSArray *)displayedAnnotations{
return super.annotations;
}
//
// enable or disable clustering
- (void)setClusteringEnabled:(BOOL)enabled{
clusteringEnabled = enabled;
[self doClustering];
}
// ======================================
#pragma mark - Clustering
- (void)doClustering{
// Remove the annotation which should be ignored
NSMutableArray *bufferArray = [[NSMutableArray alloc] initWithArray:[allAnnotations allObjects]];
[bufferArray removeObjectsInArray:[annotationsToIgnore allObjects]];
NSMutableArray *annotationsToCluster = [[NSMutableArray alloc] initWithArray:[self filterAnnotationsForVisibleMap:bufferArray]];
[bufferArray release];
//calculate cluster radius
CLLocationDistance clusterRadius = self.region.span.longitudeDelta * clusterSize;
// Do clustering
NSArray *clusteredAnnotations;
// Check if clustering is enabled and map is above the minZoom
if (clusteringEnabled && (self.region.span.longitudeDelta > minLongitudeDeltaToCluster)) {
// switch to selected algoritm
switch (clusteringMethod) {
case OCClusteringMethodBubble:{
clusteredAnnotations = [[NSArray alloc] initWithArray:[OCAlgorithms bubbleClusteringWithAnnotations:annotationsToCluster andClusterRadius:clusterRadius grouped:self.clusterByGroupTag]];
break;
}
case OCClusteringMethodGrid:{
clusteredAnnotations =[[NSArray alloc] initWithArray:[OCAlgorithms gridClusteringWithAnnotations:annotationsToCluster andClusterRect:MKCoordinateSpanMake(clusterRadius, clusterRadius) grouped:self.clusterByGroupTag]];
break;
}
default:{
clusteredAnnotations = [annotationsToCluster retain];
break;
}
}
}
// pass through without when not
else{
clusteredAnnotations = [annotationsToCluster retain];
}
// Clear map but leave Userlcoation
NSMutableArray *annotationsToRemove = [[NSMutableArray alloc] initWithArray:self.displayedAnnotations];
[annotationsToRemove removeObject:self.userLocation];
// add clustered and ignored annotations to map
[super addAnnotations: clusteredAnnotations];
// fix for flickering
[annotationsToRemove removeObjectsInArray: clusteredAnnotations];
[super removeAnnotations:annotationsToRemove];
// add ignored annotations
[super addAnnotations: [annotationsToIgnore allObjects]];
// memory
[clusteredAnnotations release];
[annotationsToCluster release];
[annotationsToRemove release];
}
// ======================================
#pragma mark - Helpers
- (NSArray *)filterAnnotationsForVisibleMap:(NSArray *)annotationsToFilter{
// return array
NSMutableArray *filteredAnnotations = [[NSMutableArray alloc] initWithCapacity:[annotationsToFilter count]];
// border calculation
CLLocationDistance a = self.region.span.latitudeDelta/2.0;
CLLocationDistance b = self.region.span.longitudeDelta /2.0;
CLLocationDistance radius = sqrt(a*a + b*b);
for (id<MKAnnotation> annotation in annotationsToFilter) {
// if annotation is not inside the coordinates, kick it
if (isLocationNearToOtherLocation(annotation.coordinate, self.centerCoordinate, radius)) {
[filteredAnnotations addObject:annotation];
}
}
return [filteredAnnotations autorelease];
}
@end