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

Update to Maps SDK for iOS v4.0.0 #1076

Merged
merged 10 commits into from
Apr 24, 2018
Merged

Update to Maps SDK for iOS v4.0.0 #1076

merged 10 commits into from
Apr 24, 2018

Conversation

bsudekum
Copy link
Contributor

@bsudekum bsudekum commented Jan 26, 2018

Close: #1057

This PR updates to the Mapbox Maps for iOS dependency to v4.0.0.

todo:

  • zoom level dependent styling
  • congestion
  • active leg opacity
  • maneuver arrow
  • update changelog

Blocked by mapbox/mapbox-gl-native#11615.

/cc @mapbox/navigation-ios

@bsudekum bsudekum added the ⚠️ DO NOT MERGE PR should not be merged! label Jan 26, 2018
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

I’m impressed that you got this far with the rudimentary state of NSExpression in the map SDK. 👍 There’s a lot more room for concision; I think this PR, as written, points to improvements we can make in our expression documentation and especially the expression migration guide we’re preparing.

/cc @jmkiley

@@ -44,7 +44,7 @@ Pod::Spec.new do |s|
s.module_name = "MapboxNavigation"

s.dependency "MapboxCoreNavigation", "#{s.version.to_s}"
s.dependency "Mapbox-iOS-SDK", "~> 3.6"
s.dependency "Mapbox-iOS-SDK", "4.0.0-beta.1"
Copy link
Contributor

Choose a reason for hiding this comment

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

Remember to update the Cartfile and MapboxNavigation-Documentation.podspec before merging.

19: MGLStyleValue(rawValue: 22),
22: MGLStyleValue(rawValue: 28)

let routeLineWidthAtZoomLevels: [Int: NSExpression] = [
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to wrap these constant values in NSExpression. The dictionary can be [Int: Float] or [Int: Int], which also means you don’t need to declare the type, either.

13: NSExpression(forConstantValue: 9),
16: NSExpression(forConstantValue: 11),
19: NSExpression(forConstantValue: 22),
22: NSExpression(forConstantValue: 28)
Copy link
Contributor

Choose a reason for hiding this comment

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

@anandthakker, currently, it isn’t possible for a zoom expression ($zoomLevel) to appear outside a curve or interpolation. But would it be feasible to relax this restriction, so that this curve could be expressed as a single multiplication expression involving $zoomLevel?

Choose a reason for hiding this comment

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

Technically, it would be possible to relax the restriction on zoom expressions to:

  • If the expression is a zoom-only expression, no restrictions at all.
  • If the expression is also feature-dependent, it must be in the form F(zoom) * G(feature), where F is a step or interpolation expression using zoom as the input (and nowhere else), and G is not zoom-dependent.

We've chosen so far to go with the existing, less permissive restriction because it's so much simpler to understand. I think the above, which is maximally permissive (barring significant changes to the data-driven styling implementation), is likely way too complicated. We could go for something in between, but it'll be tricky to decide just where to draw the line.

arrow.lineColor = MGLStyleValue(rawValue: .white)
arrow.lineCap = NSExpression(forConstantValue: cap)
arrow.lineJoin = NSExpression(forConstantValue: join)
arrow.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", argumentArray: [routeLineWidthAtZoomLevels.multiplied(by: 1.5)])
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of calling multiplied(by:) on the array, multiply the FUNCTION call by 1.5 inside the format string.

arrow.lineColor = MGLStyleValue(rawValue: .white)
arrow.lineCap = NSExpression(forConstantValue: cap)
arrow.lineJoin = NSExpression(forConstantValue: join)
arrow.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", argumentArray: [routeLineWidthAtZoomLevels.multiplied(by: 1.5)])
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you know in advance how many arguments this format string needs, use NSExpression(format:) instead of NSExpression(format:argumentArray:). The first argument to NSExpression(format:) takes a comma-separated list of arguments following the format string.

cameraStops: routeLineWidthAtZoomLevels.multiplied(by: 0.80),
options: [.defaultValue : MGLConstantStyleValue<NSNumber>(rawValue: 1.5)])
arrowStroke.lineColor = MGLStyleValue(rawValue: .defaultArrowStroke)
arrowStroke.lineCap = NSExpression(forConstantValue: cap)
Copy link
Contributor

Choose a reason for hiding this comment

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

NSExpression(forConstantValue: "cap") or NSExpression(format: "'cap'") is also allowed now, although what you’ve got is also perfectly fine if you want compile-time validation.

lineCasing.lineJoin = MGLStyleValue(rawValue: NSValue(mglLineJoin: .round))
lineCasing.lineOpacity = MGLStyleValue(rawValue: 0.9)
lineCasing.lineColor = NSExpression(forConstantValue: routeAlternateColor)
lineCasing.lineCap = NSExpression(forConstantValue: NSValue(mglLineCap: .round))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can say something like MGLLineCap.round as NSValue instead of calling NSValue(mglLineCap:). NSExpression(forConstantValue: "round") and NSExpression(format: "'round'") are also options.

symbol.textFontSize = MGLStyleValue(rawValue: 10)
symbol.textHaloWidth = MGLStyleValue(rawValue: 0.25)
symbol.textHaloColor = MGLStyleValue(rawValue: .black)
symbol.text = NSExpression(forConstantValue: "{name}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty sure mapbox/mapbox-gl-native#10399 will prevent the legacy {token} syntax from working inside an expression. Use a key path expression instead: NSExpression(forKeyPath: "name") or NSExpression(format: "name").

Copy link
Contributor

Choose a reason for hiding this comment

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

Use a key path expression instead: NSExpression(forKeyPath: "name") or NSExpression(format: "name").

/ref #1076 (comment)

"heavy": NSExpression(forConstantValue: trafficHeavyColor),
"severe": NSExpression(forConstantValue: trafficSevereColor)
])
line.lineOpacity = NSExpression(format: "FUNCTION($currentLegAttribute, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", [
Copy link
Contributor

Choose a reason for hiding this comment

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

$currentLegAttribute would be a reference to an expression variable declared with mgl_expressionWithContext:. Use currentLegAttribute, without the dollar sign, to refer to an attribute of the current feature.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, currentLegAttribute is the name of this variable. You have two options: either insert a key path expression created from the variable:

NSExpression(format: "FUNCTION(%@, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", NSExpression(forKeyPath: currentLegAttribute), )

or inline the key path and delete currentLegAttribute:

NSExpression(format: "FUNCTION(isCurrentLeg, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", )

line.lineOpacity = NSExpression(format: "FUNCTION($currentLegAttribute, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", [
true: 1,
false: 0
])
Copy link
Contributor

Choose a reason for hiding this comment

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

As a Boolean attribute, isCurrentLeg can have only two possible values, true and false, so a full stop dictionary is overkill. With an expression, you can express a condition directly:

NSExpression(format: "TERNARY(isCurrentLeg = true, 1, 0)")

or:

NSExpression(forConditional: NSPredicate(format: "isCurrentLeg = true"), true: 1, false: 0)

Copy link
Contributor

Choose a reason for hiding this comment

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

Then again, the usual way to accomplish this effect in a style is to let each line know which leg index it represents and add two layers to the style, one for the active leg and another for the inactive legs.

Both layers would have constant opacities. The filters (predicates) for the two layers would be updated every time the leg index changes:

activeLegLayer.predicate = NSPredicate(format: "legIndex = %@", routeProgess.legIndex)
inactiveLegLayer.predicate = NSPredicate(format: "legIndex != %@", routeProgress.legIndex)

@bsudekum
Copy link
Contributor Author

This is currently blocked upstream by mapbox/mapbox-gl-native#11277 & mapbox/mapbox-gl-native#10849

@akitchen akitchen changed the title Update to iOS v4.0.0 Update to Maps SDK for iOS v4.0.0 Mar 7, 2018
@brsbl-zz brsbl-zz added the pink label Mar 11, 2018
line.lineJoin = MGLStyleValue(rawValue: NSValue(mglLineJoin: .round))
line.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", argumentArray: [MBRouteLineWidthByZoomLevel])
line.lineOpacity = NSExpression(forConditional: NSPredicate(format: "\(MBCurrentLegAttribute) == true"), trueExpression: NSExpression(forConstantValue: 1), falseExpression: NSExpression(forConstantValue: 0.85))
line.lineColor = NSExpression(format: "%@.severe", [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@anandthakker @1ec5 this is failing with

Assertion failed: (literal), function operator(), file /Users/distiller/project/include/mbgl/style/conversion/data_driven_property_value.hpp, line 50.

Choose a reason for hiding this comment

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

@bsudekum @1ec5 could we dump the json expression representation that's getting produced by this NSExpression?

Copy link
Contributor

@1ec5 1ec5 Apr 3, 2018

Choose a reason for hiding this comment

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

This expression assumes all traffic is severe. The format should be:

NSExpression(format: "FUNCTION(%@, 'valueForKeyPath:', congestion)")

Alternatively, use an MGL_MATCH expression:

NSExpression(format: "MGL_MATCH(congestion, 'unknown', %@, 'low', %@, 'moderate', %@, …)")

Copy link
Contributor

Choose a reason for hiding this comment

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

We can get the JSON expression representation via NSExpression.mgl_jsonExpressionObject(). It might be worth printing that out as part of the exception handler. Currently, the exception handler is in code that only knows about mbgl expressions, not NSExpression.

Choose a reason for hiding this comment

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

This is an expression parsing bug -- reopened here: mapbox/mapbox-gl-native#10849

19: MGLStyleValue(rawValue: 22),
22: MGLStyleValue(rawValue: 28)
public let MBRouteLineWidthByZoomLevel: [Int: NSExpression] = [
10: NSExpression(forConstantValue: 8),
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty sure these don’t need to be NSExpressions; they can be bare Floats or NSNumbers.

newCameraStop[stop.key] = MGLStyleValue<NSNumber>(rawValue: NSNumber(value:newValue))
var newCameraStop: [Int: NSExpression] = [:]
for stop in self {
let currentValue = stop.value.constantValue as! Double
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that this will crash if the dictionary contains any expression that isn’t a constant value. Since this is a public extension on Dictionary, we have to assume the developer is going to try to use it wherever the compiler lets them.

I continue to believe we should make this method private or remove it outright. A few explicit multiplications won’t greatly inconvenience the developer.


arrow.lineCap = NSExpression(forConstantValue: "cap")
arrow.lineJoin = NSExpression(forConstantValue: "join")
arrow.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", argumentArray: [MBRouteLineWidthByZoomLevel.multiplied(by: 0.70)])
Copy link
Contributor

Choose a reason for hiding this comment

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

The syntax has now changed to mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@). (×7)

arrowSymbolLayer.iconAllowsOverlap = MGLStyleValue(rawValue: true)
arrowSymbolLayer.iconImageName = NSExpression(forConstantValue: "triangle-tip-navigation")
arrowSymbolLayer.iconColor = NSExpression(forConstantValue: UIColor.white)
arrowSymbolLayer.iconRotationAlignment = NSExpression(forConstantValue: NSValue(mglIconRotationAlignment: .map))
Copy link
Contributor

Choose a reason for hiding this comment

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

Simplify this to NSExpression(forConstantValue: "map") for consistency. (×2)

arrowSymbolLayerCasing.iconAllowsOverlap = MGLStyleValue(rawValue: true)
arrowSymbolLayerCasing.iconImageName = NSExpression(forConstantValue: "triangle-tip-navigation")
arrowSymbolLayerCasing.iconColor = NSExpression(forConstantValue: UIColor.defaultArrowStroke)
arrowSymbolLayerCasing.iconRotationAlignment = NSExpression(forConstantValue: NSValue(mglIconRotationAlignment: .map))
Copy link
Contributor

Choose a reason for hiding this comment

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

Reduce repetition by saying, for example:

arrowSymbolLayerCasing.iconRotationAlignment = arrowSymbolLayer.iconRotationAlignment

symbol.textFontSize = MGLStyleValue(rawValue: 10)
symbol.textHaloWidth = MGLStyleValue(rawValue: 0.25)
symbol.textHaloColor = MGLStyleValue(rawValue: .black)
symbol.text = NSExpression(forConstantValue: "{name}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Use a key path expression instead: NSExpression(forKeyPath: "name") or NSExpression(format: "name").

/ref #1076 (comment)

line.lineJoin = MGLStyleValue(rawValue: NSValue(mglLineJoin: .round))
line.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", argumentArray: [MBRouteLineWidthByZoomLevel])
line.lineOpacity = NSExpression(forConditional: NSPredicate(format: "\(MBCurrentLegAttribute) == true"), trueExpression: NSExpression(forConstantValue: 1), falseExpression: NSExpression(forConstantValue: 0.85))
line.lineColor = NSExpression(format: "%@.severe", [
Copy link
Contributor

@1ec5 1ec5 Apr 3, 2018

Choose a reason for hiding this comment

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

This expression assumes all traffic is severe. The format should be:

NSExpression(format: "FUNCTION(%@, 'valueForKeyPath:', congestion)")

Alternatively, use an MGL_MATCH expression:

NSExpression(format: "MGL_MATCH(congestion, 'unknown', %@, 'low', %@, 'moderate', %@, …)")

}
}

// TODO:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this still to do?

@@ -672,7 +672,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate {
arrowSymbolLayerCasing.minimumZoomLevel = minimumZoomLevel
arrowSymbolLayerCasing.iconImageName = NSExpression(forConstantValue: "triangle-tip-navigation")
arrowSymbolLayerCasing.iconColor = NSExpression(forConstantValue: UIColor.defaultArrowStroke)
arrowSymbolLayerCasing.iconRotationAlignment = NSExpression(forConstantValue: NSValue(mglIconRotationAlignment: .map))
arrowSymbolLayerCasing.iconRotationAlignment = arrowSymbolLayer.iconRotationAlignment
Copy link
Contributor

Choose a reason for hiding this comment

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

The idea behind #1076 (comment) was that we’d do the same thing for the dozens of other properties in this file.

"severe": trafficSevereColor
])

line.lineColor = NSExpression(format: "MGL_MATCH(congestion, 'unknown', %@, 'low' , %@, 'moderate', %@, 'heavy', %@, 'severe', %@, %@)", trafficUnknownColor, trafficLowColor, trafficModerateColor, trafficHeavyColor, trafficSevereColor, UIColor.purple)
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the case for unknown and make trafficUnknownColor the fallback.

@akitchen akitchen removed the pink label Apr 4, 2018
@1ec5 1ec5 self-assigned this Apr 4, 2018
@1ec5 1ec5 added this to the v0.17.0 milestone Apr 4, 2018
]
let expression = NSExpression.mgl_expression(withJSONObject: jsonExpression)
let localizedExpression = expression.localized(into: nil)
XCTAssertEqual(localizedExpression.mgl_jsonExpressionObject as! NSArray, localizedJSONExpression as NSArray)
Copy link
Contributor

Choose a reason for hiding this comment

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

This test will fail until mapbox/mapbox-gl-native#11615 lands in a build of the map SDK.

@1ec5

This comment has been minimized.

@bsudekum
Copy link
Contributor Author

bsudekum commented Apr 6, 2018

Fixed arrow width issue with c2b276a

1ec5
1ec5 previously approved these changes Apr 9, 2018
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

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

This PR is ready except for releasing the map SDK first (minor details!) and also updating the changelog.

}

extension NSExpression {
func localized(into locale: Locale?, replacingTokens replacesTokens: Bool) -> NSExpression {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nearly all of this functionality has been upstreamed to the map SDK in mapbox/mapbox-gl-native#11651. So the implementation here can be replaced with a simple call to the similarly named method provided by the map SDK.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in 7bbfe0deb187c350638ff579788ee843bea50474.

@1ec5
Copy link
Contributor

1ec5 commented Apr 20, 2018

I’ve been pretty involved with this PR, so I’d like to get another pair of 👀 from either @vincethecoder or @JThramer. Thanks!

@1ec5
Copy link
Contributor

1ec5 commented Apr 23, 2018

Tests are failing due to #1327.

line.lineJoin = MGLStyleValue(rawValue: NSValue(mglLineJoin: .round))
line.lineColor = NSExpression(forConstantValue: UIColor(red:0.00, green:0.45, blue:0.74, alpha:0.9))
line.lineWidth = NSExpression(forConstantValue: 5)
line.lineCap = NSExpression(forConstantValue: NSValue(mglLineCap: .round))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: NSExpression(forConstantValue: "round") seems to be the recommended way according to the documentation and we use throughout the migration except for here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed in 0294f1209b08f3c7329bd0eecb5752aea359b1a7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backwards incompatible changes that break backwards compatibility of public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants