Commit 565e8e62 authored by IOS Developer's avatar IOS Developer
Browse files

add polylines

parent 091c5cb3
Copyright © 2014–2019, Mapbox
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# MapboxDirections
[![CircleCI](https://circleci.com/gh/mapbox/MapboxDirections.swift.svg?style=svg)](https://circleci.com/gh/mapbox/MapboxDirections.swift)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods](https://img.shields.io/cocoapods/v/MapboxDirections.swift.svg)](http://cocoadocs.org/docsets/MapboxDirections.swift/)
[![codecov](https://codecov.io/gh/mapbox/MapboxDirections.swift/branch/master/graph/badge.svg)](https://codecov.io/gh/mapbox/MapboxDirections.swift)
MapboxDirections.swift makes it easy to connect your iOS, macOS, tvOS, or watchOS application to the [Mapbox Directions](https://docs.mapbox.com/api/navigation/) and [Map Matching](https://docs.mapbox.com/api/navigation/#map-matching) APIs. Quickly get driving, cycling, or walking directions, whether the trip is nonstop or it has multiple stopping points, all using a simple interface reminiscent of MapKit’s `MKDirections` API. Fit a GPX trace to the [OpenStreetMap](https://www.openstreetmap.org/) road network. The Mapbox Directions and Map Matching APIs are powered by the [OSRM](http://project-osrm.org/) routing engine. For more information, see the [Mapbox Navigation](https://www.mapbox.com/navigation/) homepage.
Despite its name, MapboxDirections.swift works in Objective-C and Cocoa-AppleScript code in addition to Swift 4.2.
MapboxDirections.swift pairs well with [MapboxGeocoder.swift](https://github.com/mapbox/MapboxGeocoder.swift), [MapboxStatic.swift](https://github.com/mapbox/MapboxStatic.swift), the [Mapbox Navigation SDK for iOS](https://github.com/mapbox/mapbox-navigation-ios/), and the [Mapbox Maps SDK for iOS](https://docs.mapbox.com/ios/maps/) or [macOS SDK](https://mapbox.github.io/mapbox-gl-native/macos/).
## Getting started
Specify the following dependency in your [Carthage](https://github.com/Carthage/Carthage) Cartfile:
```cartfile
github "mapbox/MapboxDirections.swift" ~> 0.30
```
Or in your [CocoaPods](http://cocoapods.org/) Podfile:
```podspec
pod 'MapboxDirections.swift', '~> 0.30'
```
Or in your [Swift Package Manager](https://swift.org/package-manager/) Package.swift:
```swift
.package(url: "https://github.com/mapbox/MapboxDirections.swift.git", from: "0.30.0")
```
Then `import MapboxDirections` or `@import MapboxDirections;`.
v0.12.1 is the last release of MapboxDirections.swift written in Swift 3. All subsequent releases are based on the `master` branch, which is written in Swift 4.2. The Swift examples below are written in Swift 4.2.
This repository contains example applications written in Swift and Objective-C that demonstrate how to use the framework. To run them, you need to use [Carthage](https://github.com/Carthage/Carthage) 0.19 or above to install the dependencies. Detailed documentation is available in the [Mapbox API Documentation](https://docs.mapbox.com/api/navigation/#directions).
## Usage
**[API reference](https://docs.mapbox.com/ios/api/directions/)**
You’ll need a [Mapbox access token](https://docs.mapbox.com/api/#access-tokens-and-token-scopes) in order to use the API. If you’re already using the [Mapbox Maps SDK for iOS](https://docs.mapbox.com/ios/maps/) or [macOS SDK](https://mapbox.github.io/mapbox-gl-native/macos/), MapboxDirections.swift automatically recognizes your access token, as long as you’ve placed it in the `MGLMapboxAccessToken` key of your application’s Info.plist file.
The examples below are each provided in Swift (denoted with `main.swift`), Objective-C (`main.m`), and AppleScript (`AppDelegate.applescript`). For further details, see the [MapboxDirections.swift API reference](https://docs.mapbox.com/ios/api/directions/).
### Calculating directions between locations
The main directions class is Directions (in Swift) or MBDirections (in Objective-C or AppleScript). Create a directions object using your access token:
```swift
// main.swift
import MapboxDirections
let directions = Directions(accessToken: "<#your access token#>")
```
```objc
// main.m
@import MapboxDirections;
MBDirections *directions = [[MBDirections alloc] initWithAccessToken:@"<#your access token#>"];
```
```applescript
-- AppDelegate.applescript
set theDirections to alloc of MBDirections of the current application
tell theDirections to initWithAccessToken:"<#your access token#>"
```
Alternatively, you can place your access token in the `MGLMapboxAccessToken` key of your application’s Info.plist file, then use the shared directions object:
```swift
// main.swift
let directions = Directions.shared
```
```objc
// main.m
MBDirections *directions = [MBDirections sharedDirections];
```
```applescript
-- AppDelegate.applescript
set theDirections to sharedDirections of MBDirections of the current application
```
With the directions object in hand, construct a RouteOptions or MBRouteOptions object and pass it into the `Directions.calculate(_:completionHandler:)` method.
```swift
// main.swift
let waypoints = [
Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.9131752, longitude: -77.0324047), name: "Mapbox"),
Waypoint(coordinate: CLLocationCoordinate2D(latitude: 38.8977, longitude: -77.0365), name: "White House"),
]
let options = RouteOptions(waypoints: waypoints, profileIdentifier: .automobileAvoidingTraffic)
options.includesSteps = true
let task = directions.calculate(options) { (waypoints, routes, error) in
guard error == nil else {
print("Error calculating directions: \(error!)")
return
}
if let route = routes?.first, let leg = route.legs.first {
print("Route via \(leg):")
let distanceFormatter = LengthFormatter()
let formattedDistance = distanceFormatter.string(fromMeters: route.distance)
let travelTimeFormatter = DateComponentsFormatter()
travelTimeFormatter.unitsStyle = .short
let formattedTravelTime = travelTimeFormatter.string(from: route.expectedTravelTime)
print("Distance: \(formattedDistance); ETA: \(formattedTravelTime!)")
for step in leg.steps {
print("\(step.instructions)")
let formattedDistance = distanceFormatter.string(fromMeters: step.distance)
print("— \(formattedDistance) —")
}
}
}
```
```objc
// main.m
NSArray<MBWaypoint *> *waypoints = @[
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(38.9131752, -77.0324047) coordinateAccuracy:-1 name:@"Mapbox"],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(38.8977, -77.0365) coordinateAccuracy:-1 name:@"White House"],
];
MBRouteOptions *options = [[MBRouteOptions alloc] initWithWaypoints:waypoints
profileIdentifier:MBDirectionsProfileIdentifierAutomobileAvoidingTraffic];
options.includesSteps = YES;
NSURLSessionDataTask *task = [directions calculateDirectionsWithOptions:options
completionHandler:^(NSArray<MBWaypoint *> * _Nullable waypoints,
NSArray<MBRoute *> * _Nullable routes,
NSError * _Nullable error) {
if (error) {
NSLog(@"Error calculating directions: %@", error);
return;
}
MBRoute *route = routes.firstObject;
MBRouteLeg *leg = route.legs.firstObject;
if (leg) {
NSLog(@"Route via %@:", leg);
NSLengthFormatter *distanceFormatter = [[NSLengthFormatter alloc] init];
NSString *formattedDistance = [distanceFormatter stringFromMeters:leg.distance];
NSDateComponentsFormatter *travelTimeFormatter = [[NSDateComponentsFormatter alloc] init];
travelTimeFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort;
NSString *formattedTravelTime = [travelTimeFormatter stringFromTimeInterval:route.expectedTravelTime];
NSLog(@"Distance: %@; ETA: %@", formattedDistance, formattedTravelTime);
for (MBRouteStep *step in leg.steps) {
NSLog(@"%@", step.instructions);
NSString *formattedDistance = [distanceFormatter stringFromMeters:step.distance];
NSLog(@"— %@ —", formattedDistance);
}
}
}];
```
```applescript
-- AppDelegate.applescript
set mapbox to alloc of MBWaypoint of the current application
tell mapbox to initWithCoordinate:{38.9131752, -77.0324047} coordinateAccuracy:-1 |name|:"Mapbox"
set theWhiteHouse to alloc of MBWaypoint of the current application
tell theWhiteHouse to initWithCoordinate:{38.8977, -77.0365} coordinateAccuracy:-1 |name|:"White House"
set theWaypoints to {mapbox, theWhiteHouse}
set theOptions to alloc of MBRouteOptions of the current application
tell theOptions to initWithWaypoints:theWaypoints profileIdentifier:"mapbox/driving-traffic"
set theOptions's includesSteps to true
set theURL to theDirections's URLForCalculatingDirectionsWithOptions:theOptions
set theData to the current application's NSData's dataWithContentsOfURL:theURL
set theJSON to the current application's NSJSONSerialization's JSONObjectWithData:theData options:0 |error|:(missing value)
set theRoute to alloc of MBRoute of the current application
tell theRoute to initWithJson:(the first item of theJSON's routes) waypoints:theWaypoints profileIdentifier:"mapbox/driving"
set theLeg to the first item of theRoute's legs
log "Route via " & theLeg's |name| & ":"
set theDistanceFormatter to alloc of NSLengthFormatter of the current application
tell theDistanceFormatter to init()
set theDistance to theDistanceFormatter's stringFromMeters:(theLeg's distance)
log "Distance: " & theDistance
repeat with theStep in theLeg's steps
log theStep's instructions
set theDistance to theDistanceFormatter's stringFromMeters:(theStep's distance)
log "— " & theDistance & " —"
end repeat
```
This library uses version 5 of the Mapbox Directions API by default. To use version 4 instead, replace RouteOptions with RouteOptionsV4 (or MBRouteOptions with MBRouteOptionsV4).
### Matching a trace to the road network
If you have a GPX trace or other GPS-derived location data, you can clean up the data and fit it to the road network using the Map Matching API:
```swift
// main.swift
let coordinates = [
CLLocationCoordinate2D(latitude: 32.712041, longitude: -117.172836),
CLLocationCoordinate2D(latitude: 32.712256, longitude: -117.17291),
CLLocationCoordinate2D(latitude: 32.712444, longitude: -117.17292),
CLLocationCoordinate2D(latitude: 32.71257, longitude: -117.172922),
CLLocationCoordinate2D(latitude: 32.7126, longitude: -117.172985),
CLLocationCoordinate2D(latitude: 32.712597, longitude: -117.173143),
CLLocationCoordinate2D(latitude: 32.712546, longitude: -117.173345)
]
let options = MatchOptions(coordinates: coordinates)
options.includesSteps = true
let task = directions.calculate(options) { (matches, error) in
guard error == nil else {
print("Error matching coordinates: \(error!)")
return
}
if let match = matches?.first, let leg = match.legs.first {
print("Match via \(leg):")
let distanceFormatter = LengthFormatter()
let formattedDistance = distanceFormatter.string(fromMeters: match.distance)
let travelTimeFormatter = DateComponentsFormatter()
travelTimeFormatter.unitsStyle = .short
let formattedTravelTime = travelTimeFormatter.string(from: match.expectedTravelTime)
print("Distance: \(formattedDistance); ETA: \(formattedTravelTime!)")
for step in leg.steps {
print("\(step.instructions)")
let formattedDistance = distanceFormatter.string(fromMeters: step.distance)
print("— \(formattedDistance) —")
}
}
}
```
```objc
// main.m
NSArray<MBWaypoint *> *waypoints = @[
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.712041, -117.172836) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.712256, -117.17291) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.712444, -117.17292) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.71257, -117.172922) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.7126, -117.172985) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.712597, -117.173143) coordinateAccuracy:-1 name:nil],
[[MBWaypoint alloc] initWithCoordinate:CLLocationCoordinate2DMake(32.712546, -117.173345) coordinateAccuracy:-1 name:nil],
];
MBMatchOptions *matchOptions = [[MBMatchOptions alloc] initWithWaypoints:waypoints profileIdentifier:MBDirectionsProfileIdentifierAutomobile];
NSURLSessionDataTask *task = [[[MBDirections alloc] initWithAccessToken:MapboxAccessToken] calculateMatchesWithOptions:matchOptions completionHandler:^(NSArray<MBMatch *> * _Nullable matches, NSError * _Nullable error) {
if (error) {
NSLog(@"Error matching waypoints: %@", error);
return;
}
MBMatch *match = matches.firstObject;
MBRouteLeg *leg = match.legs.firstObject;
if (leg) {
NSLog(@"Match via %@:", leg);
NSLengthFormatter *distanceFormatter = [[NSLengthFormatter alloc] init];
NSString *formattedDistance = [distanceFormatter stringFromMeters:leg.distance];
NSDateComponentsFormatter *travelTimeFormatter = [[NSDateComponentsFormatter alloc] init];
travelTimeFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort;
NSString *formattedTravelTime = [travelTimeFormatter stringFromTimeInterval:match.expectedTravelTime];
NSLog(@"Distance: %@; ETA: %@", formattedDistance, formattedTravelTime);
for (MBRouteStep *step in leg.steps) {
NSLog(@"%@", step.instructions);
NSString *formattedDistance = [distanceFormatter stringFromMeters:step.distance];
NSLog(@"— %@ —", formattedDistance);
}
}
}];
```
You can also use the `Directions.calculateRoutes(matching:completionHandler:)` method in Swift or the `-[MBDirections calculateRoutesMatchingOptions:completionHandler:]` method in Objective-C to get Route objects suitable for use anywhere a standard Directions API response would be used.
## Usage with other Mapbox libraries
### Drawing the route on a map
With the [Mapbox Maps SDK for iOS](https://docs.mapbox.com/ios/maps/) or [macOS SDK](https://mapbox.github.io/mapbox-gl-native/macos/), you can easily draw the route on a map in Swift or Objective-C:
```swift
// main.swift
if route.coordinateCount > 0 {
// Convert the route’s coordinates into a polyline.
var routeCoordinates = route.coordinates!
let routeLine = MGLPolyline(coordinates: &routeCoordinates, count: route.coordinateCount)
// Add the polyline to the map and fit the viewport to the polyline.
mapView.addAnnotation(routeLine)
mapView.setVisibleCoordinates(&routeCoordinates, count: route.coordinateCount, edgePadding: .zero, animated: true)
}
```
```objc
// main.m
if (route.coordinateCount) {
// Convert the route’s coordinates into a polyline.
CLLocationCoordinate2D *routeCoordinates = malloc(route.coordinateCount * sizeof(CLLocationCoordinate2D));
[route getCoordinates:routeCoordinates];
MGLPolyline *routeLine = [MGLPolyline polylineWithCoordinates:routeCoordinates count:route.coordinateCount];
// Add the polyline to the map and fit the viewport to the polyline.
[mapView addAnnotation:routeLine];
[mapView setVisibleCoordinates:routeCoordinates count:route.coordinateCount edgePadding:UIEdgeInsetsZero animated:YES];
// Make sure to free this array to avoid leaking memory.
free(routeCoordinates);
}
```
### Displaying a turn-by-turn navigation interface
See the [Mapbox Navigation SDK for iOS](https://github.com/mapbox/mapbox-navigation-ios/#usage) documentation for usage examples.
## Tests
To run the included unit tests, you need to use [Carthage](https://github.com/Carthage/Carthage) 0.19 or above to install the dependencies.
1. `carthage build --platform iOS`
1. `open MapboxDirections.xcodeproj`
1. Go to Product ‣ Test.
#import "MBRouteOptions.h"
MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierAutomobile = @"mapbox/driving";
MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierAutomobileAvoidingTraffic = @"mapbox/driving-traffic";
MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierCycling = @"mapbox/cycling";
MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierWalking = @"mapbox/walking";
const MBDirectionsPriority MBDirectionsPriorityLow = -1.0;
const MBDirectionsPriority MBDirectionsPriorityDefault = 0;
const MBDirectionsPriority MBDirectionsPriorityHigh = 1.0;
#import "MBAttribute.h"
#import "MBLaneIndication.h"
#import "MBRoadClasses.h"
#import "MBRouteOptions.h"
#import <Foundation/Foundation.h>
/**
Attributes are metadata information for a route leg.
When any of the attributes are specified, the resulting route leg contains one attribute value for each segment in leg, where a segment is the straight line between two coordinates in the route leg’s full geometry.
*/
typedef NS_OPTIONS(NSUInteger, MBAttributeOptions) {
/**
Distance (in meters) along the segment.
When this attribute is specified, the `RouteLeg.segmentDistances` property contains one value for each segment in the leg’s full geometry.
*/
MBAttributeDistance = (1 << 1),
/**
Expected travel time (in seconds) along the segment.
When this attribute is specified, the `RouteLeg.expectedSegmentTravelTimes` property contains one value for each segment in the leg’s full geometry.
*/
MBAttributeExpectedTravelTime = (1 << 2),
/**
Current average speed (in meters per second) along the segment.
When this attribute is specified, the `RouteLeg.segmentSpeeds` property contains one value for each segment in the leg’s full geometry.
*/
MBAttributeSpeed = (1 << 3),
/**
Traffic congestion level along the segment.
When this attribute is specified, the `RouteLeg.congestionLevels` property contains one value for each segment in the leg’s full geometry.
This attribute requires `MBDirectionsProfileIdentifierAutomobileAvoidingTraffic`. Any other profile identifier produces `CongestionLevel.unknown` for each segment along the route.
*/
MBAttributeCongestionLevel = (1 << 4),
};
#import <Foundation/Foundation.h>
/**
Each of these options specifies a maneuver direction for which a given lane can
be used.
A Lane object has zero or more indications that usually correspond to arrows on
signs or pavement markings. If no options are specified, it may be the case
that no maneuvers are indicated on signage or pavement markings for the lane.
*/
typedef NS_OPTIONS(NSUInteger, MBLaneIndication) {
/// Indicates a sharp turn to the right.
MBLaneIndicationSharpRight = (1 << 1),
/// Indicates a turn to the right.
MBLaneIndicationRight = (1 << 2),
/// Indicates a turn to the right.
MBLaneIndicationSlightRight = (1 << 3),
/// Indicates no turn.
MBLaneIndicationStraightAhead = (1 << 4),
/// Indicates a slight turn to the left.
MBLaneIndicationSlightLeft = (1 << 5),
/// Indicates a turn to the left.
MBLaneIndicationLeft = (1 << 6),
/// Indicates a sharp turn to the left.
MBLaneIndicationSharpLeft = (1 << 7),
/// Indicates a U-turn.
MBLaneIndicationUTurn = (1 << 8),
};
#import <Foundation/Foundation.h>
/**
Option set that contains attributes of a road segment.
*/
typedef NS_OPTIONS(NSUInteger, MBRoadClasses) {
/**
The road segment is [tolled](https://wiki.openstreetmap.org/wiki/Key:toll).
*/
MBRoadClassesToll = (1 << 1),
/**
The road segment has access restrictions.
A road segment may have this class if there are [general access restrictions](https://wiki.openstreetmap.org/wiki/Key:access) or a [high-occupancy vehicle](https://wiki.openstreetmap.org/wiki/Key:hov) restriction.
*/
MBRoadClassesRestricted = (1 << 2),
/**
The road segment is a [freeway](https://wiki.openstreetmap.org/wiki/Tag:highway%3Dmotorway) or [freeway ramp](https://wiki.openstreetmap.org/wiki/Tag:highway%3Dmotorway_link).
It may be desirable to suppress the name of the freeway when giving instructions and give instructions at fixed distances before an exit (such as 1 mile or 1 kilometer ahead).
*/
MBRoadClassesMotorway = (1 << 3),
/**
The user must travel this segment of the route by ferry.
The user should verify that the ferry is in operation. For driving and cycling directions, the user should also verify that his or her vehicle is permitted onboard the ferry.
In general, the transport type of the step containing the road segment is also `TransportType.ferry`.
*/
MBRoadClassesFerry = (1 << 4),
/**
The user must travel this segment of the route through a [tunnel](https://wiki.openstreetmap.org/wiki/Key:tunnel).
*/
MBRoadClassesTunnel = (1 << 5),
};
#import <Foundation/Foundation.h>
#pragma mark - Specifying the Routing Profile
/**
Options determining the primary mode of transportation for the routes.
*/
typedef NSString * MBDirectionsProfileIdentifier NS_EXTENSIBLE_STRING_ENUM;
/**
The returned directions are appropriate for driving or riding a car, truck, or motorcycle.
This profile prioritizes fast routes by preferring high-speed roads like highways. A driving route may use a ferry where necessary.
*/
extern MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierAutomobile;
/**
The returned directions are appropriate for driving or riding a car, truck, or motorcycle.
This profile avoids traffic congestion based on current traffic data. A driving route may use a ferry where necessary.
Traffic data is available in [a number of countries and territories worldwide](https://docs.mapbox.com/help/how-mapbox-works/directions/#traffic-data). Where traffic data is unavailable, this profile prefers high-speed roads like highways, similar to `MBDirectionsProfileIdentifierAutomobile`.
*/
extern MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierAutomobileAvoidingTraffic;
/**
The returned directions are appropriate for riding a bicycle.
This profile prioritizes short, safe routes by avoiding highways and preferring cycling infrastructure, such as bike lanes on surface streets. A cycling route may, where necessary, use other modes of transportation, such as ferries or trains, or require dismounting the bicycle for a distance.
*/
extern MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierCycling;
/**
The returned directions are appropriate for walking or hiking.
This profile prioritizes short routes, making use of sidewalks and trails where available. A walking route may use other modes of transportation, such as ferries or trains, where necessary.
*/
extern MBDirectionsProfileIdentifier const MBDirectionsProfileIdentifierWalking;
/**
A number that influences whether a route should prefer or avoid roadways or pathways of a given type.
*/
typedef double MBDirectionsPriority NS_TYPED_EXTENSIBLE_ENUM;
/**
The priority level with which a route avoids a particular type of roadway or pathway.
*/
extern const MBDirectionsPriority MBDirectionsPriorityLow;
/**
The priority level with which a route neither avoids nor prefers a particular type of roadway or pathway.
*/
extern const MBDirectionsPriority MBDirectionsPriorityDefault;
/**
The priority level with which a route prefers a particular type of roadway or pathway.
*/
extern const MBDirectionsPriority MBDirectionsPriorityHigh;
import Foundation
extension Array {
#if !swift(>=4.1)
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
return try flatMap(transform)
}