-
Notifications
You must be signed in to change notification settings - Fork 313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Offline routing #1808
Merged
Merged
Offline routing #1808
Changes from 66 commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
94aa6c7
Outline offline routing
frederoni 772f1b3
Add test fixtures
frederoni a8f6f12
Add OfflineRoutingTests and clean up OfflineDirections
frederoni 4056716
Clean up error handling
frederoni d66cc31
Minify fixtures
frederoni 08ac909
Capture expection and return tile count
frederoni 3d56ce9
Temporarily disable offline routing tests
frederoni ebcb07a
Add documentation fix naming conventions
frederoni fae883b
Bump MapboxDirections
frederoni 952ae0b
Return number of tiles
frederoni 4e54461
Refactor
frederoni 6733b05
Don't use offline directions by default in the example app
frederoni 3f71104
Remove unneeded Obj-C category
frederoni dca0396
Remove boilerplate code
frederoni a78c3d9
Use URLs
frederoni 3c6a554
Replace bundled translations with submodule
frederoni 0cab7df
Update submodules on CI
frederoni 1eae9d6
Remove unused import
frederoni 929f384
Fix error bridging and description
frederoni cea45a4
Naming conventions
frederoni e20d854
Remove unused error
frederoni 2488c25
Naming conventions
frederoni 6472fba
Outline Offline example
frederoni 15d24bd
Add arbitrary region downloader example
frederoni c32d501
Pixel pushing
frederoni 65328c8
Add unpacker
frederoni 4733e9c
List downloaded offline versions and clean up examples
frederoni 135b83d
Provide UI for deleting a downloaded version
frederoni ae1fe73
Show unpacking progress
frederoni 32f8dea
Pin MapboxDirections.swift
frederoni a3bb7e6
Remove unnecessary example
frederoni 40cbdeb
Clean up
frederoni ff156c6
Optional translationsPath
frederoni 20d9d71
Refactor initializer to function for reusability
frederoni 868f12b
Hook up offline capabilities to the example app
frederoni fffffc4
Update tests
frederoni 55fa068
Remove unnecessary submodule
frederoni e80d95a
Merge branch 'master' into offline-routing
frederoni 4c15c0c
Merge branch 'master' into offline-routing
frederoni 37edfad
Bump dependencies
frederoni ba56eb6
Update test fixtures
frederoni a31f01e
Enable offline routing tests
frederoni 3e73159
Bump nav native
frederoni a65432b
Bump nav-native
frederoni 7649ab0
Use offline routing in the example app when rerouting if enabled
frederoni b7e00da
Minify route fixture
frederoni 029500d
Minify GeoJSON
frederoni 38d9861
Bump nav-native
frederoni d7a5765
Offline route requests can be done asynchronously in the serial queue
frederoni 216fe26
Temporarily disable lineDistanceMetrics
frederoni 74612a7
Bump podspec
frederoni ccecaed
Merge branch 'master' into offline-routing
frederoni ec0ed10
Bump dependencies
frederoni 720c439
Remove unused submodules
frederoni d789258
Fix misc naming conventions
frederoni 2fc0605
Revert MGLShapeSource options
frederoni dbb5c5e
Remove stray refs
frederoni c935fb0
Title Case Titles
frederoni 28e0c4d
Simplify test
frederoni e28190b
Localizable error messages
frederoni d7bbc32
Clean up example
frederoni a0bb041
Extract localizable
frederoni 26a4eeb
Remove excessive protocol
frederoni 9553286
Convert all strings files, not just Core strings files
1ec5 c76ed23
Copyedited offline routing error messages
1ec5 3a80547
Made example app offline strings localizable
1ec5 e3be2ff
Refined tile URL methods
1ec5 d9e8147
Continued renaming OfflineDirections to NavigationDirections
1ec5 ea4844d
Updated changelog
1ec5 bdc1da0
Added missing files to CarPlay example app target
1ec5 ebbc84e
Unnest type aliases for Obj-C bridging
frederoni 14b3c37
result -> numberOfTiles
frederoni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "ic_resize.pdf" | ||
} | ||
], | ||
"info" : { | ||
"version" : 1, | ||
"author" : "xcode" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"images" : [ | ||
{ | ||
"idiom" : "universal", | ||
"filename" : "baseline-settings-20px.pdf" | ||
} | ||
], | ||
"info" : { | ||
"version" : 1, | ||
"author" : "xcode" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* Alert action */ | ||
"ALERT_OK" = "OK"; | ||
|
||
/* Title of button that downloads an offline region */ | ||
"OFFLINE_ITEM_DOWNLOAD" = "Download"; | ||
|
||
/* Status item while downloading an offline region */ | ||
"OFFLINE_TITLE_DOWNLOADING_TILES" = "Downloading Tiles…"; | ||
|
||
/* Status item while downloading an offline region */ | ||
"OFFLINE_TITLE_FETCHING_VERSIONS" = "Fetching Versions…"; | ||
|
||
/* Status item while downloading an offline region; 1 = percentage complete */ | ||
"OFFLINE_TITLE_UNPACKING_FMT" = "Unpacking… (%@)"; | ||
|
||
/* Title of action for dismissing waypoint removal confirmation sheet */ | ||
"REMOVE_WAYPOINT_CONFIRM_CANCEL" = "Cancel"; | ||
|
||
/* Message of sheet confirming waypoint removal */ | ||
"REMOVE_WAYPOINT_CONFIRM_MSG" = "Do you want to remove this waypoint?"; | ||
|
||
/* Title of alert sheet action for removing a waypoint */ | ||
"REMOVE_WAYPOINT_CONFIRM_REMOVE" = "Remove Waypoint"; | ||
|
||
/* Title of sheet confirming waypoint removal */ | ||
"REMOVE_WAYPOINT_CONFIRM_TITLE" = "Remove Waypoint?"; | ||
|
||
/* Alert message when a router has been configured; 1 = number of map tiles */ | ||
"ROUTER_CONFIGURED_MSG" = "Router configured with %ld tile(s)."; | ||
|
||
/* Title of table view item that downloads a new offline region */ | ||
"SETTINGS_ITEM_DOWNLOAD_REGION_TITLE" = "Download Region"; | ||
|
||
/* Section of offline settings table view */ | ||
"SETTINGS_SECTION_DOWNLOADED_VERSIONS" = "Downloaded Versions"; | ||
|
||
/* Section of offline settings table view */ | ||
"SETTINGS_SECTION_OFFLINE_EXAMPLES" = "Offline Examples"; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import UIKit | ||
import Mapbox | ||
import MapboxDirections | ||
import MapboxCoreNavigation | ||
|
||
class OfflineViewController: UIViewController { | ||
|
||
var mapView: MGLMapView! | ||
var resizableView: ResizableView! | ||
var backgroundLayer = CAShapeLayer() | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
mapView = MGLMapView(frame: view.bounds) | ||
view.addSubview(mapView) | ||
|
||
backgroundLayer.frame = view.bounds | ||
backgroundLayer.fillColor = #colorLiteral(red: 0.1450980392, green: 0.2588235294, blue: 0.3725490196, alpha: 0.196852993).cgColor | ||
view.layer.addSublayer(backgroundLayer) | ||
|
||
resizableView = ResizableView(frame: CGRect(origin: view.center, size: CGSize(width: 50, height: 50)), | ||
backgroundLayer: backgroundLayer) | ||
|
||
view.addSubview(resizableView) | ||
|
||
navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("OFFLINE_ITEM_DOWNLOAD", value: "Download", comment: "Title of button that downloads an offline region"), style: .done, target: self, action: #selector(downloadRegion)) | ||
} | ||
|
||
@objc func downloadRegion() { | ||
|
||
// Hide the download button so we can't download the same region twice | ||
navigationItem.rightBarButtonItem = nil | ||
|
||
let northWest = mapView.convert(resizableView.frame.minXY, toCoordinateFrom: nil) | ||
let southEast = mapView.convert(resizableView.frame.maxXY, toCoordinateFrom: nil) | ||
|
||
let coordinateBounds = CoordinateBounds([northWest, southEast]) | ||
|
||
updateTitle(NSLocalizedString("OFFLINE_TITLE_FETCHING_VERSIONS", value: "Fetching Versions…", comment: "Status item while downloading an offline region")) | ||
|
||
Directions.shared.fetchAvailableOfflineVersions { [weak self] (versions, error) in | ||
|
||
guard let version = versions?.first(where: { !$0.isEmpty } ) else { return } | ||
|
||
self?.updateTitle(NSLocalizedString("OFFLINE_TITLE_DOWNLOADING_TILES", value: "Downloading Tiles…", comment: "Status item while downloading an offline region")) | ||
|
||
Directions.shared.downloadTiles(in: coordinateBounds, version: version, completionHandler: { (url, response, error) in | ||
guard let url = url else { return assert(false, "Unable to locate temporary file") } | ||
|
||
let outputDirectoryURL = Bundle.mapboxCoreNavigation.suggestedTilePathURL(for: version) | ||
outputDirectoryURL?.ensureDirectoryExists() | ||
|
||
NavigationDirections.unpackTilePack(at: url, outputDirectoryURL: outputDirectoryURL!, progressHandler: { (totalBytes, bytesRemaining) in | ||
|
||
let progress = Float(bytesRemaining) / Float(totalBytes) | ||
let formattedProgress = NumberFormatter.localizedString(from: progress as NSNumber, number: .percent) | ||
let title = String.localizedStringWithFormat(NSLocalizedString("OFFLINE_TITLE_UNPACKING_FMT", value: "Unpacking… (%@)", comment: "Status item while downloading an offline region; 1 = percentage complete"), formattedProgress) | ||
self?.updateTitle(title) | ||
|
||
}, completionHandler: { (result, error) in | ||
|
||
self?.navigationController?.popViewController(animated: true) | ||
}) | ||
}).resume() | ||
}.resume() | ||
} | ||
|
||
func updateTitle(_ string: String) { | ||
DispatchQueue.main.async { [weak self] in | ||
self?.navigationItem.title = string | ||
} | ||
} | ||
} | ||
|
||
extension CGRect { | ||
|
||
var minXY: CGPoint { | ||
return CGPoint(x: minX, y: minY) | ||
} | ||
|
||
var maxXY: CGPoint { | ||
return CGPoint(x: maxX, y: maxY) | ||
} | ||
} | ||
|
||
extension URL { | ||
|
||
func ensureDirectoryExists() { | ||
try? FileManager.default.createDirectory(at: self, withIntermediateDirectories: true, attributes: nil) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import UIKit | ||
|
||
|
||
class ResizableView: UIControl { | ||
|
||
let lineLayer = CAShapeLayer() | ||
// Associated background layer will be masked by the frame of the resizable view | ||
weak var backgroundLayer: CAShapeLayer? | ||
let maskLayer = CAShapeLayer() | ||
var imageView: UIImageView! | ||
var panRecognizer: UIPanGestureRecognizer! | ||
var resizePanRecognizer: UIPanGestureRecognizer! | ||
|
||
convenience init(frame: CGRect, backgroundLayer: CAShapeLayer) { | ||
self.init(frame: frame) | ||
self.backgroundLayer = backgroundLayer | ||
} | ||
|
||
override init(frame: CGRect) { | ||
|
||
super.init(frame: frame) | ||
|
||
clipsToBounds = false | ||
layer.masksToBounds = false | ||
isUserInteractionEnabled = true | ||
backgroundColor = .clear | ||
isOpaque = false | ||
layer.backgroundColor = UIColor.clear.cgColor | ||
|
||
panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(pan(_:))) | ||
resizePanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(resizePan(_:))) | ||
|
||
let image = UIImage(named: "ic_resize")!.withPadding(x: 12, y: 12)! | ||
imageView = UIImageView(image: image.withRenderingMode(.alwaysTemplate)) | ||
imageView.layer.cornerRadius = image.size.width.mid | ||
imageView.backgroundColor = .white | ||
imageView.tintColor = #colorLiteral(red: 0, green: 0.5490196078, blue: 1, alpha: 1) | ||
imageView.isUserInteractionEnabled = true | ||
imageView.layer.shadowColor = #colorLiteral(red: 0.1029271765, green: 0.08949588804, blue: 0.1094761982, alpha: 0.8005611796) | ||
imageView.layer.shadowOffset = CGSize(width: 0, height: 1) | ||
imageView.layer.shadowOpacity = 1 | ||
imageView.layer.shadowRadius = 1.0 | ||
|
||
panRecognizer.require(toFail: resizePanRecognizer) | ||
|
||
addGestureRecognizer(panRecognizer) | ||
imageView.addGestureRecognizer(resizePanRecognizer) | ||
addSubview(imageView) | ||
} | ||
|
||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
@objc func pan(_ sender: UIPanGestureRecognizer) { | ||
if sender.state == .began || sender.state == .changed { | ||
center = sender.location(in: superview) | ||
|
||
layoutSubviews() | ||
} | ||
} | ||
|
||
@objc func resizePan(_ sender: UIPanGestureRecognizer) { | ||
|
||
let location = sender.location(in: superview) | ||
|
||
if sender.state == .began || sender.state == .changed { | ||
|
||
let origin = CGPoint(x: frame.minX, y: frame.minY) | ||
frame = CGRect(origin: origin, | ||
size: CGSize(width: location.x - origin.x, | ||
height: location.y - origin.y)) | ||
layoutSubviews() | ||
} | ||
} | ||
|
||
override func layoutSubviews() { | ||
super.layoutSubviews() | ||
|
||
if lineLayer.superlayer == nil { | ||
lineLayer.strokeColor = #colorLiteral(red: 0, green: 0.5490196078, blue: 1, alpha: 1).cgColor | ||
lineLayer.fillColor = UIColor.clear.cgColor | ||
lineLayer.lineWidth = 1 | ||
lineLayer.lineDashPattern = [5, 5] | ||
layer.addSublayer(lineLayer) | ||
} | ||
|
||
lineLayer.path = UIBezierPath(rect: bounds).cgPath | ||
|
||
let clippedPath = UIBezierPath(rect: superview!.bounds) | ||
clippedPath.append(UIBezierPath(rect: lineLayer.frame)) | ||
|
||
let superFrame = self.convert(superview!.bounds, to: self) | ||
|
||
if let backgroundLayer = backgroundLayer { | ||
backgroundLayer.path = UIBezierPath(rect: superFrame).cgPath | ||
backgroundLayer.frame = superFrame | ||
|
||
let path = UIBezierPath(rect: frame) | ||
path.append(UIBezierPath(rect: backgroundLayer.bounds)) | ||
|
||
maskLayer.fillRule = kCAFillRuleEvenOdd | ||
maskLayer.path = path.cgPath | ||
backgroundLayer.mask = maskLayer | ||
} | ||
|
||
imageView.center = CGPoint(x: bounds.maxX-5, y: bounds.maxY-5) | ||
|
||
bringSubview(toFront: imageView) | ||
} | ||
|
||
} | ||
|
||
fileprivate extension CGFloat { | ||
|
||
var mid: CGFloat { | ||
return self / 2 | ||
} | ||
} | ||
|
||
extension UIImage { | ||
|
||
func withPadding(x: CGFloat, y: CGFloat) -> UIImage? { | ||
let width: CGFloat = size.width + x | ||
let height: CGFloat = size.height + y | ||
UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 0) | ||
|
||
defer { | ||
UIGraphicsEndImageContext() | ||
} | ||
|
||
let origin: CGPoint = CGPoint(x: (width - size.width) / 2, y: (height - size.height) / 2) | ||
draw(at: origin) | ||
|
||
return UIGraphicsGetImageFromCurrentImageContext() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Foundation | ||
import MapboxCoreNavigation | ||
import MapboxDirections | ||
|
||
|
||
struct Settings { | ||
|
||
static var directions: NavigationDirections = NavigationDirections() | ||
|
||
static var selectedOfflineVersion: String? = nil | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not entirely sure it sets a good precedent to expose
Bundle.mapboxCoreNavigation
publicly. There’s little potential for collision with another library’s extension to Bundle, but maybe it would be better if applications explicitly calledBundle(for:)
? Or if we expect applications to have to callBundle.suggestedTilePathURL(for:)
, then maybe we should make it accessible from a class method and leavemapboxCoreNavigation
as an implementation detail.