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!
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.
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.
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
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)
où 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.