diff --git a/MapboxCoreNavigation/Dictionary.swift b/MapboxCoreNavigation/Dictionary.swift new file mode 100644 index 00000000000..6c6c04da139 --- /dev/null +++ b/MapboxCoreNavigation/Dictionary.swift @@ -0,0 +1,16 @@ +import Foundation + +extension Dictionary where Key == Int, Value: MGLStyleValue { + /** + Returns a copy of the stop dictionary with each value multiplied by the given factor. + */ + public func multiplied(by factor: Double) -> Dictionary { + var newCameraStop: [Int: MGLStyleValue] = [:] + for stop in self as [Int : MGLStyleValue] { + let f = stop.value as! MGLConstantStyleValue + let newValue = f.rawValue.doubleValue * factor + newCameraStop[stop.key] = MGLStyleValue(rawValue: NSNumber(value:newValue)) + } + return newCameraStop as! Dictionary + } +} diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index 599f59e34ca..1e7fd2a2bb5 100644 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -162,6 +162,7 @@ C58822001FB0F0D7008B0A2D /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58821FF1FB0F0D7008B0A2D /* Error.swift */; }; C588C3C21F33882100520EF2 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35BF8CA31F28EBD8003F6125 /* String.swift */; }; C58D6BAD1DDCF2AE00387F53 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58D6BAC1DDCF2AE00387F53 /* Constants.swift */; }; + C59094B3203B379800EB2417 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C59094B1203B378000EB2417 /* Dictionary.swift */; }; C5A6B2DD1F4CE8E8004260EA /* StyleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5A6B2DC1F4CE8E8004260EA /* StyleType.swift */; }; C5A6B2DE1F4DE57E004260EA /* Solar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C57607B01F4CC97D00C27423 /* Solar.framework */; }; C5A6B2DF1F4DE57E004260EA /* Solar.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C57607B01F4CC97D00C27423 /* Solar.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -473,6 +474,7 @@ C582FD5E203626E900A9086E /* CLLocationDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocationDirection.swift; sourceTree = ""; }; C58821FF1FB0F0D7008B0A2D /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; C58D6BAC1DDCF2AE00387F53 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + C59094B1203B378000EB2417 /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Dictionary.swift; path = ../MapboxCoreNavigation/Dictionary.swift; sourceTree = ""; }; C5A6B2DC1F4CE8E8004260EA /* StyleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyleType.swift; sourceTree = ""; }; C5A7EC5B1FD610A80008B9BA /* VisualInstructionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualInstructionComponent.swift; sourceTree = ""; }; C5A9DDBD202E12EE007D52DA /* MapboxSpeech.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapboxSpeech.framework; path = Carthage/Build/iOS/MapboxSpeech.framework; sourceTree = ""; }; @@ -857,6 +859,7 @@ children = ( C5000A921EC25C6E00563EA9 /* Abbreviations.swift */, 351BEC081E5BCC72006FE110 /* Bundle.swift */, + C59094B1203B378000EB2417 /* Dictionary.swift */, 8DF399B11FB257B30034904C /* UIGestureRecognizer.swift */, 351BEC041E5BCC6C006FE110 /* ManeuverDirection.swift */, 351BEBDF1E5BCC63006FE110 /* MGLMapView.swift */, @@ -1490,6 +1493,7 @@ 35D825FC1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m in Sources */, 353280A11FA72871005175F3 /* InstructionLabel.swift in Sources */, 351BEBFF1E5BCC63006FE110 /* ManeuversStyleKit.swift in Sources */, + C59094B3203B379800EB2417 /* Dictionary.swift in Sources */, 35D428291FA0B61F00176028 /* InstructionsBannerViewLayout.swift in Sources */, C54C655220336F2600D338E0 /* Constants.swift in Sources */, 353610CE1FAB6A8F00FB1746 /* BottomBannerView.swift in Sources */, diff --git a/MapboxNavigation/Constants.swift b/MapboxNavigation/Constants.swift index 71b2fd85ec4..4c5a0c8fa6d 100644 --- a/MapboxNavigation/Constants.swift +++ b/MapboxNavigation/Constants.swift @@ -1,6 +1,32 @@ import Foundation +import MapboxDirections + +typealias CongestionSegment = ([CLLocationCoordinate2D], CongestionLevel) + +/** + A stop dictionary representing the default line widths of the route line by zoom level when `NavigationMapViewDelegate.navigationMapView(_:routeStyleLayerWithIdentifier:source:)` is undefined. + + You may use this constant in your implementation of `NavigationMapViewDelegate.navigationMapView(_:routeStyleLayerWithIdentifier:source:)` if you want to keep the default line widths but customize other aspects of the route line. + */ +public let MBRouteLineWidthByZoomLevel: [Int: MGLStyleValue] = [ + 10: MGLStyleValue(rawValue: 8), + 13: MGLStyleValue(rawValue: 9), + 16: MGLStyleValue(rawValue: 11), + 19: MGLStyleValue(rawValue: 22), + 22: MGLStyleValue(rawValue: 28) +] /** The minium distance remaining on a route before overhead zooming is stopped. */ public var NavigationMapViewMinimumDistanceForOverheadZooming: CLLocationDistance = 200 + +/** + Attribute name for the route line that is used for identifying whether a RouteLeg is the current active leg. + */ +public let MBCurrentLegAttribute = "isCurrentLeg" + +/** + Attribute name for the route line that is used for identifying different `CongestionLevel` along the route. + */ +public let MBCongestionAttribute = "congestion" diff --git a/MapboxNavigation/Dictionary.swift b/MapboxNavigation/Dictionary.swift new file mode 100644 index 00000000000..193255ea314 --- /dev/null +++ b/MapboxNavigation/Dictionary.swift @@ -0,0 +1,17 @@ +import Foundation + +extension Dictionary where Key == Int, Value: MGLStyleValue { + /** + Returns a proportional `Dictionary` of `lineWidthAtZoomLevels` at a specific factor. + */ + public func multiplied(by factor: Double) -> Dictionary { + var newCameraStop: [Int: MGLStyleValue] = [:] + for stop in lineWidthAtZoomLevels { + let f = stop.value as! MGLConstantStyleValue + let newValue = f.rawValue.doubleValue * factor + newCameraStop[stop.key] = MGLStyleValue(rawValue: NSNumber(value:newValue)) + } + return newCameraStop as! Dictionary + } +} + diff --git a/MapboxNavigation/NavigationMapView.swift b/MapboxNavigation/NavigationMapView.swift index 85192ea657b..69b19702836 100644 --- a/MapboxNavigation/NavigationMapView.swift +++ b/MapboxNavigation/NavigationMapView.swift @@ -4,18 +4,6 @@ import MapboxDirections import MapboxCoreNavigation import Turf -typealias CongestionSegment = ([CLLocationCoordinate2D], CongestionLevel) - -let routeLineWidthAtZoomLevels: [Int: MGLStyleValue] = [ - 10: MGLStyleValue(rawValue: 8), - 13: MGLStyleValue(rawValue: 9), - 16: MGLStyleValue(rawValue: 11), - 19: MGLStyleValue(rawValue: 22), - 22: MGLStyleValue(rawValue: 28) -] - -let sourceOptions: [MGLShapeSourceOption: Any] = [.maximumZoomLevel: 16] - /** `NavigationMapView` is a subclass of `MGLMapView` with convenience functions for adding `Route` lines to a map. */ @@ -54,6 +42,8 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { */ public weak var courseTrackingDelegate: NavigationMapViewCourseTrackingDelegate? + let sourceOptions: [MGLShapeSourceOption: Any] = [.maximumZoomLevel: 16] + // MARK: Instance Properties let sourceIdentifier = "routeSource" let sourceCasingIdentifier = "routeCasingSource" @@ -75,24 +65,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let alternateSourceIdentifier = "alternateSource" let alternateLayerIdentifier = "alternateLayer" - /** - Attribute name for the route line that is used for identifying whether a RouteLeg is the current active leg. - */ - public let currentLegAttribute = "isCurrentLeg" - - /** - Attribute name for the route line that is used for identifying different `CongestionLevel` along the route. - */ - public let congestionAttribute = "congestion" - - let routeLineWidthAtZoomLevels: [Int: MGLStyleValue] = [ - 10: MGLStyleValue(rawValue: 8), - 13: MGLStyleValue(rawValue: 9), - 16: MGLStyleValue(rawValue: 12), - 19: MGLStyleValue(rawValue: 24), - 22: MGLStyleValue(rawValue: 30) - ] - @objc dynamic public var trafficUnknownColor: UIColor = .trafficUnknown @objc dynamic public var trafficLowColor: UIColor = .trafficLow @objc dynamic public var trafficModerateColor: UIColor = .trafficModerate @@ -706,7 +678,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { arrow.lineCap = MGLStyleValue(rawValue: cap) arrow.lineJoin = MGLStyleValue(rawValue: join) arrow.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.70), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 0.70), options: [.defaultValue : MGLConstantStyleValue(rawValue: 1.5)]) arrow.lineColor = MGLStyleValue(rawValue: .white) @@ -722,7 +694,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { arrowStroke.lineCap = MGLStyleValue(rawValue: cap) arrowStroke.lineJoin = MGLStyleValue(rawValue: join) arrowStroke.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.80), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 0.80), options: [.defaultValue : MGLConstantStyleValue(rawValue: 1.5)]) arrowStroke.lineColor = MGLStyleValue(rawValue: .defaultArrowStroke) @@ -751,7 +723,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { arrowSymbolLayer.iconRotationAlignment = MGLStyleValue(rawValue: NSValue(mglIconRotationAlignment: .map)) arrowSymbolLayer.iconRotation = MGLStyleValue(rawValue: shaftDirection as NSNumber) arrowSymbolLayer.iconScale = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.12), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 0.12), options: [.defaultValue : MGLConstantStyleValue(rawValue: 0.2)]) arrowSymbolLayer.iconAllowsOverlap = MGLStyleValue(rawValue: true) @@ -762,7 +734,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { arrowSymbolLayerCasing.iconRotationAlignment = MGLStyleValue(rawValue: NSValue(mglIconRotationAlignment: .map)) arrowSymbolLayerCasing.iconRotation = MGLStyleValue(rawValue: shaftDirection as NSNumber) arrowSymbolLayerCasing.iconScale = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.14), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 0.14), options: [.defaultValue : MGLConstantStyleValue(rawValue: 0.2)]) arrowSymbolLayerCasing.iconAllowsOverlap = MGLStyleValue(rawValue: true) @@ -858,11 +830,11 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let lines = mergedCongestionSegments.map { (congestionSegment: CongestionSegment) -> MGLPolylineFeature in let polyline = MGLPolylineFeature(coordinates: congestionSegment.0, count: UInt(congestionSegment.0.count)) - polyline.attributes[congestionAttribute] = String(describing: congestionSegment.1) + polyline.attributes[MBCongestionAttribute] = String(describing: congestionSegment.1) if let legIndex = legIndex { - polyline.attributes[currentLegAttribute] = index == legIndex + polyline.attributes[MBCurrentLegAttribute] = index == legIndex } else { - polyline.attributes[currentLegAttribute] = index == 0 + polyline.attributes[MBCurrentLegAttribute] = index == 0 } return polyline } @@ -883,9 +855,9 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let polyline = MGLPolylineFeature(coordinates: legCoordinates, count: UInt(legCoordinates.count)) if let legIndex = legIndex { - polyline.attributes[currentLegAttribute] = index == legIndex + polyline.attributes[MBCurrentLegAttribute] = index == legIndex } else { - polyline.attributes[currentLegAttribute] = index == 0 + polyline.attributes[MBCurrentLegAttribute] = index == 0 } linesPerLeg.append(polyline) } @@ -912,7 +884,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { // Take the default line width and make it wider for the casing lineCasing.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.85), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 0.85), options: [.defaultValue : MGLConstantStyleValue(rawValue: 1.5)]) lineCasing.lineColor = MGLStyleValue(rawValue: routeAlternateColor) @@ -952,7 +924,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { let line = MGLLineStyleLayer(identifier: identifier, source: source) line.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels, + cameraStops: MBRouteLineWidthByZoomLevel, options: [.defaultValue : MGLConstantStyleValue(rawValue: 1.5)]) line.lineColor = MGLStyleValue(interpolationMode: .categorical, sourceStops: [ @@ -961,12 +933,12 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { "moderate": MGLStyleValue(rawValue: trafficModerateColor), "heavy": MGLStyleValue(rawValue: trafficHeavyColor), "severe": MGLStyleValue(rawValue: trafficSevereColor) - ], attributeName: congestionAttribute, options: [.defaultValue: MGLStyleValue(rawValue: trafficUnknownColor)]) + ], attributeName: MBCongestionAttribute, options: [.defaultValue: MGLStyleValue(rawValue: trafficUnknownColor)]) line.lineOpacity = MGLStyleValue(interpolationMode: .categorical, sourceStops: [ true: MGLStyleValue(rawValue: 1), false: MGLStyleValue(rawValue: 0) - ], attributeName: currentLegAttribute, options: nil) + ], attributeName: MBCurrentLegAttribute, options: nil) line.lineJoin = MGLStyleValue(rawValue: NSValue(mglLineJoin: .round)) @@ -979,7 +951,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { // Take the default line width and make it wider for the casing lineCasing.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 1.5), + cameraStops: MBRouteLineWidthByZoomLevel.multiplied(by: 1.5), options: [.defaultValue : MGLConstantStyleValue(rawValue: 1.5)]) lineCasing.lineColor = MGLStyleValue(rawValue: routeCasingColor) @@ -989,7 +961,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { lineCasing.lineOpacity = MGLStyleValue(interpolationMode: .categorical, sourceStops: [ true: MGLStyleValue(rawValue: 1), false: MGLStyleValue(rawValue: 0.85) - ], attributeName: currentLegAttribute, options: nil) + ], attributeName: MBCurrentLegAttribute, options: nil) return lineCasing } @@ -1127,20 +1099,6 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate { } } -// MARK: Extensions - -extension Dictionary where Key == Int, Value: MGLStyleValue { - func multiplied(by factor: Double) -> Dictionary { - var newCameraStop: [Int: MGLStyleValue] = [:] - for stop in routeLineWidthAtZoomLevels { - let f = stop.value as! MGLConstantStyleValue - let newValue = f.rawValue.doubleValue * factor - newCameraStop[stop.key] = MGLStyleValue(rawValue: NSNumber(value:newValue)) - } - return newCameraStop as! Dictionary - } -} - /** The `NavigationMapViewDelegate` provides methods for configuring the NavigationMapView, as well as responding to events triggered by the NavigationMapView. */