From fdd18cdf4286c9f13085893528035188ca29df45 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:08:49 -0400 Subject: [PATCH 01/22] [GH-1440] - WIP Created a Highway shield struct to contain all the details for textColor and fillColor for current road feature. --- MapboxNavigation.xcodeproj/project.pbxproj | 4 + MapboxNavigation/HighwayShield.swift | 137 +++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 MapboxNavigation/HighwayShield.swift diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index 0d3bb6322c..4a21825cef 100644 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -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, ); }; }; @@ -609,6 +610,7 @@ AE46F95420EA735B00537AC2 /* VisualInstruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualInstruction.swift; sourceTree = ""; }; AE5F8770209A082500F58FDB /* route-with-banner-instructions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "route-with-banner-instructions.json"; sourceTree = ""; }; AE8B1B96207D2B2B003050F6 /* TunnelIntersectionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelIntersectionManagerTests.swift; sourceTree = ""; }; + AEC3AC992106703100A26F34 /* HighwayShield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighwayShield.swift; sourceTree = ""; }; AED2156E208F7FEA009AA673 /* NavigationViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewControllerTests.swift; sourceTree = ""; }; AEF2C8F12072B603007B061F /* routeWithTunnels_9thStreetDC.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = routeWithTunnels_9thStreetDC.json; sourceTree = ""; }; C51DF8651F38C31C006C6A15 /* Locale.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locale.swift; sourceTree = ""; }; @@ -947,6 +949,7 @@ 353AA55F1FCEF583009F0384 /* StyleManager.swift */, 359D1B271FFE70D30052FA42 /* NavigationView.swift */, C54C655120336F2600D338E0 /* Constants.swift */, + AEC3AC992106703100A26F34 /* HighwayShield.swift */, ); path = MapboxNavigation; sourceTree = ""; @@ -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 */, diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift new file mode 100644 index 0000000000..9c4fba6996 --- /dev/null +++ b/MapboxNavigation/HighwayShield.swift @@ -0,0 +1,137 @@ +struct HighwayShield { + + enum textColor { + case black, white + } + + enum fillColor { + case red, yellow, blue, green, white + } + + enum borderColor { + case black, white + } + + enum shield: String { + case `default` + case atMotorway = "at-motorway" + case atExpressway = "at-expressway" + case atStateB = "at-state-b" + case bgMotorway = "bg-motorway" + case bgNational = "bg-national" + case brFederal = "br-federal" + case brState = "br-state" + case chMotorway = "ch-motorway" + case chMain = "ch-main" + case czMotorway = "cz-motorway" + case czRoad = "cz-road" + case deMotorway = "de-motorway" + case deFederal = "de-federal" + case dkPrimary = "dk-primary" + case dkSecondary = "dk-secondary" + case fiMain = "fi-main" + case fiTrunk = "fi-trunk" + case fiRegional = "fi-regional" + case grMotorway = "gr-motorway" + case grNational = "gr-national" + case usHighway = "us-highway" + + func textColor() -> UIColor? { + switch self { + case .default: + return .black + case .atMotorway: + return .white + case .atExpressway: + return .white + case .atStateB: + return .white + case .bgMotorway: + return .white + case .bgNational: + return .white + case .brFederal: + return .black + case .brState: + return .black + case .chMotorway: + return .white + case .chMain: + return .white + case .czMotorway: + return .white + case .czRoad: + return .white + case .deMotorway: + return .white + case .deFederal: + return .black + case .dkPrimary: + return .black + case .dkSecondary: + return .black + case .fiMain: + return .white + case .fiTrunk: + return .black + case .fiRegional: + return .black + case .grMotorway: + return .white + case .grNational: + return .white + case .usHighway: + return .black + } + } + + func fillColor() -> UIColor { + switch self { + case .default: + return .white + case .atMotorway: + return .blue + case .atExpressway: + return .blue + case .atStateB: + return .blue + case .bgMotorway: + return .green + case .bgNational: + return .blue + case .brFederal: + return .white + case .brState: + return .white + case .chMotorway: + return .red + case .chMain: + return .blue + case .czMotorway: + return .blue + case .czRoad: + return .blue + case .deMotorway: + return .blue + case .deFederal: + return .yellow + case .dkPrimary: + return .yellow + case .dkSecondary: + return .white + case .fiMain: + return .red + case .fiTrunk: + return .yellow + case .fiRegional: + return .white + case .grMotorway: + return .green + case .grNational: + return .blue + case .usHighway: + return .white + } + } + } +} From 48f8e5971463fa849905e62f894daf36799d1062 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:10:22 -0400 Subject: [PATCH 02/22] [GH-1440] - Added TextPresenter which permits images to contain texts. --- MapboxNavigation/InstructionPresenter.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MapboxNavigation/InstructionPresenter.swift b/MapboxNavigation/InstructionPresenter.swift index be3b584969..803f1f897a 100644 --- a/MapboxNavigation/InstructionPresenter.swift +++ b/MapboxNavigation/InstructionPresenter.swift @@ -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 { @@ -269,12 +274,13 @@ 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 {} extension CGSize { fileprivate static var greatestFiniteSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) From cb4976ef6d54af6f0fb5c90eff5decfa395d16b0 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:10:58 -0400 Subject: [PATCH 03/22] [GH-1440] - Modify styles to contain an attributed text. --- MapboxNavigation/Style.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MapboxNavigation/Style.swift b/MapboxNavigation/Style.swift index c3ab4dfc25..d35809e6b9 100644 --- a/MapboxNavigation/Style.swift +++ b/MapboxNavigation/Style.swift @@ -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 } From 7874ada5de2f7ae1160adb5f8ca53326983688d8 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:23:31 -0400 Subject: [PATCH 04/22] [GH-1440] - Modify styles to contain an attributed text. --- MapboxNavigation/RouteMapViewController.swift | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index e9f28c419a..5e2f35abac 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -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] = [] @@ -808,18 +809,56 @@ extension RouteMapViewController: NavigationViewDelegate { } else { currentName = nil } + + if let line = feature as? MGLPolylineFeature { + if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { + + let currentShield = HighwayShield.shield(rawValue: shieldRawValue) + let textColor = currentShield?.textColor() + + let imageName = "\(shieldRawValue)-\(reflen)" + if let image = mapView.style?.image(forName: imageName) { + currentShieldName = attributedString(withFont: UIFont.systemFont(ofSize: 12.0), shieldImage: image, text: text, color: textColor) + } + } + } + } } } - 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 attributedString(withFont font: UIFont, shieldImage: UIImage, text: String, color: UIColor?) -> NSAttributedString { + let attachment = RoadNameLabelAttachment() + attachment.font = font + attachment.text = text + + let textHeight = font.lineHeight + let pointY = (shieldImage.size.height - textHeight) / 2 + + let compositeImage = shieldImage.insert(text: (text as NSString), + color: color ?? .black, + font: font, + atPoint: CGPoint(x: 0, y: pointY), + scale: UIScreen.main.scale) + + attachment.image = compositeImage + + return NSAttributedString(attachment: attachment) + } + @objc func updateETA() { guard isViewLoaded, routeController != nil else { return } navigationView.bottomBannerView.updateETA(routeProgress: routeController.routeProgress) From 6de1d559fe094b15be5ab287ee1a7b02f75146af Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:25:11 -0400 Subject: [PATCH 05/22] [GH-1440] - Added function to insert a text in the current image. --- MapboxNavigation/UIImage.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/MapboxNavigation/UIImage.swift b/MapboxNavigation/UIImage.swift index 876c277f05..00943868b4 100644 --- a/MapboxNavigation/UIImage.swift +++ b/MapboxNavigation/UIImage.swift @@ -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 newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage + } } From 187fb0f7a56afa07d03f2d8d95839d236e3fd8e5 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 14:08:49 -0400 Subject: [PATCH 06/22] [GH-1440] - Refactor to clean up certain function signature. Cartfile updated. --- Cartfile.resolved | 2 +- MapboxNavigation/HighwayShield.swift | 22 +++++-------------- MapboxNavigation/RouteMapViewController.swift | 12 +++++----- MapboxNavigation/UIImage.swift | 6 ++--- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index f2552f9794..d7f42028e9 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -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" diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 9c4fba6996..535477e5ce 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -1,19 +1,7 @@ struct HighwayShield { - enum textColor { - case black, white - } - - enum fillColor { - case red, yellow, blue, green, white - } - - enum borderColor { - case black, white - } - - enum shield: String { - case `default` + enum Identifier: String { + case generic = "default" case atMotorway = "at-motorway" case atExpressway = "at-expressway" case atStateB = "at-state-b" @@ -38,7 +26,7 @@ struct HighwayShield { func textColor() -> UIColor? { switch self { - case .default: + case .generic: return .black case .atMotorway: return .white @@ -87,7 +75,7 @@ struct HighwayShield { func fillColor() -> UIColor { switch self { - case .default: + case .generic: return .white case .atMotorway: return .blue @@ -133,5 +121,7 @@ struct HighwayShield { return .white } } + + // TODO: borderColor } } diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 5e2f35abac..9e47e1faf5 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -813,7 +813,7 @@ extension RouteMapViewController: NavigationViewDelegate { if let line = feature as? MGLPolylineFeature { if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { - let currentShield = HighwayShield.shield(rawValue: shieldRawValue) + let currentShield = HighwayShield.Identifier(rawValue: shieldRawValue) let textColor = currentShield?.textColor() let imageName = "\(shieldRawValue)-\(reflen)" @@ -848,11 +848,11 @@ extension RouteMapViewController: NavigationViewDelegate { let textHeight = font.lineHeight let pointY = (shieldImage.size.height - textHeight) / 2 - let compositeImage = shieldImage.insert(text: (text as NSString), - color: color ?? .black, - font: font, - atPoint: CGPoint(x: 0, y: pointY), - scale: UIScreen.main.scale) + let compositeImage = shieldImage.composited(text: (text as NSString), + color: color ?? .black, + font: font, + atPoint: CGPoint(x: 0, y: pointY), + scale: UIScreen.main.scale) attachment.image = compositeImage diff --git a/MapboxNavigation/UIImage.swift b/MapboxNavigation/UIImage.swift index 00943868b4..09873fb46f 100644 --- a/MapboxNavigation/UIImage.swift +++ b/MapboxNavigation/UIImage.swift @@ -10,7 +10,7 @@ extension UIImage { return tintedImage } - func insert(text: NSString, color: UIColor, font: UIFont, atPoint: CGPoint, scale: CGFloat) -> UIImage? { + func composited(text: NSString, color: UIColor, font: UIFont, atPoint: CGPoint, scale: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, scale) let textStyle = NSMutableParagraphStyle() @@ -22,9 +22,9 @@ extension UIImage { let rect = CGRect(x: atPoint.x, y: atPoint.y, width: size.width, height: size.height) text.draw(in: rect.integral, withAttributes: textFontAttributes) - let newImage = UIGraphicsGetImageFromCurrentImageContext() + let compositedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() - return newImage + return compositedImage } } From 776eda82894e31cc3769d2d0ab143903eb205a44 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 15:43:01 -0400 Subject: [PATCH 07/22] [GH-1440] - Deleted un-used fill color added to the highway shield struct. --- MapboxNavigation/HighwayShield.swift | 51 ---------------------------- 1 file changed, 51 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 535477e5ce..af95ce7ac5 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -72,56 +72,5 @@ struct HighwayShield { return .black } } - - func fillColor() -> UIColor { - switch self { - case .generic: - return .white - case .atMotorway: - return .blue - case .atExpressway: - return .blue - case .atStateB: - return .blue - case .bgMotorway: - return .green - case .bgNational: - return .blue - case .brFederal: - return .white - case .brState: - return .white - case .chMotorway: - return .red - case .chMain: - return .blue - case .czMotorway: - return .blue - case .czRoad: - return .blue - case .deMotorway: - return .blue - case .deFederal: - return .yellow - case .dkPrimary: - return .yellow - case .dkSecondary: - return .white - case .fiMain: - return .red - case .fiTrunk: - return .yellow - case .fiRegional: - return .white - case .grMotorway: - return .green - case .grNational: - return .blue - case .usHighway: - return .white - } - } - - // TODO: borderColor } } From 4a0938af275ada7298cddb348e642184096f556c Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 24 Jul 2018 17:50:30 -0400 Subject: [PATCH 08/22] [GH-1440] - WIP - Struct to handle the parsing of highway shield icons. --- MapboxNavigation/HighwayShield.swift | 51 ++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index af95ce7ac5..100c33f0c6 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -1,5 +1,56 @@ struct HighwayShield { + enum RoadClass: String { + case alternate, duplex, business, truck, bypass + case oneB = "1b", twoA = "2a", twoB = "2b", b + } + + enum RoadType { + case generic, motorway, expressway, state(RoadClass), highway(RoadClass) + case national, federal, main, road, primary, secondary, trunk, regional + case voivodeship, county, communal, interstate(RoadClass) + } + + enum Country { + case us(RoadType), at(RoadType), bg(RoadType), br(RoadType), ch(RoadType) + case cz(RoadType), de(RoadType), dk(RoadType), fi(RoadType), gr(RoadType) + case hr(RoadType), hu(RoadType), `in`(RoadType), mx(RoadType), nz(RoadType) + case pe(RoadType), pl(RoadType), ro(RoadType), rs(RoadType), se(RoadType) + case si(RoadType), sk(RoadType), za(RoadType), e(RoadType) + case `default` + + func textColor() -> UIColor { + switch self { + case .us(.interstate(let roadClass)): + switch roadClass { + case .duplex, .business, .truck: + return .white + default: + return .black + } + case .us(.highway(let roadClass)): + switch roadClass { + case .duplex, .alternate, .business, .bypass, .truck: fallthrough + default: + return .black + } + case .default: + return .black + case .at(.motorway): + return .white + case .at(.state(let roadClass)): + switch roadClass { + case .b: + return .white + default: + return .black + } + default: + return .black + } + } + } + enum Identifier: String { case generic = "default" case atMotorway = "at-motorway" From 0907c18356b90b005fdb5cc24bd5a41aa395527a Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Wed, 25 Jul 2018 22:50:47 -0400 Subject: [PATCH 09/22] [GH-1440] - Modified the highway shield with concise enum variations. Updated route map view controller to integrate the new changes in the highway shield struct. --- MapboxNavigation/HighwayShield.swift | 230 ++++++++++-------- MapboxNavigation/RouteMapViewController.swift | 4 +- 2 files changed, 129 insertions(+), 105 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 100c33f0c6..459a820a35 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -5,123 +5,147 @@ struct HighwayShield { case oneB = "1b", twoA = "2a", twoB = "2b", b } - enum RoadType { - case generic, motorway, expressway, state(RoadClass), highway(RoadClass) - case national, federal, main, road, primary, secondary, trunk, regional - case voivodeship, county, communal, interstate(RoadClass) - } - - enum Country { - case us(RoadType), at(RoadType), bg(RoadType), br(RoadType), ch(RoadType) - case cz(RoadType), de(RoadType), dk(RoadType), fi(RoadType), gr(RoadType) - case hr(RoadType), hu(RoadType), `in`(RoadType), mx(RoadType), nz(RoadType) - case pe(RoadType), pl(RoadType), ro(RoadType), rs(RoadType), se(RoadType) - case si(RoadType), sk(RoadType), za(RoadType), e(RoadType) - case `default` + enum RoadType: RawRepresentable { + typealias RawValue = String + typealias RoadTypeTemplate = (Locale, RoadClass?) -> RoadType - func textColor() -> UIColor { + var rawValue: String { switch self { - case .us(.interstate(let roadClass)): - switch roadClass { - case .duplex, .business, .truck: - return .white - default: - return .black + case .generic: + return "generic" + case .motorway: + return "motorway" + case .expressway: + return "expressway" + case .state: + return "state" + case .highway: + return "highway" + case .national: + return "national" + case .federal: + return "federal" + case .main: + return "main" + case .road: + return "road" + case .primary: + return "primary" + case .secondary: + return "secondary" + case .trunk: + return "trunk" + case .regional: + return "regional" + case .voivodeship: + return "voivodeship" + case .county: + return "county" + case .communal: + return "communal" + case .interstate: + return "interstate" + } + } + + 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 } - case .us(.highway(let roadClass)): - switch roadClass { - case .duplex, .alternate, .business, .bypass, .truck: fallthrough - default: - return .black + 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) -> RoadTypeTemplate? { + 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 "state": + return RoadType.state + case "highway": + return RoadType.highway + case "interstate": + return RoadType.interstate + default: + return nil + } + } + + typealias LocaleOnly = (Locale) -> RoadType + static func localeOnlyTransform(_ closure: @escaping LocaleOnly) -> RoadTypeTemplate { + return { locale, _ in + return closure(locale) + } + } + + var textColor: UIColor? { + switch self { + case let .highway(locale, _): + if locale == .slovakia { + return .white } - case .default: return .black - case .at(.motorway): + case .generic, .communal: + return .black + case .motorway, .expressway: return .white - case .at(.state(let roadClass)): - switch roadClass { - case .b: + case let .state(locale, roadClass): + switch (locale) { + case .austria, .croatia, .newZealand, + .serbia where roadClass == RoadClass.oneB: return .white default: return .black } default: - return .black + 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?) } - enum Identifier: String { - case generic = "default" - case atMotorway = "at-motorway" - case atExpressway = "at-expressway" - case atStateB = "at-state-b" - case bgMotorway = "bg-motorway" - case bgNational = "bg-national" - case brFederal = "br-federal" - case brState = "br-state" - case chMotorway = "ch-motorway" - case chMain = "ch-main" - case czMotorway = "cz-motorway" - case czRoad = "cz-road" - case deMotorway = "de-motorway" - case deFederal = "de-federal" - case dkPrimary = "dk-primary" - case dkSecondary = "dk-secondary" - case fiMain = "fi-main" - case fiTrunk = "fi-trunk" - case fiRegional = "fi-regional" - case grMotorway = "gr-motorway" - case grNational = "gr-national" - case usHighway = "us-highway" - - func textColor() -> UIColor? { - switch self { - case .generic: - return .black - case .atMotorway: - return .white - case .atExpressway: - return .white - case .atStateB: - return .white - case .bgMotorway: - return .white - case .bgNational: - return .white - case .brFederal: - return .black - case .brState: - return .black - case .chMotorway: - return .white - case .chMain: - return .white - case .czMotorway: - return .white - case .czRoad: - return .white - case .deMotorway: - return .white - case .deFederal: - return .black - case .dkPrimary: - return .black - case .dkSecondary: - return .black - case .fiMain: - return .white - case .fiTrunk: - return .black - case .fiRegional: - return .black - case .grMotorway: - return .white - case .grNational: - return .white - case .usHighway: - return .black - } - } + enum Locale: String { + case usa = "us", 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", southAfrica = "za", eRoad = "e" } } diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 9e47e1faf5..58e7aa341a 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -813,8 +813,8 @@ extension RouteMapViewController: NavigationViewDelegate { if let line = feature as? MGLPolylineFeature { if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { - let currentShield = HighwayShield.Identifier(rawValue: shieldRawValue) - let textColor = currentShield?.textColor() + let currentShield = HighwayShield.RoadType(rawValue: shieldRawValue) + let textColor = currentShield?.textColor ?? .black let imageName = "\(shieldRawValue)-\(reflen)" if let image = mapView.style?.image(forName: imageName) { From ab7cd594f2bbd31a5dd414c887cf0653adffb939 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Wed, 25 Jul 2018 22:50:47 -0400 Subject: [PATCH 10/22] [GH-1440] - Modified the highway shield with concise enum variations. Updated route map view controller to integrate the new changes in the highway shield struct. Co-authored-by: Jerrad Thramer Co-authored-by: Vincent Sam --- MapboxNavigation/HighwayShield.swift | 230 ++++++++++-------- MapboxNavigation/RouteMapViewController.swift | 4 +- 2 files changed, 129 insertions(+), 105 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 100c33f0c6..459a820a35 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -5,123 +5,147 @@ struct HighwayShield { case oneB = "1b", twoA = "2a", twoB = "2b", b } - enum RoadType { - case generic, motorway, expressway, state(RoadClass), highway(RoadClass) - case national, federal, main, road, primary, secondary, trunk, regional - case voivodeship, county, communal, interstate(RoadClass) - } - - enum Country { - case us(RoadType), at(RoadType), bg(RoadType), br(RoadType), ch(RoadType) - case cz(RoadType), de(RoadType), dk(RoadType), fi(RoadType), gr(RoadType) - case hr(RoadType), hu(RoadType), `in`(RoadType), mx(RoadType), nz(RoadType) - case pe(RoadType), pl(RoadType), ro(RoadType), rs(RoadType), se(RoadType) - case si(RoadType), sk(RoadType), za(RoadType), e(RoadType) - case `default` + enum RoadType: RawRepresentable { + typealias RawValue = String + typealias RoadTypeTemplate = (Locale, RoadClass?) -> RoadType - func textColor() -> UIColor { + var rawValue: String { switch self { - case .us(.interstate(let roadClass)): - switch roadClass { - case .duplex, .business, .truck: - return .white - default: - return .black + case .generic: + return "generic" + case .motorway: + return "motorway" + case .expressway: + return "expressway" + case .state: + return "state" + case .highway: + return "highway" + case .national: + return "national" + case .federal: + return "federal" + case .main: + return "main" + case .road: + return "road" + case .primary: + return "primary" + case .secondary: + return "secondary" + case .trunk: + return "trunk" + case .regional: + return "regional" + case .voivodeship: + return "voivodeship" + case .county: + return "county" + case .communal: + return "communal" + case .interstate: + return "interstate" + } + } + + 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 } - case .us(.highway(let roadClass)): - switch roadClass { - case .duplex, .alternate, .business, .bypass, .truck: fallthrough - default: - return .black + 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) -> RoadTypeTemplate? { + 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 "state": + return RoadType.state + case "highway": + return RoadType.highway + case "interstate": + return RoadType.interstate + default: + return nil + } + } + + typealias LocaleOnly = (Locale) -> RoadType + static func localeOnlyTransform(_ closure: @escaping LocaleOnly) -> RoadTypeTemplate { + return { locale, _ in + return closure(locale) + } + } + + var textColor: UIColor? { + switch self { + case let .highway(locale, _): + if locale == .slovakia { + return .white } - case .default: return .black - case .at(.motorway): + case .generic, .communal: + return .black + case .motorway, .expressway: return .white - case .at(.state(let roadClass)): - switch roadClass { - case .b: + case let .state(locale, roadClass): + switch (locale) { + case .austria, .croatia, .newZealand, + .serbia where roadClass == RoadClass.oneB: return .white default: return .black } default: - return .black + 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?) } - enum Identifier: String { - case generic = "default" - case atMotorway = "at-motorway" - case atExpressway = "at-expressway" - case atStateB = "at-state-b" - case bgMotorway = "bg-motorway" - case bgNational = "bg-national" - case brFederal = "br-federal" - case brState = "br-state" - case chMotorway = "ch-motorway" - case chMain = "ch-main" - case czMotorway = "cz-motorway" - case czRoad = "cz-road" - case deMotorway = "de-motorway" - case deFederal = "de-federal" - case dkPrimary = "dk-primary" - case dkSecondary = "dk-secondary" - case fiMain = "fi-main" - case fiTrunk = "fi-trunk" - case fiRegional = "fi-regional" - case grMotorway = "gr-motorway" - case grNational = "gr-national" - case usHighway = "us-highway" - - func textColor() -> UIColor? { - switch self { - case .generic: - return .black - case .atMotorway: - return .white - case .atExpressway: - return .white - case .atStateB: - return .white - case .bgMotorway: - return .white - case .bgNational: - return .white - case .brFederal: - return .black - case .brState: - return .black - case .chMotorway: - return .white - case .chMain: - return .white - case .czMotorway: - return .white - case .czRoad: - return .white - case .deMotorway: - return .white - case .deFederal: - return .black - case .dkPrimary: - return .black - case .dkSecondary: - return .black - case .fiMain: - return .white - case .fiTrunk: - return .black - case .fiRegional: - return .black - case .grMotorway: - return .white - case .grNational: - return .white - case .usHighway: - return .black - } - } + enum Locale: String { + case usa = "us", 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", southAfrica = "za", eRoad = "e" } } diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 9e47e1faf5..58e7aa341a 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -813,8 +813,8 @@ extension RouteMapViewController: NavigationViewDelegate { if let line = feature as? MGLPolylineFeature { if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { - let currentShield = HighwayShield.Identifier(rawValue: shieldRawValue) - let textColor = currentShield?.textColor() + let currentShield = HighwayShield.RoadType(rawValue: shieldRawValue) + let textColor = currentShield?.textColor ?? .black let imageName = "\(shieldRawValue)-\(reflen)" if let image = mapView.style?.image(forName: imageName) { From 2494cc620741506e96a1a89074403aa7dfaa5f93 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Thu, 26 Jul 2018 11:39:16 -0400 Subject: [PATCH 11/22] [GH-1440] - Refactored the closures for locale and locale with road class used within the highway shield struct. --- MapboxNavigation/HighwayShield.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 459a820a35..9f71b9abf4 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -1,13 +1,14 @@ struct HighwayShield { enum RoadClass: String { - case alternate, duplex, business, truck, bypass - case oneB = "1b", twoA = "2a", twoB = "2b", b + case alternate, duplex, business, truck, bypass, b + case oneB = "1b", twoA = "2a", twoB = "2b" } enum RoadType: RawRepresentable { typealias RawValue = String - typealias RoadTypeTemplate = (Locale, RoadClass?) -> RoadType + typealias RoadTypeForLocaleRoadClassClosure = (Locale, RoadClass?) -> RoadType + typealias RoadTypeForLocaleClosure = (Locale) -> RoadType var rawValue: String { switch self { @@ -70,7 +71,7 @@ struct HighwayShield { } } - private static func type(for identifier: String) -> RoadTypeTemplate? { + private static func type(for identifier: String) -> RoadTypeForLocaleRoadClassClosure? { switch identifier { case "motorway": return localeOnlyTransform(RoadType.motorway) @@ -109,8 +110,8 @@ struct HighwayShield { } } - typealias LocaleOnly = (Locale) -> RoadType - static func localeOnlyTransform(_ closure: @escaping LocaleOnly) -> RoadTypeTemplate { + + static func localeOnlyTransform(_ closure: @escaping RoadTypeForLocaleClosure) -> RoadTypeForLocaleRoadClassClosure { return { locale, _ in return closure(locale) } @@ -146,6 +147,6 @@ struct HighwayShield { } enum Locale: String { - case usa = "us", 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", southAfrica = "za", eRoad = "e" + 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", eRoad = "e" } } From afc19bf659b1478c63d13b3e810532c1006d93b2 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Thu, 26 Jul 2018 11:41:00 -0400 Subject: [PATCH 12/22] [GH-1440] - Renamed the insert method in the uiimage extension. --- MapboxNavigation/RouteMapViewController.swift | 10 +++++----- MapboxNavigation/UIImage.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 58e7aa341a..c62cfdcc93 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -848,11 +848,11 @@ extension RouteMapViewController: NavigationViewDelegate { let textHeight = font.lineHeight let pointY = (shieldImage.size.height - textHeight) / 2 - let compositeImage = shieldImage.composited(text: (text as NSString), - color: color ?? .black, - font: font, - atPoint: CGPoint(x: 0, y: pointY), - scale: UIScreen.main.scale) + let compositeImage = shieldImage.insert(text: (text as NSString), + color: color ?? .black, + font: font, + atPoint: CGPoint(x: 0, y: pointY), + scale: UIScreen.main.scale) attachment.image = compositeImage diff --git a/MapboxNavigation/UIImage.swift b/MapboxNavigation/UIImage.swift index 09873fb46f..c08626c7c3 100644 --- a/MapboxNavigation/UIImage.swift +++ b/MapboxNavigation/UIImage.swift @@ -10,7 +10,7 @@ extension UIImage { return tintedImage } - func composited(text: NSString, color: UIColor, font: UIFont, atPoint: CGPoint, scale: CGFloat) -> UIImage? { + func insert(text: NSString, color: UIColor, font: UIFont, atPoint: CGPoint, scale: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, scale) let textStyle = NSMutableParagraphStyle() From 5c15b03e1c8e38662bebf94140a7f84234f6fac5 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Thu, 26 Jul 2018 12:37:27 -0400 Subject: [PATCH 13/22] [GH-1440] - Bolden the text of the system font centered text in the highway shield. --- MapboxNavigation/RouteMapViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index c62cfdcc93..c95dd3e0e7 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -818,7 +818,7 @@ extension RouteMapViewController: NavigationViewDelegate { let imageName = "\(shieldRawValue)-\(reflen)" if let image = mapView.style?.image(forName: imageName) { - currentShieldName = attributedString(withFont: UIFont.systemFont(ofSize: 12.0), shieldImage: image, text: text, color: textColor) + currentShieldName = attributedString(withFont: UIFont.boldSystemFont(ofSize: 12.0), shieldImage: image, text: text, color: textColor) } } } From a5d75bf9eae4dbab647902919e218819999c1c4e Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Thu, 26 Jul 2018 16:56:28 -0400 Subject: [PATCH 14/22] [GH-1440] - Added missing enum types. --- MapboxNavigation/HighwayShield.swift | 39 ++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index 9f71b9abf4..f36efd1a3b 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -46,6 +46,10 @@ struct HighwayShield { return "communal" case .interstate: return "interstate" + case .metropolitan: + return "metropolitan" + case .provincial: + return "provincial" } } @@ -99,6 +103,10 @@ struct HighwayShield { 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": @@ -124,18 +132,39 @@ struct HighwayShield { return .white } return .black - case .generic, .communal: + case .generic, .communal, .voivodeship, .trunk, .primary, .secondary: return .black - case .motorway, .expressway: + case .motorway, .expressway, .road, .interstate: return .white case let .state(locale, roadClass): - switch (locale) { + 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 } @@ -143,10 +172,10 @@ struct HighwayShield { 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?) + 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", eRoad = "e" + 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 } } From 795f55395e4be5f4d1ee73267b1f108810bcae0b Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Mon, 30 Jul 2018 12:35:14 -0400 Subject: [PATCH 15/22] [GH-1440] - Added convenience initializer to handle the composition of text and image for road name label attachment. Refactor to retrieve the appropriate attachment for current shields on a road along the route. --- MapboxNavigation/InstructionPresenter.swift | 28 ++++++++++++- MapboxNavigation/RouteMapViewController.swift | 39 +++++-------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/MapboxNavigation/InstructionPresenter.swift b/MapboxNavigation/InstructionPresenter.swift index 803f1f897a..da0e0e4d61 100644 --- a/MapboxNavigation/InstructionPresenter.swift +++ b/MapboxNavigation/InstructionPresenter.swift @@ -280,7 +280,33 @@ class TextInstruction: ImageInstruction {} class ShieldAttachment: ImageInstruction {} class GenericShieldAttachment: ShieldAttachment {} class ExitAttachment: ImageInstruction {} -class RoadNameLabelAttachment: TextInstruction {} +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) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index c95dd3e0e7..f32dac251c 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -802,15 +802,15 @@ 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 { + 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 { + currentName = name + } else { + currentName = nil + } + if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { let currentShield = HighwayShield.RoadType(rawValue: shieldRawValue) @@ -818,11 +818,11 @@ extension RouteMapViewController: NavigationViewDelegate { let imageName = "\(shieldRawValue)-\(reflen)" if let image = mapView.style?.image(forName: imageName) { - currentShieldName = attributedString(withFont: UIFont.boldSystemFont(ofSize: 12.0), shieldImage: image, text: text, color: textColor) + let attachment = RoadNameLabelAttachment(image: image, text: text, color: textColor, font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize), scale: UIScreen.main.scale) + currentShieldName = NSAttributedString(attachment: attachment) } } } - } } } @@ -840,25 +840,6 @@ extension RouteMapViewController: NavigationViewDelegate { } } - private func attributedString(withFont font: UIFont, shieldImage: UIImage, text: String, color: UIColor?) -> NSAttributedString { - let attachment = RoadNameLabelAttachment() - attachment.font = font - attachment.text = text - - let textHeight = font.lineHeight - let pointY = (shieldImage.size.height - textHeight) / 2 - - let compositeImage = shieldImage.insert(text: (text as NSString), - color: color ?? .black, - font: font, - atPoint: CGPoint(x: 0, y: pointY), - scale: UIScreen.main.scale) - - attachment.image = compositeImage - - return NSAttributedString(attachment: attachment) - } - @objc func updateETA() { guard isViewLoaded, routeController != nil else { return } navigationView.bottomBannerView.updateETA(routeProgress: routeController.routeProgress) From a76fafdba8060ed20d17f860f22af514d406f00c Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Mon, 30 Jul 2018 17:00:52 -0400 Subject: [PATCH 16/22] [GH-1440] - Refactored the logic that retrieves the shield name from the current polyline and multipolyline features. Updates to return the original full format of the rawValue init with HighwayShield. --- MapboxNavigation/HighwayShield.swift | 76 ++++++++++--------- MapboxNavigation/RouteMapViewController.swift | 32 +++++--- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/MapboxNavigation/HighwayShield.swift b/MapboxNavigation/HighwayShield.swift index f36efd1a3b..a2da3aa392 100644 --- a/MapboxNavigation/HighwayShield.swift +++ b/MapboxNavigation/HighwayShield.swift @@ -14,42 +14,45 @@ struct HighwayShield { switch self { case .generic: return "generic" - case .motorway: - return "motorway" - case .expressway: - return "expressway" - case .state: - return "state" - case .highway: - return "highway" - case .national: - return "national" - case .federal: - return "federal" - case .main: - return "main" - case .road: - return "road" - case .primary: - return "primary" - case .secondary: - return "secondary" - case .trunk: - return "trunk" - case .regional: - return "regional" - case .voivodeship: - return "voivodeship" - case .county: - return "county" - case .communal: - return "communal" - case .interstate: - return "interstate" - case .metropolitan: - return "metropolitan" - case .provincial: - return "provincial" + 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" } } @@ -118,7 +121,6 @@ struct HighwayShield { } } - static func localeOnlyTransform(_ closure: @escaping RoadTypeForLocaleClosure) -> RoadTypeForLocaleRoadClassClosure { return { locale, _ in return closure(locale) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index f32dac251c..0b1c7c22ce 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -811,17 +811,12 @@ extension RouteMapViewController: NavigationViewDelegate { currentName = nil } - if let text = line.attribute(forKey: "ref") as? String, let shieldRawValue = line.attribute(forKey: "shield") as? String, let reflen = line.attribute(forKey: "reflen") { - - let currentShield = HighwayShield.RoadType(rawValue: shieldRawValue) - let textColor = currentShield?.textColor ?? .black - - let imageName = "\(shieldRawValue)-\(reflen)" - if let image = mapView.style?.image(forName: imageName) { - let attachment = RoadNameLabelAttachment(image: image, text: text, color: textColor, font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize), scale: UIScreen.main.scale) - currentShieldName = NSAttributedString(attachment: attachment) - } + 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) } } } @@ -840,6 +835,23 @@ extension RouteMapViewController: NavigationViewDelegate { } } + 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) From 32942de2ea80da969b657ae1be8b06e6033cb1aa Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Mon, 30 Jul 2018 17:11:26 -0400 Subject: [PATCH 17/22] [GH-1440] - Minor clean up. --- MapboxNavigation/RouteMapViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 0b1c7c22ce..d41f8797a7 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -840,7 +840,6 @@ extension RouteMapViewController: NavigationViewDelegate { let currentShield = HighwayShield.RoadType(rawValue: shield) let textColor = currentShield?.textColor ?? .black - let imageName = "\(shield)-\(reflen)" guard let image = mapView.style?.image(forName: imageName) else { From 0926a42a6a6e8ce52a3008ca89dec630a95d9203 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 31 Jul 2018 13:20:43 -0400 Subject: [PATCH 18/22] [GH-1440] - Refactor to retrieve the data for shield and names for the given road segment. --- MapboxNavigation/RouteMapViewController.swift | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index d41f8797a7..9e462df54f 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -803,20 +803,13 @@ extension RouteMapViewController: NavigationViewDelegate { smallestLabelDistance = minDistanceBetweenPoints if let line = feature as? MGLPolylineFeature { - 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 { - 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) + let roadNameRecord = roadFeature(for: line) + currentShieldName = roadNameRecord.shieldName + currentName = roadNameRecord.roadName + } else if let line = feature as? MGLMultiPolylineFeature { + let roadNameRecord = roadFeature(for: line) + currentShieldName = roadNameRecord.shieldName + currentName = roadNameRecord.roadName } } } @@ -835,6 +828,36 @@ extension RouteMapViewController: NavigationViewDelegate { } } + private func roadFeature(for line: MGLPolylineFeature) -> (roadName: String?, shieldName: NSAttributedString?) { + let roadNameRecord = roadFeatureHelper(ref: line.attribute(forKey: "ref"), + shield: line.attribute(forKey: "shield"), + reflen: line.attribute(forKey: "reflen"), + name: line.attribute(forKey: "name")) + + return (roadName: roadNameRecord.roadName, shieldName: roadNameRecord.shieldName) + } + + private func roadFeature(for line: MGLMultiPolylineFeature) -> (roadName: String?, shieldName: NSAttributedString?) { + let roadNameRecord = roadFeatureHelper(ref: line.attribute(forKey: "ref"), + shield: line.attribute(forKey: "shield"), + reflen: line.attribute(forKey: "reflen"), + name: line.attribute(forKey: "name")) + + return (roadName: roadNameRecord.roadName, shieldName: roadNameRecord.shieldName) + } + + private func roadFeatureHelper(ref: Any?, shield: Any?, reflen: Any?, name: Any?) -> (roadName: String?, shieldName: NSAttributedString?) { + var currentShieldName: NSAttributedString?, currentRoadName: String? + + if let t = ref as? String, let s = shield as? String, let r = reflen as? Int { + currentShieldName = roadShieldName(for: t, shield: s, reflen: r) + } else if let roadName = name as? String { + currentRoadName = roadName + } + + return (roadName: currentRoadName, shieldName: currentShieldName) + } + private func roadShieldName(for text: String?, shield: String?, reflen: Int?) -> NSAttributedString? { guard let text = text, let shield = shield, let reflen = reflen else { return nil } From d33a3b9ec2429e2802c15efe8eab817a42d80d19 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 31 Jul 2018 13:22:41 -0400 Subject: [PATCH 19/22] [GH-1440] - Minor clean up - removed extra spaces. --- MapboxNavigation/RouteMapViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 9e462df54f..7abfaa3aa3 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -873,7 +873,6 @@ extension RouteMapViewController: NavigationViewDelegate { return NSAttributedString(attachment: attachment) } - @objc func updateETA() { guard isViewLoaded, routeController != nil else { return } navigationView.bottomBannerView.updateETA(routeProgress: routeController.routeProgress) From 12f9954b07f8b91850fd9916ddf0b9851584e9a2 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Tue, 31 Jul 2018 13:25:54 -0400 Subject: [PATCH 20/22] [GH-1440] - Minor clean up - refactored variable names. --- MapboxNavigation/RouteMapViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 7abfaa3aa3..570ec02636 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -849,8 +849,8 @@ extension RouteMapViewController: NavigationViewDelegate { private func roadFeatureHelper(ref: Any?, shield: Any?, reflen: Any?, name: Any?) -> (roadName: String?, shieldName: NSAttributedString?) { var currentShieldName: NSAttributedString?, currentRoadName: String? - if let t = ref as? String, let s = shield as? String, let r = reflen as? Int { - currentShieldName = roadShieldName(for: t, shield: s, reflen: r) + if let text = ref as? String, let shieldID = shield as? String, let reflenDigit = reflen as? Int { + currentShieldName = roadShieldName(for: text, shield: shieldID, reflen: reflenDigit) } else if let roadName = name as? String { currentRoadName = roadName } From aedb833f5270552d18c944cce8ed7ccc2928eebc Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Wed, 1 Aug 2018 17:19:52 -0400 Subject: [PATCH 21/22] [GH-1440] - Updates to include the road name when the road shield is displayed. --- MapboxNavigation/RouteMapViewController.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 570ec02636..773543518c 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -851,10 +851,18 @@ extension RouteMapViewController: NavigationViewDelegate { if let text = ref as? String, let shieldID = shield as? String, let reflenDigit = reflen as? Int { currentShieldName = roadShieldName(for: text, shield: shieldID, reflen: reflenDigit) - } else if let roadName = name as? String { + } + + if let roadName = name as? String { currentRoadName = roadName } + if let compositeShieldImage = currentShieldName, let roadName = currentRoadName { + let compositeShield = NSMutableAttributedString(string: " \(roadName)") + compositeShield.insert(compositeShieldImage, at: 0) + currentShieldName = compositeShield + } + return (roadName: currentRoadName, shieldName: currentShieldName) } From efbbb5a0d627b65b33253f4d5f0a92fad82a6d77 Mon Sep 17 00:00:00 2001 From: Vincent Sam Date: Wed, 1 Aug 2018 22:34:52 -0400 Subject: [PATCH 22/22] [GH-1440] - Minor refactor, to keep things aligned. --- MapboxNavigation/RouteMapViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MapboxNavigation/RouteMapViewController.swift b/MapboxNavigation/RouteMapViewController.swift index 773543518c..1a2456dc48 100644 --- a/MapboxNavigation/RouteMapViewController.swift +++ b/MapboxNavigation/RouteMapViewController.swift @@ -830,9 +830,9 @@ extension RouteMapViewController: NavigationViewDelegate { private func roadFeature(for line: MGLPolylineFeature) -> (roadName: String?, shieldName: NSAttributedString?) { let roadNameRecord = roadFeatureHelper(ref: line.attribute(forKey: "ref"), - shield: line.attribute(forKey: "shield"), - reflen: line.attribute(forKey: "reflen"), - name: line.attribute(forKey: "name")) + shield: line.attribute(forKey: "shield"), + reflen: line.attribute(forKey: "reflen"), + name: line.attribute(forKey: "name")) return (roadName: roadNameRecord.roadName, shieldName: roadNameRecord.shieldName) }