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

Retrieve additional map attributes to display reliable current road name. #1576

Merged
merged 24 commits into from
Aug 2, 2018
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fdd18cd
[GH-1440] - WIP Created a Highway shield struct to contain all the de…
vincethecoder Jul 24, 2018
48f8e59
[GH-1440] - Added TextPresenter which permits images to contain texts.
vincethecoder Jul 24, 2018
cb4976e
[GH-1440] - Modify styles to contain an attributed text.
vincethecoder Jul 24, 2018
7874ada
[GH-1440] - Modify styles to contain an attributed text.
vincethecoder Jul 24, 2018
6de1d55
[GH-1440] - Added function to insert a text in the current image.
vincethecoder Jul 24, 2018
187fb0f
[GH-1440] - Refactor to clean up certain function signature. Cartfile…
vincethecoder Jul 24, 2018
776eda8
[GH-1440] - Deleted un-used fill color added to the highway shield st…
vincethecoder Jul 24, 2018
4a0938a
[GH-1440] - WIP - Struct to handle the parsing of highway shield icons.
vincethecoder Jul 24, 2018
0907c18
[GH-1440] - Modified the highway shield with concise enum variations.…
vincethecoder Jul 26, 2018
ab7cd59
[GH-1440] - Modified the highway shield with concise enum variations.…
vincethecoder Jul 26, 2018
254b832
Merge branch 'navigation/reliable_current_roadname_label' of github.c…
vincethecoder Jul 26, 2018
2494cc6
[GH-1440] - Refactored the closures for locale and locale with road c…
vincethecoder Jul 26, 2018
afc19bf
[GH-1440] - Renamed the insert method in the uiimage extension.
vincethecoder Jul 26, 2018
5c15b03
[GH-1440] - Bolden the text of the system font centered text in the h…
vincethecoder Jul 26, 2018
a5d75bf
[GH-1440] - Added missing enum types.
vincethecoder Jul 26, 2018
795f553
[GH-1440] - Added convenience initializer to handle the composition o…
vincethecoder Jul 30, 2018
a76fafd
[GH-1440] - Refactored the logic that retrieves the shield name from …
vincethecoder Jul 30, 2018
32942de
[GH-1440] - Minor clean up.
vincethecoder Jul 30, 2018
0926a42
[GH-1440] - Refactor to retrieve the data for shield and names for th…
vincethecoder Jul 31, 2018
d33a3b9
[GH-1440] - Minor clean up - removed extra spaces.
vincethecoder Jul 31, 2018
12f9954
[GH-1440] - Minor clean up - refactored variable names.
vincethecoder Jul 31, 2018
5deaa9f
Merge branch 'master' into navigation/reliable_current_roadname_label
vincethecoder Jul 31, 2018
aedb833
[GH-1440] - Updates to include the road name when the road shield is …
vincethecoder Aug 1, 2018
efbbb5a
[GH-1440] - Minor refactor, to keep things aligned.
vincethecoder Aug 2, 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
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ github "Quick/Nimble" "v7.1.3"
github "Quick/Quick" "v1.3.1"
github "ceeK/Solar" "2.1.0"
github "mapbox/MapboxDirections.swift" "v0.22.0"
github "mapbox/mapbox-events-ios" "v0.4.2"
github "mapbox/mapbox-events-ios" "v0.4.3"
github "mapbox/mapbox-voice-swift" "v0.0.1"
github "mapbox/turf-swift" "v0.2.0"
github "raphaelmor/Polyline" "v4.2.0"
Expand Down
4 changes: 4 additions & 0 deletions MapboxNavigation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
AE5F8771209A082500F58FDB /* route-with-banner-instructions.json in Resources */ = {isa = PBXBuildFile; fileRef = AE5F8770209A082500F58FDB /* route-with-banner-instructions.json */; };
AE8B1B95207BFAEF003050F6 /* TunnelIntersectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE09EC13207BD88200782A33 /* TunnelIntersectionManager.swift */; };
AE8B1B97207D2B2B003050F6 /* TunnelIntersectionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE8B1B96207D2B2B003050F6 /* TunnelIntersectionManagerTests.swift */; };
AEC3AC9A2106703100A26F34 /* HighwayShield.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC3AC992106703100A26F34 /* HighwayShield.swift */; };
AEF2C8F22072B603007B061F /* routeWithTunnels_9thStreetDC.json in Resources */ = {isa = PBXBuildFile; fileRef = AEF2C8F12072B603007B061F /* routeWithTunnels_9thStreetDC.json */; };
C51245F21F19471C00E33B52 /* MapboxMobileEvents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C549F8311F17F2C5001A0A2D /* MapboxMobileEvents.framework */; };
C51245F31F19471C00E33B52 /* MapboxMobileEvents.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C549F8311F17F2C5001A0A2D /* MapboxMobileEvents.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -609,6 +610,7 @@
AE46F95420EA735B00537AC2 /* VisualInstruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualInstruction.swift; sourceTree = "<group>"; };
AE5F8770209A082500F58FDB /* route-with-banner-instructions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-banner-instructions.json"; sourceTree = "<group>"; };
AE8B1B96207D2B2B003050F6 /* TunnelIntersectionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelIntersectionManagerTests.swift; sourceTree = "<group>"; };
AEC3AC992106703100A26F34 /* HighwayShield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighwayShield.swift; sourceTree = "<group>"; };
AED2156E208F7FEA009AA673 /* NavigationViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewControllerTests.swift; sourceTree = "<group>"; };
AEF2C8F12072B603007B061F /* routeWithTunnels_9thStreetDC.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = routeWithTunnels_9thStreetDC.json; sourceTree = "<group>"; };
C51DF8651F38C31C006C6A15 /* Locale.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locale.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -947,6 +949,7 @@
353AA55F1FCEF583009F0384 /* StyleManager.swift */,
359D1B271FFE70D30052FA42 /* NavigationView.swift */,
C54C655120336F2600D338E0 /* Constants.swift */,
AEC3AC992106703100A26F34 /* HighwayShield.swift */,
);
path = MapboxNavigation;
sourceTree = "<group>";
Expand Down Expand Up @@ -1822,6 +1825,7 @@
35D428291FA0B61F00176028 /* InstructionsBannerViewLayout.swift in Sources */,
C54C655220336F2600D338E0 /* Constants.swift in Sources */,
353610CE1FAB6A8F00FB1746 /* BottomBannerView.swift in Sources */,
AEC3AC9A2106703100A26F34 /* HighwayShield.swift in Sources */,
C58822001FB0F0D7008B0A2D /* Error.swift in Sources */,
35C9973F1E732C1B00544D1C /* RouteVoiceController.swift in Sources */,
8D24A2FA20449B430098CBF8 /* Dictionary.swift in Sources */,
Expand Down
183 changes: 183 additions & 0 deletions MapboxNavigation/HighwayShield.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
struct HighwayShield {

enum RoadClass: String {
case alternate, duplex, business, truck, bypass, b
case oneB = "1b", twoA = "2a", twoB = "2b"
}

enum RoadType: RawRepresentable {
typealias RawValue = String
typealias RoadTypeForLocaleRoadClassClosure = (Locale, RoadClass?) -> RoadType
typealias RoadTypeForLocaleClosure = (Locale) -> RoadType

var rawValue: String {
switch self {
case .generic:
return "generic"
case let .motorway(locale):
return "\(locale.rawValue)-motorway"
case let .expressway(locale):
return "\(locale.rawValue)-expressway"
case let .state(locale, roadClass):
guard let currentRoadClass = roadClass else { return "\(locale)-state" }
return "\(locale.rawValue)-state-\(currentRoadClass.rawValue)"
case let .highway(locale, roadClass):
guard let currentRoadClass = roadClass else { return "\(locale)-highway" }
return "\(locale.rawValue)-highway-\(currentRoadClass.rawValue)"
case let .national(locale):
return "\(locale.rawValue)-national"
case let .federal(locale):
return "\(locale.rawValue)-federal"
case let .main(locale):
return "\(locale.rawValue)-main"
case let .road(locale):
return "\(locale.rawValue)-road"
case let .primary(locale):
return "\(locale.rawValue)-primary"
case let .secondary(locale):
return "\(locale.rawValue)-secondary"
case let .trunk(locale):
return "\(locale.rawValue)-trunk"
case let .regional(locale):
return "\(locale.rawValue)-regional"
case let .voivodeship(locale):
return "\(locale.rawValue)-voivodeship"
case let .county(locale):
return "\(locale.rawValue)-county"
case let .communal(locale):
return "\(locale)-communal"
case let .interstate(locale, roadClass):
guard let currentRoadClass = roadClass else { return "\(locale.rawValue)-interstate"}
return "\(locale.rawValue)-interstate-\(currentRoadClass.rawValue)"
case let .metropolitan(locale):
return "\(locale.rawValue)-metropolitan"
case let .provincial(locale):
return "\(locale.rawValue)-provincial"
}
}

init?(rawValue: RawValue) {
let fields = rawValue.split(separator: "-").compactMap(String.init(_:))
switch fields.count {
case 1 where rawValue == "default":
self = .generic
case 2:
guard let roadType = RoadType.type(for: fields.last!),
let locale = Locale(rawValue: fields.first!) else {
return nil
}
self = roadType(locale, nil)
case 3:
guard let roadType = RoadType.type(for: fields[1]),
let locale = Locale(rawValue: fields[0]), let roadClass = RoadClass(rawValue: fields[2]) else {
return nil
}
self = roadType(locale, roadClass)
default:
return nil
}
}

private static func type(for identifier: String) -> RoadTypeForLocaleRoadClassClosure? {
switch identifier {
case "motorway":
return localeOnlyTransform(RoadType.motorway)
case "expressway":
return localeOnlyTransform(RoadType.expressway)
case "national":
return localeOnlyTransform(RoadType.national)
case "federal":
return localeOnlyTransform(RoadType.federal)
case "main":
return localeOnlyTransform(RoadType.main)
case "road":
return localeOnlyTransform(RoadType.road)
case "primary":
return localeOnlyTransform(RoadType.primary)
case "secondary":
return localeOnlyTransform(RoadType.secondary)
case "trunk":
return localeOnlyTransform(RoadType.trunk)
case "regional":
return localeOnlyTransform(RoadType.regional)
case "voivodeship":
return localeOnlyTransform(RoadType.voivodeship)
case "county":
return localeOnlyTransform(RoadType.county)
case "communal":
return localeOnlyTransform(RoadType.communal)
case "provincial":
return localeOnlyTransform(RoadType.provincial)
case "metropolitan":
return localeOnlyTransform(RoadType.metropolitan)
case "state":
return RoadType.state
case "highway":
return RoadType.highway
case "interstate":
return RoadType.interstate
default:
return nil
}
}

static func localeOnlyTransform(_ closure: @escaping RoadTypeForLocaleClosure) -> RoadTypeForLocaleRoadClassClosure {
return { locale, _ in
return closure(locale)
}
}

var textColor: UIColor? {
switch self {
case let .highway(locale, _):
if locale == .slovakia {
return .white
}
return .black
case .generic, .communal, .voivodeship, .trunk, .primary, .secondary:
return .black
case .motorway, .expressway, .road, .interstate:
return .white
case let .state(locale, roadClass):
switch locale {
case .austria, .croatia, .newZealand,
.serbia where roadClass == RoadClass.oneB:
return .white
default:
return .black
}
case .regional, .metropolitan, .provincial:
return .yellow
case let .county(locale):
if locale == .romania {
return .white
}
return .black
case let .main(locale):
if locale == .slovenia {
return .black
}
return .white
case let .national(locale):
switch locale {
case .southAfrica:
return .yellow
case .poland, .romania, .greece, .bulgeria:
return .white
default:
return .black
}
default:
return nil
}
}

case generic, motorway(Locale), expressway(Locale), state(Locale, RoadClass?), highway(Locale, RoadClass?)
case national(Locale), federal(Locale), main(Locale), road(Locale), primary(Locale), secondary(Locale), trunk(Locale), regional(Locale)
case voivodeship(Locale), county(Locale), communal(Locale), interstate(Locale, RoadClass?), metropolitan(Locale), provincial(Locale)
}

enum Locale: String {
case austria = "at", bulgeria = "bg", brazil = "br", switzerland = "ch", czech = "cz", germany = "de", denmark = "dk", finland = "fi", greece = "gr", croatia = "hr", hungary = "hu", india = "in", mexico = "mx", newZealand = "nz", peru = "pe", poland = "pl", romania = "ro", serbia = "rs", sweden = "se", slovenia = "si", slovakia = "sk", usa = "us", southAfrica = "za", e
}
}
36 changes: 34 additions & 2 deletions MapboxNavigation/InstructionPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,18 @@ class InstructionPresenter {

}

protocol ImagePresenter {
protocol ImagePresenter: TextPresenter {
var image: UIImage? { get }
}

protocol TextPresenter {
var text: String? { get }
var font: UIFont { get }
}

class ImageInstruction: NSTextAttachment, ImagePresenter {
var font: UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize)
var text: String?

override func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
guard let image = image else {
Expand All @@ -269,12 +274,39 @@ class ImageInstruction: NSTextAttachment, ImagePresenter {
let yOrigin = (font.capHeight - image.size.height).rounded() / 2
return CGRect(x: 0, y: yOrigin, width: image.size.width, height: image.size.height)
}

}

class TextInstruction: ImageInstruction {}
class ShieldAttachment: ImageInstruction {}
class GenericShieldAttachment: ShieldAttachment {}
class ExitAttachment: ImageInstruction {}
class RoadNameLabelAttachment: TextInstruction {
var scale: CGFloat?
var color: UIColor?

var compositeImage: UIImage? {
guard let image = image, let text = text, let color = color, let scale = scale else {
return nil
}

var currentImage: UIImage?
let textHeight = font.lineHeight
let pointY = (image.size.height - textHeight) / 2
currentImage = image.insert(text: text as NSString, color: color, font: font, atPoint: CGPoint(x: 0, y: pointY), scale: scale)

return currentImage
}

convenience init(image: UIImage, text: String, color: UIColor, font: UIFont, scale: CGFloat) {
self.init()
self.image = image
self.font = font
self.text = text
self.color = color
self.scale = scale
self.image = compositeImage ?? image
}
}

extension CGSize {
fileprivate static var greatestFiniteSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
Expand Down
48 changes: 40 additions & 8 deletions MapboxNavigation/RouteMapViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@ extension RouteMapViewController: NavigationViewDelegate {
let features = mapView.visibleFeatures(at: userPuck, styleLayerIdentifiers: Set([roadLabelLayerIdentifier]))
var smallestLabelDistance = Double.infinity
var currentName: String?
var currentShieldName: NSAttributedString?

for feature in features {
var allLines: [MGLPolyline] = []
Expand Down Expand Up @@ -801,25 +802,56 @@ extension RouteMapViewController: NavigationViewDelegate {
if minDistanceBetweenPoints < smallestLabelDistance {
smallestLabelDistance = minDistanceBetweenPoints

if let line = feature as? MGLPolylineFeature, let name = line.attribute(forKey: "name") as? String {
currentName = name
} else if let line = feature as? MGLMultiPolylineFeature, let name = line.attribute(forKey: "name") as? String {
currentName = name
} else {
currentName = nil
if let line = feature as? MGLPolylineFeature {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've already pulled this value out above. See if you can combine these if let statements.

if let name = line.attribute(forKey: "name") as? String {
currentName = name
} else if let line = feature as? MGLMultiPolylineFeature, let name = line.attribute(forKey: "name") as? String {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line smells suspicious to me. It's existing logic that checks for line = feature as? MGLPolylineFeature and line = feature as? MGLMultiPolylineFeature within its scope. Needs further investigation why this exists.
/cc @bsudekum @1ec5

currentName = name
} else {
currentName = nil
}

if let text = line.attribute(forKey: "ref") as? String, let shield = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") as? Int {
currentShieldName = roadShieldName(for: text, shield: shield, reflen: reflen)
}

} else if let line = feature as? MGLMultiPolylineFeature, let text = line.attribute(forKey: "ref") as? String, let shield = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") as? Int {
currentShieldName = roadShieldName(for: text, shield: shield, reflen: reflen)
}
}
}
}

if smallestLabelDistance < 5 && currentName != nil {
navigationView.wayNameView.text = currentName
let hasWayName = currentName != nil || currentShieldName != nil
if smallestLabelDistance < 5 && hasWayName {
if let currentShieldName = currentShieldName {
navigationView.wayNameView.attributedText = currentShieldName
} else if let currentName = currentName {
navigationView.wayNameView.text = currentName
}
navigationView.wayNameView.isHidden = false
} else {
navigationView.wayNameView.isHidden = true
}
}

private func roadShieldName(for text: String?, shield: String?, reflen: Int?) -> NSAttributedString? {
guard let text = text, let shield = shield, let reflen = reflen else { return nil }

let currentShield = HighwayShield.RoadType(rawValue: shield)
let textColor = currentShield?.textColor ?? .black

let imageName = "\(shield)-\(reflen)"

guard let image = mapView.style?.image(forName: imageName) else {
return nil
}

let attachment = RoadNameLabelAttachment(image: image, text: text, color: textColor, font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize), scale: UIScreen.main.scale)
return NSAttributedString(attachment: attachment)
}


@objc func updateETA() {
guard isViewLoaded, routeController != nil else { return }
navigationView.bottomBannerView.updateETA(routeProgress: routeController.routeProgress)
Expand Down
9 changes: 9 additions & 0 deletions MapboxNavigation/Style.swift
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ open class WayNameView: UIView {
}
}

var attributedText: NSAttributedString? {
get {
return label.attributedText
}
set {
label.attributedText = newValue
}
}

@objc dynamic public var borderColor: UIColor? {
get {
guard let color = layer.borderColor else { return nil }
Expand Down
18 changes: 18 additions & 0 deletions MapboxNavigation/UIImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,22 @@ extension UIImage {
UIGraphicsEndImageContext()
return tintedImage
}

func insert(text: NSString, color: UIColor, font: UIFont, atPoint: CGPoint, scale: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)

let textStyle = NSMutableParagraphStyle()
textStyle.alignment = .center

let textFontAttributes: [NSAttributedStringKey: Any] = [.font: font, .foregroundColor: color, .paragraphStyle: textStyle]
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

let rect = CGRect(x: atPoint.x, y: atPoint.y, width: size.width, height: size.height)
text.draw(in: rect.integral, withAttributes: textFontAttributes)

let compositedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return compositedImage
}
}