I have a class which has those data
class Events {
var name: String!
var latitude: Double!
var longitude: Double!
}
And I fill it out with data from json.
So some Events have the same lat and lon but they are not on continuous, i mean its not that event3 is the same as event4 etc.
So I'm trying to show them on a map
Filling out this array
var events = [Events]()
And in this for loop i'm making the pins.
for events in events {
let annotation = MKPointAnnotation()
annotation.title = events.name
annotation.coordinate = CLLocationCoordinate2D(latitude: events.latitude, longitude: events.longitude)
mapView.addAnnotation(annotation)
}
How can I make a quick search before deploying the pins, to see if a pin has the same lat and lon with another pin, to add some digits just to show them both close?
Thanks a lot!
Use Set
to find unique instances. In order to use Set
your base element, Events
in this case must be Hashable
and by implication Equatable
:
class Events : Hashable {
var name: String!
var latitude: Double!
var longitude: Double!
// implement Hashable
var hashValue: Int {
return latitude.hashValue | longitude.hashValue
}
// Implement Equatable
static func ==(lhs:Events, rhs:Events) -> Bool {
return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
}
}
Then your main loop is a direct extension of what you already have, note that this collapses all matches down to a single point and changes the name to indicate how many matches there are:
// Use a Set to filter out duplicates
for event in Set<Events>(events) {
let annotation = MKPointAnnotation()
// Count number of occurrences of each item in the original array
let count = events.filter { $0 == event }.count
// Append (count) to the title if it's not 1
annotation.title = count > 1 ? "\(event.name) (\(count))" : event.name
// add to the map
}
If, instead, you want to move the points so that they don't stack up, then you want something like, where we build up the set of occupied locations as we go and mutate the points to move them a little.
func placeEvents(events:[Events], mapView:MKMapView) {
var placed = Set<Events>()
for event in events {
if placed.contains(event) {
// collision: mutate the location of event as needed,
}
// Add the mutated point to occupied points
placed.formUnion([event])
// Add the point to the map here
}
}
If the values aren't expected to be exactly the same, but only within, eg., .0001 of each other, then you could use the following for hashValue
and ==
fileprivate let tolerance = 1.0 / 0.0001
private var tolerantLat : Long { return Long(tolerance * latitude) }
private var tolerantLon : Long { return Long(tolerance * longitude) }
var hashValue : Int {
return tolerantLat.hashValue | tolerantLon.hashValue
}
static func ==(lhs:Events, rhs:Events) -> Bool {
return lhs.tolerantLat == rhs.tolerantLat && lhs.tolerantLon == rhs.tolerantLon
}