平滑 MKPolyline 跟随道路

Smooth MKPolyline to follow the road

我知道几年前有人问过这个问题,但所有答案都涉及使用 Google 地图 API 来解决这个问题。我想知道现在是否有合适的方法来解决这个问题 iOS8 已经出来了,它给原生 MapKit 带来了很多改进。

基本上我在路上画了一条折线,由很多中间点组成。

    var locations = [CLLocation]()
    for item in list {
        locations.append(CLLocation(latitude: CLLocationDegrees(item["location"]["coordinate"]["x"].doubleValue), longitude: CLLocationDegrees(item["location"]["coordinate"]["y"].doubleValue)))
    }
    var coordinates = locations.map({(location: CLLocation!) -> CLLocationCoordinate2D in return location.coordinate})
    var polyline = MKPolyline(coordinates: &coordinates, count: locations.count)
    mapView.addOverlay(polyline)

地图上一次有 5 到 30 个点。当折线连接它们时,我或多或少地得到了这次旅行的公平代表。问题是:它不粘在路上。

所以我不时得到 "rough" 优势。即使我使用 Google 方向 API,它也仅限于 8 个方向点,并且基本上为您决定如何从 A 点到达 B 点,沿途绘制平滑的折线。此外,路线 API 每 24 小时限制为 2500 次使用。我需要做的就是将我当前的折线调整到最近的道路

非常感谢

经过一番挖掘,我设法找到了这个问题的答案,虽然我不太确定它对整体性能的影响,是否会让 Apple 满意,因为它发出了很多小MKDirectionsRequest 的。对我来说,30+ 分就很好了。

    var myRoute : MKRoute?
    var directionsRequest = MKDirectionsRequest()
    var placemarks = [MKMapItem]()
    for item in list {
        var placemark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: CLLocationDegrees(item["location"]["coordinate"]["x"].doubleValue), longitude: CLLocationDegrees(item["location"]["coordinate"]["y"].doubleValue)), addressDictionary: nil )
        placemarks.append(MKMapItem(placemark: placemark))
    }
    directionsRequest.transportType = MKDirectionsTransportType.Automobile
    for (k, item) in enumerate(placemarks) {
        if k < (placemarks.count - 1) {
            directionsRequest.setSource(item)
            directionsRequest.setDestination(placemarks[k+1])
            var directions = MKDirections(request: directionsRequest)
            directions.calculateDirectionsWithCompletionHandler { (response:MKDirectionsResponse!, error: NSError!) -> Void in
                if error == nil {
                    self.myRoute = response.routes[0] as? MKRoute
                    self.mapView.addOverlay(self.myRoute?.polyline)
                }
            }
        }
    }

特别感谢 Anna 为我指明了正确的方向。

我使用递归块得到了 72 点的路线。它总是有效,直到 50 个请求然后 api 抛出我 error.I 猜测 50 是一分钟的限制。 50 岁之后,我必须等待一段时间才能 api 工作。

我已经使用 google 地图绘制折线。所以下面的代码采用经纬度数组并获取方向并将路径转换为点集并在 Google 地图上渲染。

//Pathstr是|分离 // 让我知道这是否需要任何改进。 // 我正在使用递归块,因为计算错误可能会在计算方向时引发错误,因此将其作为使用递归的顺序块。

NSArray *arr = [pathStr componentsSeparatedByString:@"|"];
    int pointsCount = (int)[arr count];
    NSMutableArray* pointsToUse = [[NSMutableArray alloc] init];

    for(NSString* locationStr in arr)
    {
        NSArray *locArr = [locationStr componentsSeparatedByString:@","];

        CLLocation *trackLocation = [[CLLocation alloc] initWithLatitude:[[locArr objectAtIndex:0] doubleValue] longitude:[[locArr objectAtIndex:1] doubleValue]];
        [pointsToUse addObject:trackLocation];
    }


    // __block declaration of the block makes it possible to call the block from within itself
    __block void (^urlFetchBlock)();


    __block int urlIndex = 0;

    // the 'recursive' block
    urlFetchBlock = [^void () {

        MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
        NSLog(@"Index Value:::%d", urlIndex);
        if(urlIndex < (pointsCount - 5))
        {
            MKPlacemark *sourcePlacemark = [[MKPlacemark alloc] initWithCoordinate:((CLLocation*)pointsToUse[urlIndex]).coordinate addressDictionary:nil];
            MKMapItem *sourceMapItem = [[MKMapItem alloc] initWithPlacemark:sourcePlacemark];
            [request setSource:sourceMapItem];

            MKPlacemark *destPlacemark = [[MKPlacemark alloc] initWithCoordinate:((CLLocation*)pointsToUse[urlIndex + 5]).coordinate addressDictionary:nil];
            MKMapItem *destMapItem = [[MKMapItem alloc] initWithPlacemark:destPlacemark];

            [request setDestination:destMapItem];
            //            NSLog(@"Source:%f ::%f, Dest: %f :: %f", ((CLLocation*)pointsToUse[i]).coordinate.latitude,((CLLocation*)pointsToUse[i]).coordinate.longitude, ((CLLocation*)pointsToUse[i+1]).coordinate.latitude, ((CLLocation*)pointsToUse[i+1]).coordinate.longitude);

            [request setTransportType:MKDirectionsTransportTypeAny];

            request.requestsAlternateRoutes = NO;

            MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
            [directions calculateDirectionsWithCompletionHandler:
             ^(MKDirectionsResponse *response, NSError *error) {
                 if (error) {
                     // Handle Error
                     NSLog(@"Error for this particular call");
                     urlIndex +=5;
                     urlFetchBlock();
                 } else {

                     for (MKRoute * route in response.routes) {

                         NSUInteger pointCount = route.polyline.pointCount;

                         NSLog(@"%lu", (unsigned long)pointCount);
                         //allocate a C array to hold this many points/coordinates...
                         CLLocationCoordinate2D *routeCoordinates
                         = malloc(pointCount * sizeof(CLLocationCoordinate2D));

                         //get the coordinates (all of them)...
                         [route.polyline getCoordinates:routeCoordinates
                                                  range:NSMakeRange(0, pointCount)];

                         GMSMutablePath *path = [GMSMutablePath path];

                         //this part just shows how to use the results...
                         for (int c=0; c < pointCount; c++)
                         {
                             [path addLatitude:routeCoordinates[c].latitude longitude:routeCoordinates[c].longitude];

                         }
                         GMSPolyline *polyline = [GMSPolyline polylineWithPath: path];

                         polyline.tappable  =  YES;
                         polyline.strokeWidth = width;

                         //                        polyline.strokeColor = [UIColor redColor];
                         polyline.geodesic = YES;
                         //    polyline.title = @"Driving route";
                         polyline.map = gMapView;
                         polyline.spans = @[[GMSStyleSpan spanWithColor:[UIColor redColor]]];
                     }
                     urlIndex +=5;
                     urlFetchBlock();

                 }
             }];

        }
    } copy];

    // initiate the url requests
    urlFetchBlock();

我尝试了很多代码,最后我得到了如下代码:

获取完整代码: https://github.com/javedmultani16/MapKitWithPolyLine

这是相同的代码:

  var locRoute : MKRoute?
    var directionsRequest = MKDirections.Request()
    var arrayPlacemarks = [MKMapItem]()

    var selectedPin:MKPlacemark? = nil
    let locationManager = CLLocationManager()

并在 ViewDidLoad() 上编写以下代码:

    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.requestLocation()


        locationSearchTable.mapView = mapView
        mapView.delegate = self
    }

现在使用委托方法:


extension ViewController : MKMapViewDelegate {
    func mapView(_: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
        if annotation is MKUserLocation {
            //return nil so map view draws "blue dot" for standard user location

            //Add source location as user location
            let arrayPlacemarksource = MKPlacemark(coordinate: annotation.coordinate, addressDictionary: nil)
            arrayPlacemarks.append(MKMapItem(placemark: arrayPlacemarksource))
            return nil
        }
        let reuseId = "pin"
        var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
        pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        pinView?.pinTintColor = UIColor.orange
        pinView?.canShowCallout = true
        let smallSquare = CGSize(width: 30, height: 30)
        let button = UIButton(frame: CGRect(origin: CGPoint(x: 0,y :0), size: smallSquare))
        button.setBackgroundImage(UIImage(named: "car"), for: .normal)
        button.addTarget(self, action: #selector(ViewController.getDirections), for: .touchUpInside)
        pinView?.leftCalloutAccessoryView = button
        return pinView
    }
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay.isKind(of: MKPolyline.self){
                var polylineRenderer = MKPolylineRenderer(overlay: overlay)
                polylineRenderer.fillColor = UIColor.blue
                polylineRenderer.strokeColor = UIColor.blue
                polylineRenderer.lineWidth = 2

            return polylineRenderer
     }
        return MKOverlayRenderer(overlay: overlay)
    }

}

像这样绘制折线:


        //Add destination placemark...
        arrayPlacemarks.append(MKMapItem(placemark: placemark))

        directionsRequest.transportType = MKDirectionsTransportType.automobile

        //Draw polyline by using MKRoute so it follows the street roads...
        for (k, item) in arrayPlacemarks.enumerated() {
            if k < (arrayPlacemarks.count - 1) {
                directionsRequest.source = item
                directionsRequest.destination = arrayPlacemarks[k+1]

                let directions = MKDirections(request: directionsRequest)
          directions.calculate { (response:MKDirections.Response!, error: Error!) -> Void in
                    if error == nil {
                        self.locRoute = response.routes[0] as? MKRoute
                        let geodesic:MKPolyline = self.locRoute!.polyline
                        self.mapView.addOverlay(geodesic)
                    }
                }
            }
        }

        mapView.addAnnotation(annotation)
        let span = MKCoordinateSpan.init(latitudeDelta: 0.05, longitudeDelta: 0.05) //MKCoordinateSpanMake(0.05, 0.05)
        let region = MKCoordinateRegion.init(center: placemark.coordinate, span: span)
        mapView.setRegion(region, animated: true)