-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1808 from mapbox/offline-routing
Offline routing
- Loading branch information
Showing
46 changed files
with
1,122 additions
and
50 deletions.
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
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.suggestedTileURL(version: 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.