0

Idea :

App lets drivers see the closest shop/restaurants to customers.

What I have :

  • Coordinates saved as strings

let clientLat = "24.449384" let clientLng = "56.343243"

  • a function to find all the shops in my local area

I tried to save all the coordinates of a shop in my local area and I succeeded:

  var coordinates: [CLLocationCoordinate2D] = [CLLocationCoordinate2D]()

 func performSearch() {
    coordinates.removeAll()

    let request = MKLocalSearchRequest()
    request.naturalLanguageQuery = "starbucks"
    request.region = mapView.region

    let search = MKLocalSearch(request: request)

    search.start(completionHandler: {(response, error) in

        if error != nil {
            print("Error occured in search: \(error!.localizedDescription)")
        } else if response!.mapItems.count == 0 {
            print("No matches found")
        } else {
            print("Matches found")

            for item in response!.mapItems {

                self.coordinates.append(item.placemark.coordinate)


               // need to sort coordinates

                // need to find the closest


                let annotation = MKPointAnnotation()
                annotation.coordinate = item.placemark.coordinate
                annotation.title = item.name
                self.mapView.addAnnotation(annotation)
            }
        }
    })

}

What I need:

I wish to loop through the coordinates and find the closest shop (kilometers) to the lat and long strings then put a pin on it.

UPDATE

  func performSearch() {
    coordinates.removeAll()

    let request = MKLocalSearchRequest()
    request.naturalLanguageQuery = "starbucks"
    request.region = mapView.region

    let search = MKLocalSearch(request: request)

    search.start(completionHandler: {(response, error) in

        if error != nil {
            print("Error occured in search: \(error!.localizedDescription)")
        } else if response!.mapItems.count == 0 {
            print("No matches found")
        } else {
            print("Matches found")

            for item in response!.mapItems {

                self.coordinates.append(item.placemark.coordinate)

                let pointToCompare = CLLocation(latitude: 24.741721, longitude: 46.891440)
              let  storedCorrdinates =  self.coordinates.map({CLLocation(latitude: $0.latitude, longitude: $0.longitude)}).sorted(by: {
                    $0.distance(from: pointToCompare) < $1.distance(from: pointToCompare)


                })
                self.coordinate = storedCorrdinates
            }

            let annotation = MKPointAnnotation()
            annotation.coordinate = self.coordinate[0].coordinate
            self.mapView.addAnnotation(annotation)
        }
    })

} Thank you @brimstone

AlmoDev
  • 969
  • 2
  • 18
  • 46
  • Is `coordinates` an array of CLLocationCoordinate2D or CLLocation? – brimstone May 23 '17 at 17:50
  • Perhaps you could use the distance formula? see [This question](https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula) For a JavaScript implementation of the distance formula between 2 lat/longs. (Haversine formula) – Chris Gilardi May 23 '17 at 17:50
  • @brimstone yes `var coordinates: [CLLocationCoordinate2D] = [CLLocationCoordinate2D]()` – AlmoDev May 23 '17 at 17:54

2 Answers2

3

You can compare distances between coordinates by converting them to CLLocation types and then using the distance(from:) method. For example, take your coordinates array and map it to CLLocation, then sort that based on the distance from the point you are comparing them to.

let coordinates = [CLLocationCoordinate2D]()
let pointToCompare = CLLocation(latitude: <#yourLat#>, longitude: <#yourLong#>)

let sortedCoordinates = coordinates.map({CLLocation(latitude: $0.latitude, longitude: $0.longitude)}).sorted(by: {
    $0.distance(from: pointToCompare) < $1.distance(from: pointToCompare)
})

Then, to set your annotation's coordinate to the nearest coordinate, just subscript the sortedCoordinates array.

annotation.coordinate = sortedCoordinates[0].coordinate
brimstone
  • 3,370
  • 3
  • 28
  • 49
  • Thanks a lot. The where is the first sorted coordinates ? I mean how can I pick it to have it in `annotation.coordinate = item.placemark.coordinate`? – AlmoDev May 23 '17 at 18:12
  • @leo0019 See edit for getting the nearest coordinate – brimstone May 23 '17 at 18:16
  • It now displays the closest one to me and one more pin for another Starbucks !!! why I have two pins ? – AlmoDev May 23 '17 at 18:24
  • @leo0019 You're probably setting another annotation somewhere in your code. Did you put the code inside a loop? This is also not necessary. – brimstone May 23 '17 at 18:27
  • If I set it outside the loop it gives me error `unresolved identifier`. I updated my question again please check it @brimstone – AlmoDev May 23 '17 at 18:32
  • Take all the code I gave you and put it outside of the loop. This does not need to be in here as the loop seems to serve as a way to get the coordinates from the map search. I assume your error is from trying to set the title of the annotation. You'll need to find another way to do this outside of the loop. – brimstone May 23 '17 at 18:34
  • Thanks I saved it in another variable. I don't know if it's a good thing to do but it works fine :) check my question – AlmoDev May 23 '17 at 18:41
  • @leo0019 I'm sure it's fine. Glad I could help! – brimstone May 23 '17 at 18:44
0

I would like to share my solution :)

1) In my case, I upload data from the API, so I need to create a model.

import MapKit

struct StoresMap: Codable {
    let id: Int?
    let title: String?
    let latitude: Double?
    let longitude: Double?
    let schedule: String?
    let phone: String?
    let ukmStoreId: Int?
    var distanceToUser: CLLocationDistance?
}

The last variable is not from API, but from myself to define distance for each store.

2) In ViewController I define:

func fetchStoresList() {
    NetworkManager.downloadStoresListForMap(firstPartURL: backendURL) { (storesList) in
        self.shopList = storesList
        let initialLocation = self.locationManager.location!

        for i in 0..<self.shopList.count {
            self.shopList[i].distanceToUser = initialLocation.distance(from: CLLocation(latitude: self.shopList[i].latitude!, longitude: self.shopList[i].longitude!))
        }
        self.shopList.sort(by: { $0.distanceToUser! < $1.distanceToUser!})
        print("Closest shop - ", self.shopList[0])
    }
}

3) Don't forget to call the function in viewDidLoad() and import MapView framework :)

Ely
  • 8,259
  • 1
  • 54
  • 67