web-dev-qa-db-fra.com

Calculer la distance entre ma position et une broche MapKit sur Swift

J'ai besoin de votre aide, je travaille sur une application sur laquelle j'ai quelques épingles (emplacements) et ce que je veux, c'est obtenir la distance qui les sépare de mon emplacement. Mon code est le suivant

let annotation = MKPointAnnotation()
let annotationTwo = MKPointAnnotation()
let saintPaulHospitalBC = MKPointAnnotation()

override func viewDidLoad() {
    super.viewDidLoad()

    mapita.showsUserLocation = true // Mapita is the name of the MapView.

    annotation.coordinate = CLLocationCoordinate2D(latitude: 25.647399800, longitude: -100.334304500)
    mapita.addAnnotation(annotation)

    annotationTwo.coordinate = CLLocationCoordinate2D(latitude: 25.589339000, longitude: -100.257724800)
    mapita.addAnnotation(annotationTwo)

    saintPaulHospitalBC.coordinate = CLLocationCoordinate2D(latitude: 49.280524700, longitude:  -123.128232600)
    mapita.addAnnotation(SaintPaulHospitalBC)
}

Lorsque je lance le code, la carte affiche les repères, mais que puis-je faire d'autre pour commencer à calculer la distance? Je vous remercie!

5
Daniel C.

Vous devrez convertir les coordonnées de vos annotations en types CLLocation, puis obtenir la distance qui les sépare. Pour ignorer la hauteur des coordonnées, car elles sont 2D, utilisez simplement les propriétés de latitude et de longitude des coordonnées 2D, comme suit:

let loc1 = CLLocation(latitude: coord1.latitude, longitude: coord1.longitude)

Cependant, CLLocation possède d'autres propriétés telles que la vitesse et la hauteur. Par conséquent, si vous souhaitez les factoriser, vous devrez fournir plus d'informations. Pour trouver la distance entre les deux emplacements, procédez comme suit:

let distance = loc1.distance(from: loc2)

Cela donnera votre réponse sous forme de double en mètres. 

6
brimstone

Créez une fonction d'assistance pour calculer la distance entre l'emplacement de l'utilisateur et une broche MKPointAnnotation donnée:

/// Returns the distance (in meters) from the
/// user's location to the specified point.
private func userDistance(from point: MKPointAnnotation) -> Double? {
    guard let userLocation = mapita.userLocation.location else {
        return nil // User location unknown!
    }
    let pointLocation = CLLocation(
        latitude:  point.coordinate.latitude, 
        longitude: point.coordinate.longitude
    )
    return userLocation.distance(from: pointLocation)
}

Enfin, pour obtenir la distance de l'utilisateur à Saint Paulhôpital:

if let distance = userDistance(from: saintPaulHospitalBC) {
    // Use distance here...
}

Latence de suivi de géolocalisation . Il y a cependant un inconvénient: la distance utilisateur pourrait ne pas être toujours disponible au début, car le suivi de la géolocalisation MapKit/CoreLocation pourrait toujours s'exécuter en arrière-plan.

Une solution consiste à se conformer au protocole MKMapViewDelegate et à attendre le rappel mapView(_:didUpdate:) avant de calculer enfin vos distances.

3
Paulo Mattos

C'est facile d'essayer mon code ci-dessous. 

N'oubliez pas d'importer CoreLocation ou MapKit, espérons que cela vous aidera

func calculateDistancefrom(sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: @escaping (_ expectedTravelTim: TimeInterval) -> Void) {

        let request: MKDirectionsRequest = MKDirectionsRequest()

        request.source = sourceLocation
        request.destination = destinationLocation
        request.requestsAlternateRoutes = true
        request.transportType = .automobile

        let directions = MKDirections(request: request)
        directions.calculate { (directions, error) in

            if var routeResponse = directions?.routes {
                routeResponse.sort(by: {$0.expectedTravelTime <
                    $1.expectedTravelTime})
                let quickestRouteForSegment: MKRoute = routeResponse[0]

                doneSearching(quickestRouteForSegment.distance)

            }
        }
    }

    func getDistance(lat: Double, lon: Double, completionHandler: @escaping (_ distance: Int) -> Void) {

        let destinationItem =  MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2DMake(lat, lon)))
        guard let currentLocation = self.locationManager?.location else { return }
        let sourceItem =  MKMapItem(placemark: MKPlacemark(coordinate: currentLocation.coordinate))

            self.calculateDistancefrom(sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
                completionHandler(distance)
            })
    }


   //Thereafter get the distance in meters by calling 

         self.getDistance(lat: yourLat, lon: YourLon) { distance in

            }

 //you can divide by 1000 to convert to KM...  .etc 
0
Vision Mkhabela

Pour le mettre en recul, vous devez d’abord préciser la "distance" que vous recherchez. Si vous recherchez un simple Distance euclidienne , l’une des autres réponses ou l’utilisation de distanceFromLocation fonctionnerait. Selon la documentation d'Apple sur distanceFromLocation

Cette méthode mesure la distance entre les deux endroits en traçant Une ligne entre eux qui suit la courbure de la Terre. L'arc résultant Est une courbe lisse et ne tient pas compte de Changements d'altitude spécifiques entre les deux emplacements.

Cela signifie que la distance calculée à l'aide de cette méthode ne sera pas la distance réelle d'itinéraire/de transport entre deux points. Si c'est ce que vous cherchez, passez à la réponse que j'ai liée ci-dessus, sinon conservez lecture (mais dans tous les cas, je vous encourage à lire tout le post :). 

Si vous recherchez une "route" (distance parcourable) pouvant être parcourue entre votre position et les autres annotations de la carte, il vous faudra un peu plus de travail à l'aide de l'objet MKRoute. Pour être plus précis, vous devez d’abord avoir accès aux objets MKMapItem de chacune de vos annotations, puis un méthode personnalisée comme ci-dessous pourrait obtenir les informations de route entre deux objets MapItem

Remarque - si vous n'avez pas MapItems, vous pouvez les créer en utilisant simplement les coordonnées de chacune de vos annotations, comme suit.

ley myCoordinates CLLocationCoordinate2D(latitude: 25.647399800, longitude: -100.334304500)
let myPlacemark = MKPlacemark(coordinate: myCoordinates)
let myMapItem = MKMapItem(placemark: myPlacemark)

Définissez une variable MKRoute globalement dans votre classe (ou classe ViewController). Cette var tiendra les informations de route calculées entre deux points.

var route: MKRoute!

et alors

func getDistanceToDestination(srcMapItem srcmapItem: MKMapItem, destMapItem destmapItem: MKMapItem){
        let request = MKDirectionsRequest() //create a direction request object
        request.source = srcmapItem //this is the source location mapItem object
        request.destination = destmapItem //this is the destination location mapItem object
        request.transportType = MKDirectionsTransportType.automobile //define the transportation method

        let directions = MKDirections(request: request) //request directions
        directions.calculate { (response, error) in
            guard let response = response else {
                print(error.debugDescription)
                return
            }
            self.route = response.routes[0] //get the routes, could be multiple routes in the routes[] array but usually [0] is the best route
        }
    }

L'utilisation serait

self.getDistanceToDestination(srcMapItem: yourSourceMapItemObj, destMapItem: yourDestinationMapitemObj)

yourSourceMapItemObj et yourDestinationMapitemObj sont deux objets MapItem, alias points source et destination.

Et puis vous pouvez accéder à la distance en utilisant self.route.distance pour obtenir la distance du premier meilleur itinéraire renvoyé par MKRoute. Il existe toute une série d'autres propriétés pour l'objet MKRoute, route, que vous pouvez également utiliser pour afficher/calculer d'autres éléments, et je vous encourage à consulter regardez également . Vous pouvez utiliser la fonction ci-dessus pour dessiner également une ployLine, c'est-à-dire une ligne indiquant la route entre les deux emplacements de la MapView en ajoutant simplement self.mapView.add(self.route.polyline) à la fin de la méthode personnalisée ci-dessus, puis utilisez la fonction MKMapViewDelegate ci-dessous pour restituer la polyligne.

func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let linerenderer = MKPolylineRenderer(overlay: self.route.polyline)
        linerenderer.strokeColor = .blue
        linerenderer.lineWidth = 3.5
        return linerenderer
    }

Enfin, assurez-vous que votre classe (ou son extension de classe) est conforme aux protocoles CLLocationManagerDelegate et MKMapViewDelegate et que le délégué mapview a pointé self (ce que je suppose que vous faites déjà) pour que tout ce qui précède fonctionne.

0
Annjawn