变更日志
Mapbox方向
MapboxDirections.swift使得连接iOS、macOS、tvOS或watchOS应用程序到Mapbox方向API和Mapbox匹配API变得容易。快速获取驾驶、骑行或步行路线,无论是单向还是多个停靠点,都可以通过简单易用的接口实现,这类似于MapKit的MKDirections
API。将GPX轨迹拟合到OpenStreetMap道路网络上。Mapbox方向和匹配API是由OSRM路由引擎驱动的。
尽管其名称如此,MapboxDirections.swift不仅可以用于Swift 4,也适用于Objective-C和Cocoa-AppleScript代码。
MapboxDirections.swift与MapboxGeocoder.swift、MapboxStatic.swift、iOS的Mapbox导航SDK,以及iOS的Mapbox Maps SDK或macOS SDK具有良好的兼容性。
入门
在您的Carthage Cartfile中指定以下依赖项
github "mapbox/MapboxDirections.swift" ~> 0.23
或您的CocoaPods Podfile中
pod 'MapboxDirections.swift', '~> 0.23'
然后导入MapboxDirections
或@import MapboxDirections;
。
v0.12.1 是使用 Swift 3 编写的 MapboxDirections.swift 的最后一个版本。所有后续版本都将基于以 Swift 4 编写的 master
分支。下面的 Swift 示例都是使用 Swift 4 编写的。
此仓库包含使用 Swift 和 Objective-C 编写的示例应用程序,展示了如何使用此框架。要运行它们,您需要使用 0.19 版本或更高版本的 Carthage 安装依赖项。更多示例和详细文档可以在Mapbox API 文档中找到。
用法
要使用此 API,您需要一个Mapbox 访问令牌。如果您已经在使用Mapbox Maps SDK for iOS或macOS SDK,只要您已在应用的 Info.plist 文件中将其置于 MGLMapboxAccessToken
键,MapboxDirections.swift 会自动识别您的访问令牌。
下面的示例提供了 Swift(标记为 main.swift
)、Objective-C(标记为 main.m
)和 AppleScript(标记为 AppDelegate.applescript
)。有关更多详细信息,请参阅MapboxDirections.swift API 参考。
计算两个位置之间的路线
主要的路线类是 Directions(在 Swift 中)或 MBDirections(在 Objective-C 或 AppleScript 中)。使用您的访问令牌创建一个路线对象
// main.swift
import MapboxDirections
let directions = Directions(accessToken: "<#your access token#>")
// main.m
@import MapboxDirections;
MBDirections *directions = [[MBDirections alloc] initWithAccessToken:@"<#your access token#>"];
-- AppDelegate.applescript
set theDirections to alloc of MBDirections of the current application
tell theDirections to initWithAccessToken:"<#your access token#>"
或者,您可以将访问令牌置于应用的 Info.plist 文件中的 MGLMapboxAccessToken
键,然后使用共享的路线对象
// main.swift
let directions = Directions.shared
// main.m
MBDirections *directions = [MBDirections sharedDirections];
-- AppDelegate.applescript
set theDirections to sharedDirections of MBDirections of the current application
拥有路线对象后,构造一个 RouteOptions 或 MBRouteOptions 对象,并将其传递到 Directions.calculate(_:completionHandler:) 方法中。
// 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) —")
}
}
}
// 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);
}
}
}];
-- 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
此库默认使用 Mapbox Directions API 的第 5 版。要使用第 4 版,将 RouteOptions 替换为 RouteOptionsV4(或将 MBRouteOptions 替换为 MBRouteOptionsV4)。
将路径点匹配到道路网络
如果您有一个 GPX 跟踪或其他 GPS 导出的位置数据,您可以使用 Map Matching API 清理数据并使其适应道路网络
// 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) —")
}
}
}
// 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);
}
}
}];
您还可以使用 Swift 中的 Directions.calculateRoutes(matching:completionHandler:) 方法或 Objective-C 中的 -[MBDirections calculateRoutesMatchingOptions:completionHandler:]
方法来获取适用于标准 Directions API 响应的任何位置的 Route 对象。
与其他 Mapbox 库一起使用
在地图上绘制路线
使用Mapbox Maps SDK for iOS或macOS SDK,您可以在 Swift 或 Objective-C 中轻松地将路线绘制到地图上
// 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)
}
// 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);
}
显示逐段导航界面
请参阅Mapbox Navigation SDK for iOS文档中的用法示例。
测试
要运行包含的单元测试,您需要使用 0.19 版本或更高版本的Carthage安装依赖项。
carthage build --platform iOS
打开 MapboxDirections.xcodeproj
- 转到 Product ‣ Test。