如何获得顺序 MKDirection 请求响应 Swift

Posted

技术标签:

【中文标题】如何获得顺序 MKDirection 请求响应 Swift【英文标题】:How to get sequential MKDirection requests responses Swift 【发布时间】:2019-12-21 18:49:05 【问题描述】:

我有一个将[CLLocation] 作为输入的函数。在while 循环中,它将其拆分为多个块,对于每个块发出一个MKDirection 请求,将响应存储在一个新的[CLLocation] 中并在完成后返回它。

问题是新数组中的所有块都不是连续的,所以生成的路由会到处跳转。如何在创建新请求之前等待上一个请求得到响应?我尝试了DispatchQueue.global().syncDispatchQueue.main.sync,但这并没有什么不同。 我试图从Cannot wait for the result of MKDirections.calculate, getting nil instead of it 实现第一个答案,这似乎是我同样的问题,但我不明白如何适应我的情况。 你能帮我按顺序得到回复吗? 这是函数,注释掉的部分是路由的最新位,这将是最后一个请求。 一如既往,非常感谢您的帮助和时间。

    func repositionLocation2(route: [CLLocation], completion: @escaping ([CLLocation]) -> Void) 
        let group = DispatchGroup()
        var pos = 0
        var nextPos = 3
        var repositioned = [CLLocation]()
        //        repositioned.append(route.first!)

        guard route.count > nextPos else print("Reposision Location failed, not enough positions");return
        let request = MKDirections.Request()
        request.requestsAlternateRoutes = false
        request.transportType = .walking

        while pos < route.count - nextPos 
            print(" pos in \(pos)")
            //            repositioned.removeAll()

            group.enter()
            // get a small chunk of the input route
            let a = route[pos].coordinate//repositioned.last!.coordinate//
            let b = route[pos + nextPos].coordinate


            // get directions for the small chunk
            request.source = MKMapItem(placemark: MKPlacemark(coordinate: a))
            request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b))
            let directions = MKDirections(request: request)
//            DispatchQueue.main.sync 
//            DispatchQueue.global().sync 
//                group.enter()
                directions.calculate  [unowned self] response, error in
                    if let err = error 
                        print("direction error : \(err)")
                    
                    guard let unwrappedResponse = response else print("no suggested routes available"); return 
                    print("Response is: \(unwrappedResponse.debugDescription)")
                    guard let coord = unwrappedResponse.routes.first?.steps else print("No coordinates");return
                    print("coord is: \(coord)")
                    // save response coordinates into a new array
                    for location in coord 
                        let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
                        print("point is: \(point)") // prints a correct CLLocation with coordinates
                        repositioned.append(point)
                        print("repositioned in for loop is : \(repositioned)") // prints just first appended location CLLocation with coordinates
//                        group.leave() 
                    
//                    group.wait() // hangs the app
                    completion(repositioned)
                
//            
            print("repositioned in while loop is : \(repositioned)")
            // shift to nex addiacent chunk
            pos += 3
            nextPos += 3
        

        //        // last chunk
        //        let a = route[pos - 5].coordinate//repositioned.last!.coordinate
        //        let b = route.last?.coordinate
        //        request.source = MKMapItem(placemark: MKPlacemark(coordinate: a))
        //        request.destination = MKMapItem(placemark: MKPlacemark(coordinate: b!))
        //        let directions = MKDirections(request: request)
        //        directions.calculate  [unowned self] response, error in
        //            if let err = error 
        //                print("direction error : \(err)")
        //            
        //            guard let unwrappedResponse = response else print("no suggested routes available"); return 
        //            print("Response is: \(unwrappedResponse.debugDescription)")
        //            guard let coord = unwrappedResponse.routes.first?.steps else print("No coordinates");return
        //            print("coord is: \(coord)")
        //            for location in coord 
        //
        //                let point: CLLocation = CLLocation(latitude: location.polyline.coordinate.latitude, longitude: location.polyline.coordinate.longitude)
        //                print("point is: \(point)")
        //                repositioned.append(point)
        //                print("repositioned in for loop is : \(repositioned)")
        //            
        //            completion(repositioned)
        //        
        //        print("repositioned in while loop is : \(repositioned)")

    

【问题讨论】:

【参考方案1】:

当您有一系列异步任务(可能以任意顺序完成)并且您希望结果按顺序排列时,只需将其保存到顺序无关紧要的结构中,只需在最后进行排序即可。例如,您可以使用由整数索引索引的字典:

var routes: [Int: [CLLocationCoordinate2D]] = [:]

然后当任何给定的循环结束时,它可以更新这个字典:

routes[i] = ...

如果你想要一个排序后的平面数组:

let coordinates = steps.sorted  $0.0 < $1.0 
    .flatMap  $0.1 

或者,您可以使用预先填充的选项数组,您可以在数组中的正确位置插入特定路线:

var routes: [[CLLocationCoordinate2D]?] = Array(repeating: nil, count: pointCount - 1)

当你想更新一个时:

routes[i-1] = ...

然后,最后,您可以使用 compactMap 删除可选项并使用 flatMap 将其展平:

let coordinates = steps.compactMap  $0 .flatMap  $0 

因此:

func fetchDirections(_ locations: [CLLocation], completion: @escaping ([CLLocationCoordinate2D]) -> Void) 
    let pointCount = locations.count

    guard pointCount > 1 else  return 

    var routes: [[CLLocationCoordinate2D]?] = Array(repeating: nil, count: pointCount - 1)
    let group = DispatchGroup()

    for i in 1 ..< pointCount 
        group.enter()
        directions(from: locations[i-1], to: locations[i]).calculate  response, error in
            defer  group.leave() 

            guard
                error == nil,
                let response = response,
                let route = response.routes.first
            else  return 

            routes[i-1] = self.coordinates(for: route.steps)
        
    

    group.notify(queue: .main) 
        let coordinates = routes.compactMap  $0 .flatMap  $0 
        completion(coordinates)
    


func directions(from: CLLocation, to: CLLocation) -> MKDirections 
    let request = MKDirections.Request()
    request.source = MKMapItem(placemark: MKPlacemark(coordinate: from.coordinate))
    request.destination = MKMapItem(placemark: MKPlacemark(coordinate: to.coordinate))
    request.requestsAlternateRoutes = false
    request.transportType = .walking
    return MKDirections(request: request)


func coordinates(for steps: [MKRoute.Step]) -> [CLLocationCoordinate2D] 
    guard !steps.isEmpty else  return [] 

    var coordinates: [CLLocationCoordinate2D] = []

    for step in steps 
        let count = step.polyline.pointCount
        let pointer = step.polyline.points()
        for i in 0 ..< count 
            let coordinate = pointer[i].coordinate
            if coordinate.latitude != coordinates.last?.latitude, coordinate.longitude != coordinates.last?.longitude 
                coordinates.append(coordinate)
            
        
    

    return coordinates

地点:

fetchDirections(locations)  coordinates in
    let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
    self.mapView.addOverlay(polyline)

让步,在 Apple 的建筑群中漫步:


顺便说一句,请注意我不只是使用coordinatepolylineMKRoute.Step。那是折线的中心。您大概想遍历points()

话虽如此,当我获取路线时,通常只是在地图上显示它,所以我通常只是将polyline直接添加为叠加层,而不是将其分解为@987654340的数组@,但我认为您有其他原因想要这样做。

【讨论】:

非常感谢所有的解释。我这样做的原因是 GPS 位置非常不精确,因此使用 GSP 跟踪路线会导致非常差的曲折跟踪。我使用的完整顺序是:第一个跟踪路线。 (非常疯狂的跟踪)第二次过滤掉低精度信号、非连续信号以及太远或太近的信号。 (好多了,但仍然是曲折的,点在任何地方都结束了......就像在建筑物的中间......)第三(使用这个功能)我有点将过滤后的路线捕捉到实际的步行路径...... 很难想象要获得正确的跟踪。我花了几个月的时间反复试验才想出这个解决方案,但它是我应用程序的基础,所以这是我今年能得到的最好的圣诞礼物。非常感谢,如果想出一个如此优雅的解决方案来捕捉道路部分,我会花费更长的时间。当然我可以使用谷歌地图功能.. 但我宁愿坚持使用 swift 的地图。最后,如果 Google 可以,我们也可以。 除了在您的示例中拱形左侧的几个侧面偏差之外,它工作得很好。我会尝试想出一个函数来清理它并称之为完成。我正在考虑在一个循环中检查 3 个点,如果 1 和 3 具有相同的坐标或在一个范围内,则排除 2。 FWIW,我的道路上的偏差是故意的。他们从健身中心走到迎宾中心,再到剧院,这意味着他们实际上是在原路返回。 我建议,如果您正在考虑丢弃或平滑哪些数据点,请考虑 horizontalAccuracy。您可能希望彻底消除因 GPS 数据不佳而导致的曲折,但不想消除用户实际走的弯路。

以上是关于如何获得顺序 MKDirection 请求响应 Swift的主要内容,如果未能解决你的问题,请参考以下文章

如何最大限度地缩短响应时间,在客户端以极不同的速率发送请求的环境中最大化系统总体性能?

如何使用 system.io.pipes 发送请求并获得响应

如何等待http请求在处理到angular 7中的下一步之前获得响应[重复]

如何根据使用wiremock和JSON的请求正文匹配获得响应

如何使用cURL获得请求和响应时间?

如何从 IPN 加密货币中获得响应