【问题标题】:Calculate distance between my location and a MapKit pin on Swift计算我的位置和 Swift 上的 MapKit 引脚之间的距离
【发布时间】:2017-05-23 16:00:44
【问题描述】:

我需要你的帮助,我正在开发一个应用程序,其中有一些图钉(位置),我想要的是获得每个图钉与我的位置之间的距离。我的代码如下

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)
}

当我运行代码时,地图会显示大头针,但我还能做些什么来开始计算距离?谢谢!

【问题讨论】:

    标签: ios swift geolocation mapkit mapkitannotation


    【解决方案1】:

    您必须将注释的坐标转换为 CLLocation 类型,然后获取它们之间的距离。要忽略坐标的高度,因为它们是 2D,只需使用 2D 坐标的纬度和经度属性,如下所示:

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

    但是,CLLocation 还具有一些其他属性,例如速度和高度,因此如果您想将这些因素考虑在内,则必须提供更多信息。要查找两个位置之间的距离,请执行以下操作:

    let distance = loc1.distance(from: loc2)
    

    这将使您的答案以米为单位的两倍。

    【讨论】:

    • 这应该是公认的答案。创建两个CLLocation然后使用距离实例方法获取它们的距离非常简单。
    【解决方案2】:

    创建一个辅助函数来计算用户位置和给定MKPointAnnotation pin 之间的距离

    /// 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)
    }
    

    最后,获取用户到圣保罗医院的距离:

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

    地理位置跟踪延迟。但是有一个问题:用户距离可能一开始并不总是可用,因为 MapKit/CoreLocation 地理定位跟踪可能仍在后台运行。

    解决此问题的一种方法是遵守MKMapViewDelegate protocol 并等待mapView(_:didUpdate:) callback 最终计算您的距离。

    【讨论】:

    • 嘿@DanielC。如果我的回答解决了您的问题,请务必在有机会时将其标记为已接受的答案。谢谢大佬!
    【解决方案3】:

    从透视角度来看,您需要首先指定您要寻找的“距离”。如果您正在寻找简单的Euclidean Distance,那么任何其他答案或使用distanceFromLocation 都可以。根据苹果在distanceFromLocation上的文档

    此方法通过追踪来测量两个位置之间的距离 它们之间的一条线,沿着地球的曲率。这 产生的圆弧是平滑曲线,不考虑 两个位置之间的特定高度变化。

    这意味着,使用此方法得出的距离将不是两点之间的实际路线/运输距离。 如果这就是您要查找的内容,请转到我上面链接的答案,如果不是,请继续阅读(但无论哪种方式,我都鼓励您阅读整篇文章:)。

    如果您正在寻找您所在位置与地图中其他注释之间的“路线”距离(可行驶、可步行等),则使用MKRoute 对象将需要更多的工作。更具体地说,您需要首先访问每个注释的 MKMapItem 对象,然后像下面这样的 自定义方法 将能够获取两个 MapItem 对象之间的路由信息​​。

    注意 - 如果您没有 MapItems,那么您可以使用每个注释的坐标来创建它们,就像这样

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

    在您的类(或 ViewController 类)中全局定义一个 MKRoute 变量。这个var 将保存计算出的两点之间的路线信息。

    var route: MKRoute!

    然后

    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
            }
        }
    

    用法是

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

    其中yourSourceMapItemObjyourDestinationMapitemObj 是两个MapItem 对象,即源点和目标点。

    然后你可以使用self.route.distance获取距离,得到MKRoute返回的第一条最佳路线的距离。 MKRoute 对象route 还有一大堆其他属性,您也可以使用它们来显示/计算其他内容,我鼓励您使用look at those too。您还可以使用上面的函数绘制ployLine,即一条线,显示MapView 中两个位置之间的路线,只需在上面的自定义方法末尾添加self.mapView.add(self.route.polyline),然后使用下面的MKMapViewDelegate下面的函数来渲染折线。

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

    最后,确保您的类(或您的类扩展)符合CLLocationManagerDelegateMKMapViewDelegate 协议,并且mapview 委托指向self(我假设您已经这样做了),以使上述所有内容都能正常工作。

    【讨论】:

      【解决方案4】:

      在下面尝试我的代码很容易。

      别忘了导入 CoreLocation 或 MapKit,希望对你有帮助

      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 
      

      【讨论】:

      • 距离和旅行时间之间存在混淆。您的 getDistance 方法接受带有名为 travelTime 的属性的完成处理程序。在您的示例中,您使用了周围的距离。
      • 我以前用它来获取旅行时间,所以我对其进行了修改以获取距离,但现在任何人都可以使用它
      • 谢谢,我已经更新了您的答案并发布了更新的代码并进行了一些修改。我希望你不要介意。再次感谢您
      【解决方案5】:

      使用 MapKit 和 Swift 5

      计算两个位置位置之间的距离

      示例函数:我已经在 Google Map 和 Apple Map 中进行了测试

              let startLocation : CLLocation = CLLocation.init(latitude: 23.0952779, longitude: 72.5274129)
              let endLocation : CLLocation = CLLocation.init(latitude: 23.0981711, longitude: 72.5294229)
              let distance = startLocation.distance(from: endLocation)
              self.getDistance(departureDate: Date().adjust(hour: 8, minute: 0, second: 0, day: 0, month: 0), arrivalDate: Date().adjust(hour: 8, minute: 10, second: 0, day: 0, month: 0), startLocation: startLocation, endLocation: endLocation) { (distanceInMeters) in
      
                  print("fake distance: \(distance)")
                  let fakedistanceInMeter = Measurement(value: distance, unit: UnitLength.meters)
                  let fakedistanceInKM = fakedistanceInMeter.converted(to: UnitLength.kilometers).value
                  let fakedistanceInMiles = fakedistanceInMeter.converted(to: UnitLength.miles).value
                  print("fakedistanceInKM :\(fakedistanceInKM)")
                  print("fakedistanceInMiles :\(fakedistanceInMiles)")
      
      
                  print("actualDistance : \(distanceInMeters)")
      
                  let distanceInMeter = Measurement(value: distanceInMeters, unit: UnitLength.meters)
                  let distanceInKM = distanceInMeter.converted(to: UnitLength.kilometers).value
                  let distanceInMiles = distanceInMeter.converted(to: UnitLength.miles).value
                  print("distanceInKM :\(distanceInKM)")
                  print("distanceInMiles :\(distanceInMiles)")
              }
      
      

      函数的使用

                          self.getDistance(departureDate: trip.departure.dateTime, arrivalDate: trip.arrival.dateTime, startLocation: startLocation, endLocation: endLocation) { (actualDistance) in
                              print("actualDistance : \(actualDistance)")
                          }
      
      

      我对上面的功能进行了改进,在这里添加了代码,希望对大家有所帮助。

      func calculateDistancefrom(departureDate: Date, arrivalDate: Date, sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: @escaping (_ distance: CLLocationDistance) -> Void) {
      
              let request: MKDirections.Request = MKDirections.Request()
      
              request.departureDate = departureDate
              request.arrivalDate = arrivalDate
      
              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(departureDate: Date, arrivalDate: Date, startLocation : CLLocation, endLocation : CLLocation, completionHandler: @escaping (_ distance: CLLocationDistance) -> Void) {
      
              let destinationItem =  MKMapItem(placemark: MKPlacemark(coordinate: startLocation.coordinate))
              let sourceItem      =  MKMapItem(placemark: MKPlacemark(coordinate: endLocation.coordinate))
              self.calculateDistancefrom(departureDate: departureDate, arrivalDate: arrivalDate, sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
                  completionHandler(distance)
              })
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-20
        • 2016-12-22
        • 1970-01-01
        • 2015-10-26
        • 1970-01-01
        • 1970-01-01
        • 2012-10-05
        相关资源
        最近更新 更多