Search code examples
iosswiftxcodeannotationsmapkit

Mapkit, how to change annotation coordinates to nearest address?


I have a navigation application I am working on, and one use of it is that it can calculate the average of all the annotations coordinates placed by the user(through a search table, and each annotation is placed when they press a result) and find what you might call a middle point, in between all the annotations. This midpoint, however, only goes by coordinates at the moment, meaning that depending on where the users current annotations are, this mid point could wind up in the middle of a lake or a forest, which is not helpful. I want it to find the nearest address to the coordinates of my middle point, and redirect the annotation to there instead. Here's how the annotation is created:

@IBAction func middleFinderButton(_ sender: Any) {

    let totalLatitude = mapView.annotations.reduce(0) { $0 + $1.coordinate.latitude }

    let totalLongitude = mapView.annotations.reduce(0) { $0 + $1.coordinate.longitude }

    let averageLatitude = totalLatitude/Double(mapView.annotations.count)

    let averageLongitude = totalLongitude/Double(mapView.annotations.count)

    let centerPoint = MKPointAnnotation()

    centerPoint.coordinate.latitude = averageLatitude
    centerPoint.coordinate.longitude = averageLongitude

    mapView.addAnnotation(centerPoint)
}

How can I get this annotation 'centerPoint' to adjust to the nearest address? Thanks.


Solution

  • I would just use a reverse geocode here returning an MKPlacemark. The documentation suggests that normally just one placemark will be returned by the completion handler, on the main thread, so you can use the result straightaway to update the UI. MKPlacemark conforms to the annotation protocol so you can put it directly on the map:

    func resolveAddress(for averageCoordinate: CLLocationCoordinate2D, completion: @escaping (MKPlacemark?) -> () ) {
    
        let geocoder = CLGeocoder()
        let averageLocation = CLLocation(latitude: averageCoordinate.latitude, longitude: averageCoordinate.longitude)
        geocoder.reverseGeocodeLocation(averageLocation) { (placemarks, error) in
            guard error == nil,
                let placemark = placemarks?.first
                 else {
                completion(nil)
                return
            }
            completion(MKPlacemark(placemark: placemark))
        }
    }
    
    @IBAction func middleFinderButton(_ sender: Any) {
    
        // your code to find center annotation
    
        resolveAddress(for: centerPoint.coordinate) { placemark in 
            if let placemark = placemark { 
                self.mapView.addAnnotation(placemark) 
            } else {
                self.mapView.addAnnotation(centerCoordinate) 
        }    
    }