Skip to content
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 72 commits into from
Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
94aa6c7
Outline offline routing
frederoni Oct 8, 2018
772f1b3
Add test fixtures
frederoni Oct 15, 2018
a8f6f12
Add OfflineRoutingTests and clean up OfflineDirections
frederoni Oct 15, 2018
4056716
Clean up error handling
frederoni Oct 15, 2018
d66cc31
Minify fixtures
frederoni Oct 15, 2018
08ac909
Capture expection and return tile count
frederoni Oct 15, 2018
3d56ce9
Temporarily disable offline routing tests
frederoni Oct 15, 2018
ebcb07a
Add documentation fix naming conventions
frederoni Oct 16, 2018
fae883b
Bump MapboxDirections
frederoni Oct 23, 2018
952ae0b
Return number of tiles
frederoni Oct 24, 2018
4e54461
Refactor
frederoni Oct 24, 2018
6733b05
Don't use offline directions by default in the example app
frederoni Oct 24, 2018
3f71104
Remove unneeded Obj-C category
frederoni Oct 25, 2018
dca0396
Remove boilerplate code
frederoni Oct 25, 2018
a78c3d9
Use URLs
frederoni Oct 25, 2018
3c6a554
Replace bundled translations with submodule
frederoni Oct 25, 2018
0cab7df
Update submodules on CI
frederoni Oct 25, 2018
1eae9d6
Remove unused import
frederoni Oct 26, 2018
929f384
Fix error bridging and description
frederoni Oct 26, 2018
cea45a4
Naming conventions
frederoni Oct 26, 2018
e20d854
Remove unused error
frederoni Oct 26, 2018
2488c25
Naming conventions
frederoni Oct 26, 2018
6472fba
Outline Offline example
frederoni Oct 29, 2018
15d24bd
Add arbitrary region downloader example
frederoni Oct 30, 2018
c32d501
Pixel pushing
frederoni Oct 30, 2018
65328c8
Add unpacker
frederoni Nov 1, 2018
4733e9c
List downloaded offline versions and clean up examples
frederoni Nov 1, 2018
135b83d
Provide UI for deleting a downloaded version
frederoni Nov 1, 2018
ae1fe73
Show unpacking progress
frederoni Nov 1, 2018
32f8dea
Pin MapboxDirections.swift
frederoni Nov 1, 2018
a3bb7e6
Remove unnecessary example
frederoni Nov 1, 2018
40cbdeb
Clean up
frederoni Nov 1, 2018
ff156c6
Optional translationsPath
frederoni Nov 1, 2018
20d9d71
Refactor initializer to function for reusability
frederoni Nov 21, 2018
868f12b
Hook up offline capabilities to the example app
frederoni Nov 21, 2018
fffffc4
Update tests
frederoni Nov 21, 2018
55fa068
Remove unnecessary submodule
frederoni Nov 21, 2018
e80d95a
Merge branch 'master' into offline-routing
frederoni Nov 22, 2018
4c15c0c
Merge branch 'master' into offline-routing
frederoni Nov 22, 2018
37edfad
Bump dependencies
frederoni Nov 22, 2018
ba56eb6
Update test fixtures
frederoni Nov 23, 2018
a31f01e
Enable offline routing tests
frederoni Nov 23, 2018
3e73159
Bump nav native
frederoni Nov 27, 2018
a65432b
Bump nav-native
frederoni Nov 27, 2018
7649ab0
Use offline routing in the example app when rerouting if enabled
frederoni Nov 27, 2018
b7e00da
Minify route fixture
frederoni Nov 27, 2018
029500d
Minify GeoJSON
frederoni Nov 27, 2018
38d9861
Bump nav-native
frederoni Nov 28, 2018
d7a5765
Offline route requests can be done asynchronously in the serial queue
frederoni Nov 28, 2018
216fe26
Temporarily disable lineDistanceMetrics
frederoni Nov 28, 2018
74612a7
Bump podspec
frederoni Nov 28, 2018
ccecaed
Merge branch 'master' into offline-routing
frederoni Dec 3, 2018
ec0ed10
Bump dependencies
frederoni Dec 3, 2018
720c439
Remove unused submodules
frederoni Dec 3, 2018
d789258
Fix misc naming conventions
frederoni Dec 3, 2018
2fc0605
Revert MGLShapeSource options
frederoni Dec 3, 2018
dbb5c5e
Remove stray refs
frederoni Dec 4, 2018
c935fb0
Title Case Titles
frederoni Dec 4, 2018
28e0c4d
Simplify test
frederoni Dec 4, 2018
e28190b
Localizable error messages
frederoni Dec 4, 2018
d7bbc32
Clean up example
frederoni Dec 4, 2018
a0bb041
Extract localizable
frederoni Dec 4, 2018
26a4eeb
Remove excessive protocol
frederoni Dec 4, 2018
9553286
Convert all strings files, not just Core strings files
1ec5 Dec 5, 2018
c76ed23
Copyedited offline routing error messages
1ec5 Dec 5, 2018
3a80547
Made example app offline strings localizable
1ec5 Dec 5, 2018
e3be2ff
Refined tile URL methods
1ec5 Dec 5, 2018
d9e8147
Continued renaming OfflineDirections to NavigationDirections
1ec5 Dec 5, 2018
ea4844d
Updated changelog
1ec5 Dec 5, 2018
bdc1da0
Added missing files to CarPlay example app target
1ec5 Dec 5, 2018
ebbc84e
Unnest type aliases for Obj-C bridging
frederoni Dec 5, 2018
14b3c37
result -> numberOfTiles
frederoni Dec 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .gitmodules
Empty file.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## v0.26.0

### Client-side routing

* Added a `NavigationDirections` class that manages offline tile packs and client-side route calculation. ([#1808](https://github.com/mapbox/mapbox-navigation-ios/pull/1808))
* Extended `Bundle` with `Bundle.suggestedTileURL` and other properties to facilitate offline downloads. ([#1808](https://github.com/mapbox/mapbox-navigation-ios/pull/1808))

### CarPlay

* When selecting a search result in CarPlay, the resulting routes lead to the search result’s routable location when available. Routes to a routable location are more likely to be passable. ([#1859](https://github.com/mapbox/mapbox-navigation-ios/pull/1859))
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" ~> 4.3
binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" ~> 3.2
binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" ~> 3.4.6
github "mapbox/MapboxDirections.swift" ~> 0.25.1
github "mapbox/turf-swift" ~> 0.2
github "mapbox/mapbox-events-ios" ~> 0.6
Expand Down
4 changes: 2 additions & 2 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" "4.6.0"
binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" "3.4.1"
binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" "3.4.6"
github "CedarBDD/Cedar" "v1.0"
github "Quick/Nimble" "v7.3.1"
github "Quick/Quick" "v1.3.2"
Expand All @@ -10,4 +10,4 @@ github "mapbox/mapbox-events-ios" "v0.6.0"
github "mapbox/mapbox-speech-swift" "v0.1.0"
github "mapbox/turf-swift" "v0.2.1"
github "raphaelmor/Polyline" "v4.2.0"
github "uber/ios-snapshot-test-case" "4.0.0"
github "uber/ios-snapshot-test-case" "4.0.1"
1 change: 1 addition & 0 deletions Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if isRunningTests() {
window!.rootViewController = UIViewController()
}

return true
}

Expand Down
12 changes: 12 additions & 0 deletions Example/Assets.xcassets/ic_resize.imageset/Contents.json
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.
12 changes: 12 additions & 0 deletions Example/Assets.xcassets/settings.imageset/Contents.json
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.
39 changes: 39 additions & 0 deletions Example/Base.lproj/Localizable.strings
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";

92 changes: 92 additions & 0 deletions Example/OfflineViewController.swift
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)
}
}
137 changes: 137 additions & 0 deletions Example/ResizableView.swift
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()
}
}
12 changes: 12 additions & 0 deletions Example/Settings.swift
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

}
Loading