-
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
Offline routing #1808
Changes from 62 commits
94aa6c7
772f1b3
a8f6f12
4056716
d66cc31
08ac909
3d56ce9
ebcb07a
fae883b
952ae0b
4e54461
6733b05
3f71104
dca0396
a78c3d9
3c6a554
0cab7df
1eae9d6
929f384
cea45a4
e20d854
2488c25
6472fba
15d24bd
c32d501
65328c8
4733e9c
135b83d
ae1fe73
32f8dea
a3bb7e6
40cbdeb
ff156c6
20d9d71
868f12b
fffffc4
55fa068
e80d95a
4c15c0c
37edfad
ba56eb6
a31f01e
3e73159
a65432b
7649ab0
b7e00da
029500d
38d9861
d7a5765
216fe26
74612a7
ccecaed
ec0ed10
720c439
d789258
2fc0605
dbb5c5e
c935fb0
28e0c4d
e28190b
d7bbc32
a0bb041
26a4eeb
9553286
c76ed23
3a80547
e3be2ff
d9e8147
ea4844d
bdc1da0
ebbc84e
14b3c37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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" | ||
} | ||
} |
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" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
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: "Download", style: .done, target: self, action: #selector(downloadRegion)) | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
} | ||
|
||
@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("Fetching Versions") | ||
|
||
Directions.shared.fetchAvailableOfflineVersions { [weak self] (versions, error) in | ||
|
||
guard let version = versions?.first(where: { !$0.isEmpty } ) else { return } | ||
|
||
self?.updateTitle("Downloading Tiles") | ||
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m not entirely sure it sets a good precedent to expose |
||
outputDirectoryURL?.ensureDirectoryExists() | ||
|
||
NavigationDirections.unpackTilePack(at: url, outputDirectoryURL: outputDirectoryURL!, progressHandler: { (totalBytes, bytesRemaining) in | ||
|
||
let progress = (Float(bytesRemaining) / Float(totalBytes)) * 100 | ||
self?.updateTitle("Unpacking \(Int(progress))%") | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
||
}, 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) | ||
} | ||
} |
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() | ||
} | ||
} |
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 | ||
|
||
} |
This comment was marked as resolved.
Sorry, something went wrong.