diff options
85 files changed, 7580 insertions, 7261 deletions
diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp index c82c04bbb0..dbed74b4cd 100644 --- a/include/mbgl/style/expression/interpolate.hpp +++ b/include/mbgl/style/expression/interpolate.hpp @@ -70,6 +70,7 @@ public: {} const std::unique_ptr<Expression>& getInput() const { return input; } + const Interpolator& getInterpolator() const { return interpolator; } void eachChild(const std::function<void(const Expression&)>& visit) const override { visit(*input); @@ -77,6 +78,12 @@ public: visit(*stop.second); } } + + void eachStop(const std::function<void(double, const Expression&)>& visit) const { + for (const auto& stop : stops) { + visit(stop.first, *stop.second); + } + } // Return the smallest range of stops that covers the interval [lower, upper] Range<float> getCoveringStops(const double lower, const double upper) const { diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp index 4a0a724d7c..6bf42e20f1 100644 --- a/include/mbgl/style/expression/step.hpp +++ b/include/mbgl/style/expression/step.hpp @@ -27,6 +27,7 @@ public: EvaluationResult evaluate(const EvaluationContext& params) const override; void eachChild(const std::function<void(const Expression&)>& visit) const override; + void eachStop(const std::function<void(double, const Expression&)>& visit) const; const std::unique_ptr<Expression>& getInput() const { return input; } Range<float> getCoveringStops(const double lower, const double upper) const; diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp index 015abd3e62..1da5d2c601 100644 --- a/include/mbgl/style/function/camera_function.hpp +++ b/include/mbgl/style/function/camera_function.hpp @@ -76,7 +76,9 @@ public: } bool useIntegerZoom = false; - + + const expression::Expression& getExpression() const { return *expression; } + // retained for compatibility with pre-expression function API Stops stops; diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index 24578f599c..f391b101ae 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -111,6 +111,8 @@ public: return *lhs.expression == *rhs.expression; } + const expression::Expression& getExpression() const { return *expression; } + std::string property; Stops stops; optional<T> defaultValue; diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp index bd7b109fd8..d3caa90ee5 100644 --- a/include/mbgl/style/function/source_function.hpp +++ b/include/mbgl/style/function/source_function.hpp @@ -68,6 +68,8 @@ public: bool useIntegerZoom = false; + const expression::Expression& getExpression() const { return *expression; } + // retained for compatibility with pre-expression function API std::string property; Stops stops; diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp index 92f23d6718..56d2ab6ead 100644 --- a/include/mbgl/util/unitbezier.hpp +++ b/include/mbgl/util/unitbezier.hpp @@ -42,6 +42,17 @@ struct UnitBezier { , ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) { } + std::pair<double, double> getP1() const { + return { cx / 3.0, cy / 3.0 }; + } + + std::pair<double, double> getP2() const { + return { + (bx + (3.0 * cx / 3.0) + cx) / 3.0, + (by + (3.0 * cy / 3.0) + cy) / 3.0, + }; + } + double sampleCurveX(double t) const { // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. return ((ax * t + bx) * t + cx) * t; diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 06a8907704..3f6c3fc2a4 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -263,12 +263,16 @@ In style JSON | In Objective-C | In Swift ## Setting attribute values Each property representing a layout or paint attribute is set to an -`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for style functions). The -style value object is a container for the raw value or function parameters that -you want the attribute to be set to. +`NSExpression` object. `NSExpression` objects play the same role as +[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions), +but you create the former using a very different syntax. `NSExpression`’s format +string syntax is reminiscent of a spreadsheet formula or an expression in a +database query. See the +“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +overview of the expression support in this SDK. This SDK no longer supports +style functions; use expressions instead. -### Constant style values +### Constant values in expressions In contrast to the JSON type that the style specification defines for each layout or paint property, the style value object often contains a more specific @@ -279,10 +283,10 @@ or set. In style JSON | In Objective-C | In Swift --------------|-----------------------|--------- Color | `<%- cocoaPrefix %>Color` | `<%- cocoaPrefix %>Color` -Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`) +Enum | `NSString` | `String` String | `NSString` | `String` -Boolean | `NSNumber.boolValue` | `Bool` -Number | `NSNumber.floatValue` | `Float` +Boolean | `NSNumber.boolValue` | `NSNumber.boolValue` +Number | `NSNumber.floatValue` | `NSNumber.floatValue` Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]` Array (`-font`) | `NSArray<NSString>` | `[String]` <% if (iOS) { -%> @@ -312,38 +316,77 @@ translation downward. This is the reverse of how `CGVector` is interpreted on iOS. <% } -%> -### Style functions - -A _style function_ allows you to vary the value of a layout or paint attribute -based on the zoom level, data provided by content sources, or both. For more -information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. - -Each kind of style function is represented by a distinct class, but you -typically create style functions as you create any other style value, using -class methods on `MGLStyleValue`: - -In style specification | SDK class | SDK factory method ----------------------------|-----------------------------|------------------- -zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` -property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` -zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` - -The documentation for each individual style layer property indicates the kinds -of style functions that are enabled for that property. - -When you create a style function, you specify an _interpolation mode_ and a -series of _stops_. Each stop determines the effective value displayed at a -particular zoom level (for camera functions) or the effective value on features -with a particular attribute value in the content source (for source functions). -The interpolation mode tells the SDK how to calculate the effective value -between any two stops: - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` +### Expression operators + +In style specification | Method, function, or predicate type | Format string syntax +-----------------------|-------------------------------------|--------------------- +`array` | | +`boolean` | | +`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary` +`number` | | +`string` | | +`to-boolean` | `boolValue` | +`to-color` | | +`to-number` | `mgl_numberWithFallbackValues:` | +`to-string` | `stringValue` | +`typeof` | | +`geometry-type` | | +`id` | | +`properties` | | +`at` | | +`get` | `+[NSExpression expressionForKeyPath:]` | Key path +`has` | | +`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})` +`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)` +`!=` | `NSNotEqualToPredicateOperatorType` | `key != value` +`<` | `NSLessThanPredicateOperatorType` | `key < value` +`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value` +`==` | `NSEqualToPredicateOperatorType` | `key == value` +`>` | `NSGreaterThanPredicateOperatorType` | `key > value` +`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value` +`all` | `NSAndPredicateType` | `p0 AND … AND pn` +`any` | `NSOrPredicateType` | `p0 OR … OR pn` +`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)` +`coalesce` | | +`match` | | +`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` | +`step` | `mgl_stepWithMinimum:stops:` | +`let` | `mgl_expressionWithContext:` | +`var` | `+[NSExpression expressionForVariable:]` | `$variable` +`concat` | `stringByAppendingString:` | +`downcase` | `lowercase:` | `lowercase('DOWNTOWN')` +`upcase` | `uppercase:` | `uppercase('Elysian Fields')` +<% if (macOS) { -%> +`rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | +`rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | +<% } else { %> +`rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | +`rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | +<% } -%> +`to-rgba` | | +`-` | `from:subtract:` | `2 - 1` +`*` | `multiply:by:` | `1 * 2` +`/` | `divide:by:` | `1 / 2` +`%` | `modulus:by:` | +`^` | `raise:toPower:` | `2 ** 2` +`+` | `add:to:` | `1 + 2` +`acos` | | +`asin` | | +`atan` | | +`cos` | | +`e` | | `%@` representing `NSNumber` containing `M_E` +`ln` | `ln:` | `ln(2)` +`ln2` | | `%@` representing `NSNumber` containing `M_LN2` +`log10` | `log:` | `log(1)` +`log2` | | +`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` +`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` +`pi` | | `%@` representing `NSNumber` containing `M_PI` +`sin` | | +`sqrt` | `sqrt:` | `sqrt(2)` +`tan` | | +`zoom` | | `$zoom` +`heatmap-density` | | `$heatmapDensity` ## Filtering sources @@ -368,5 +411,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the `MGLVectorStyleLayer.predicate` documentation for a full description of -the supported operators and operand types. +See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +a full description of the supported operators and operand types. diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md new file mode 100644 index 0000000000..19d98fd4c1 --- /dev/null +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -0,0 +1,414 @@ +# Predicates and expressions + +Style layers use predicates and expressions to determine what to display and how +to format it. _Predicates_ are represented by the same `NSPredicate` class that +filters results from Core Data or items in an `NSArray` in Objective-C. +Predicates are based on _expressions_, represented by the `NSExpression` class. +Somewhat unusually, style layers also use expressions on their own. + +This document discusses the specific subset of the predicate and expression +syntax supported by this SDK. For a more general introduction to predicates and +expressions, consult the +_[Predicate Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)_ +in Apple developer documentation. + +## Using predicates to filter vector data + +Most style layer classes display `MGLFeature` objects that you can show or hide +based on the feature’s attributes. Use the `MGLVectorStyleLayer.predicate` +property to include only the features in the source layer that satisfy a +condition that you define. + +The following comparison operators are supported: + +`NSPredicateOperatorType` | Format string syntax +----------------------------------------------|--------------------- +`NSEqualToPredicateOperatorType` | `key = value`<br />`key == value` +`NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value`<br />`key => value` +`NSLessThanOrEqualToPredicateOperatorType` | `key <= value`<br />`key =< value` +`NSGreaterThanPredicateOperatorType` | `key > value` +`NSLessThanPredicateOperatorType` | `key < value` +`NSNotEqualToPredicateOperatorType` | `key != value`<br />`key <> value` +`NSBetweenPredicateOperatorType` | `key BETWEEN { 32, 212 }` + +The following compound operators are supported: + +`NSCompoundPredicateType` | Format string syntax +--------------------------|--------------------- +`NSAndPredicateType` | `predicate1 AND predicate2`<br />`predicate1 && predicate2` +`NSOrPredicateType` | `predicate1 OR predicate2`<br />`predicate1 || predicate2` +`NSNotPredicateType` | `NOT predicate`<br />`!predicate` + +The following aggregate operators are supported: + +`NSPredicateOperatorType` | Format string syntax +----------------------------------|--------------------- +`NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }` +`NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key` + +To test whether a feature has or lacks a specific attribute, compare the +attribute to `NULL` or `NIL`. Predicates created using the +`+[NSPredicate predicateWithValue:]` method are also supported. String +operators and custom operators are not supported. + +For details about the predicate format string syntax, consult the “Predicate +Format String Syntax” chapter of the +_[Predicate Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html)_ +in Apple developer documentation. + +The predicate's left-hand expression must be a string that identifies a feature +attribute or, alternatively, one of the following special attributes: + +<table> +<thead> +<tr><th>Attribute</th><th>Meaning</th></tr> +</thead> +<tbody> +<tr> + <td><code>$id</code></td> + <td> + A value that uniquely identifies the feature in the containing source. + For details on the types of values that may be associated with this key, + consult the documentation for the <code>MGLFeature</code> protocol’s + <code>identifier</code> property. + </td> +</tr> +<tr> + <td><code>$type</code></td> + <td> + The type of geometry represented by the feature. A feature’s type is + guaranteed to be one of the following strings: + <ul> + <li> + <code>Point</code> for point features, corresponding to the + <code>MGLPointAnnotation</code> class + </li> + <li> + <code>LineString</code> for polyline features, corresponding to + the <code>MGLPolyline</code> class + </li> + <li> + <code>Polygon</code> for polygon features, corresponding to the + <code>MGLPolygon</code> class + </li> + </ul> + </td> +</tr> +<tr> + <td><code>point_count</code></td> + <td>The number of point features in a given cluster.</td> +</tr> +</tbody> +</table> + +The predicate’s right-hand expression must be an `NSString` (to match strings) +or `NSNumber` (to match numbers, including Boolean values) or an array of +`NSString`s or `NSNumber`s, depending on the operator and the type of values +expected for the attribute being tested. For floating-point values, use +`-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]` +to avoid precision issues. + +Automatic type casting is not performed. Therefore, a feature only matches this +predicate if its value for the attribute in question is of the same type as the +value specified in the predicate. Also, operator modifiers such as `c` (for +case insensitivity), `d` (for diacritic insensitivity), and `l` (for locale +sensitivity) are unsupported for comparison and aggregate operators that are +used in the predicate. + +It is possible to create expressions that contain special characters in the +predicate format syntax. This includes the `$` in the `$id` and `$type` special +style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use +`%K` in the format string to represent these variables: +`@"%K == 'LineString'", @"$type"`. + +## Using expressions to configure layout and paint attributes + +### Functions + +#### Key paths + +A key path expression refers to an attribute of the `MGLFeature` object being +evaluated for display. For example, if a polygon’s `MGLFeature.attributes` +dictionary contains the `floorCount` key, then the key path `floorCount` refers +to the value of the `floorCount` attribute when evaluating that particular +polygon. + +#### Predefined functions + +Of the +[functions predefined by the `+[NSExpression expressionForFunction:arguments:]` method](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init#discussion), +the following subset is supported in layer attribute values: + +Initializer parameter | Format string syntax +----------------------|--------------------- +`average:` | `average({1, 2, 2, 3, 4, 7, 9})` +`sum:` | `sum({1, 2, 2, 3, 4, 7, 9})` +`count:` | `count({1, 2, 2, 3, 4, 7, 9})` +`min:` | `min({1, 2, 2, 3, 4, 7, 9})` +`max:` | `max({1, 2, 2, 3, 4, 7, 9})` +`add:to:` | `1 + 2` +`from:subtract:` | `2 - 1` +`multiply:by:` | `1 * 2` +`divide:by:` | `1 / 2` +`modulus:by:` | `modulus:by:(1, 2)` +`sqrt:` | `sqrt(2)` +`log:` | `log(10)` +`ln:` | `ln(2)` +`raise:toPower:` | `2 ** 2` +`exp:` | `exp(0)` +`ceiling:` | `ceiling(0.99999)` +`abs:` | `abs(-1)` +`trunc:` | `trunc(6378.1370)` +`floor:` | `floor(-0.99999)` +`uppercase:` | `uppercase('Elysian Fields')` +`lowercase:` | `lowercase('DOWNTOWN')` +`noindex:` | `noindex(0 + 2 + c)` + +The following predefined functions are not supported: + +Initializer parameter | Format string syntax +----------------------|--------------------- +`median:` | `median({1, 2, 2, 3, 4, 7, 9})` +`mode:` | `mode({1, 2, 2, 3, 4, 7, 9})` +`stddev:` | `stddev({1, 2, 2, 3, 4, 7, 9})` +`random` | `random()` +`randomn:` | `randomn(10)` +`now` | `now()` +`bitwiseAnd:with:` | `bitwiseAnd:with:(5, 3)` +`bitwiseOr:with:` | `bitwiseOr:with:(5, 3)` +`bitwiseXor:with:` | `bitwiseXor:with:(5, 3)` +`leftshift:by:` | `leftshift:by:(23, 1)` +`rightshift:by:` | `rightshift:by:(23, 1)` +`onesComplement:` | `onesComplement(255)` +`distanceToLocation:fromLocation:` | `distanceToLocation:fromLocation:(there, here)` + +#### Mapbox-specific functions + +For compatibility with the Mapbox Style Specification, the following functions +are defined by this SDK for use with style layers. Because these functions are +not predefined by `NSExpression`, you must use the +`+[NSExpression expressionForFunction:selectorName:arguments:]` method or the +`FUNCTION()` format string syntax instead. + +<table> +<thead> +<tr><th>Selector name</th><th>Target</th><th>Arguments</th><th>Returns</th></tr> +</thead> +<tbody> +<tr> + <td><code>boolValue</code></td> + <td> + An `NSExpression` that evaluates to a number or string. + </td> + <td></td> + <td> + A Boolean representation of the target: `FALSE` when then input is an + empty string, 0, `FALSE`, `NIL`, or NaN, otherwise `TRUE`. + </td> +</tr> +<tr> + <td><code>mgl_expressionWithContext:</code></td> + <td> + An `NSExpression` that may contain references to the variables defined in + the context dictionary. + </td> + <td> + An `NSDictionary` with `NSString`s as keys and `NSExpression`s as values. + Each key is a variable name and each value is the variable’s value within + the target expression. + </td> + <td> + The target expression with variable subexpressions replaced with the + values defined in the context dictionary. + </td> +</tr> +<tr> + <td><code>mgl_interpolateWithCurveType:parameters:stops:</code></td> + <td> + An `NSExpression` that evaluates to a number and contains a variable or + key path expression. + </td> + <td> + The first argument is one of the following strings denoting curve types: + `linear`, `exponential`, or `cubic-bezier`. + + The second argument is an expression providing parameters for the curve: + + <ul> + <li>If the curve type is `linear`, the argument is `NIL`.</li> + <li> + If the curve type is `exponential`, the argument is an expression + that evaluates to a number, specifying the base of the exponential + interpolation. + </li> + <li> + If the curve type is `cubic-bezier`, the argument is an array or + aggregate expression containing four expressions, each evaluating to + a number. The four numbers are control points for the cubic Bézier + curve. + </li> + </ul> + + The third argument is an `NSDictionary` object representing the + interpolation’s stops, with numeric zoom levels as keys and expressions as + values. + </td> + <td> + A value interpolated along the continuous mathematical function defined by + the arguments, with the target as the input to the function. + </td> +</tr> +<tr> + <td> + <code>mgl_numberWithFallbackValues:</code>, + <code>doubleValue</code>, + <code>floatValue</code>, + <code>decimalValue</code> + </td> + <td> + An `NSExpression` that evaluates to a Boolean value, number, or string. + </td> + <td> + Zero or more `NSExpression`s, each evaluating to a Boolean value or + string. + </td> + <td> + A numeric representation of the target: + <ul> + <li>If the target is `NIL` or `FALSE`, the result is 0.</li> + <li>If the target is true, the result is 1.</li> + <li> + If the target is a string, it is converted to a number as specified + by the + “<a href="https://tc39.github.io/ecma262/#sec-tonumber-applied-to-the-string-type">ToNumber Applied to the String Type</a>” + algorithm of the ECMAScript Language Specification. + </li> + <li> + If multiple values are provided, each one is evaluated in order + until the first successful conversion is obtained. + </li> + </ul> + </td> +</tr> +<tr> + <td><code>mgl_stepWithMinimum:stops:</code></td> + <td> + An `NSExpression` that evaluates to a number and contains a variable or + key path expression. + </td> + <td> + The first argument is an expression that evaluates to a number, specifying + the minimum value in case the target is less than any of the stops in the + second argument. + + The second argument is an `NSDictionary` object representing the + interpolation’s stops, with numeric zoom levels as keys and expressions as + values. + </td> + <td> + The output value of the stop whose key is just less than the evaluated + target, or the minimum value if the target is less than the least of the + stops’ keys. + </td> +</tr> +<tr> + <td><code>stringByAppendingString:</code></td> + <td> + An `NSExpression` that evaluates to a string. + </td> + <td> + One or more `NSExpression`s, each evaluating to a string. + </td> + <td> + The target string with each of the argument strings appended in order. + </td> +</tr> +<tr> + <td><code>stringValue</code></td> + <td> + An `NSExpression` that evaluates to a Boolean value, number, or string. + </td> + <td></td> + <td> + A string representation of the target: + <ul> + <li>If the target is `NIL`, the result is the string `null`.</li> + <li> + If the target is a Boolean value, the result is the string `true` or + `false`. + </li> + <li> + If the target is a number, it is converted to a string as specified + by the + “<a href="https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type">NumberToString</a>” + algorithm of the ECMAScript Language Specification. + </li> + <li> + If the target is a color, it is converted to a string of the form + `rgba(r,g,b,a)`, where <var>r</var>, <var>g</var>, and <var>b</var> + are numerals ranging from 0 to 255 and <var>a</var> ranges from 0 to + 1. + </li> + <li> + Otherwise, the target is converted to a string in the format + specified by the + [`JSON.stringify()`](https://tc39.github.io/ecma262/#sec-json.stringify) + function of the ECMAScript Language Specification. + </li> + </ul> + </td> +</tr> +</tbody> +</table> + +Some of these functions are defined as methods on their respective target +classes, but you should not call them directly outside the context of an +expression, because the result may differ from the evaluated expression’s result +or may result in undefined behavior. + +### Variables + +The following variables are defined by this SDK for use with style layers: + +<table> +<thead> +<tr><th>Variable</th><th>Type</th><th>Meaning</th></tr> +</thead> +<tbody> +<tr> + <td><code>$heatmapDensity</code></td> + <td>Number</td> + <td> + The + <a href="https://en.wikipedia.org/wiki/Kernel_density_estimation">kernel density estimation</a> + of a screen point in a heatmap layer; in other words, a relative measure + of how many data points are crowded around a particular pixel. This + variable can only be used with the `heatmapColor` property. + </td> +</tr> +<tr> + <td><code>$zoomLevel</code></td> + <td>Number</td> + <td> + The current zoom level. In style layout and paint properties, this + variable may only appear as the target of a top-level interpolation or + step expression. + </td> +</tr> +</tbody> +</table> + +In addition to these variables, you can define your own variables and refer to +them elsewhere in the expression. The syntax for defining a variable makes use +of a [Mapbox-specific function](#mapbox-specific-functions) that takes an +`NSDictionary` as an argument: + +```objc +[NSExpression expressionWithFormat:@"FUNCTION($floorCount + 1, 'mgl_expressionWithContext:', %@)", + {@"floorCount": @2}]; +``` + +```swift +NSExpression(format: "FUNCTION($floorCount + 1, 'mgl_expressionWithContext:', %@)", + ["floorCount": 2]) +``` diff --git a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs index e41ac1c832..61034a674f 100644 --- a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs +++ b/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs @@ -34,7 +34,7 @@ The documentation for each individual style layer property notes which style fun ## Stops -Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. +Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys. <%- guideExample(guide, 'Stops', os) %> diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 51a5052110..84ee7ac263 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -13,6 +13,9 @@ const suffix = 'StyleLayer'; let spec = _.merge(require('../../../mapbox-gl-js/src/style-spec/reference/v8'), require('./style-spec-overrides-v8.json')); +// Temporarily ignore layer types defined in the style specification but not yet supported in mbgl. +delete spec.layer.type.values.heatmap; + // Rename properties and keep `original` for use with setters and getters _.forOwn(cocoaConventions, function (properties, kind) { _.forOwn(properties, function (newName, oldName) { @@ -94,37 +97,42 @@ global.testImplementation = function (property, layerType, isFunction) { return `layer.${objCName(property)} = [MGLRuntimeStylingHelper ${helperMsg}];`; }; -global.objCTestValue = function (property, layerType, indent) { +global.objCTestValue = function (property, layerType, arraysAsStructs, indent) { let propertyName = originalPropertyName(property); switch (property.type) { case 'boolean': - return property.default ? '@NO' : '@YES'; + return property.default ? '@"false"' : '@"true"'; case 'number': - return '@0xff'; + return '@"0xff"'; case 'string': - return `@"${_.startCase(propertyName)}"`; + return `@"'${_.startCase(propertyName)}'"`; case 'enum': - let type = objCType(layerType, property.name); - let value = `${type}${camelize(_.last(_.keys(property.values)))}`; - return `[NSValue valueWith${type}:${value}]`; + return `@"'${_.last(_.keys(property.values))}'"`; case 'color': - return '[MGLColor redColor]'; + return '@"%@", [MGLColor redColor]'; case 'array': switch (arrayType(property)) { case 'dasharray': - return '@[@1, @2]'; + return '@"{1, 2}"'; case 'font': - return `@[@"${_.startCase(propertyName)}", @"${_.startCase(_.reverse(propertyName.split('')).join(''))}"]`; + return `@"{'${_.startCase(propertyName)}', '${_.startCase(_.reverse(propertyName.split('')).join(''))}'}"`; case 'padding': { - let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); - let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); - return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + if (arraysAsStructs) { + let iosValue = '[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); + let macosValue = '[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)]'.indent(indent * 4); + return `@"%@",\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + } + return '@"{1, 1, 1, 1}"'; } case 'offset': - case 'translate': - let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4); - let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4); - return `\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + case 'translate': { + if (arraysAsStructs) { + let iosValue = '[NSValue valueWithCGVector:CGVectorMake(1, 1)]'.indent(indent * 4); + let macosValue = '[NSValue valueWithMGLVector:CGVectorMake(1, -1)]'.indent(indent * 4); + return `@"%@",\n#if TARGET_OS_IPHONE\n${iosValue}\n#else\n${macosValue}\n#endif\n${''.indent((indent - 1) * 4)}`; + } + return '@"{1, 1}"'; + } default: throw new Error(`unknown array type for ${property.name}`); } @@ -293,29 +301,23 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { } doc += `\n\nThis attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#${anchor}"><code>${property.original}</code></a> layout property in the Mapbox Style Specification.`; } - doc += '\n\nYou can set this property to an instance of:\n\n' + - '* `MGLConstantStyleValue`\n'; + doc += '\n\nYou can set this property to an expression containing any of the following:\n\n'; + doc += `* Constant ${describeType(property)} values\n`; + if (property.type === 'enum') { + doc += '* Any of the following constant string values:\n'; + doc += Object.keys(property.values).map(value => ' * `' + value + '`: ' + property.values[value].doc).join('\n') + '\n'; + } + doc += '* Predefined functions, including mathematical and string operators\n' + + '* Conditional expressions\n' + + '* Variable assignments and references to assigned variables\n'; if (property["property-function"]) { - doc += '* `MGLCameraStyleFunction` with an interpolation mode of:\n' + - ' * `MGLInterpolationModeExponential`\n' + - ' * `MGLInterpolationModeInterval`\n' + - '* `MGLSourceStyleFunction` with an interpolation mode of:\n' + - ' * `MGLInterpolationModeExponential`\n' + - ' * `MGLInterpolationModeInterval`\n' + - ' * `MGLInterpolationModeCategorical`\n' + - ' * `MGLInterpolationModeIdentity`\n' + - '* `MGLCompositeStyleFunction` with an interpolation mode of:\n' + - ' * `MGLInterpolationModeExponential`\n' + - ' * `MGLInterpolationModeInterval`\n' + - ' * `MGLInterpolationModeCategorical`\n'; + doc += '* Interpolation and step functions applied to the `$zoomLevel` variable and/or feature attributes\n'; + } else if (property.function === "interpolated") { + doc += '* Interpolation and step functions applied to the `$zoomLevel` variable\n\n' + + 'This property does not support applying interpolation or step functions to feature attributes.'; } else { - if (property.function === "interpolated") { - doc += '* `MGLCameraStyleFunction` with an interpolation mode of:\n' + - ' * `MGLInterpolationModeExponential`\n' + - ' * `MGLInterpolationModeInterval`\n'; - } else { - doc += '* `MGLCameraStyleFunction` with an interpolation mode of `MGLInterpolationModeInterval`\n'; - } + doc += '* Step functions applied to the `$zoomLevel` variable\n\n' + + 'This property does not support applying interpolation functions to the `$zoomLevel` variable or applying interpolation or step functions to feature attributes.'; } } return doc; @@ -329,7 +331,7 @@ global.propertyReqs = function (property, propertiesByName, type) { return '`' + camelizeWithLeadingLowercase(req['!']) + '` is set to `nil`'; } else { let name = Object.keys(req)[0]; - return '`' + camelizeWithLeadingLowercase(name) + '` is set to an `MGLStyleValue` object containing ' + describeValue(req[name], propertiesByName[name], type); + return '`' + camelizeWithLeadingLowercase(name) + '` is set to an expression that evaluates to ' + describeValue(req[name], propertiesByName[name], type); } }).join(', and ') + '. Otherwise, it is ignored.'; }; @@ -344,12 +346,55 @@ global.parseColor = function (str) { }; }; +global.describeType = function (property) { + switch (property.type) { + case 'boolean': + return 'Boolean'; + case 'number': + return 'numeric'; + case 'string': + return 'string'; + case 'enum': + return '`MGL' + camelize(property.name) + '`'; + case 'color': + return '`UIColor`'; + case 'array': + switch (arrayType(property)) { + case 'padding': + return '`UIEdgeInsets`'; + case 'offset': + case 'translate': + return '`CGVector`'; + case 'position': + return '`MGLSphericalPosition`'; + default: + return 'array'; + } + break; + default: + throw new Error(`unknown type for ${property.name}`); + } +} + global.describeValue = function (value, property, layerType) { + if (Array.isArray(value) && property.type !== 'array' && property.type !== 'enum') { + switch (value[0]) { + case 'interpolate': { + let curveType = value[1][0]; + let minimum = describeValue(value[3 + value.length % 2], property, layerType); + let maximum = describeValue(_.last(value), property, layerType); + return `${curveType.match(/^[aeiou]/i) ? 'an' : 'a'} ${curveType} interpolation expression ranging from ${minimum} to ${maximum}`; + } + default: + throw new Error(`No description available for ${value[0]} expression in ${property.name} of ${layerType}.`); + } + } + switch (property.type) { case 'boolean': - return 'an `NSNumber` object containing ' + (value ? '`YES`' : '`NO`'); + return value ? '`YES`' : '`NO`'; case 'number': - return 'an `NSNumber` object containing the float `' + value + '`'; + return 'the float `' + value + '`'; case 'string': if (value === '') { return 'the empty string'; @@ -366,13 +411,10 @@ global.describeValue = function (value, property, layerType) { let objCType = global.objCType(layerType, property.name); return `${conjunction}\`${objCType}${camelize(possibleValue)}\``; }).join(separator); - } else if (property['light-property']) { - displayValue = `\`${prefix}Light${camelize(property.name)}${camelize(value)}\``; } else { - let objCType = global.objCType(layerType, property.name); - displayValue = `\`${objCType}${camelize(value)}\``; + displayValue = `\`${value}\``; } - return `an \`NSValue\` object containing ${displayValue}`; + return displayValue; case 'color': let color = parseColor(value); if (!color) { @@ -414,9 +456,9 @@ global.describeValue = function (value, property, layerType) { global.propertyDefault = function (property, layerType) { if (property.name === 'heatmap-color') { - return 'a rainbow color scale from blue to red'; + return 'an expression that evaluates to a rainbow color scale from blue to red'; } else { - return 'an `MGLStyleValue` object containing ' + describeValue(property.default, property, layerType); + return 'an expression that evaluates to ' + describeValue(property.default, property, layerType); } }; diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h index 659903914c..9d2673c859 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.h +++ b/platform/darwin/src/MGLBackgroundStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -38,45 +37,28 @@ which it is added. #pragma mark - Accessing the Paint Attributes -#if TARGET_OS_IPHONE /** The color with which the background will be drawn. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `backgroundPattern` is set to `nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *backgroundColor; -#else -/** - The color with which the background will be drawn. + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `backgroundPattern` is set to - `nil`. Otherwise, it is ignored. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *backgroundColor; -#endif +@property (nonatomic, null_resettable) NSExpression *backgroundColor; /** The transition affecting any changes to this layer’s `backgroundColor` property. @@ -88,18 +70,21 @@ which it is added. /** The opacity at which the background will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *backgroundOpacity; +@property (nonatomic, null_resettable) NSExpression *backgroundOpacity; /** The transition affecting any changes to this layer’s `backgroundOpacity` property. @@ -113,13 +98,19 @@ which it is added. seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *backgroundPattern; +@property (nonatomic, null_resettable) NSExpression *backgroundPattern; /** The transition affecting any changes to this layer’s `backgroundPattern` property. diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm index 47b491fc65..7c4cf79709 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.mm +++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm @@ -32,21 +32,21 @@ #pragma mark - Accessing the Paint Attributes -- (void)setBackgroundColor:(MGLStyleValue<MGLColor *> *)backgroundColor { +- (void)setBackgroundColor:(NSExpression *)backgroundColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(backgroundColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(backgroundColor); self.rawLayer->setBackgroundColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)backgroundColor { +- (NSExpression *)backgroundColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getBackgroundColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultBackgroundColor()); + propertyValue = self.rawLayer->getDefaultBackgroundColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setBackgroundColorTransition:(MGLTransition )transition { @@ -67,21 +67,21 @@ return transition; } -- (void)setBackgroundOpacity:(MGLStyleValue<NSNumber *> *)backgroundOpacity { +- (void)setBackgroundOpacity:(NSExpression *)backgroundOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(backgroundOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(backgroundOpacity); self.rawLayer->setBackgroundOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)backgroundOpacity { +- (NSExpression *)backgroundOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getBackgroundOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultBackgroundOpacity()); + propertyValue = self.rawLayer->getDefaultBackgroundOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setBackgroundOpacityTransition:(MGLTransition )transition { @@ -102,21 +102,21 @@ return transition; } -- (void)setBackgroundPattern:(MGLStyleValue<NSString *> *)backgroundPattern { +- (void)setBackgroundPattern:(NSExpression *)backgroundPattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(backgroundPattern); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(backgroundPattern); self.rawLayer->setBackgroundPattern(mbglValue); } -- (MGLStyleValue<NSString *> *)backgroundPattern { +- (NSExpression *)backgroundPattern { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getBackgroundPattern(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultBackgroundPattern()); + propertyValue = self.rawLayer->getDefaultBackgroundPattern(); } - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } - (void)setBackgroundPatternTransition:(MGLTransition )transition { diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index 86143894b1..caa6d2f6cb 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -83,12 +82,11 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) { ```swift let layer = MGLCircleStyleLayer(identifier: "circles", source: population) layer.sourceLayerIdentifier = "population" - layer.circleColor = MGLStyleValue(rawValue: .green) - layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, - cameraStops: [12: MGLStyleValue(rawValue: 2), - 22: MGLStyleValue(rawValue: 180)], - options: [.interpolationBase: 1.75]) - layer.circleOpacity = MGLStyleValue(rawValue: 0.7) + layer.circleColor = NSExpression(forConstantValue: UIColor.green) + layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)", + [12: 2, + 22: 180]) + layer.circleOpacity = NSExpression(forConstantValue: 0.7) layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married") mapView.style?.addLayer(layer) ``` @@ -117,27 +115,19 @@ MGL_EXPORT Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleBlur; +@property (nonatomic, null_resettable) NSExpression *circleBlur; /** The transition affecting any changes to this layer’s `circleBlur` property. @@ -146,57 +136,23 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition circleBlurTransition; -#if TARGET_OS_IPHONE /** The fill color of the circle. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleColor; -#else -/** - The fill color of the circle. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleColor; -#endif +@property (nonatomic, null_resettable) NSExpression *circleColor; /** The transition affecting any changes to this layer’s `circleColor` property. @@ -208,27 +164,19 @@ MGL_EXPORT /** The opacity at which the circle will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleOpacity; +@property (nonatomic, null_resettable) NSExpression *circleOpacity; /** The transition affecting any changes to this layer’s `circleOpacity` property. @@ -240,44 +188,44 @@ MGL_EXPORT /** Orientation of circle when map is pitched. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLCirclePitchAlignmentViewport`. Set this - property to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to + `viewport`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLCirclePitchAlignment` values + * Any of the following constant string values: + * `map`: The circle is aligned to the plane of the map. + * `viewport`: The circle is aligned to the plane of the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchAlignment; +@property (nonatomic, null_resettable) NSExpression *circlePitchAlignment; /** Circle radius. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `5`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `5`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleRadius; +@property (nonatomic, null_resettable) NSExpression *circleRadius; /** The transition affecting any changes to this layer’s `circleRadius` property. @@ -289,75 +237,50 @@ MGL_EXPORT /** Controls the scaling behavior of the circle when the map is pitched. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLCircleScaleAlignmentMap`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-pitch-scale"><code>circle-pitch-scale</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + You can set this property to an expression containing any of the following: + + * Constant `MGLCircleScaleAlignment` values + * Any of the following constant string values: + * `map`: Circles are scaled according to their apparent distance to the + camera. + * `viewport`: Circles are not scaled. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleScaleAlignment; +@property (nonatomic, null_resettable) NSExpression *circleScaleAlignment; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead."))); +@property (nonatomic, null_resettable) NSExpression *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead."))); -#if TARGET_OS_IPHONE /** The stroke color of the circle. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *circleStrokeColor; -#else -/** - The stroke color of the circle. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *circleStrokeColor; -#endif +@property (nonatomic, null_resettable) NSExpression *circleStrokeColor; /** The transition affecting any changes to this layer’s `circleStrokeColor` property. @@ -369,27 +292,19 @@ MGL_EXPORT /** The opacity of the circle's stroke. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeOpacity; +@property (nonatomic, null_resettable) NSExpression *circleStrokeOpacity; /** The transition affecting any changes to this layer’s `circleStrokeOpacity` property. @@ -404,27 +319,19 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *circleStrokeWidth; +@property (nonatomic, null_resettable) NSExpression *circleStrokeWidth; /** The transition affecting any changes to this layer’s `circleStrokeWidth` property. @@ -439,7 +346,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -447,21 +354,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation; +@property (nonatomic, null_resettable) NSExpression *circleTranslation; #else /** The geometry's offset. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -469,14 +380,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate"><code>circle-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslation; +@property (nonatomic, null_resettable) NSExpression *circleTranslation; #endif /** @@ -486,14 +401,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition circleTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslate __attribute__((unavailable("Use circleTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *circleTranslate __attribute__((unavailable("Use circleTranslation instead."))); /** Controls the frame of reference for `circleTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLCircleTranslationAnchorMap`. Set this property - to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `circleTranslation` is non-`nil`. Otherwise, it is ignored. @@ -502,15 +416,24 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-circle-translate-anchor"><code>circle-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLCircleTranslationAnchor` values + * Any of the following constant string values: + * `map`: The circle is translated relative to the map. + * `viewport`: The circle is translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *circleTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circleTranslateAnchor __attribute__((unavailable("Use circleTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *circleTranslateAnchor __attribute__((unavailable("Use circleTranslationAnchor instead."))); @end diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm index 71ae37035e..0be3920987 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.mm +++ b/platform/darwin/src/MGLCircleStyleLayer.mm @@ -87,21 +87,21 @@ namespace mbgl { #pragma mark - Accessing the Paint Attributes -- (void)setCircleBlur:(MGLStyleValue<NSNumber *> *)circleBlur { +- (void)setCircleBlur:(NSExpression *)circleBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleBlur); self.rawLayer->setCircleBlur(mbglValue); } -- (MGLStyleValue<NSNumber *> *)circleBlur { +- (NSExpression *)circleBlur { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleBlur()); + propertyValue = self.rawLayer->getDefaultCircleBlur(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setCircleBlurTransition:(MGLTransition )transition { @@ -122,21 +122,21 @@ namespace mbgl { return transition; } -- (void)setCircleColor:(MGLStyleValue<MGLColor *> *)circleColor { +- (void)setCircleColor:(NSExpression *)circleColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(circleColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(circleColor); self.rawLayer->setCircleColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)circleColor { +- (NSExpression *)circleColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleColor()); + propertyValue = self.rawLayer->getDefaultCircleColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setCircleColorTransition:(MGLTransition )transition { @@ -157,21 +157,21 @@ namespace mbgl { return transition; } -- (void)setCircleOpacity:(MGLStyleValue<NSNumber *> *)circleOpacity { +- (void)setCircleOpacity:(NSExpression *)circleOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleOpacity); self.rawLayer->setCircleOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)circleOpacity { +- (NSExpression *)circleOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleOpacity()); + propertyValue = self.rawLayer->getDefaultCircleOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setCircleOpacityTransition:(MGLTransition )transition { @@ -192,38 +192,38 @@ namespace mbgl { return transition; } -- (void)setCirclePitchAlignment:(MGLStyleValue<NSValue *> *)circlePitchAlignment { +- (void)setCirclePitchAlignment:(NSExpression *)circlePitchAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumPropertyValue(circlePitchAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(circlePitchAlignment); self.rawLayer->setCirclePitchAlignment(mbglValue); } -- (MGLStyleValue<NSValue *> *)circlePitchAlignment { +- (NSExpression *)circlePitchAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCirclePitchAlignment(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultCirclePitchAlignment()); + propertyValue = self.rawLayer->getDefaultCirclePitchAlignment(); } - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toExpression(propertyValue); } -- (void)setCircleRadius:(MGLStyleValue<NSNumber *> *)circleRadius { +- (void)setCircleRadius:(NSExpression *)circleRadius { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleRadius); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleRadius); self.rawLayer->setCircleRadius(mbglValue); } -- (MGLStyleValue<NSNumber *> *)circleRadius { +- (NSExpression *)circleRadius { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleRadius(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleRadius()); + propertyValue = self.rawLayer->getDefaultCircleRadius(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setCircleRadiusTransition:(MGLTransition )transition { @@ -244,45 +244,45 @@ namespace mbgl { return transition; } -- (void)setCircleScaleAlignment:(MGLStyleValue<NSValue *> *)circleScaleAlignment { +- (void)setCircleScaleAlignment:(NSExpression *)circleScaleAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumPropertyValue(circleScaleAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::CirclePitchScaleType>>(circleScaleAlignment); self.rawLayer->setCirclePitchScale(mbglValue); } -- (MGLStyleValue<NSValue *> *)circleScaleAlignment { +- (NSExpression *)circleScaleAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCirclePitchScale(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumStyleValue(self.rawLayer->getDefaultCirclePitchScale()); + propertyValue = self.rawLayer->getDefaultCirclePitchScale(); } - return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::CirclePitchScaleType, NSValue *, mbgl::style::CirclePitchScaleType, MGLCircleScaleAlignment>().toExpression(propertyValue); } -- (void)setCirclePitchScale:(MGLStyleValue<NSValue *> *)circlePitchScale { +- (void)setCirclePitchScale:(NSExpression *)circlePitchScale { } -- (MGLStyleValue<NSValue *> *)circlePitchScale { +- (NSExpression *)circlePitchScale { return self.circleScaleAlignment; } -- (void)setCircleStrokeColor:(MGLStyleValue<MGLColor *> *)circleStrokeColor { +- (void)setCircleStrokeColor:(NSExpression *)circleStrokeColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(circleStrokeColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(circleStrokeColor); self.rawLayer->setCircleStrokeColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)circleStrokeColor { +- (NSExpression *)circleStrokeColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleStrokeColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeColor()); + propertyValue = self.rawLayer->getDefaultCircleStrokeColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setCircleStrokeColorTransition:(MGLTransition )transition { @@ -303,21 +303,21 @@ namespace mbgl { return transition; } -- (void)setCircleStrokeOpacity:(MGLStyleValue<NSNumber *> *)circleStrokeOpacity { +- (void)setCircleStrokeOpacity:(NSExpression *)circleStrokeOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleStrokeOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleStrokeOpacity); self.rawLayer->setCircleStrokeOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)circleStrokeOpacity { +- (NSExpression *)circleStrokeOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleStrokeOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeOpacity()); + propertyValue = self.rawLayer->getDefaultCircleStrokeOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setCircleStrokeOpacityTransition:(MGLTransition )transition { @@ -338,21 +338,21 @@ namespace mbgl { return transition; } -- (void)setCircleStrokeWidth:(MGLStyleValue<NSNumber *> *)circleStrokeWidth { +- (void)setCircleStrokeWidth:(NSExpression *)circleStrokeWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(circleStrokeWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(circleStrokeWidth); self.rawLayer->setCircleStrokeWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)circleStrokeWidth { +- (NSExpression *)circleStrokeWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleStrokeWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultCircleStrokeWidth()); + propertyValue = self.rawLayer->getDefaultCircleStrokeWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setCircleStrokeWidthTransition:(MGLTransition )transition { @@ -373,21 +373,21 @@ namespace mbgl { return transition; } -- (void)setCircleTranslation:(MGLStyleValue<NSValue *> *)circleTranslation { +- (void)setCircleTranslation:(NSExpression *)circleTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(circleTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(circleTranslation); self.rawLayer->setCircleTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)circleTranslation { +- (NSExpression *)circleTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultCircleTranslate()); + propertyValue = self.rawLayer->getDefaultCircleTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setCircleTranslationTransition:(MGLTransition )transition { @@ -408,34 +408,34 @@ namespace mbgl { return transition; } -- (void)setCircleTranslate:(MGLStyleValue<NSValue *> *)circleTranslate { +- (void)setCircleTranslate:(NSExpression *)circleTranslate { } -- (MGLStyleValue<NSValue *> *)circleTranslate { +- (NSExpression *)circleTranslate { return self.circleTranslation; } -- (void)setCircleTranslationAnchor:(MGLStyleValue<NSValue *> *)circleTranslationAnchor { +- (void)setCircleTranslationAnchor:(NSExpression *)circleTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumPropertyValue(circleTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(circleTranslationAnchor); self.rawLayer->setCircleTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)circleTranslationAnchor { +- (NSExpression *)circleTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getCircleTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultCircleTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultCircleTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLCircleTranslationAnchor>().toExpression(propertyValue); } -- (void)setCircleTranslateAnchor:(MGLStyleValue<NSValue *> *)circleTranslateAnchor { +- (void)setCircleTranslateAnchor:(NSExpression *)circleTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)circleTranslateAnchor { +- (NSExpression *)circleTranslateAnchor { return self.circleTranslationAnchor; } diff --git a/platform/darwin/src/MGLConversion.h b/platform/darwin/src/MGLConversion.h index 0d18d4e716..92a6720e6a 100644 --- a/platform/darwin/src/MGLConversion.h +++ b/platform/darwin/src/MGLConversion.h @@ -62,10 +62,14 @@ public: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnullability-completeness" template <class Fn> - static optional<Error> eachMember(const Holder&, Fn&&) { + static optional<Error> eachMember(const Holder& holder, Fn&& visit) { #pragma clang diagnostic pop - // Not implemented (unneeded for MGLStyleFunction conversion). - NSCAssert(NO, @"eachMember not implemented"); + [holder.value enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + auto result = visit(std::string(static_cast<const char *>([key UTF8String])), obj); + if (result) { + *stop = YES; + } + }]; return {}; } diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm index 033052bda8..ee2c71be21 100644 --- a/platform/darwin/src/MGLFeature.mm +++ b/platform/darwin/src/MGLFeature.mm @@ -12,7 +12,7 @@ #import "NSDictionary+MGLAdditions.h" #import "NSArray+MGLAdditions.h" -#import "NSExpression+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" #import <mbgl/util/geometry.hpp> #import <mbgl/style/conversion/geojson.hpp> diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 02d6b2ff88..d1d5af6ba2 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -45,8 +44,8 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) { ```swift let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings) layer.sourceLayerIdentifier = "building" - layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) - layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) + layer.fillExtrusionHeight = NSExpression(forKeyPath: "height") + layer.fillExtrusionBase = NSExpression(forKeyPath: "min_height") layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) ``` @@ -77,30 +76,22 @@ MGL_EXPORT This property is measured in meters. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionHeight` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionBase; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionBase; /** The transition affecting any changes to this layer’s `fillExtrusionBase` property. @@ -109,69 +100,29 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition fillExtrusionBaseTransition; -#if TARGET_OS_IPHONE /** The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fillExtrusionOpacity` to set layer opacityco. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionPattern` is set to `nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillExtrusionColor; -#else -/** - The base color of this layer. The extrusion's surfaces will be shaded - differently based on this color in combination with the `light` settings. If - this color is specified with an alpha component, the alpha component will be - ignored; use `fillExtrusionOpacity` to set layer opacityco. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `fillExtrusionPattern` is set to - `nil`. Otherwise, it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillExtrusionColor; -#endif +@property (nonatomic, null_resettable) NSExpression *fillExtrusionColor; /** The transition affecting any changes to this layer’s `fillExtrusionColor` property. @@ -185,27 +136,19 @@ MGL_EXPORT This property is measured in meters. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionHeight; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionHeight; /** The transition affecting any changes to this layer’s `fillExtrusionHeight` property. @@ -218,18 +161,21 @@ MGL_EXPORT The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionOpacity; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionOpacity; /** The transition affecting any changes to this layer’s `fillExtrusionOpacity` property. @@ -243,13 +189,19 @@ MGL_EXPORT seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillExtrusionPattern; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionPattern; /** The transition affecting any changes to this layer’s `fillExtrusionPattern` property. @@ -264,7 +216,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -272,21 +224,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslation; #else /** The geometry's offset. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -294,14 +250,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslation; #endif /** @@ -311,14 +271,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition fillExtrusionTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead."))); /** Controls the frame of reference for `fillExtrusionTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLFillExtrusionTranslationAnchorMap`. Set this - property to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionTranslation` is non-`nil`. Otherwise, it is ignored. @@ -327,15 +286,24 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate-anchor"><code>fill-extrusion-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLFillExtrusionTranslationAnchor` values + * Any of the following constant string values: + * `map`: The fill extrusion is translated relative to the map. + * `viewport`: The fill extrusion is translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead."))); @end diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm index b00ed8e09f..1baa264689 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -77,21 +77,21 @@ namespace mbgl { #pragma mark - Accessing the Paint Attributes -- (void)setFillExtrusionBase:(MGLStyleValue<NSNumber *> *)fillExtrusionBase { +- (void)setFillExtrusionBase:(NSExpression *)fillExtrusionBase { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionBase); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillExtrusionBase); self.rawLayer->setFillExtrusionBase(mbglValue); } -- (MGLStyleValue<NSNumber *> *)fillExtrusionBase { +- (NSExpression *)fillExtrusionBase { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionBase(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionBase()); + propertyValue = self.rawLayer->getDefaultFillExtrusionBase(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setFillExtrusionBaseTransition:(MGLTransition )transition { @@ -112,21 +112,21 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionColor:(MGLStyleValue<MGLColor *> *)fillExtrusionColor { +- (void)setFillExtrusionColor:(NSExpression *)fillExtrusionColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillExtrusionColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillExtrusionColor); self.rawLayer->setFillExtrusionColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)fillExtrusionColor { +- (NSExpression *)fillExtrusionColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionColor()); + propertyValue = self.rawLayer->getDefaultFillExtrusionColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setFillExtrusionColorTransition:(MGLTransition )transition { @@ -147,21 +147,21 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionHeight:(MGLStyleValue<NSNumber *> *)fillExtrusionHeight { +- (void)setFillExtrusionHeight:(NSExpression *)fillExtrusionHeight { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionHeight); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillExtrusionHeight); self.rawLayer->setFillExtrusionHeight(mbglValue); } -- (MGLStyleValue<NSNumber *> *)fillExtrusionHeight { +- (NSExpression *)fillExtrusionHeight { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionHeight(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionHeight()); + propertyValue = self.rawLayer->getDefaultFillExtrusionHeight(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setFillExtrusionHeightTransition:(MGLTransition )transition { @@ -182,21 +182,21 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionOpacity:(MGLStyleValue<NSNumber *> *)fillExtrusionOpacity { +- (void)setFillExtrusionOpacity:(NSExpression *)fillExtrusionOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(fillExtrusionOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(fillExtrusionOpacity); self.rawLayer->setFillExtrusionOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)fillExtrusionOpacity { +- (NSExpression *)fillExtrusionOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionOpacity()); + propertyValue = self.rawLayer->getDefaultFillExtrusionOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setFillExtrusionOpacityTransition:(MGLTransition )transition { @@ -217,21 +217,21 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionPattern:(MGLStyleValue<NSString *> *)fillExtrusionPattern { +- (void)setFillExtrusionPattern:(NSExpression *)fillExtrusionPattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillExtrusionPattern); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(fillExtrusionPattern); self.rawLayer->setFillExtrusionPattern(mbglValue); } -- (MGLStyleValue<NSString *> *)fillExtrusionPattern { +- (NSExpression *)fillExtrusionPattern { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionPattern(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionPattern()); + propertyValue = self.rawLayer->getDefaultFillExtrusionPattern(); } - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } - (void)setFillExtrusionPatternTransition:(MGLTransition )transition { @@ -252,21 +252,21 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionTranslation:(MGLStyleValue<NSValue *> *)fillExtrusionTranslation { +- (void)setFillExtrusionTranslation:(NSExpression *)fillExtrusionTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillExtrusionTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(fillExtrusionTranslation); self.rawLayer->setFillExtrusionTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)fillExtrusionTranslation { +- (NSExpression *)fillExtrusionTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionTranslate()); + propertyValue = self.rawLayer->getDefaultFillExtrusionTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setFillExtrusionTranslationTransition:(MGLTransition )transition { @@ -287,34 +287,34 @@ namespace mbgl { return transition; } -- (void)setFillExtrusionTranslate:(MGLStyleValue<NSValue *> *)fillExtrusionTranslate { +- (void)setFillExtrusionTranslate:(NSExpression *)fillExtrusionTranslate { } -- (MGLStyleValue<NSValue *> *)fillExtrusionTranslate { +- (NSExpression *)fillExtrusionTranslate { return self.fillExtrusionTranslation; } -- (void)setFillExtrusionTranslationAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor { +- (void)setFillExtrusionTranslationAnchor:(NSExpression *)fillExtrusionTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumPropertyValue(fillExtrusionTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(fillExtrusionTranslationAnchor); self.rawLayer->setFillExtrusionTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor { +- (NSExpression *)fillExtrusionTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillExtrusionTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillExtrusionTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultFillExtrusionTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toExpression(propertyValue); } -- (void)setFillExtrusionTranslateAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor { +- (void)setFillExtrusionTranslateAnchor:(NSExpression *)fillExtrusionTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor { +- (NSExpression *)fillExtrusionTranslateAnchor { return self.fillExtrusionTranslationAnchor; } diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index e385256aae..5caab91b45 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -44,7 +43,7 @@ typedef NS_ENUM(NSUInteger, MGLFillTranslationAnchor) { ```swift let layer = MGLFillStyleLayer(identifier: "parks", source: parks) layer.sourceLayerIdentifier = "parks" - layer.fillColor = MGLStyleValue(rawValue: .green) + layer.fillColor = NSExpression(forConstantValue: UIColor.green) layer.predicate = NSPredicate(format: "type == %@", "national-park") mapView.style?.addLayer(layer) ``` @@ -72,81 +71,49 @@ MGL_EXPORT /** Whether or not the fill should be antialiased. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `YES`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `YES`. + Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-antialias"><code>fill-antialias</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable, getter=isFillAntialiased) MGLStyleValue<NSNumber *> *fillAntialiased; +@property (nonatomic, null_resettable, getter=isFillAntialiased) NSExpression *fillAntialiased; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillAntialias __attribute__((unavailable("Use fillAntialiased instead."))); +@property (nonatomic, null_resettable) NSExpression *fillAntialias __attribute__((unavailable("Use fillAntialiased instead."))); -#if TARGET_OS_IPHONE /** The color of the filled part of this layer. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillPattern` is set to `nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillColor; -#else -/** - The color of the filled part of this layer. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `fillPattern` is set to `nil`. - Otherwise, it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillColor; -#endif +@property (nonatomic, null_resettable) NSExpression *fillColor; /** The transition affecting any changes to this layer’s `fillColor` property. @@ -159,27 +126,19 @@ MGL_EXPORT The opacity of the entire fill layer. In contrast to the `fillColor`, this value will also affect the 1pt stroke around the fill, if the stroke is used. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillOpacity; +@property (nonatomic, null_resettable) NSExpression *fillOpacity; /** The transition affecting any changes to this layer’s `fillOpacity` property. @@ -188,57 +147,23 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition fillOpacityTransition; -#if TARGET_OS_IPHONE /** The outline color of the fill. Matches the value of `fillColor` if unspecified. This property is only applied to the style if `fillPattern` is set to `nil`, - and `fillAntialiased` is set to an `MGLStyleValue` object containing an - `NSNumber` object containing `YES`. Otherwise, it is ignored. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillOutlineColor; -#else -/** - The outline color of the fill. Matches the value of `fillColor` if unspecified. + and `fillAntialiased` is set to an expression that evaluates to `YES`. + Otherwise, it is ignored. - This property is only applied to the style if `fillPattern` is set to `nil`, - and `fillAntialiased` is set to an `MGLStyleValue` object containing an - `NSNumber` object containing `YES`. Otherwise, it is ignored. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + You can set this property to an expression containing any of the following: + + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillOutlineColor; -#endif +@property (nonatomic, null_resettable) NSExpression *fillOutlineColor; /** The transition affecting any changes to this layer’s `fillOutlineColor` property. @@ -251,13 +176,19 @@ MGL_EXPORT Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillPattern; +@property (nonatomic, null_resettable) NSExpression *fillPattern; /** The transition affecting any changes to this layer’s `fillPattern` property. @@ -272,7 +203,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -280,21 +211,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation; +@property (nonatomic, null_resettable) NSExpression *fillTranslation; #else /** The geometry's offset. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -302,14 +237,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate"><code>fill-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslation; +@property (nonatomic, null_resettable) NSExpression *fillTranslation; #endif /** @@ -319,14 +258,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition fillTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslate __attribute__((unavailable("Use fillTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *fillTranslate __attribute__((unavailable("Use fillTranslation instead."))); /** Controls the frame of reference for `fillTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLFillTranslationAnchorMap`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillTranslation` is non-`nil`. Otherwise, it is ignored. @@ -335,15 +273,24 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-translate-anchor"><code>fill-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLFillTranslationAnchor` values + * Any of the following constant string values: + * `map`: The fill is translated relative to the map. + * `viewport`: The fill is translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *fillTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillTranslateAnchor __attribute__((unavailable("Use fillTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *fillTranslateAnchor __attribute__((unavailable("Use fillTranslationAnchor instead."))); @end diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm index 71b01a6661..ab28b414b5 100644 --- a/platform/darwin/src/MGLFillStyleLayer.mm +++ b/platform/darwin/src/MGLFillStyleLayer.mm @@ -77,45 +77,45 @@ namespace mbgl { #pragma mark - Accessing the Paint Attributes -- (void)setFillAntialiased:(MGLStyleValue<NSNumber *> *)fillAntialiased { +- (void)setFillAntialiased:(NSExpression *)fillAntialiased { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(fillAntialiased); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(fillAntialiased); self.rawLayer->setFillAntialias(mbglValue); } -- (MGLStyleValue<NSNumber *> *)isFillAntialiased { +- (NSExpression *)isFillAntialiased { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillAntialias(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillAntialias()); + propertyValue = self.rawLayer->getDefaultFillAntialias(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setFillAntialias:(MGLStyleValue<NSNumber *> *)fillAntialias { +- (void)setFillAntialias:(NSExpression *)fillAntialias { } -- (MGLStyleValue<NSNumber *> *)fillAntialias { +- (NSExpression *)fillAntialias { return self.isFillAntialiased; } -- (void)setFillColor:(MGLStyleValue<MGLColor *> *)fillColor { +- (void)setFillColor:(NSExpression *)fillColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillColor); self.rawLayer->setFillColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)fillColor { +- (NSExpression *)fillColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillColor()); + propertyValue = self.rawLayer->getDefaultFillColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setFillColorTransition:(MGLTransition )transition { @@ -136,21 +136,21 @@ namespace mbgl { return transition; } -- (void)setFillOpacity:(MGLStyleValue<NSNumber *> *)fillOpacity { +- (void)setFillOpacity:(NSExpression *)fillOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(fillOpacity); self.rawLayer->setFillOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)fillOpacity { +- (NSExpression *)fillOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillOpacity()); + propertyValue = self.rawLayer->getDefaultFillOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setFillOpacityTransition:(MGLTransition )transition { @@ -171,21 +171,21 @@ namespace mbgl { return transition; } -- (void)setFillOutlineColor:(MGLStyleValue<MGLColor *> *)fillOutlineColor { +- (void)setFillOutlineColor:(NSExpression *)fillOutlineColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillOutlineColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(fillOutlineColor); self.rawLayer->setFillOutlineColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)fillOutlineColor { +- (NSExpression *)fillOutlineColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillOutlineColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillOutlineColor()); + propertyValue = self.rawLayer->getDefaultFillOutlineColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setFillOutlineColorTransition:(MGLTransition )transition { @@ -206,21 +206,21 @@ namespace mbgl { return transition; } -- (void)setFillPattern:(MGLStyleValue<NSString *> *)fillPattern { +- (void)setFillPattern:(NSExpression *)fillPattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillPattern); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(fillPattern); self.rawLayer->setFillPattern(mbglValue); } -- (MGLStyleValue<NSString *> *)fillPattern { +- (NSExpression *)fillPattern { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillPattern(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillPattern()); + propertyValue = self.rawLayer->getDefaultFillPattern(); } - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } - (void)setFillPatternTransition:(MGLTransition )transition { @@ -241,21 +241,21 @@ namespace mbgl { return transition; } -- (void)setFillTranslation:(MGLStyleValue<NSValue *> *)fillTranslation { +- (void)setFillTranslation:(NSExpression *)fillTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(fillTranslation); self.rawLayer->setFillTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)fillTranslation { +- (NSExpression *)fillTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillTranslate()); + propertyValue = self.rawLayer->getDefaultFillTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setFillTranslationTransition:(MGLTransition )transition { @@ -276,34 +276,34 @@ namespace mbgl { return transition; } -- (void)setFillTranslate:(MGLStyleValue<NSValue *> *)fillTranslate { +- (void)setFillTranslate:(NSExpression *)fillTranslate { } -- (MGLStyleValue<NSValue *> *)fillTranslate { +- (NSExpression *)fillTranslate { return self.fillTranslation; } -- (void)setFillTranslationAnchor:(MGLStyleValue<NSValue *> *)fillTranslationAnchor { +- (void)setFillTranslationAnchor:(NSExpression *)fillTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumPropertyValue(fillTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(fillTranslationAnchor); self.rawLayer->setFillTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)fillTranslationAnchor { +- (NSExpression *)fillTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getFillTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultFillTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillTranslationAnchor>().toExpression(propertyValue); } -- (void)setFillTranslateAnchor:(MGLStyleValue<NSValue *> *)fillTranslateAnchor { +- (void)setFillTranslateAnchor:(NSExpression *)fillTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)fillTranslateAnchor { +- (NSExpression *)fillTranslateAnchor { return self.fillTranslationAnchor; } diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h index 0fe49d510d..c1fa069844 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.h +++ b/platform/darwin/src/MGLHillshadeStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -59,41 +58,26 @@ MGL_EXPORT #pragma mark - Accessing the Paint Attributes -#if TARGET_OS_IPHONE /** The shading color used to accentuate rugged terrain like sharp cliffs and gorges. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *hillshadeAccentColor; -#else -/** - The shading color used to accentuate rugged terrain like sharp cliffs and - gorges. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *hillshadeAccentColor; -#endif +@property (nonatomic, null_resettable) NSExpression *hillshadeAccentColor; /** The transition affecting any changes to this layer’s `hillshadeAccentColor` property. @@ -105,18 +89,21 @@ MGL_EXPORT /** Intensity of the hillshade - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0.5`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `0.5`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *hillshadeExaggeration; +@property (nonatomic, null_resettable) NSExpression *hillshadeExaggeration; /** The transition affecting any changes to this layer’s `hillshadeExaggeration` property. @@ -125,39 +112,25 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition hillshadeExaggerationTransition; -#if TARGET_OS_IPHONE /** The shading color of areas that faces towards the light source. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.whiteColor`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *hillshadeHighlightColor; -#else -/** - The shading color of areas that faces towards the light source. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.whiteColor`. Set this property to `nil` to reset it to the default - value. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *hillshadeHighlightColor; -#endif +@property (nonatomic, null_resettable) NSExpression *hillshadeHighlightColor; /** The transition affecting any changes to this layer’s `hillshadeHighlightColor` property. @@ -169,17 +142,26 @@ MGL_EXPORT /** Direction of light source when map is rotated. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLHillshadeIlluminationAnchorViewport`. Set this - property to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to + `viewport`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLHillshadeIlluminationAnchor` values + * Any of the following constant string values: + * `map`: The hillshade illumination is relative to the north direction. + * `viewport`: The hillshade illumination is relative to the top of the + viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *hillshadeIlluminationAnchor; +@property (nonatomic, null_resettable) NSExpression *hillshadeIlluminationAnchor; /** The direction of the light source used to generate the hillshading with 0 as @@ -187,18 +169,21 @@ MGL_EXPORT `MGLHillshadeIlluminationAnchorViewport` and due north if `hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `335`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `335`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *hillshadeIlluminationDirection; +@property (nonatomic, null_resettable) NSExpression *hillshadeIlluminationDirection; /** The transition affecting any changes to this layer’s `hillshadeIlluminationDirection` property. @@ -207,39 +192,25 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition hillshadeIlluminationDirectionTransition; -#if TARGET_OS_IPHONE /** The shading color of areas that face away from the light source. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *hillshadeShadowColor; -#else -/** - The shading color of areas that face away from the light source. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *hillshadeShadowColor; -#endif +@property (nonatomic, null_resettable) NSExpression *hillshadeShadowColor; /** The transition affecting any changes to this layer’s `hillshadeShadowColor` property. diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.mm b/platform/darwin/src/MGLHillshadeStyleLayer.mm index 3225feb587..ac12b52506 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.mm +++ b/platform/darwin/src/MGLHillshadeStyleLayer.mm @@ -77,21 +77,21 @@ namespace mbgl { #pragma mark - Accessing the Paint Attributes -- (void)setHillshadeAccentColor:(MGLStyleValue<MGLColor *> *)hillshadeAccentColor { +- (void)setHillshadeAccentColor:(NSExpression *)hillshadeAccentColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(hillshadeAccentColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeAccentColor); self.rawLayer->setHillshadeAccentColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)hillshadeAccentColor { +- (NSExpression *)hillshadeAccentColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeAccentColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultHillshadeAccentColor()); + propertyValue = self.rawLayer->getDefaultHillshadeAccentColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setHillshadeAccentColorTransition:(MGLTransition )transition { @@ -112,21 +112,21 @@ namespace mbgl { return transition; } -- (void)setHillshadeExaggeration:(MGLStyleValue<NSNumber *> *)hillshadeExaggeration { +- (void)setHillshadeExaggeration:(NSExpression *)hillshadeExaggeration { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(hillshadeExaggeration); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(hillshadeExaggeration); self.rawLayer->setHillshadeExaggeration(mbglValue); } -- (MGLStyleValue<NSNumber *> *)hillshadeExaggeration { +- (NSExpression *)hillshadeExaggeration { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeExaggeration(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultHillshadeExaggeration()); + propertyValue = self.rawLayer->getDefaultHillshadeExaggeration(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setHillshadeExaggerationTransition:(MGLTransition )transition { @@ -147,21 +147,21 @@ namespace mbgl { return transition; } -- (void)setHillshadeHighlightColor:(MGLStyleValue<MGLColor *> *)hillshadeHighlightColor { +- (void)setHillshadeHighlightColor:(NSExpression *)hillshadeHighlightColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(hillshadeHighlightColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeHighlightColor); self.rawLayer->setHillshadeHighlightColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)hillshadeHighlightColor { +- (NSExpression *)hillshadeHighlightColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeHighlightColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultHillshadeHighlightColor()); + propertyValue = self.rawLayer->getDefaultHillshadeHighlightColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setHillshadeHighlightColorTransition:(MGLTransition )transition { @@ -182,38 +182,38 @@ namespace mbgl { return transition; } -- (void)setHillshadeIlluminationAnchor:(MGLStyleValue<NSValue *> *)hillshadeIlluminationAnchor { +- (void)setHillshadeIlluminationAnchor:(NSExpression *)hillshadeIlluminationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toEnumPropertyValue(hillshadeIlluminationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::HillshadeIlluminationAnchorType>>(hillshadeIlluminationAnchor); self.rawLayer->setHillshadeIlluminationAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)hillshadeIlluminationAnchor { +- (NSExpression *)hillshadeIlluminationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeIlluminationAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultHillshadeIlluminationAnchor()); + propertyValue = self.rawLayer->getDefaultHillshadeIlluminationAnchor(); } - return MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::HillshadeIlluminationAnchorType, NSValue *, mbgl::style::HillshadeIlluminationAnchorType, MGLHillshadeIlluminationAnchor>().toExpression(propertyValue); } -- (void)setHillshadeIlluminationDirection:(MGLStyleValue<NSNumber *> *)hillshadeIlluminationDirection { +- (void)setHillshadeIlluminationDirection:(NSExpression *)hillshadeIlluminationDirection { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(hillshadeIlluminationDirection); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(hillshadeIlluminationDirection); self.rawLayer->setHillshadeIlluminationDirection(mbglValue); } -- (MGLStyleValue<NSNumber *> *)hillshadeIlluminationDirection { +- (NSExpression *)hillshadeIlluminationDirection { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeIlluminationDirection(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultHillshadeIlluminationDirection()); + propertyValue = self.rawLayer->getDefaultHillshadeIlluminationDirection(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setHillshadeIlluminationDirectionTransition:(MGLTransition )transition { @@ -234,21 +234,21 @@ namespace mbgl { return transition; } -- (void)setHillshadeShadowColor:(MGLStyleValue<MGLColor *> *)hillshadeShadowColor { +- (void)setHillshadeShadowColor:(NSExpression *)hillshadeShadowColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(hillshadeShadowColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(hillshadeShadowColor); self.rawLayer->setHillshadeShadowColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)hillshadeShadowColor { +- (NSExpression *)hillshadeShadowColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getHillshadeShadowColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultHillshadeShadowColor()); + propertyValue = self.rawLayer->getDefaultHillshadeShadowColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setHillshadeShadowColorTransition:(MGLTransition )transition { diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h index 55b789f043..d7e64cbfad 100644 --- a/platform/darwin/src/MGLLight.h +++ b/platform/darwin/src/MGLLight.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSUInteger, MGLLightAnchor) { A structure containing information about the position of the light source relative to lit geometries. */ -typedef struct MGLSphericalPosition { +typedef struct __attribute__((objc_boxable)) MGLSphericalPosition { /** Distance from the center of the base of an object to its light. */ CGFloat radial; /** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds @@ -65,20 +65,31 @@ MGL_EXPORT /** Whether extruded geometries are lit relative to the map or viewport. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLLightAnchorViewport`. + The default value of this property is an expression that evaluates to + `viewport`. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLAnchor` values + * Any of the following constant string values: + * `map`: The position of the light source is aligned to the rotation of the + map. + * `viewport`: The position of the light source is aligned to the rotation of + the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. This property corresponds to the <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-anchor"><code>anchor</code></a> light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLStyleValue<NSValue *> *anchor; +@property (nonatomic) NSExpression *anchor; /** Position of the `MGLLight` source relative to lit (extruded) geometries, in a @@ -90,21 +101,25 @@ MGL_EXPORT corresponds to due north, and degrees proceed clockwise), and polar indicates the height of the light (from 0°, directly above, to 180°, directly below). - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `MGLSphericalPosition` struct set to 1.15 radial, 210 azimuthal and 30 polar. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLSphericalPosition` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. This property corresponds to the <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position"><code>position</code></a> light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLStyleValue<NSValue *> *position; +@property (nonatomic) NSExpression *position; /** The transition affecting any changes to this layer’s `position` property. @@ -113,45 +128,28 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition positionTransition; -#if TARGET_OS_IPHONE /** Color tint for lighting extruded geometries. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.whiteColor`. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - - This property corresponds to the <a - href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a> - light property in the Mapbox Style Specification. - */ -@property (nonatomic) MGLStyleValue<UIColor *> *color; -#else -/** - Color tint for lighting extruded geometries. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.whiteColor`. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. This property corresponds to the <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a> light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLStyleValue<NSColor *> *color; -#endif +@property (nonatomic) NSExpression *color; /** The transition affecting any changes to this layer’s `color` property. @@ -164,21 +162,25 @@ MGL_EXPORT Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0.5`. + The default value of this property is an expression that evaluates to the float + `0.5`. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. This property corresponds to the <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-intensity"><code>intensity</code></a> light property in the Mapbox Style Specification. */ -@property (nonatomic) MGLStyleValue<NSNumber *> *intensity; +@property (nonatomic) NSExpression *intensity; /** The transition affecting any changes to this layer’s `intensity` property. diff --git a/platform/darwin/src/MGLLight.h.ejs b/platform/darwin/src/MGLLight.h.ejs index 26ecefc3af..56c3312107 100644 --- a/platform/darwin/src/MGLLight.h.ejs +++ b/platform/darwin/src/MGLLight.h.ejs @@ -33,7 +33,7 @@ typedef NS_ENUM(NSUInteger, MGLLight<%- camelize(property.name) %>) { A structure containing information about the position of the light source relative to lit geometries. */ -typedef struct MGLSphericalPosition { +typedef struct __attribute__((objc_boxable)) MGLSphericalPosition { /** Distance from the center of the base of an object to its light. */ CGFloat radial; /** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds @@ -77,7 +77,7 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-<%- originalPropertyName(property) %>"><code><%- originalPropertyName(property) %></code></a> light property in the Mapbox Style Specification. */ -@property (nonatomic<% if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>; +@property (nonatomic<% if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>; <% if (property.transition) { -%> /** @@ -89,7 +89,7 @@ MGL_EXPORT <% } -%> <% if (property.original) { -%> -@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); +@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); <% } -%> <% } -%> diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm index c83ef127a6..db2893ed44 100644 --- a/platform/darwin/src/MGLLight.mm +++ b/platform/darwin/src/MGLLight.mm @@ -44,39 +44,39 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition { if (self = [super init]) { auto anchor = mbglLight->getAnchor(); - MGLStyleValue<NSValue *> *anchorStyleValue; + NSExpression *anchorExpression; if (anchor.isUndefined()) { mbgl::style::PropertyValue<mbgl::style::LightAnchorType> defaultAnchor = mbglLight->getDefaultAnchor(); - anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(defaultAnchor); + anchorExpression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toExpression(defaultAnchor); } else { - anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(anchor); + anchorExpression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toExpression(anchor); } - _anchor = anchorStyleValue; + _anchor = anchorExpression; auto positionValue = mbglLight->getPosition(); if (positionValue.isUndefined()) { - _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(mbglLight->getDefaultPosition()); + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toExpression(mbglLight->getDefaultPosition()); } else { - _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(positionValue); + _position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toExpression(positionValue); } _positionTransition = MGLTransitionFromOptions(mbglLight->getPositionTransition()); auto colorValue = mbglLight->getColor(); if (colorValue.isUndefined()) { - _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(mbglLight->getDefaultColor()); + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(mbglLight->getDefaultColor()); } else { - _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(colorValue); + _color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(colorValue); } _colorTransition = MGLTransitionFromOptions(mbglLight->getColorTransition()); auto intensityValue = mbglLight->getIntensity(); if (intensityValue.isUndefined()) { - _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(mbglLight->getDefaultIntensity()); + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toExpression(mbglLight->getDefaultIntensity()); } else { - _intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(intensityValue); + _intensity = MGLStyleValueTransformer<float, NSNumber *>().toExpression(intensityValue); } _intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition()); @@ -89,20 +89,20 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition - (mbgl::style::Light)mbglLight { mbgl::style::Light mbglLight; - auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumPropertyValue(self.anchor); + auto anchor = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::LightAnchorType>>(self.anchor); mbglLight.setAnchor(anchor); - auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toInterpolatablePropertyValue(self.position); + auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::Position>>(self.position); mbglLight.setPosition(position); mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransition)); - auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(self.color); + auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::PropertyValue<mbgl::Color>>(self.color); mbglLight.setColor(color); mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransition)); - auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(self.intensity); + auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(self.intensity); mbglLight.setIntensity(intensity); mbglLight.setIntensityTransition(MGLOptionsFromTransition(self.intensityTransition)); diff --git a/platform/darwin/src/MGLLight.mm.ejs b/platform/darwin/src/MGLLight.mm.ejs index 0d0da124c8..b241269519 100644 --- a/platform/darwin/src/MGLLight.mm.ejs +++ b/platform/darwin/src/MGLLight.mm.ejs @@ -55,15 +55,15 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition <% for (const property of properties) { -%> <% if (property.type == "enum") { -%> auto <%- camelizeWithLeadingLowercase(property.name) -%> = mbglLight->get<%- camelize(property.name) -%>(); - MGLStyleValue<NSValue *> *<%- camelizeWithLeadingLowercase(property.name) -%>StyleValue; + NSExpression *<%- camelizeWithLeadingLowercase(property.name) -%>Expression; if (<%- camelizeWithLeadingLowercase(property.name) -%>.isUndefined()) { mbgl::style::PropertyValue<mbgl::style::Light<%- camelize(property.name) -%>Type> default<%- camelize(property.name) -%> = mbglLight->getDefault<%- camelize(property.name) -%>(); - <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumStyleValue(default<%- camelize(property.name) -%>); + <%- camelizeWithLeadingLowercase(property.name) -%>Expression = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toExpression(default<%- camelize(property.name) -%>); } else { - <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumStyleValue(<%- camelizeWithLeadingLowercase(property.name) -%>); + <%- camelizeWithLeadingLowercase(property.name) -%>Expression = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toExpression(<%- camelizeWithLeadingLowercase(property.name) -%>); } - _<%- camelizeWithLeadingLowercase(property.name) -%> = <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue; + _<%- camelizeWithLeadingLowercase(property.name) -%> = <%- camelizeWithLeadingLowercase(property.name) -%>Expression; <% if (property.transition) { -%> _<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition()); @@ -72,9 +72,9 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition <% } else {-%> auto <%- camelizeWithLeadingLowercase(property.name) -%>Value = mbglLight->get<%- camelize(property.name) -%>(); if (<%- camelizeWithLeadingLowercase(property.name) -%>Value.isUndefined()) { - _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(mbglLight->getDefault<%- camelize(property.name) -%>()); + _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(mbglLight->getDefault<%- camelize(property.name) -%>()); } else { - _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(<%- camelizeWithLeadingLowercase(property.name) -%>Value); + _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(<%- camelizeWithLeadingLowercase(property.name) -%>Value); } <% if (property.transition) { -%> _<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition()); @@ -93,11 +93,11 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition <% if (properties.length) { -%> <% for (const property of properties) { -%> <% if (property.type == "enum") { -%> - auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toEnumPropertyValue(self.<%- camelizeWithLeadingLowercase(property.name) -%>); + auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<mbgl::style::Light<%- camelize(property.name) -%>Type, NSValue *, mbgl::style::Light<%- camelize(property.name) -%>Type, MGLLight<%- camelize(property.name) -%>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(self.<%- camelizeWithLeadingLowercase(property.name) -%>); mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>); <% } else {-%> - auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(self.<%- camelizeWithLeadingLowercase(property.name) -%>); + auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(self.<%- camelizeWithLeadingLowercase(property.name) -%>); mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>); <% } -%> diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index e48951d97b..003f48ae43 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -94,12 +93,11 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) { ```swift let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails) layer.sourceLayerIdentifier = "trails" - layer.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: [14: MGLStyleValue(rawValue: 2), - 18: MGLStyleValue(rawValue: 20)], - options: [.interpolationBase: 1.5]) - layer.lineColor = MGLStyleValue(rawValue: .brown) - layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round)) + layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)", + [14: 2, + 18: 20]) + layer.lineColor = NSExpression(forConstantValue: UIColor.brown) + layer.lineCap = NSExpression(forConstantValue: "round") layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking") mapView.style?.addLayer(layer) ``` @@ -127,82 +125,99 @@ MGL_EXPORT /** The display of line endings. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLLineCapButt`. Set this property to `nil` to - reset it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + The default value of this property is an expression that evaluates to `butt`. + Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant `MGLLineCap` values + * Any of the following constant string values: + * `butt`: A cap with a squared-off end which is drawn to the exact endpoint + of the line. + * `round`: A cap with a rounded end which is drawn beyond the endpoint of the + line at a radius of one-half of the line's width and centered on the endpoint + of the line. + * `square`: A cap with a squared-off end which is drawn beyond the endpoint + of the line at a distance of one-half of the line's width. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineCap; +@property (nonatomic, null_resettable) NSExpression *lineCap; /** The display of lines when joining. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLLineJoinMiter`. Set this property to `nil` to - reset it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to `miter`. + Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant `MGLLineJoin` values + * Any of the following constant string values: + * `bevel`: A join with a squared-off end which is drawn beyond the endpoint + of the line at a distance of one-half of the line's width. + * `round`: A join with a rounded end which is drawn beyond the endpoint of + the line at a radius of one-half of the line's width and centered on the + endpoint of the line. + * `miter`: A join with a sharp, angled corner which is drawn with the outer + sides beyond the endpoint of the path until they meet. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineJoin; +@property (nonatomic, null_resettable) NSExpression *lineJoin; /** Used to automatically convert miter joins to bevel joins for sharp angles. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `2`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an - `MGLStyleValue` object containing an `NSValue` object containing - `MGLLineJoinMiter`. Otherwise, it is ignored. + expression that evaluates to `miter`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineMiterLimit; +@property (nonatomic, null_resettable) NSExpression *lineMiterLimit; /** Used to automatically convert round joins to miter joins for shallow angles. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1.05`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `1.05`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an - `MGLStyleValue` object containing an `NSValue` object containing - `MGLLineJoinRound`. Otherwise, it is ignored. + expression that evaluates to `round`. Otherwise, it is ignored. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineRoundLimit; +@property (nonatomic, null_resettable) NSExpression *lineRoundLimit; #pragma mark - Accessing the Paint Attributes @@ -211,27 +226,19 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineBlur; +@property (nonatomic, null_resettable) NSExpression *lineBlur; /** The transition affecting any changes to this layer’s `lineBlur` property. @@ -240,63 +247,26 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition lineBlurTransition; -#if TARGET_OS_IPHONE /** The color with which the line will be drawn. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `linePattern` is set to `nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *lineColor; -#else -/** - The color with which the line will be drawn. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `linePattern` is set to `nil`. - Otherwise, it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *lineColor; -#endif +@property (nonatomic, null_resettable) NSExpression *lineColor; /** The transition affecting any changes to this layer’s `lineColor` property. @@ -319,13 +289,19 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-dasharray"><code>line-dasharray</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant array values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDashPattern; +@property (nonatomic, null_resettable) NSExpression *lineDashPattern; /** The transition affecting any changes to this layer’s `lineDashPattern` property. @@ -334,7 +310,7 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition lineDashPatternTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSNumber *> *> *lineDasharray __attribute__((unavailable("Use lineDashPattern instead."))); +@property (nonatomic, null_resettable) NSExpression *lineDasharray __attribute__((unavailable("Use lineDashPattern instead."))); /** Draws a line casing outside of a line's actual path. Value indicates the width @@ -342,27 +318,19 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineGapWidth; +@property (nonatomic, null_resettable) NSExpression *lineGapWidth; /** The transition affecting any changes to this layer’s `lineGapWidth` property. @@ -379,27 +347,19 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOffset; +@property (nonatomic, null_resettable) NSExpression *lineOffset; /** The transition affecting any changes to this layer’s `lineOffset` property. @@ -411,27 +371,19 @@ MGL_EXPORT /** The opacity at which the line will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineOpacity; +@property (nonatomic, null_resettable) NSExpression *lineOpacity; /** The transition affecting any changes to this layer’s `lineOpacity` property. @@ -444,13 +396,19 @@ MGL_EXPORT Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *linePattern; +@property (nonatomic, null_resettable) NSExpression *linePattern; /** The transition affecting any changes to this layer’s `linePattern` property. @@ -465,7 +423,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -473,21 +431,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation; +@property (nonatomic, null_resettable) NSExpression *lineTranslation; #else /** The geometry's offset. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -495,14 +457,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate"><code>line-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslation; +@property (nonatomic, null_resettable) NSExpression *lineTranslation; #endif /** @@ -512,14 +478,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition lineTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslate __attribute__((unavailable("Use lineTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *lineTranslate __attribute__((unavailable("Use lineTranslation instead."))); /** Controls the frame of reference for `lineTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLLineTranslationAnchorMap`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineTranslation` is non-`nil`. Otherwise, it is ignored. @@ -528,42 +493,43 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-line-translate-anchor"><code>line-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLLineTranslationAnchor` values + * Any of the following constant string values: + * `map`: The line is translated relative to the map. + * `viewport`: The line is translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *lineTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *lineTranslateAnchor __attribute__((unavailable("Use lineTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *lineTranslateAnchor __attribute__((unavailable("Use lineTranslationAnchor instead."))); /** Stroke thickness. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. - - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *lineWidth; +@property (nonatomic, null_resettable) NSExpression *lineWidth; /** The transition affecting any changes to this layer’s `lineWidth` property. diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index 5b2652cdeb..4e6ff27b39 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -89,91 +89,91 @@ namespace mbgl { #pragma mark - Accessing the Layout Attributes -- (void)setLineCap:(MGLStyleValue<NSValue *> *)lineCap { +- (void)setLineCap:(NSExpression *)lineCap { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumPropertyValue(lineCap); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::LineCapType>>(lineCap); self.rawLayer->setLineCap(mbglValue); } -- (MGLStyleValue<NSValue *> *)lineCap { +- (NSExpression *)lineCap { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineCap(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumStyleValue(self.rawLayer->getDefaultLineCap()); + propertyValue = self.rawLayer->getDefaultLineCap(); } - return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::LineCapType, NSValue *, mbgl::style::LineCapType, MGLLineCap>().toExpression(propertyValue); } -- (void)setLineJoin:(MGLStyleValue<NSValue *> *)lineJoin { +- (void)setLineJoin:(NSExpression *)lineJoin { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenPropertyValue(lineJoin); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::LineJoinType>>(lineJoin); self.rawLayer->setLineJoin(mbglValue); } -- (MGLStyleValue<NSValue *> *)lineJoin { +- (NSExpression *)lineJoin { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineJoin(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineJoin()); + propertyValue = self.rawLayer->getDefaultLineJoin(); } - return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toExpression(propertyValue); } -- (void)setLineMiterLimit:(MGLStyleValue<NSNumber *> *)lineMiterLimit { +- (void)setLineMiterLimit:(NSExpression *)lineMiterLimit { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(lineMiterLimit); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(lineMiterLimit); self.rawLayer->setLineMiterLimit(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineMiterLimit { +- (NSExpression *)lineMiterLimit { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineMiterLimit(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultLineMiterLimit()); + propertyValue = self.rawLayer->getDefaultLineMiterLimit(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setLineRoundLimit:(MGLStyleValue<NSNumber *> *)lineRoundLimit { +- (void)setLineRoundLimit:(NSExpression *)lineRoundLimit { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(lineRoundLimit); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(lineRoundLimit); self.rawLayer->setLineRoundLimit(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineRoundLimit { +- (NSExpression *)lineRoundLimit { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineRoundLimit(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultLineRoundLimit()); + propertyValue = self.rawLayer->getDefaultLineRoundLimit(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } #pragma mark - Accessing the Paint Attributes -- (void)setLineBlur:(MGLStyleValue<NSNumber *> *)lineBlur { +- (void)setLineBlur:(NSExpression *)lineBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineBlur); self.rawLayer->setLineBlur(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineBlur { +- (NSExpression *)lineBlur { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineBlur()); + propertyValue = self.rawLayer->getDefaultLineBlur(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setLineBlurTransition:(MGLTransition )transition { @@ -194,21 +194,21 @@ namespace mbgl { return transition; } -- (void)setLineColor:(MGLStyleValue<MGLColor *> *)lineColor { +- (void)setLineColor:(NSExpression *)lineColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(lineColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(lineColor); self.rawLayer->setLineColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)lineColor { +- (NSExpression *)lineColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineColor()); + propertyValue = self.rawLayer->getDefaultLineColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setLineColorTransition:(MGLTransition )transition { @@ -229,21 +229,21 @@ namespace mbgl { return transition; } -- (void)setLineDashPattern:(MGLStyleValue<NSArray<NSNumber *> *> *)lineDashPattern { +- (void)setLineDashPattern:(NSExpression *)lineDashPattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toPropertyValue(lineDashPattern); + auto mbglValue = MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toPropertyValue<mbgl::style::PropertyValue<std::vector<float>>>(lineDashPattern); self.rawLayer->setLineDasharray(mbglValue); } -- (MGLStyleValue<NSArray<NSNumber *> *> *)lineDashPattern { +- (NSExpression *)lineDashPattern { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineDasharray(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toStyleValue(self.rawLayer->getDefaultLineDasharray()); + propertyValue = self.rawLayer->getDefaultLineDasharray(); } - return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::vector<float>, NSArray<NSNumber *> *, float>().toExpression(propertyValue); } - (void)setLineDashPatternTransition:(MGLTransition )transition { @@ -264,28 +264,28 @@ namespace mbgl { return transition; } -- (void)setLineDasharray:(MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray { +- (void)setLineDasharray:(NSExpression *)lineDasharray { } -- (MGLStyleValue<NSArray<NSNumber *> *> *)lineDasharray { +- (NSExpression *)lineDasharray { return self.lineDashPattern; } -- (void)setLineGapWidth:(MGLStyleValue<NSNumber *> *)lineGapWidth { +- (void)setLineGapWidth:(NSExpression *)lineGapWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineGapWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineGapWidth); self.rawLayer->setLineGapWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineGapWidth { +- (NSExpression *)lineGapWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineGapWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineGapWidth()); + propertyValue = self.rawLayer->getDefaultLineGapWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setLineGapWidthTransition:(MGLTransition )transition { @@ -306,21 +306,21 @@ namespace mbgl { return transition; } -- (void)setLineOffset:(MGLStyleValue<NSNumber *> *)lineOffset { +- (void)setLineOffset:(NSExpression *)lineOffset { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineOffset); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineOffset); self.rawLayer->setLineOffset(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineOffset { +- (NSExpression *)lineOffset { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineOffset(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineOffset()); + propertyValue = self.rawLayer->getDefaultLineOffset(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setLineOffsetTransition:(MGLTransition )transition { @@ -341,21 +341,21 @@ namespace mbgl { return transition; } -- (void)setLineOpacity:(MGLStyleValue<NSNumber *> *)lineOpacity { +- (void)setLineOpacity:(NSExpression *)lineOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineOpacity); self.rawLayer->setLineOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineOpacity { +- (NSExpression *)lineOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineOpacity()); + propertyValue = self.rawLayer->getDefaultLineOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setLineOpacityTransition:(MGLTransition )transition { @@ -376,21 +376,21 @@ namespace mbgl { return transition; } -- (void)setLinePattern:(MGLStyleValue<NSString *> *)linePattern { +- (void)setLinePattern:(NSExpression *)linePattern { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(linePattern); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::PropertyValue<std::string>>(linePattern); self.rawLayer->setLinePattern(mbglValue); } -- (MGLStyleValue<NSString *> *)linePattern { +- (NSExpression *)linePattern { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLinePattern(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultLinePattern()); + propertyValue = self.rawLayer->getDefaultLinePattern(); } - return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } - (void)setLinePatternTransition:(MGLTransition )transition { @@ -411,21 +411,21 @@ namespace mbgl { return transition; } -- (void)setLineTranslation:(MGLStyleValue<NSValue *> *)lineTranslation { +- (void)setLineTranslation:(NSExpression *)lineTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(lineTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(lineTranslation); self.rawLayer->setLineTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)lineTranslation { +- (NSExpression *)lineTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultLineTranslate()); + propertyValue = self.rawLayer->getDefaultLineTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setLineTranslationTransition:(MGLTransition )transition { @@ -446,52 +446,52 @@ namespace mbgl { return transition; } -- (void)setLineTranslate:(MGLStyleValue<NSValue *> *)lineTranslate { +- (void)setLineTranslate:(NSExpression *)lineTranslate { } -- (MGLStyleValue<NSValue *> *)lineTranslate { +- (NSExpression *)lineTranslate { return self.lineTranslation; } -- (void)setLineTranslationAnchor:(MGLStyleValue<NSValue *> *)lineTranslationAnchor { +- (void)setLineTranslationAnchor:(NSExpression *)lineTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumPropertyValue(lineTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(lineTranslationAnchor); self.rawLayer->setLineTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)lineTranslationAnchor { +- (NSExpression *)lineTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultLineTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultLineTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLLineTranslationAnchor>().toExpression(propertyValue); } -- (void)setLineTranslateAnchor:(MGLStyleValue<NSValue *> *)lineTranslateAnchor { +- (void)setLineTranslateAnchor:(NSExpression *)lineTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)lineTranslateAnchor { +- (NSExpression *)lineTranslateAnchor { return self.lineTranslationAnchor; } -- (void)setLineWidth:(MGLStyleValue<NSNumber *> *)lineWidth { +- (void)setLineWidth:(NSExpression *)lineWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(lineWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(lineWidth); self.rawLayer->setLineWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)lineWidth { +- (NSExpression *)lineWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getLineWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineWidth()); + propertyValue = self.rawLayer->getDefaultLineWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setLineWidthTransition:(MGLTransition )transition { diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h index e808bb5242..55d49329c2 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.h +++ b/platform/darwin/src/MGLRasterStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLForegroundStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -29,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN ```swift let layer = MGLRasterStyleLayer(identifier: "clouds", source: source) - layer.rasterOpacity = MGLStyleValue(rawValue: 0.5) + layer.rasterOpacity = NSExpression(forConstantValue: 0.5) mapView.style?.addLayer(layer) ``` */ @@ -57,22 +56,25 @@ MGL_EXPORT Increase or reduce the brightness of the image. The value is the maximum brightness. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-max"><code>raster-brightness-max</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumRasterBrightness; +@property (nonatomic, null_resettable) NSExpression *maximumRasterBrightness; /** The transition affecting any changes to this layer’s `maximumRasterBrightness` property. @@ -81,28 +83,31 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition maximumRasterBrightnessTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMax __attribute__((unavailable("Use maximumRasterBrightness instead."))); +@property (nonatomic, null_resettable) NSExpression *rasterBrightnessMax __attribute__((unavailable("Use maximumRasterBrightness instead."))); /** Increase or reduce the brightness of the image. The value is the minimum brightness. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-min"><code>raster-brightness-min</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *minimumRasterBrightness; +@property (nonatomic, null_resettable) NSExpression *minimumRasterBrightness; /** The transition affecting any changes to this layer’s `minimumRasterBrightness` property. @@ -111,23 +116,26 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition minimumRasterBrightnessTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterBrightnessMin __attribute__((unavailable("Use minimumRasterBrightness instead."))); +@property (nonatomic, null_resettable) NSExpression *rasterBrightnessMin __attribute__((unavailable("Use minimumRasterBrightness instead."))); /** Increase or reduce the contrast of the image. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterContrast; +@property (nonatomic, null_resettable) NSExpression *rasterContrast; /** The transition affecting any changes to this layer’s `rasterContrast` property. @@ -141,40 +149,46 @@ MGL_EXPORT This property is measured in milliseconds. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `300`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `300`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterFadeDuration; +@property (nonatomic, null_resettable) NSExpression *rasterFadeDuration; /** Rotates hues around the color wheel. This property is measured in degrees. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-hue-rotate"><code>raster-hue-rotate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotation; +@property (nonatomic, null_resettable) NSExpression *rasterHueRotation; /** The transition affecting any changes to this layer’s `rasterHueRotation` property. @@ -183,23 +197,26 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition rasterHueRotationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterHueRotate __attribute__((unavailable("Use rasterHueRotation instead."))); +@property (nonatomic, null_resettable) NSExpression *rasterHueRotate __attribute__((unavailable("Use rasterHueRotation instead."))); /** The opacity at which the image will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterOpacity; +@property (nonatomic, null_resettable) NSExpression *rasterOpacity; /** The transition affecting any changes to this layer’s `rasterOpacity` property. @@ -211,18 +228,21 @@ MGL_EXPORT /** Increase or reduce the saturation of the image. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *rasterSaturation; +@property (nonatomic, null_resettable) NSExpression *rasterSaturation; /** The transition affecting any changes to this layer’s `rasterSaturation` property. diff --git a/platform/darwin/src/MGLRasterStyleLayer.mm b/platform/darwin/src/MGLRasterStyleLayer.mm index 277b2b82a6..94a58409de 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.mm +++ b/platform/darwin/src/MGLRasterStyleLayer.mm @@ -39,21 +39,21 @@ #pragma mark - Accessing the Paint Attributes -- (void)setMaximumRasterBrightness:(MGLStyleValue<NSNumber *> *)maximumRasterBrightness { +- (void)setMaximumRasterBrightness:(NSExpression *)maximumRasterBrightness { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumRasterBrightness); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(maximumRasterBrightness); self.rawLayer->setRasterBrightnessMax(mbglValue); } -- (MGLStyleValue<NSNumber *> *)maximumRasterBrightness { +- (NSExpression *)maximumRasterBrightness { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterBrightnessMax(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterBrightnessMax()); + propertyValue = self.rawLayer->getDefaultRasterBrightnessMax(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setMaximumRasterBrightnessTransition:(MGLTransition )transition { @@ -74,28 +74,28 @@ return transition; } -- (void)setRasterBrightnessMax:(MGLStyleValue<NSNumber *> *)rasterBrightnessMax { +- (void)setRasterBrightnessMax:(NSExpression *)rasterBrightnessMax { } -- (MGLStyleValue<NSNumber *> *)rasterBrightnessMax { +- (NSExpression *)rasterBrightnessMax { return self.maximumRasterBrightness; } -- (void)setMinimumRasterBrightness:(MGLStyleValue<NSNumber *> *)minimumRasterBrightness { +- (void)setMinimumRasterBrightness:(NSExpression *)minimumRasterBrightness { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(minimumRasterBrightness); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(minimumRasterBrightness); self.rawLayer->setRasterBrightnessMin(mbglValue); } -- (MGLStyleValue<NSNumber *> *)minimumRasterBrightness { +- (NSExpression *)minimumRasterBrightness { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterBrightnessMin(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterBrightnessMin()); + propertyValue = self.rawLayer->getDefaultRasterBrightnessMin(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setMinimumRasterBrightnessTransition:(MGLTransition )transition { @@ -116,28 +116,28 @@ return transition; } -- (void)setRasterBrightnessMin:(MGLStyleValue<NSNumber *> *)rasterBrightnessMin { +- (void)setRasterBrightnessMin:(NSExpression *)rasterBrightnessMin { } -- (MGLStyleValue<NSNumber *> *)rasterBrightnessMin { +- (NSExpression *)rasterBrightnessMin { return self.minimumRasterBrightness; } -- (void)setRasterContrast:(MGLStyleValue<NSNumber *> *)rasterContrast { +- (void)setRasterContrast:(NSExpression *)rasterContrast { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterContrast); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterContrast); self.rawLayer->setRasterContrast(mbglValue); } -- (MGLStyleValue<NSNumber *> *)rasterContrast { +- (NSExpression *)rasterContrast { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterContrast(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterContrast()); + propertyValue = self.rawLayer->getDefaultRasterContrast(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setRasterContrastTransition:(MGLTransition )transition { @@ -158,38 +158,38 @@ return transition; } -- (void)setRasterFadeDuration:(MGLStyleValue<NSNumber *> *)rasterFadeDuration { +- (void)setRasterFadeDuration:(NSExpression *)rasterFadeDuration { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterFadeDuration); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterFadeDuration); self.rawLayer->setRasterFadeDuration(mbglValue); } -- (MGLStyleValue<NSNumber *> *)rasterFadeDuration { +- (NSExpression *)rasterFadeDuration { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterFadeDuration(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterFadeDuration()); + propertyValue = self.rawLayer->getDefaultRasterFadeDuration(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setRasterHueRotation:(MGLStyleValue<NSNumber *> *)rasterHueRotation { +- (void)setRasterHueRotation:(NSExpression *)rasterHueRotation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterHueRotation); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterHueRotation); self.rawLayer->setRasterHueRotate(mbglValue); } -- (MGLStyleValue<NSNumber *> *)rasterHueRotation { +- (NSExpression *)rasterHueRotation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterHueRotate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterHueRotate()); + propertyValue = self.rawLayer->getDefaultRasterHueRotate(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setRasterHueRotationTransition:(MGLTransition )transition { @@ -210,28 +210,28 @@ return transition; } -- (void)setRasterHueRotate:(MGLStyleValue<NSNumber *> *)rasterHueRotate { +- (void)setRasterHueRotate:(NSExpression *)rasterHueRotate { } -- (MGLStyleValue<NSNumber *> *)rasterHueRotate { +- (NSExpression *)rasterHueRotate { return self.rasterHueRotation; } -- (void)setRasterOpacity:(MGLStyleValue<NSNumber *> *)rasterOpacity { +- (void)setRasterOpacity:(NSExpression *)rasterOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterOpacity); self.rawLayer->setRasterOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)rasterOpacity { +- (NSExpression *)rasterOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterOpacity()); + propertyValue = self.rawLayer->getDefaultRasterOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setRasterOpacityTransition:(MGLTransition )transition { @@ -252,21 +252,21 @@ return transition; } -- (void)setRasterSaturation:(MGLStyleValue<NSNumber *> *)rasterSaturation { +- (void)setRasterSaturation:(NSExpression *)rasterSaturation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(rasterSaturation); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(rasterSaturation); self.rawLayer->setRasterSaturation(mbglValue); } -- (MGLStyleValue<NSNumber *> *)rasterSaturation { +- (NSExpression *)rasterSaturation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getRasterSaturation(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultRasterSaturation()); + propertyValue = self.rawLayer->getDefaultRasterSaturation(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setRasterSaturationTransition:(MGLTransition )transition { diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 5221e838f8..fefd8fcf37 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -677,70 +677,31 @@ static NSURL *MGLStyleURL_trafficNight; return localizedString; }; - if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { - NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue]; + if (layer.text.expressionType == NSConstantValueExpressionType) { + NSString *textField = layer.text.constantValue; NSString *localizingString = stringByLocalizingString(textField); if (![textField isEqualToString:localizingString]) { MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField updatedTextField:localizingString]; [self.localizedLayersByIdentifier setObject:@{ textField : textLanguage } forKey:layer.identifier]; - layer.text = [MGLStyleValue<NSString *> valueWithRawValue:localizingString]; + layer.text = [NSExpression expressionForConstantValue:localizingString]; } } - else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text; - NSMutableDictionary *stops = function.stops.mutableCopy; - NSMutableDictionary *cameraStops = [NSMutableDictionary dictionary]; - [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) { - NSString *textField = stop.rawValue; - NSString *localizingString = stringByLocalizingString(textField); - if (![textField isEqualToString:localizingString]) { - MGLTextLanguage *textLanguage = [[MGLTextLanguage alloc] initWithTextLanguage:textField - updatedTextField:localizingString]; - [cameraStops setObject:textLanguage forKey:zoomLevel]; - stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:localizingString]; - } - - }]; - if (cameraStops.count > 0) { - [self.localizedLayersByIdentifier setObject:cameraStops forKey:layer.identifier]; - } - function.stops = stops; - layer.text = function; - } } } else { [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) { MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier]; - if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) { - NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue]; + if (layer.text.expressionType == NSConstantValueExpressionType) { + NSString *textField = layer.text.constantValue; [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) { if ([textLanguage.updatedTextField isEqualToString:textField]) { - layer.text = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField]; + layer.text = [NSExpression expressionForConstantValue:textLanguage.originalTextField]; } }]; } - else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text; - NSMutableDictionary *stops = function.stops.mutableCopy; - [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *zoomKey, MGLTextLanguage *textLanguage, BOOL *done) { - if ([zoomKey isKindOfClass:[NSNumber class]]) { - NSNumber *zoomLevel = (NSNumber*)zoomKey; - MGLConstantStyleValue<NSString *> *stop = [stops objectForKey:zoomLevel]; - NSString *textField = stop.rawValue; - if ([textLanguage.updatedTextField isEqualToString:textField]) { - stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:textLanguage.originalTextField]; - } - } - }]; - - function.stops = stops; - layer.text = function; - } - }]; self.localizedLayersByIdentifier = [NSMutableDictionary dictionary]; diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs index df42621c6d..db16df3de5 100644 --- a/platform/darwin/src/MGLStyleLayer.h.ejs +++ b/platform/darwin/src/MGLStyleLayer.h.ejs @@ -9,7 +9,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGL<%- (type === 'background' ? '' : (type === 'raster' ? 'Foreground' : @@ -120,10 +119,10 @@ which it is added. /** <%- propertyDoc(property.name, property, type, 'layout').wrap(80, 1) %> */ -@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>; +@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>; <% if (property.original) { %> -@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); +@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); <% } -%> <% } -%> @@ -135,7 +134,7 @@ which it is added. /** <%- propertyDoc(property.name, property, type, 'paint').wrap(80, 1) %> */ -@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(property.name) %>; +@property (nonatomic<% if (!property.required) { %>, null_resettable<% } if (property.getter) { %>, getter=<%- objCGetter(property) -%><% } %>) NSExpression *<%- camelizeWithLeadingLowercase(property.name) %>; <% if (property["transition"]) { -%> /** @@ -147,7 +146,7 @@ which it is added. <% } -%> <% if (property.original) { -%> -@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); +@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) NSExpression *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead."))); <% } -%> <% } -%> diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index da67cbd633..8101c1f463 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -118,52 +118,32 @@ namespace mbgl { #pragma mark - Accessing the Layout Attributes <% for (const property of layoutProperties) { -%> -- (void)set<%- camelize(property.name) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCName(property) %> { +- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); <% if (property["property-function"]) { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenPropertyValue(<%- objCName(property) %>); + auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } else { -%> -<% if (property.type == "enum") { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toEnumPropertyValue(<%- objCName(property) %>); -<% } else if (property.function == "piecewise-constant") { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>); -<% } else { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(<%- objCName(property) %>); -<% } -%> + auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } -%> self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue); } -- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCGetter(property) %> { +- (NSExpression *)<%- objCGetter(property) %> { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>(); -<% if (property["property-function"]) { -%> if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); + propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>(); } - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(propertyValue); -<% } else { -%> -<% if (property.type == "enum") { -%> - if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); - } - return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue); -<% } else { -%> - if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); - } - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue); -<% } -%> -<% } -%> + return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue); } <% if (property.original) { -%> -- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { +- (void)set<%- camelize(originalPropertyName(property)) %>:(NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { } -- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { +- (NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { return self.<%- objCGetter(property) %>; } @@ -174,45 +154,25 @@ namespace mbgl { #pragma mark - Accessing the Paint Attributes <% for (const property of paintProperties) { -%> -- (void)set<%- camelize(property.name) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCName(property) %> { +- (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); <% if (property["property-function"]) { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenPropertyValue(<%- objCName(property) %>); + auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } else { -%> -<% if (property.type == "enum") { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toEnumPropertyValue(<%- objCName(property) %>); -<% } else if (property.function == "piecewise-constant") { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue(<%- objCName(property) %>); -<% } else { -%> - auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(<%- objCName(property) %>); -<% } -%> + auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } -%> self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue); } -- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- objCGetter(property) %> { +- (NSExpression *)<%- objCGetter(property) %> { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->get<%- camelize(originalPropertyName(property)) %>(); -<% if (property["property-function"]) { -%> if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); + propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>(); } - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toDataDrivenStyleValue(propertyValue); -<% } else { -%> -<% if (property.type == "enum") { -%> - if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); - } - return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue); -<% } else { -%> - if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>()); - } - return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(propertyValue); -<% } -%> -<% } -%> + return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue); } <% if (property["transition"]) { -%> @@ -236,10 +196,10 @@ namespace mbgl { <% } -%> <% if (property.original) { -%> -- (void)set<%- camelize(originalPropertyName(property)) %>:(MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { +- (void)set<%- camelize(originalPropertyName(property)) %>:(NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { } -- (MGLStyleValue<<%- propertyType(property, true) %>> *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { +- (NSExpression *)<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> { return self.<%- objCGetter(property) %>; } <% } -%> diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h index 9c9b1dc4d1..f6e9c51729 100644 --- a/platform/darwin/src/MGLStyleValue.h +++ b/platform/darwin/src/MGLStyleValue.h @@ -6,485 +6,43 @@ NS_ASSUME_NONNULL_BEGIN -/** - Options for `MGLStyleFunction` objects. - */ -typedef NSString *MGLStyleFunctionOption NS_STRING_ENUM; +typedef NSString *MGLStyleFunctionOption NS_STRING_ENUM NS_UNAVAILABLE; -/** - An `NSNumber` object containing an integer that determines the style function's - exponential interpolation base. - - The exponential interpolation base controls the rate at which the function’s - output values increase. A value of 1 causes the function to increase linearly - based on zoom level or attribute value. A higher exponential interpolation base - causes the function’s output values to vary exponentially, increasing more rapidly - towards the high end of the function’s range. The default value of this property - is 1, for a linear curve. +extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolateWithCurveType:parameters:stops: function with a curve type of “exponential” and a non-nil parameter."))); - This attribute corresponds to the - <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#function-base"><code>base</code></a> - function property in the Mapbox Style Specification. +extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue __attribute__((unavailable("Use +[NSExpression expressionForConditional:trueExpression:falseExpression:] instead."))); - This option only applies to functions that use an - `MGLInterpolationModeExponential` interpolation mode that are assigned to - interpolatable style layer properties. - */ -extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase; - -/** - An `MGLConstantStyleValue` object that specifies a default value that a style - function can use when it can't otherwise determine a value. - - A default value can be used to set the value of a style layer property that - is not otherwise set by a function. For example, a source function with a - `MGLInterpolationModeCategorical` interpolation mode with stops that specify - color values to use based on a feature's attributes would set any feature - that does not have attributes that match the stop key values to this - default value. - - This option only applies to `MGLSourceStyleFunction` and - `MGLCompositeStyleFunction` functions. - */ -extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue; - -/** - The modes used to interpolate property values between map zoom level changes - or over a range of feature attribute values. - */ typedef NS_ENUM(NSUInteger, MGLInterpolationMode) { - /** - Values between two stops are interpolated linearly, or exponentially based on - the `MGLStyleFunctionOptionInterpolationBase`. A higher interpolation base - causes the function’s output values to vary exponentially, increasing more rapidly - towards the high end of the function’s range. The default interpolation base of 1 - creates a linear interpolation. Use exponential interpolation mode to show values - relative to stop keys. - */ - MGLInterpolationModeExponential = 0, - /** - Values between two stops are not interpolated. Instead, properties are set - to the value of the stop just less than the function input. Use interval - interpolation mode to show values that fall within a range. - */ - MGLInterpolationModeInterval, - /** - Values between two stops are not interpolated. Instead, properties are set - to the value of the stop equal to the function input's key value. Use - categorical interpolation mode to show values that fit into categories. - */ - MGLInterpolationModeCategorical, - /** - Values between two stops are not interpolated. Instead, for any given feature, the - style value matches a value in that feature’s attributes dictionary. Use identity - interpolation mode to show attribute values that can be used as style values. - */ - MGLInterpolationModeIdentity -}; - -/** - An `MGLStyleValue` object is a generic container for a style attribute value. - The layout and paint attribute properties of `MGLStyleLayer` can be set to - `MGLStyleValue` objects. - - The `MGLStyleValue` class itself represents a class cluster. Under the hood, a - particular `MGLStyleValue` object may be either an `MGLConstantStyleValue` to - represent a constant value or one of the concrete subclasses of - `MGLStyleFunction` to represent a value function. Do not initialize an - `MGLStyleValue` object directly; instead, use one of the class factory methods - to create an `MGLStyleValue` object. + MGLInterpolationModeExponential __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolateWithCurveType:parameters:stops: function with a curve type of “exponential”."))) = 0, + MGLInterpolationModeInterval __attribute__((unavailable("Use NSExpression instead, calling the mgl_stepWithMinimum:stops: function."))), + MGLInterpolationModeCategorical __attribute__((unavailable("Use NSExpression instead."))), + MGLInterpolationModeIdentity __attribute__((unavailable("Use +[NSExpression expressionForKeyPath:] instead."))) +} __attribute__((unavailable("Use NSExpression instead."))); - The `MGLStyleValue` class takes a generic parameter `T` that indicates the - Foundation class being wrapped by this class. Common values for `T` include: - - <ul> - <li><code>NSNumber</code> (for Boolean values and floating-point numbers)</li> - <li><code>NSValue</code> (for <code>CGVector</code>, <code>NSEdgeInsets</code>, <code>UIEdgeInsets</code>, and enumerations)</li> - <li><code>NSString</code></li> - <li><code>NSColor</code> or <code>UIColor</code></li> - <li><code>NSArray</code></li> - </ul> - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use NSExpression instead."))) @interface MGLStyleValue<T> : NSObject - -#pragma mark Creating a Style Value - -/** - Creates and returns an `MGLConstantStyleValue` object containing a raw value. - - @param rawValue The constant value contained by the object. - @return An `MGLConstantStyleValue` object containing `rawValue`, which is - treated as a constant value. - */ -+ (instancetype)valueWithRawValue:(T)rawValue; - -#pragma mark Function values - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a linear camera - function with one or more stops. - - @param stops A dictionary associating zoom levels with style values. - @return An `MGLCameraStyleFunction` object with the given stops. - */ -+ (instancetype)valueWithStops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]"))); - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a camera - function with an exponential interpolation base and one or more stops. - - @param interpolationBase The exponential base of the interpolation curve. - @param stops A dictionary associating zoom levels with style values. - @return An `MGLCameraStyleFunction` object with the given interpolation base and stops. - */ -+ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]"))); - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a camera function - with one or more stops. - - @param interpolationMode The mode used to interpolate property values between - map zoom level changes. - @param cameraStops A dictionary associating zoom levels with style values. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLCameraStyleFunction` object with the given interpolation mode, - camera stops, and options. - */ -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode cameraStops:(NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)cameraStops options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - -/** - Creates and returns an `MGLSourceStyleFunction` object representing a source function. - - @param interpolationMode The mode used to interpolate property values over a - range of feature attribute values. - @param sourceStops A dictionary associating feature attributes with style values. - @param attributeName Specifies the feature attribute to take as the function - input. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLSourceStyleFunction` object with the given interpolation mode, - source stops, attribute name, and options. - */ -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode sourceStops:(nullable NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)sourceStops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - -/** - Creates and returns an `MGLCompositeStyleFunction` object representing a composite - function. - - @param interpolationMode The mode used to interpolate property values over a - range of feature attribute values for each outer zoom level. - @param compositeStops A dictionary associating feature attributes with style - values. - @param attributeName Specifies the feature attribute to take as the function - input. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLCompositeStyleFunction` object with the given interpolation mode, - composite stops, attribute name, and options. - */ -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode compositeStops:(NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *)compositeStops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - @end -/** - An `MGLConstantStyleValue` object is a generic container for a style attribute - value that remains constant as the zoom level changes. The layout and paint - attribute properties of `MGLStyleLayer` objects can be set to - `MGLConstantStyleValue` objects. - - The `MGLConstantStyleValue` class takes a generic parameter `T` that indicates - the Foundation class being wrapped by this class. - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use +[NSExpression expressionForConstantValue:] instead."))) @interface MGLConstantStyleValue<T> : MGLStyleValue<T> - -#pragma mark Creating a Style Constant Value - -/** - Creates and returns an `MGLConstantStyleValue` object containing a raw value. - - @param rawValue The constant value contained by the object. - @return An `MGLConstantStyleValue` object containing `rawValue`, which is - treated as a constant value. - */ -+ (instancetype)valueWithRawValue:(T)rawValue; - -#pragma mark Initializing a Style Constant Value - -- (instancetype)init NS_UNAVAILABLE; - -/** - Returns an `MGLConstantStyleValue` object containing a raw value. - - @param rawValue The value contained by the receiver. - @return An `MGLConstantStyleValue` object containing `rawValue`. - */ -- (instancetype)initWithRawValue:(T)rawValue NS_DESIGNATED_INITIALIZER; - -#pragma mark Accessing the Underlying Value - -/** - The raw value contained by the receiver. - */ -@property (nonatomic) T rawValue; - @end @compatibility_alias MGLStyleConstantValue MGLConstantStyleValue; -/** - An `MGLStyleFunction` is a is an abstract superclass for functions that are - defined by an `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, or - `MGLCompositeStyleFunction` object. - - Create instances of `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, and - `MGLCompositeStyleFunction` in order to use `MGLStyleFunction`'s methods. Do - not create instances of `MGLStyleFunction` directly, and do not create your - own subclasses of this class. - - The `MGLStyleFunction` class takes a generic parameter `T` that indicates the - Foundation class being wrapped by this class. - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, calling the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function."))) @interface MGLStyleFunction<T> : MGLStyleValue<T> - -#pragma mark Creating a Style Function - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a camera - function with a linear interpolation curve. - - @note Do not create function instances using this method unless it is required - for backwards compatiblity with your application code. - - @param stops A dictionary associating zoom levels with style values. - @return An `MGLCameraStyleFunction` object with the given stops. - */ -+ (instancetype)functionWithStops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]"))); - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a camera - function with an interpolation curve controlled by the provided interpolation - base. - - @note Do not create function instances using this method unless it is required - for backwards compatiblity with your application code. - - @param interpolationBase The exponential base of the interpolation curve. - @param stops A dictionary associating zoom levels with style values. - @return An `MGLCameraStyleFunction` object with the given interpolation base and stops. - */ -+ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]"))); - -#pragma mark Initializing a Style Function - -/** - Returns an `MGLStyleFunction` object representing a camera function. If the - function is set as a style layer property value, it will be interpreted - as a camera function with an interpolation curve controlled by the provided - interpolation base. - - @note Do not create instances of `MGLStyleFunction` unless it is required for - backwards compatiblity with your application code. You should create and use - instances of `MGLCameraStyleFunction` to specify how properties will - be visualized at different zoom levels. - - @param interpolationBase The exponential base of the interpolation curve. - @param stops A dictionary associating zoom levels with style values. - @return An `MGLStyleFunction` object with the given interpolation base and stops. - */ -- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NS_DICTIONARY_OF(NSNumber *, MGLStyleValue<T> *) *)stops __attribute__((deprecated("Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]"))); - -#pragma mark Accessing the Parameters of a Function - -/** - The modes used to interpolate property values between map zoom level changes or - over a range of feature attribute values. - */ -@property (nonatomic) MGLInterpolationMode interpolationMode; - -/** - A dictionary associating zoom levels with style values. - */ -@property (nonatomic, copy, nullable) NSDictionary *stops; - -/** - The exponential interpolation base of the function’s interpolation curve. - - @note This property specifies the exponential base of the interpolation curve - of `MGLCameraStyleFunction` and `MGLSourceStyleFunction` functions that use - a `MGLInterpolationModeExponential` `interpolationMode`. Otherwise, it is - ignored. - */ -@property (nonatomic) CGFloat interpolationBase; - @end -/** - An `MGLCameraStyleFunction` is a value function defining a style value that changes - as the zoom level changes. The layout and paint attribute properties of an - `MGLStyleLayer` object can be set to `MGLCameraStyleFunction` objects. Use a camera - function to create the illusion of depth and control data density. - - The `MGLCameraStyleFunction` class takes a generic parameter `T` that indicates the - Foundation class being wrapped by this class. - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to the $zoomLevel variable."))) @interface MGLCameraStyleFunction<T> : MGLStyleFunction<T> - -#pragma mark Creating a Camera Function - -/** - Creates and returns an `MGLCameraStyleFunction` object representing a camera - function with one or more stops. - - @param interpolationMode The mode used to interpolate property values between - map zoom level changes. - @param stops A dictionary associating zoom levels with style values. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLCameraStyleFunction` object with the given interpolation mode, - camera stops, and options. - */ -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)stops options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - -#pragma mark Accessing the Parameters of a Camera Function - -/** - A dictionary associating zoom levels with style values. - - Each of the function’s stops is represented by one key-value pair in the - dictionary. Each key in the dictionary is an `NSNumber` object containing a - floating-point zoom level. Each value in the dictionary is an `MGLStyleValue` - object containing the value of the style attribute when the map is at the - associated zoom level. An `MGLStyleFunction` object may not be used recursively - as a stop value. - */ -@property (nonatomic, copy) NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *stops; - @end -/** - An `MGLSourceStyleFunction` is a value function defining a style value that - changes with its properties. The layout and paint attribute properties of an - `MGLStyleLayer` object can be set to `MGLSourceStyleFunction` objects. - Use source functions to visually differentate types of features within the same - layer or create data visualizations. - - The `MGLSourceStyleFunction` class takes a generic parameter `T` that indicates the - Foundation class being wrapped by this class. - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to a key path expression."))) @interface MGLSourceStyleFunction<T> : MGLStyleFunction<T> - -#pragma mark Creating a Source Function - -/** - Creates and returns an `MGLSourceStyleFunction` object representing a source - function. - - @param interpolationMode The mode used to interpolate property values over a - range of feature attribute values. - @param stops A dictionary associating feature attributes with style values. - @param attributeName Specifies the feature attribute to take as the function - input. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLSourceStyleFunction` object with the given interpolation mode, - source stops, attribute name, and options. -*/ -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(nullable NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *)stops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - -#pragma mark Accessing the Parameters of a Source Function - -/** - A string that specifies the feature attribute key whose value be used as the function - input. -*/ -@property (nonatomic, copy) NSString *attributeName; - -/** - A dictionary associating attribute values with style values. - - Each of the function’s stops is represented by one key-value pair in the - dictionary. Each key in the dictionary is an object representing a feature - attribute key or interpolation stop. Each value in the dictionary is an - `MGLStyleValue` object containing the value to use when the function is given - the associated attribute key. An `MGLStyleFunction` object may not be used - recursively as a stop value. - */ -@property (nonatomic, copy, nullable) NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *stops; - -/** - An `MGLStyleValue` object containing the default value to use when there is - no input to provide to the function. - */ -@property (nonatomic, nullable) MGLStyleValue<T> *defaultValue; - @end -/** - An `MGLCompositeStyleFunction` is a value function defining a style value that - changes with the feature attributes at each map zoom level. The layout and paint - attribute properties of an `MGLStyleLayer` object can be set to - `MGLCompositeStyleFunction` objects. Use composite functions to allow the - appearance of a map feature to change with both its attributes and the map zoom - level. - - The `MGLCompositeStyleFunction` class takes a generic parameter `T` that indicates the - Foundation class being wrapped by this class. - */ -MGL_EXPORT +MGL_EXPORT __attribute__((unavailable("Use a NSExpression instead with nested mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function calls."))) @interface MGLCompositeStyleFunction<T> : MGLStyleFunction<T> - -#pragma mark Creating a Composite Function - -/** - Creates and returns an `MGLCompositeStyleFunction` object representing a composite - function. - - @param interpolationMode The mode used to interpolate property values over a - range of feature attribute values for each outer zoom level. - @param stops A dictionary associating feature attributes with style values. - @param attributeName Specifies the feature attribute to take as the function - input. - @param options A dictionary containing `MGLStyleFunctionOption` values that - specify how a function is applied. - @return An `MGLCompositeStyleFunction` object with the given interpolation mode, - composite stops, attribute name, and options. - */ -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *)stops attributeName:(NSString *)attributeName options:(nullable NS_DICTIONARY_OF(MGLStyleFunctionOption, id) *)options; - -#pragma mark Accessing the Parameters of a Composite Function - -/** - A string that specifies the feature attribute key whose value be used as the function - input. - */ -@property (nonatomic, copy) NSString *attributeName; - -/** - A dictionary associating attribute values with style values. - - Each of the function’s stops is represented by one key-value pair in the - dictionary. Each key in the dictionary is an `NSNumber` object containing a - floating-point zoom level. Each value in the dictionary is an inner nested - dictionary. Each key in the nested dictionary is an object representing a feature - attribute key or interpolation stop. Each value in the nested dictionary is an - `MGLStyleValue` object containing the value to use when the function is given - the associated attribute key. An `MGLStyleFunction` object may not be used - recursively as a value inside the nested dictionary. - */ -@property (nonatomic, copy) NS_DICTIONARY_OF(id, NS_DICTIONARY_OF(id, MGLStyleValue<T> *) *) *stops; - -/** - An `MGLStyleValue` object containing the default value to use when there is - no input to provide to the function. - */ -@property (nonatomic, nullable) MGLStyleValue<T> *defaultValue; - @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm index 4dd6b550d8..74e1926f79 100644 --- a/platform/darwin/src/MGLStyleValue.mm +++ b/platform/darwin/src/MGLStyleValue.mm @@ -1,308 +1,121 @@ #import "MGLStyleValue_Private.h" +#include <mbgl/style/expression/expression.hpp> + const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase = @"MGLStyleFunctionOptionInterpolationBase"; const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunctionOptionDefaultValue"; -@implementation MGLStyleValue - -+ (instancetype)valueWithRawValue:(id)rawValue { - return [MGLConstantStyleValue valueWithRawValue:rawValue]; -} - -+ (instancetype)valueWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}]; -} - -+ (instancetype)valueWithStops:(NSDictionary *)stops { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:nil]; -} - -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode cameraStops:(NSDictionary *)cameraStops options:(NSDictionary *)options { - return [MGLCameraStyleFunction functionWithInterpolationMode:interpolationMode stops:cameraStops options:options]; -} - -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode sourceStops:(NSDictionary *)sourceStops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - return [MGLSourceStyleFunction functionWithInterpolationMode:interpolationMode stops:sourceStops attributeName:attributeName options:options]; -} - -+ (instancetype)valueWithInterpolationMode:(MGLInterpolationMode)interpolationMode compositeStops:(NSDictionary *)compositeStops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - return [MGLCompositeStyleFunction functionWithInterpolationMode:interpolationMode stops:compositeStops attributeName:attributeName options:options]; -} - -@end - -@implementation MGLConstantStyleValue - -+ (instancetype)valueWithRawValue:(id)rawValue { - return [[self alloc] initWithRawValue:rawValue]; +id MGLJSONObjectFromMBGLValue(const mbgl::style::expression::Value &value) { + return value.match([](const mbgl::NullValue) -> id { + return [NSNull null]; + }, [](const bool value) { + return @(value); + }, [](const float value) { + return @(value); + }, [](const int64_t value) { + return @(value); + }, [](const double value) { + return @(value); + }, [](const std::string &value) { + return @(value.c_str()); + }, [](const mbgl::Color &value) { + return [MGLColor mgl_colorWithColor:value]; + }, [](const mbgl::style::Position &value) { + std::array<float, 3> spherical = value.getSpherical(); + MGLSphericalPosition position = MGLSphericalPositionMake(spherical[0], spherical[1], spherical[2]); + return [NSValue valueWithMGLSphericalPosition:position]; + }, [&](const std::vector<mbgl::style::expression::Value> &vector) { + NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()]; + for (auto value : vector) { + [array addObject:MGLJSONObjectFromMBGLValue(value)]; + } + return @[@"literal", array]; + }, [&](const std::unordered_map<std::string, mbgl::style::expression::Value> &map) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()]; + for (auto &item : map) { + dictionary[@(item.first.c_str())] = MGLJSONObjectFromMBGLValue(item.second); + } + return @[@"literal", dictionary]; + }, [](const auto &) -> id { + return nil; + }); } -- (instancetype)initWithRawValue:(id)rawValue { - if (self = [super init]) { - _rawValue = rawValue; +id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) { + using namespace mbgl::style::expression; + if (auto literalExpression = dynamic_cast<const Literal *>(&mbglExpression)) { + auto result = literalExpression->evaluate({ nullptr }); + return result ? MGLJSONObjectFromMBGLValue(*result) : nil; } - return self; -} - -- (NSString *)description { - return [self.rawValue description]; -} - -- (NSString *)debugDescription { - return [self.rawValue debugDescription]; -} - -- (BOOL)isEqual:(MGLConstantStyleValue *)other { - return [other isKindOfClass:[self class]] && [other.rawValue isEqual:self.rawValue]; -} - -- (NSUInteger)hash { - return [self.rawValue hash]; -} - -@end - -@implementation MGLStyleFunction - -+ (instancetype)functionWithStops:(NSDictionary *)stops { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:nil]; -} - -+ (instancetype)functionWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}]; -} - -- (instancetype)init { - if (self = [super init]) { - self.interpolationBase = 1.0; - self.stops = @{}; + if (auto assertExpression = dynamic_cast<const ArrayAssertion *>(&mbglExpression)) { + NSMutableArray *inputs = [NSMutableArray array]; + assertExpression->eachChild([&](const Expression &child) { + [inputs addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + return @[@"literal", inputs.lastObject]; } - return self; -} - -- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - if (self = [super init]) { - self.interpolationBase = interpolationBase; - self.stops = stops; + if (auto assertExpression = dynamic_cast<const Assertion *>(&mbglExpression)) { + NSMutableArray *inputs = [NSMutableArray array]; + assertExpression->eachChild([&](const Expression &child) { + [inputs addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + return inputs.firstObject; } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, \ - stops = %@, \ - interpolationBase = %f>", - NSStringFromClass([self class]), (void *)self, - self.stops, - self.interpolationBase]; -} - -- (BOOL)isEqual:(MGLStyleFunction *)other { - return ([other isKindOfClass:[self class]] - && [other.stops isEqualToDictionary:self.stops] - && other.interpolationBase == self.interpolationBase); -} - -- (NSUInteger)hash { - return self.stops.hash + @(self.interpolationBase).hash; -} - -@end - -@implementation MGLCameraStyleFunction - -@dynamic stops; - -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops options:(NSDictionary *)options { - return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops options:options]; -} - -- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}]; -} - -- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops options:(NSDictionary *)options { - if (![stops count]) { - [NSException raise:NSInvalidArgumentException - format:@"Camera functions must have at least one stop."]; - return {}; + if (auto compoundExpression = dynamic_cast<const CompoundExpressionBase *>(&mbglExpression)) { + const std::string name = compoundExpression->getName(); + mbgl::optional<std::size_t> parameterCount = compoundExpression->getParameterCount(); + NSMutableArray *expressionObject = parameterCount ? [NSMutableArray arrayWithCapacity:*parameterCount + 1] : [NSMutableArray array]; + [expressionObject addObject:@(name.c_str())]; + compoundExpression->eachChild([&](const Expression &child) { + [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + return expressionObject; } - - if (self = [super init]) { - self.interpolationMode = interpolationMode; - self.stops = stops; - - if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) { - if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) { - NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase]; - self.interpolationBase = [value floatValue]; - } else { - [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."]; - } + if (auto stepExpression = dynamic_cast<const Step *>(&mbglExpression)) { + auto &input = stepExpression->getInput(); + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", MGLJSONObjectFromMBGLExpression(*input.get()), nil]; + stepExpression->eachStop([&](double stop, const Expression &child) { + [expressionObject addObject:@(stop)]; + [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + if ([expressionObject[2] isEqual:@(-INFINITY)]) { + [expressionObject removeObjectAtIndex:2]; } + return expressionObject; } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, \ - interpolationMode = %lu, \ - stops = %@, \ - interpolationBase = %f>", - NSStringFromClass([self class]), (void *)self, - (unsigned long)self.interpolationMode, - self.stops, - self.interpolationBase]; -} - -- (BOOL)isEqual:(MGLCameraStyleFunction *)other { - return ([other isKindOfClass:[self class]] - && other.interpolationMode == self.interpolationMode - && [other.stops isEqualToDictionary:self.stops] - && other.interpolationBase == self.interpolationBase); -} - -- (NSUInteger)hash { - return @(self.interpolationMode).hash + self.stops.hash + @(self.interpolationBase).hash; -} - -@end - -@implementation MGLSourceStyleFunction - -@dynamic stops; - -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops attributeName:attributeName options:options]; -} - -- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops attributeName:@"" options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}]; -} - -- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self = [super init]) { - self.interpolationMode = interpolationMode; - self.stops = stops; - _attributeName = attributeName; - - if ([options.allKeys containsObject:MGLStyleFunctionOptionDefaultValue]) { - if ([options[MGLStyleFunctionOptionDefaultValue] isKindOfClass:[MGLStyleValue class]]) { - MGLStyleValue *value = (MGLStyleValue *)options[MGLStyleFunctionOptionDefaultValue]; - _defaultValue = value; - } else { - [NSException raise:NSInvalidArgumentException format:@"Default value must be an MGLStyleValue"]; - } - } - - if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) { - if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) { - NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase]; - self.interpolationBase = [value floatValue]; - } else { - [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."]; - } + if (auto interpolateExpression = dynamic_cast<const InterpolateBase *>(&mbglExpression)) { + auto &interpolator = interpolateExpression->getInterpolator(); + auto &input = interpolateExpression->getInput(); + NSArray *interpolatorObject; + if (interpolator.is<ExponentialInterpolator>()) { + auto exponentialInterpolator = interpolator.get<ExponentialInterpolator>(); + interpolatorObject = exponentialInterpolator.base == 1 ? @[@"linear"] : @[@"exponential", @(exponentialInterpolator.base)]; + } else if (interpolator.is<CubicBezierInterpolator>()) { + auto cubicBezierInterpolator = interpolator.get<CubicBezierInterpolator>(); + auto bezier = cubicBezierInterpolator.ub; + interpolatorObject = @[ + @"cubic-bezier", + @(bezier.getP1().first), @(bezier.getP1().second), + @(bezier.getP2().first), @(bezier.getP2().second), + ]; + } else { + NSCAssert(NO, @"Unrecognized interpolator type."); } + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolatorObject, MGLJSONObjectFromMBGLExpression(*input.get()), nil]; + interpolateExpression->eachStop([&](double stop, const Expression &child) { + [expressionObject addObject:@(stop)]; + [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + return expressionObject; } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, \ - interpolationMode = %lu, \ - stops = %@, \ - attributeName = %@, \ - defaultValue = %@, \ - interpolationBase = %f>", - NSStringFromClass([self class]), - (void *)self, - (unsigned long)self.interpolationMode, - self.stops, - self.attributeName, - self.defaultValue, - self.interpolationBase]; -} - -- (BOOL)isEqual:(MGLSourceStyleFunction *)other { - return ([other isKindOfClass:[self class]] - && other.interpolationMode == self.interpolationMode - && ((self.stops && [other.stops isEqualToDictionary:self.stops]) || (!self.stops && !other.stops)) - && [other.attributeName isEqual:self.attributeName] - && ((self.defaultValue && [other.defaultValue isEqual:self.defaultValue]) || (!self.defaultValue && !other.defaultValue)) - && other.interpolationBase == self.interpolationBase); -} - -- (NSUInteger)hash { - return @(self.interpolationMode).hash + self.stops.hash + self.attributeName.hash + self.defaultValue.hash + @(self.interpolationBase).hash; -} - -@end - -@implementation MGLCompositeStyleFunction - -@dynamic stops; - -+ (instancetype)functionWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - return [[self alloc] initWithInterpolationMode:interpolationMode stops:stops attributeName:attributeName options:options]; -} - -- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops { - return [self initWithInterpolationMode:MGLInterpolationModeExponential stops:stops attributeName:@"" options:@{MGLStyleFunctionOptionInterpolationBase: @(interpolationBase)}]; -} - -- (instancetype)initWithInterpolationMode:(MGLInterpolationMode)interpolationMode stops:(NSDictionary *)stops attributeName:(NSString *)attributeName options:(NSDictionary *)options { - if (self = [super init]) { - self.interpolationMode = interpolationMode; - self.stops = stops; - _attributeName = attributeName; - - if ([options.allKeys containsObject:MGLStyleFunctionOptionDefaultValue]) { - if ([options[MGLStyleFunctionOptionDefaultValue] isKindOfClass:[MGLStyleValue class]]) { - MGLStyleValue *value = (MGLStyleValue *)options[MGLStyleFunctionOptionDefaultValue]; - _defaultValue = value; - } else { - [NSException raise:NSInvalidArgumentException format:@"Default value must be an MGLStyleValue"]; - } - } - - if ([options.allKeys containsObject:MGLStyleFunctionOptionInterpolationBase]) { - if ([options[MGLStyleFunctionOptionInterpolationBase] isKindOfClass:[NSNumber class]]) { - NSNumber *value = (NSNumber *)options[MGLStyleFunctionOptionInterpolationBase]; - self.interpolationBase = [value floatValue]; - } else { - [NSException raise:NSInvalidArgumentException format:@"Interpolation base must be an NSNumber that represents a CGFloat."]; - } - } + if (auto caseExpression = dynamic_cast<const Case *>(&mbglExpression)) { + NSMutableArray *expressionObject = [NSMutableArray arrayWithObject:@"case"]; + caseExpression->eachChild([&](const Expression &child) { + [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)]; + }); + return expressionObject; } - return self; -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, \ - interpolationMode = %lu, \ - stops = %@, \ - attributeName = %@, \ - defaultValue = %@, \ - interpolationBase = %f>", - NSStringFromClass([self class]), (void *)self, - (unsigned long)self.interpolationMode, - self.stops, - self.attributeName, - self.defaultValue, - self.interpolationBase]; -} - -- (BOOL)isEqual:(MGLCompositeStyleFunction *)other { - return ([other isKindOfClass:[self class]] - && other.interpolationMode == self.interpolationMode - && [other.stops isEqualToDictionary:self.stops] - && [other.attributeName isEqual:self.attributeName] - && ((self.defaultValue && [other.defaultValue isEqual:self.defaultValue]) || (!self.defaultValue && !other.defaultValue)) - && other.interpolationBase == self.interpolationBase); -} - -- (NSUInteger)hash { - return @(self.interpolationMode).hash + self.stops.hash + self.attributeName.hash + @(self.interpolationBase).hash; + NSCAssert(NO, @"Unrecognized expression type."); + return nil; } -@end diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 5914e0a2aa..ba4b413a3c 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -4,11 +4,13 @@ #import "NSValue+MGLStyleAttributeAdditions.h" #import "NSValue+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" #import "MGLTypes.h" #import "MGLConversion.h" +#include <mbgl/style/conversion/property_value.hpp> #include <mbgl/style/conversion/data_driven_property_value.hpp> -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/position.hpp> #import <mbgl/style/types.hpp> #import <mbgl/util/enum.hpp> @@ -23,166 +25,68 @@ #import "NSColor+MGLAdditions.h" #endif +namespace mbgl { + namespace style { + namespace expression { + class Expression; + } + } +} + +id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression); + template <typename MBGLType, typename ObjCType, typename MBGLElement = MBGLType, typename ObjCEnum = ObjCType> class MGLStyleValueTransformer { public: - - // Convert an mbgl property value into an mgl style value - MGLStyleValue<ObjCType> *toStyleValue(const mbgl::style::PropertyValue<MBGLType> &mbglValue) { - PropertyValueEvaluator evaluator; - return mbglValue.evaluate(evaluator); - } - - // Convert an mbgl data driven property value into an mgl style value - MGLStyleValue<ObjCType> *toDataDrivenStyleValue(const mbgl::style::DataDrivenPropertyValue<MBGLType> &mbglValue) { - PropertyValueEvaluator evaluator; + + /// Convert an mbgl property value into an mgl style value + NSExpression *toExpression(const mbgl::style::PropertyValue<MBGLType> &mbglValue) { + PropertyExpressionEvaluator evaluator; return mbglValue.evaluate(evaluator); } - - // Convert an mbgl property value containing an enum into an mgl style value - template <typename MBGLEnum = MBGLType, - class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type, - typename MGLEnum = ObjCEnum, - class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type> - MGLStyleValue<ObjCType> *toEnumStyleValue(const mbgl::style::PropertyValue<MBGLEnum> &mbglValue) { - EnumPropertyValueEvaluator<MBGLEnum, ObjCEnum> evaluator; + + /// Convert an mbgl data driven property value into an mgl style value + template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum> + NSExpression *toExpression(const mbgl::style::DataDrivenPropertyValue<MBGLEnum> &mbglValue) { + PropertyExpressionEvaluator evaluator; return mbglValue.evaluate(evaluator); } - - // Convert an mgl style value into a non interpolatable (camera with interval stops) mbgl property value - mbgl::style::PropertyValue<MBGLType> toPropertyValue(MGLStyleValue<ObjCType> *value) { - if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) { - [NSException raise:NSInvalidArgumentException - format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."]; - return {}; - } - - if ([value isKindOfClass:[MGLConstantStyleValue class]]) { - return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value); - } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction<ObjCType> *cameraStyleFunction = (MGLCameraStyleFunction<ObjCType> *)value; - // Intentionally ignore the stop type set by the developer becuase non interpolatable property values - // can only have interval stops. This also allows for backwards compatiblity when the developer uses - // a deprecated MGLStyleValue method (that used to create an MGLStyleFunction) to create a function - // for properties that are piecewise-constant (i.e. enum, bool, string) - return toMBGLIntervalCameraFunction(cameraStyleFunction); - } else if ([value isMemberOfClass:[MGLStyleFunction class]]) { - MGLStyleFunction<ObjCType> *styleFunction = (MGLStyleFunction<ObjCType> *)value; - return toMBGLIntervalCameraFunction(styleFunction); - } else if (value) { - [NSException raise:@"MGLAbstractClassException" format: - @"The style value %@ cannot be applied to the style. " - @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.", - NSStringFromClass([value class])]; - return {}; - } else { - return {}; - } - } - - // Convert an mgl style value into a non interpolatable (camera with exponential or interval stops) mbgl property value - mbgl::style::PropertyValue<MBGLType> toInterpolatablePropertyValue(MGLStyleValue<ObjCType> *value) { - if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) { - [NSException raise:NSInvalidArgumentException - format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."]; + + /** + Converts an NSExpression to an mbgl property value. + */ + template <typename MBGLValue> + MBGLValue toPropertyValue(NSExpression *expression) { + if (!expression) { return {}; } - - if ([value isKindOfClass:[MGLConstantStyleValue class]]) { - return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value); - } else if ([value isMemberOfClass:[MGLStyleFunction class]]) { - MGLStyleFunction<ObjCType> *styleFunction = (MGLStyleFunction<ObjCType> *)value; - return toMBGLExponentialCameraFunction(styleFunction); - } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction<ObjCType> *cameraStyleFunction = (MGLCameraStyleFunction<ObjCType> *)value; - switch (cameraStyleFunction.interpolationMode) { - case MGLInterpolationModeExponential: - return toMBGLExponentialCameraFunction(cameraStyleFunction); - break; - case MGLInterpolationModeInterval: - return toMBGLIntervalCameraFunction(cameraStyleFunction); - break; - default: - [NSException raise:NSInvalidArgumentException - format:@"A camera function must use either exponential or interval stops."]; - break; - } - return {}; - } else if (value) { - [NSException raise:@"MGLAbstractClassException" format: - @"The style value %@ cannot be applied to the style. " - @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.", - NSStringFromClass([value class])]; - return {}; - } else { - return {}; + + if (expression.expressionType == NSConstantValueExpressionType) { + MBGLType mbglValue; + getMBGLValue(expression.constantValue, mbglValue); + return mbglValue; } - } - - // Convert an mgl style value into a mbgl data driven property value - mbgl::style::DataDrivenPropertyValue<MBGLType> toDataDrivenPropertyValue(MGLStyleValue<ObjCType> *value) { - if ([value isKindOfClass:[MGLConstantStyleValue class]]) { - return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value); - } else if ([value isKindOfClass:[MGLStyleFunction class]]) { - mbgl::style::conversion::Error error; - auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>( - mbgl::style::conversion::makeConvertible(toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value)), error); - NSCAssert(result, @(error.message.c_str())); - return *result; - } else { - return {}; + if (expression.expressionType == NSAggregateExpressionType) { + MBGLType mbglValue; + getMBGLValue(expression.collection, mbglValue); + return mbglValue; } - } - - // Convert an mgl style value containing an enum into a mbgl property value containing an enum - template <typename MBGLEnum = MBGLType, - class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type, - typename MGLEnum = ObjCEnum, - class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type> - mbgl::style::PropertyValue<MBGLEnum> toEnumPropertyValue(MGLStyleValue<ObjCType> *value) { - if ([value isKindOfClass:[MGLSourceStyleFunction class]] || [value isKindOfClass:[MGLCompositeStyleFunction class]]) { + + NSArray *jsonExpression = expression.mgl_jsonExpressionObject; + + mbgl::style::conversion::Error valueError; + auto value = mbgl::style::conversion::convert<MBGLValue>( + mbgl::style::conversion::makeConvertible(jsonExpression), valueError); + if (!value) { [NSException raise:NSInvalidArgumentException - format:@"This property can only be set to camera functions. Use +[MGLStyleValue valueWithInterpolationMode:cameraStops:options:] instead."]; - return {}; - } - - if ([value isKindOfClass:[MGLConstantStyleValue class]]) { - MBGLEnum mbglValue; - getMBGLValue([(MGLConstantStyleValue<ObjCType> *)value rawValue], mbglValue); - return mbglValue; - } else if ([value isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction<NSValue *> *cameraStyleFunction = (MGLCameraStyleFunction<NSValue *> *)value; - __block std::map<float, MBGLType> stops = {}; - [cameraStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<NSValue *> * _Nonnull stopValue, BOOL * _Nonnull stop) { - NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues"); - auto mbglStopValue = toEnumPropertyValue(stopValue); - NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant"); - stops[zoomKey.floatValue] = mbglStopValue.asConstant(); - }]; - - // Enumerations can only ever use interval stops. - mbgl::style::IntervalStops<MBGLType> intervalStops = {stops}; - - mbgl::style::CameraFunction<MBGLType> cameraFunction = {intervalStops}; - return cameraFunction; - } else if (value) { - [NSException raise:@"MGLAbstractClassException" format: - @"The style value %@ cannot be applied to the style. " - @"Make sure the style value was created as a member of a concrete subclass of MGLStyleValue.", - NSStringFromClass([value class])]; - return {}; - } else { + format:@"Invalid property value: %@", @(valueError.message.c_str())]; return {}; } + + return *value; } private: // Private utilities for converting from mgl to mbgl values - - MBGLType toMBGLConstantValue(MGLConstantStyleValue<ObjCType> *value) { - MBGLType mbglValue; - getMBGLValue(value.rawValue, mbglValue); - return mbglValue; - } /** As hack to allow converting enum => string values, we accept a second, dummy parameter in @@ -220,164 +124,6 @@ private: // Private utilities for converting from mgl to mbgl values return @(color.mgl_color.stringify().c_str()); } - - NSObject* toRawStyleSpecValue(MGLStyleFunction<ObjCType>* styleFunction) { - NSMutableDictionary * rawFunction = [NSMutableDictionary new]; - // interpolationMode => type - switch (styleFunction.interpolationMode) { - case MGLInterpolationModeExponential: - rawFunction[@"type"] = @"exponential"; - break; - case MGLInterpolationModeInterval: - rawFunction[@"type"] = @"interval"; - break; - case MGLInterpolationModeCategorical: - rawFunction[@"type"] = @"categorical"; - break; - case MGLInterpolationModeIdentity: - rawFunction[@"type"] = @"identity"; - break; - } - - // interpolationBase => base - if (styleFunction.interpolationBase) { - rawFunction[@"base"] = @(styleFunction.interpolationBase); - } - - // stops and default value - if ([styleFunction isKindOfClass:[MGLCameraStyleFunction class]]) { - // zoom-only function (no default value) - __block NSMutableArray *stops = [[NSMutableArray alloc] init]; - [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLConstantStyleValue<ObjCType> * _Nonnull outputValue, BOOL * _Nonnull stop) { - MBGLType dummyMbglValue; - NSArray *rawStop = @[zoomKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)]; - [stops addObject:rawStop]; - }]; - rawFunction[@"stops"] = stops; - - } else if ([styleFunction isKindOfClass:[MGLSourceStyleFunction class]]) { - auto sourceStyleFunction = (MGLSourceStyleFunction<ObjCType> *)styleFunction; - rawFunction[@"property"] = sourceStyleFunction.attributeName; - // property-only function - __block NSMutableArray *stops = [[NSMutableArray alloc] init]; - [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSObject * _Nonnull propertyKey, MGLConstantStyleValue<ObjCType> * _Nonnull outputValue, BOOL * _Nonnull stop) { - MBGLType dummyMbglValue; - NSArray *rawStop = @[propertyKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)]; - [stops addObject:rawStop]; - }]; - rawFunction[@"stops"] = stops; - - // defaultValue => default - if (sourceStyleFunction.defaultValue) { - NSCAssert([sourceStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant"); - MBGLType dummyMbglValue; - rawFunction[@"default"] = toRawStyleSpecValue([(MGLConstantStyleValue<ObjCType> *)sourceStyleFunction.defaultValue rawValue], dummyMbglValue); - } - } else if ([styleFunction isKindOfClass:[MGLCompositeStyleFunction class]]) { - // zoom-and-property function - auto compositeStyleFunction = (MGLCompositeStyleFunction<ObjCType> *)styleFunction; - rawFunction[@"property"] = compositeStyleFunction.attributeName; - - __block NSMutableArray *stops = [[NSMutableArray alloc] init]; - [compositeStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, NSDictionary * _Nonnull stopValue, BOOL * _Nonnull stop) { - for (NSObject *valueKey in stopValue.allKeys) { - NSDictionary *stopKey = @{ - @"zoom": zoomKey, - @"value": valueKey - }; - MGLConstantStyleValue<ObjCType> *outputValue = stopValue[valueKey]; - NSCAssert([outputValue isKindOfClass:[MGLConstantStyleValue<ObjCType> class]], @"Stop outputs should be MGLConstantStyleValues"); - MBGLType dummyMbglValue; - NSArray *rawStop = @[stopKey, toRawStyleSpecValue([outputValue rawValue], dummyMbglValue)]; - [stops addObject:rawStop]; - } - }]; - rawFunction[@"stops"] = stops; - - // defaultValue => default - if (compositeStyleFunction.defaultValue) { - NSCAssert([compositeStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant"); - MBGLType dummyMbglValue; - rawFunction[@"default"] = toRawStyleSpecValue([(MGLConstantStyleValue<ObjCType> *)compositeStyleFunction.defaultValue rawValue], dummyMbglValue); - } - } - - return rawFunction; - } - - mbgl::style::CameraFunction<MBGLType> toMBGLExponentialCameraFunction(MGLStyleFunction<ObjCType> *styleFunction) { - __block std::map<float, MBGLType> stops = {}; - [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<ObjCType> * _Nonnull stopValue, BOOL * _Nonnull stop) { - NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues"); - auto mbglStopValue = toPropertyValue(stopValue); - NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant"); - stops[zoomKey.floatValue] = mbglStopValue.asConstant(); - }]; - - // Camera function with Exponential stops - mbgl::style::ExponentialStops<MBGLType> exponentialStops = {stops, (float)styleFunction.interpolationBase}; - mbgl::style::CameraFunction<MBGLType> cameraFunction = {exponentialStops}; - - return cameraFunction; - } - - mbgl::style::CameraFunction<MBGLType> toMBGLIntervalCameraFunction(MGLStyleFunction<ObjCType> *styleFunction) { - __block std::map<float, MBGLType> stops = {}; - [styleFunction.stops enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull zoomKey, MGLStyleValue<ObjCType> * _Nonnull stopValue, BOOL * _Nonnull stop) { - NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues"); - auto mbglStopValue = toPropertyValue(stopValue); - NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant"); - stops[zoomKey.floatValue] = mbglStopValue.asConstant(); - }]; - - // Camera function with Interval stops - mbgl::style::IntervalStops<MBGLType> intervalStops = {stops}; - mbgl::style::CameraFunction<MBGLType> cameraFunction = {intervalStops}; - - return cameraFunction; - } - - mbgl::style::SourceFunction<MBGLType> toMBGLCategoricalSourceFunction(MGLSourceStyleFunction<ObjCType> *sourceStyleFunction) { - __block std::map<mbgl::style::CategoricalValue, MBGLType> stops = {}; - [sourceStyleFunction.stops enumerateKeysAndObjectsUsingBlock:^(id categoryKey, MGLStyleValue<ObjCType> *stopValue, BOOL *stop) { - NSCAssert([stopValue isKindOfClass:[MGLStyleValue class]], @"Stops should be MGLStyleValues"); - auto mbglStopValue = toPropertyValue(stopValue); - NSCAssert(mbglStopValue.isConstant(), @"Stops must be constant"); - - if ([categoryKey isKindOfClass:[NSString class]]) { - const std::string& convertedValueKey = [((NSString *)categoryKey) UTF8String]; - stops[mbgl::style::CategoricalValue(convertedValueKey)] = mbglStopValue.asConstant(); - } else if ([categoryKey isKindOfClass:[NSNumber class]]) { - NSNumber *key = (NSNumber *)categoryKey; - if ((strcmp([key objCType], @encode(char)) == 0) || - (strcmp([key objCType], @encode(BOOL)) == 0)) { - stops[mbgl::style::CategoricalValue((bool)[key boolValue])] = mbglStopValue.asConstant(); - } else if (strcmp([key objCType], @encode(double)) == 0 || - strcmp([key objCType], @encode(float)) == 0) { - NSCAssert(mbglStopValue.isConstant(), @"Categorical stop keys must be strings, booleans, or integers"); - } else if ([key compare:@(0)] == NSOrderedDescending || - [key compare:@(0)] == NSOrderedSame || - [key compare:@(0)] == NSOrderedAscending) { - stops[mbgl::style::CategoricalValue((int64_t)[key integerValue])] = mbglStopValue.asConstant(); - } - } - }]; - mbgl::style::CategoricalStops<MBGLType> categoricalStops = {stops}; - mbgl::style::SourceFunction<MBGLType> sourceFunction = {sourceStyleFunction.attributeName.UTF8String, categoricalStops}; - setDefaultMBGLValue(sourceStyleFunction, sourceFunction); - return sourceFunction; - } - - void setDefaultMBGLValue(MGLSourceStyleFunction<ObjCType> *sourceStyleFunction, mbgl::style::SourceFunction<MBGLType> &sourceFunction) { - if (sourceStyleFunction.defaultValue) { - NSCAssert([sourceStyleFunction.defaultValue isKindOfClass:[MGLConstantStyleValue class]], @"Default value must be constant"); - MBGLType mbglValue; - id mglValue = [(MGLConstantStyleValue<ObjCType> *)sourceStyleFunction.defaultValue rawValue]; - getMBGLValue(mglValue, mbglValue); - sourceFunction.defaultValue = mbglValue; - } - } - // Bool void getMBGLValue(NSNumber *rawValue, bool &mbglValue) { mbglValue = !!rawValue.boolValue; @@ -394,13 +140,28 @@ private: // Private utilities for converting from mgl to mbgl values } // Offsets - void getMBGLValue(NSValue *rawValue, std::array<float, 2> &mbglValue) { - mbglValue = rawValue.mgl_offsetArrayValue; + void getMBGLValue(id rawValue, std::array<float, 2> &mbglValue) { + if ([rawValue isKindOfClass:[NSValue class]]) { + mbglValue = [rawValue mgl_offsetArrayValue]; + } else if ([rawValue isKindOfClass:[NSArray class]]) { + NSArray *array = (NSArray *)rawValue; + getMBGLValue(array[0], mbglValue[0]); + getMBGLValue(array[1], mbglValue[1]); + } } // Padding - void getMBGLValue(NSValue *rawValue, std::array<float, 4> &mbglValue) { - mbglValue = rawValue.mgl_paddingArrayValue; + void getMBGLValue(id rawValue, std::array<float, 4> &mbglValue) { + if ([rawValue isKindOfClass:[NSValue class]]) { + mbglValue = [rawValue mgl_paddingArrayValue]; + } else if ([rawValue isKindOfClass:[NSArray class]]) { + NSArray *array = (NSArray *)rawValue; + getMBGLValue(array[0], mbglValue[0]); + getMBGLValue(array[1], mbglValue[1]); + getMBGLValue(array[2], mbglValue[2]); + getMBGLValue(array[3], mbglValue[3]); + getMBGLValue(array[4], mbglValue[4]); + } } // Color @@ -412,8 +173,12 @@ private: // Private utilities for converting from mgl to mbgl values void getMBGLValue(ObjCType rawValue, std::vector<MBGLElement> &mbglValue) { mbglValue.reserve(rawValue.count); for (id obj in rawValue) { + id constantObject = obj; + if ([obj isKindOfClass:[NSExpression class]] && [obj expressionType] == NSConstantValueExpressionType) { + constantObject = [constantObject constantValue]; + } MBGLElement mbglElement; - getMBGLValue(obj, mbglElement); + getMBGLValue(constantObject, mbglElement); mbglValue.push_back(mbglElement); } } @@ -429,11 +194,15 @@ private: // Private utilities for converting from mgl to mbgl values class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type, typename MGLEnum = ObjCEnum, class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type> - void getMBGLValue(ObjCType rawValue, MBGLEnum &mbglValue) { - MGLEnum mglEnum; - [rawValue getValue:&mglEnum]; - auto str = mbgl::Enum<MGLEnum>::toString(mglEnum); - mbglValue = *mbgl::Enum<MBGLEnum>::toEnum(str); + void getMBGLValue(id rawValue, MBGLEnum &mbglValue) { + if ([rawValue isKindOfClass:[NSString class]]) { + mbglValue = *mbgl::Enum<MBGLEnum>::toEnum([(NSString *)rawValue UTF8String]); + } else { + MGLEnum mglEnum; + [(NSValue *)rawValue getValue:&mglEnum]; + auto str = mbgl::Enum<MGLEnum>::toString(mglEnum); + mbglValue = *mbgl::Enum<MBGLEnum>::toEnum(str); + } } private: // Private utilities for converting from mbgl to mgl values @@ -477,7 +246,7 @@ private: // Private utilities for converting from mbgl to mgl values static ObjCType toMGLRawStyleValue(const std::vector<MBGLElement> &mbglStopValue) { NSMutableArray *array = [NSMutableArray arrayWithCapacity:mbglStopValue.size()]; for (const auto &mbglElement: mbglStopValue) { - [array addObject:toMGLRawStyleValue(mbglElement)]; + [array addObject:[NSExpression expressionForConstantValue:toMGLRawStyleValue(mbglElement)]]; } return array; } @@ -496,212 +265,49 @@ private: // Private utilities for converting from mbgl to mgl values return [NSValue value:&mglType withObjCType:@encode(MGLEnum)]; } - // Converts mbgl stops to an equivilent NSDictionary for mgl - static NSMutableDictionary *toConvertedStops(const std::map<float, MBGLType> &mbglStops) { - NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.size()]; - for (const auto &mbglStop : mbglStops) { - auto rawValue = toMGLRawStyleValue(mbglStop.second); - stops[@(mbglStop.first)] = [MGLStyleValue valueWithRawValue:rawValue]; - } - return stops; - } - - // Converts mbgl interval stop categorical values to an equivilant object for mgl - class CategoricalValueVisitor { - public: - id operator()(const bool value) { - return toMGLRawStyleValue(value); - } - - id operator()(const int64_t value) { - return toMGLRawStyleValue(value); - } - - id operator()(const std::string value) { - return toMGLRawStyleValue(value); - } - }; - - // Converts all types of mbgl property values containing enumerations into an equivilant mgl style value - template <typename MBGLEnum = MBGLType, typename MGLEnum = ObjCEnum> - class EnumPropertyValueEvaluator { + /// Converts all types of mbgl property values into an equivalent NSExpression. + class PropertyExpressionEvaluator { public: - id operator()(const mbgl::style::Undefined) const { + NSExpression *operator()(const mbgl::style::Undefined) const { return nil; } - id operator()(const MBGLEnum &value) const { - auto str = mbgl::Enum<MBGLEnum>::toString(value); - MGLEnum mglType = *mbgl::Enum<MGLEnum>::toEnum(str); - return [MGLConstantStyleValue<ObjCType> valueWithRawValue:[NSValue value:&mglType withObjCType:@encode(MGLEnum)]]; - } - - id operator()(const mbgl::style::CameraFunction<MBGLEnum> &mbglValue) const { - CameraFunctionStopsVisitor visitor; - return apply_visitor(visitor, mbglValue.stops); - } - }; - - // Converts all possible mbgl camera function stops into an equivilant mgl style value - class CameraFunctionStopsVisitor { - public: - id operator()(const mbgl::style::ExponentialStops<MBGLType> &mbglStops) { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential - stops:toConvertedStops(mbglStops.stops) - options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}]; - } - - id operator()(const mbgl::style::IntervalStops<MBGLType> &mbglStops) { - return [MGLCameraStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval - stops:toConvertedStops(mbglStops.stops) - options:nil]; - } - }; - - // Converts a source function and all possible mbgl source function stops into an equivilant mgl style value - class SourceFunctionStopsVisitor { - public: - id operator()(const mbgl::style::ExponentialStops<MBGLType> &mbglStops) { - MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential - stops:toConvertedStops(mbglStops.stops) - attributeName:@(mbglFunction.property.c_str()) - options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}]; - if (mbglFunction.defaultValue) { - sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; + /** + As hack to allow converting enum => string values, we accept a second, dummy parameter in + the toRawStyleSpecValue() methods for converting 'atomic' (non-style-function) values. + This allows us to use `std::enable_if` to test (at compile time) whether or not MBGLType is an Enum. + */ + template <typename MBGLEnum = MBGLType, + class = typename std::enable_if<!std::is_enum<MBGLEnum>::value>::type, + typename MGLEnum = ObjCEnum, + class = typename std::enable_if<!std::is_enum<MGLEnum>::value>::type> + NSExpression *operator()(const MBGLType &value) const { + id constantValue = toMGLRawStyleValue(value); + if ([constantValue isKindOfClass:[NSArray class]]) { + return [NSExpression expressionForAggregate:constantValue]; } - return sourceFunction; + return [NSExpression expressionForConstantValue:constantValue]; } - - id operator()(const mbgl::style::IntervalStops<MBGLType> &mbglStops) { - MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval - stops:toConvertedStops(mbglStops.stops) - attributeName:@(mbglFunction.property.c_str()) - options:nil]; - if (mbglFunction.defaultValue) { - sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return sourceFunction; - } - - id operator()(const mbgl::style::CategoricalStops<MBGLType> &mbglStops) { - NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()]; - for (const auto &mbglStop : mbglStops.stops) { - auto categoricalValue = mbglStop.first; - auto rawValue = toMGLRawStyleValue(mbglStop.second); - CategoricalValueVisitor categoricalValueVisitor; - id stopKey = apply_visitor(categoricalValueVisitor, categoricalValue); - stops[stopKey] = [MGLStyleValue valueWithRawValue:rawValue]; - } - - MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeCategorical - stops:stops - attributeName:@(mbglFunction.property.c_str()) - options:nil]; - if (mbglFunction.defaultValue) { - sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return sourceFunction; - - } - - id operator()(const mbgl::style::IdentityStops<MBGLType> &mbglStops) { - MGLSourceStyleFunction *sourceFunction = [MGLSourceStyleFunction functionWithInterpolationMode:MGLInterpolationModeIdentity - stops:nil - attributeName:@(mbglFunction.property.c_str()) options:nil]; - if (mbglFunction.defaultValue) { - sourceFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return sourceFunction; - } - - const mbgl::style::SourceFunction<MBGLType> &mbglFunction; - }; - - // Converts a composite function and all possible mbgl stops into an equivilant mgl style value - class CompositeFunctionStopsVisitor { - public: - id operator()(const mbgl::style::CompositeExponentialStops<MBGLType> &mbglStops) { - NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()]; - for (auto const& outerStop: mbglStops.stops) { - stops[@(outerStop.first)] = toConvertedStops(outerStop.second); - } - MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeExponential - stops:stops - attributeName:@(mbglFunction.property.c_str()) - options:@{MGLStyleFunctionOptionInterpolationBase: @(mbglStops.base)}]; - if (mbglFunction.defaultValue) { - compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return compositeFunction; - } - - id operator()(const mbgl::style::CompositeIntervalStops<MBGLType> &mbglStops) { - NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()]; - for (auto const& outerStop: mbglStops.stops) { - stops[@(outerStop.first)] = toConvertedStops(outerStop.second); - } - MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeInterval - stops:stops - attributeName:@(mbglFunction.property.c_str()) - options:nil]; - if (mbglFunction.defaultValue) { - compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return compositeFunction; - } - - id operator()(const mbgl::style::CompositeCategoricalStops<MBGLType> &mbglStops) { - NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:mbglStops.stops.size()]; - for (auto const& outerStop: mbglStops.stops) { - NSMutableDictionary *innerStops = [NSMutableDictionary dictionaryWithCapacity:outerStop.second.size()]; - for (const auto &mbglStop : outerStop.second) { - auto categoricalValue = mbglStop.first; - auto rawValue = toMGLRawStyleValue(mbglStop.second); - CategoricalValueVisitor categoricalValueVisitor; - id stopKey = apply_visitor(categoricalValueVisitor, categoricalValue); - innerStops[stopKey] = [MGLStyleValue valueWithRawValue:rawValue]; - } - stops[@(outerStop.first)] = innerStops; - } - - MGLCompositeStyleFunction *compositeFunction = [MGLCompositeStyleFunction functionWithInterpolationMode:MGLInterpolationModeCategorical - stops:stops attributeName:@(mbglFunction.property.c_str()) - options:nil]; - if (mbglFunction.defaultValue) { - compositeFunction.defaultValue = [MGLStyleValue valueWithRawValue:toMGLRawStyleValue(*mbglFunction.defaultValue)]; - } - return compositeFunction; - } - - const mbgl::style::CompositeFunction<MBGLType> &mbglFunction; - }; - - - // Converts all types of mbgl property values that don't contain enumerations into an equivilant mgl style value - class PropertyValueEvaluator { - public: - id operator()(const mbgl::style::Undefined) const { - return nil; - } - - id operator()(const MBGLType &value) const { - auto rawValue = toMGLRawStyleValue(value); - return [MGLConstantStyleValue<ObjCType> valueWithRawValue:rawValue]; + + template <typename MBGLEnum = MBGLType, + class = typename std::enable_if<std::is_enum<MBGLEnum>::value>::type, + typename MGLEnum = ObjCEnum, + class = typename std::enable_if<std::is_enum<MGLEnum>::value>::type> + NSExpression *operator()(const MBGLEnum &value) const { + NSString *constantValue = @(mbgl::Enum<MBGLEnum>::toString(value)); + return [NSExpression expressionForConstantValue:constantValue]; } - id operator()(const mbgl::style::CameraFunction<MBGLType> &mbglValue) const { - CameraFunctionStopsVisitor visitor; - return apply_visitor(visitor, mbglValue.stops); + NSExpression *operator()(const mbgl::style::CameraFunction<MBGLType> &mbglValue) const { + return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())]; } - id operator()(const mbgl::style::SourceFunction<MBGLType> &mbglValue) const { - SourceFunctionStopsVisitor visitor { mbglValue }; - return apply_visitor(visitor, mbglValue.stops); + NSExpression *operator()(const mbgl::style::SourceFunction<MBGLType> &mbglValue) const { + return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())]; } - MGLCompositeStyleFunction<ObjCType> * operator()(const mbgl::style::CompositeFunction<MBGLType> &mbglValue) const { - CompositeFunctionStopsVisitor visitor { mbglValue }; - return apply_visitor(visitor, mbglValue.stops); + NSExpression *operator()(const mbgl::style::CompositeFunction<MBGLType> &mbglValue) const { + return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())]; } }; }; diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 84d32cd0b1..1017db5442 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -2,7 +2,6 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLFoundation.h" -#import "MGLStyleValue.h" #import "MGLVectorStyleLayer.h" NS_ASSUME_NONNULL_BEGIN @@ -335,12 +334,12 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslationAnchor) { ```swift let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois) layer.sourceLayerIdentifier = "pois" - layer.iconImageName = MGLStyleValue(rawValue: "coffee") - layer.iconScale = MGLStyleValue(rawValue: 0.5) - layer.text = MGLStyleValue(rawValue: "{name}") - layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) - layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left)) - layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left)) + layer.iconImageName = NSExpression(forConstantValue: "coffee") + layer.iconScale = NSExpression(forConstantValue: 0.5) + layer.text = NSExpression(forKeyPath: "name") + layer.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) + layer.textJustification = NSExpression(forConstantValue: "left") + layer.textAnchor = NSExpression(forConstantValue: "left") layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee") mapView.style?.addLayer(layer) ``` @@ -369,9 +368,8 @@ MGL_EXPORT If true, the icon will be visible even if it collides with other previously drawn symbols. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -380,51 +378,62 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-allow-overlap"><code>icon-allow-overlap</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowsOverlap; +@property (nonatomic, null_resettable) NSExpression *iconAllowsOverlap; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconAllowOverlap __attribute__((unavailable("Use iconAllowsOverlap instead."))); +@property (nonatomic, null_resettable) NSExpression *iconAllowOverlap __attribute__((unavailable("Use iconAllowsOverlap instead."))); /** Part of the icon placed closest to the anchor. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconAnchorCenter`. Set this property to `nil` - to reset it to the default value. + The default value of this property is an expression that evaluates to `center`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconAnchor; + You can set this property to an expression containing any of the following: + + * Constant `MGLIconAnchor` values + * Any of the following constant string values: + * `center`: The center of the icon is placed closest to the anchor. + * `left`: The left side of the icon is placed closest to the anchor. + * `right`: The right side of the icon is placed closest to the anchor. + * `top`: The top of the icon is placed closest to the anchor. + * `bottom`: The bottom of the icon is placed closest to the anchor. + * `top-left`: The top left corner of the icon is placed closest to the + anchor. + * `top-right`: The top right corner of the icon is placed closest to the + anchor. + * `bottom-left`: The bottom left corner of the icon is placed closest to the + anchor. + * `bottom-right`: The bottom right corner of the icon is placed closest to + the anchor. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *iconAnchor; /** If true, other symbols can be visible even if they collide with the icon. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -433,16 +442,22 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-ignore-placement"><code>icon-ignore-placement</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconIgnoresPlacement; +@property (nonatomic, null_resettable) NSExpression *iconIgnoresPlacement; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconIgnorePlacement __attribute__((unavailable("Use iconIgnoresPlacement instead."))); +@property (nonatomic, null_resettable) NSExpression *iconIgnorePlacement __attribute__((unavailable("Use iconIgnoresPlacement instead."))); /** Name of image in sprite to use for drawing an image background. Within literal @@ -455,103 +470,87 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-image"><code>icon-image</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImageName; +@property (nonatomic, null_resettable) NSExpression *iconImageName; -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *iconImage __attribute__((unavailable("Use iconImageName instead."))); +@property (nonatomic, null_resettable) NSExpression *iconImage __attribute__((unavailable("Use iconImageName instead."))); #if TARGET_OS_IPHONE /** Offset distance of icon from its anchor. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 rightward and 0 downward. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset; + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *iconOffset; #else /** Offset distance of icon from its anchor. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 rightward and 0 upward. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconOffset; + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *iconOffset; #endif /** If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable, getter=isIconOptional) MGLStyleValue<NSNumber *> *iconOptional; +@property (nonatomic, null_resettable, getter=isIconOptional) NSExpression *iconOptional; /** Size of the additional area around the icon bounding box used for detecting @@ -559,48 +558,59 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `2`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconPadding; +@property (nonatomic, null_resettable) NSExpression *iconPadding; /** Orientation of icon when map is pitched. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconPitchAlignmentAuto`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `auto`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLIconPitchAlignment` values + * Any of the following constant string values: + * `map`: The icon is aligned to the plane of the map. + * `viewport`: The icon is aligned to the plane of the viewport. + * `auto`: Automatically matches the value of `icon-rotation-alignment`. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconPitchAlignment; +@property (nonatomic, null_resettable) NSExpression *iconPitchAlignment; /** Rotates the icon clockwise. This property is measured in degrees. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -609,45 +619,51 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-rotate"><code>icon-rotate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotation; +@property (nonatomic, null_resettable) NSExpression *iconRotation; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconRotate __attribute__((unavailable("Use iconRotation instead."))); +@property (nonatomic, null_resettable) NSExpression *iconRotate __attribute__((unavailable("Use iconRotation instead."))); /** In combination with `symbolPlacement`, determines the rotation behavior of icons. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconRotationAlignmentAuto`. Set this property - to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `auto`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLIconRotationAlignment` values + * Any of the following constant string values: + * `map`: When `symbol-placement` is set to `point`, aligns icons east-west. + When `symbol-placement` is set to `line`, aligns icon x-axes with the line. + * `viewport`: Produces icons whose x-axes are aligned with the x-axis of the + viewport, regardless of the value of `symbol-placement`. + * `auto`: When `symbol-placement` is set to `point`, this is equivalent to + `viewport`. When `symbol-placement` is set to `line`, this is equivalent to + `map`. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconRotationAlignment; +@property (nonatomic, null_resettable) NSExpression *iconRotationAlignment; /** Scales the original size of the icon by the provided factor. The new point size @@ -656,9 +672,8 @@ MGL_EXPORT This property is measured in factor of the original icon sizes. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -667,44 +682,49 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-size"><code>icon-size</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconScale; +@property (nonatomic, null_resettable) NSExpression *iconScale; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconSize __attribute__((unavailable("Use iconScale instead."))); +@property (nonatomic, null_resettable) NSExpression *iconSize __attribute__((unavailable("Use iconScale instead."))); /** Scales the icon to fit around the associated text. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconTextFitNone`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to `none`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant `MGLIconTextFit` values + * Any of the following constant string values: + * `none`: The icon is displayed at its intrinsic aspect ratio. + * `width`: The icon is scaled in the x-dimension to fit the width of the + text. + * `height`: The icon is scaled in the y-dimension to fit the height of the + text. + * `both`: The icon is scaled in both x- and y-dimensions. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFit; +@property (nonatomic, null_resettable) NSExpression *iconTextFit; #if TARGET_OS_IPHONE /** @@ -712,142 +732,160 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing `UIEdgeInsetsZero`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and - `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object - containing an `NSValue` object containing `MGLIconTextFitBoth`, - `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored. + `text` is non-`nil`, and `iconTextFit` is set to an expression that evaluates + to `MGLIconTextFitBoth`, `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. + Otherwise, it is ignored. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `UIEdgeInsets` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding; +@property (nonatomic, null_resettable) NSExpression *iconTextFitPadding; #else /** Size of the additional area added to dimensions determined by `iconTextFit`. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing `NSEdgeInsetsZero`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and - `text` is non-`nil`, and `iconTextFit` is set to an `MGLStyleValue` object - containing an `NSValue` object containing `MGLIconTextFitBoth`, - `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. Otherwise, it is ignored. + `text` is non-`nil`, and `iconTextFit` is set to an expression that evaluates + to `MGLIconTextFitBoth`, `MGLIconTextFitWidth`, or `MGLIconTextFitHeight`. + Otherwise, it is ignored. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `NSEdgeInsets` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTextFitPadding; +@property (nonatomic, null_resettable) NSExpression *iconTextFitPadding; #endif /** If true, the icon may be flipped to prevent it from being rendered upside-down. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and - `iconRotationAlignment` is set to an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconRotationAlignmentMap`, and - `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` - object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored. + `iconRotationAlignment` is set to an expression that evaluates to `map`, and + `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, + it is ignored. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-keep-upright"><code>icon-keep-upright</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsIconUpright; +@property (nonatomic, null_resettable) NSExpression *keepsIconUpright; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconKeepUpright __attribute__((unavailable("Use keepsIconUpright instead."))); +@property (nonatomic, null_resettable) NSExpression *iconKeepUpright __attribute__((unavailable("Use keepsIconUpright instead."))); /** If true, the text may be flipped vertically to prevent it from being rendered upside-down. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `YES`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `YES`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and - `textRotationAlignment` is set to an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextRotationAlignmentMap`, and - `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` - object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored. + `textRotationAlignment` is set to an expression that evaluates to `map`, and + `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, + it is ignored. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-keep-upright"><code>text-keep-upright</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *keepsTextUpright; +@property (nonatomic, null_resettable) NSExpression *keepsTextUpright; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textKeepUpright __attribute__((unavailable("Use keepsTextUpright instead."))); +@property (nonatomic, null_resettable) NSExpression *textKeepUpright __attribute__((unavailable("Use keepsTextUpright instead."))); /** Maximum angle change between adjacent characters. This property is measured in degrees. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `45`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `45`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and - `symbolPlacement` is set to an `MGLStyleValue` object containing an `NSValue` - object containing `MGLSymbolPlacementLine`. Otherwise, it is ignored. + `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, + it is ignored. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-angle"><code>text-max-angle</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextAngle; +@property (nonatomic, null_resettable) NSExpression *maximumTextAngle; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textMaxAngle __attribute__((unavailable("Use maximumTextAngle instead."))); +@property (nonatomic, null_resettable) NSExpression *textMaxAngle __attribute__((unavailable("Use maximumTextAngle instead."))); /** The maximum line width for text wrapping. This property is measured in ems. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `10`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `10`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -856,26 +894,19 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-max-width"><code>text-max-width</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *maximumTextWidth; +@property (nonatomic, null_resettable) NSExpression *maximumTextWidth; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textMaxWidth __attribute__((unavailable("Use maximumTextWidth instead."))); +@property (nonatomic, null_resettable) NSExpression *textMaxWidth __attribute__((unavailable("Use maximumTextWidth instead."))); /** If true, the symbols will not cross tile edges to avoid mutual collisions. @@ -883,61 +914,77 @@ MGL_EXPORT prevent collisions, or if it is a point symbol layer placed after a line symbol layer. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-symbol-avoid-edges"><code>symbol-avoid-edges</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolAvoidsEdges; +@property (nonatomic, null_resettable) NSExpression *symbolAvoidsEdges; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolAvoidEdges __attribute__((unavailable("Use symbolAvoidsEdges instead."))); +@property (nonatomic, null_resettable) NSExpression *symbolAvoidEdges __attribute__((unavailable("Use symbolAvoidsEdges instead."))); /** Label placement relative to its geometry. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLSymbolPlacementPoint`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `point`. + Set this property to `nil` to reset it to the default value. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant `MGLSymbolPlacement` values + * Any of the following constant string values: + * `point`: The label is placed at the point where the geometry is located. + * `line`: The label is placed along the line of the geometry. Can only be + used on `LineString` and `Polygon` geometries. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *symbolPlacement; +@property (nonatomic, null_resettable) NSExpression *symbolPlacement; /** Distance between two symbol anchors. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `250`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `250`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `symbolPlacement` is set to an - `MGLStyleValue` object containing an `NSValue` object containing - `MGLSymbolPlacementLine`. Otherwise, it is ignored. + expression that evaluates to `line`. Otherwise, it is ignored. + + You can set this property to an expression containing any of the following: - You can set this property to an instance of: + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *symbolSpacing; +@property (nonatomic, null_resettable) NSExpression *symbolSpacing; /** Value to use for a text label. Within literal values, attribute names enclosed @@ -945,41 +992,33 @@ MGL_EXPORT attribute. Expressions do not support this syntax; for equivalent functionality in expressions, use `stringByAppendingString:` and key path expressions. - The default value of this property is an `MGLStyleValue` object containing the - empty string. Set this property to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to the empty + string. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-field"><code>text-field</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant string values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *text; +@property (nonatomic, null_resettable) NSExpression *text; -@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *textField __attribute__((unavailable("Use text instead."))); +@property (nonatomic, null_resettable) NSExpression *textField __attribute__((unavailable("Use text instead."))); /** If true, the text will be visible even if it collides with other previously drawn symbols. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -988,44 +1027,56 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-allow-overlap"><code>text-allow-overlap</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textAllowsOverlap; +@property (nonatomic, null_resettable) NSExpression *textAllowsOverlap; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textAllowOverlap __attribute__((unavailable("Use textAllowsOverlap instead."))); +@property (nonatomic, null_resettable) NSExpression *textAllowOverlap __attribute__((unavailable("Use textAllowsOverlap instead."))); /** Part of the text placed closest to the anchor. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextAnchorCenter`. Set this property to `nil` - to reset it to the default value. + The default value of this property is an expression that evaluates to `center`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textAnchor; + You can set this property to an expression containing any of the following: + + * Constant `MGLTextAnchor` values + * Any of the following constant string values: + * `center`: The center of the text is placed closest to the anchor. + * `left`: The left side of the text is placed closest to the anchor. + * `right`: The right side of the text is placed closest to the anchor. + * `top`: The top of the text is placed closest to the anchor. + * `bottom`: The bottom of the text is placed closest to the anchor. + * `top-left`: The top left corner of the text is placed closest to the + anchor. + * `top-right`: The top right corner of the text is placed closest to the + anchor. + * `bottom-left`: The bottom left corner of the text is placed closest to the + anchor. + * `bottom-right`: The bottom right corner of the text is placed closest to + the anchor. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *textAnchor; /** An array of font face names used to display the text. @@ -1040,9 +1091,9 @@ MGL_EXPORT the text, if the first font lacks a glyph for the character, the next font is applied as a fallback, and so on. - The default value of this property is an `MGLStyleValue` object containing the - array `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to the array + `Open Sans Regular`, `Arial Unicode MS Regular`. Set this property to `nil` to + reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1051,35 +1102,27 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-font"><code>text-font</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant array values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFontNames; +@property (nonatomic, null_resettable) NSExpression *textFontNames; -@property (nonatomic, null_resettable) MGLStyleValue<NSArray<NSString *> *> *textFont __attribute__((unavailable("Use textFontNames instead."))); +@property (nonatomic, null_resettable) NSExpression *textFont __attribute__((unavailable("Use textFontNames instead."))); /** Font size. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `16`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `16`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1088,33 +1131,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-size"><code>text-size</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textFontSize; +@property (nonatomic, null_resettable) NSExpression *textFontSize; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textSize __attribute__((unavailable("Use textFontSize instead."))); +@property (nonatomic, null_resettable) NSExpression *textSize __attribute__((unavailable("Use textFontSize instead."))); /** If true, other symbols can be visible even if they collide with the text. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1123,23 +1158,28 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-ignore-placement"><code>text-ignore-placement</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textIgnoresPlacement; +@property (nonatomic, null_resettable) NSExpression *textIgnoresPlacement; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textIgnorePlacement __attribute__((unavailable("Use textIgnoresPlacement instead."))); +@property (nonatomic, null_resettable) NSExpression *textIgnorePlacement __attribute__((unavailable("Use textIgnoresPlacement instead."))); /** Text justification options. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextJustificationCenter`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `center`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1148,77 +1188,69 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-justify"><code>text-justify</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `MGLTextJustification` values + * Any of the following constant string values: + * `left`: The text is aligned to the left. + * `center`: The text is centered. + * `right`: The text is aligned to the right. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textJustification; +@property (nonatomic, null_resettable) NSExpression *textJustification; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textJustify __attribute__((unavailable("Use textJustification instead."))); +@property (nonatomic, null_resettable) NSExpression *textJustify __attribute__((unavailable("Use textJustification instead."))); /** Text tracking amount. This property is measured in ems. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLetterSpacing; +@property (nonatomic, null_resettable) NSExpression *textLetterSpacing; /** Text leading value for multi-line text. This property is measured in ems. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1.2`. Set this property to `nil` to - reset it to the default value. + The default value of this property is an expression that evaluates to the float + `1.2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textLineHeight; +@property (nonatomic, null_resettable) NSExpression *textLineHeight; #if TARGET_OS_IPHONE /** @@ -1226,80 +1258,71 @@ MGL_EXPORT This property is measured in ems. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0 ems downward. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset; + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *textOffset; #else /** Offset distance of text from its anchor. This property is measured in ems. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 ems rightward and 0 ems upward. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textOffset; + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *textOffset; #endif /** If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing `NO`. Set this property to `nil` to reset it to - the default value. + The default value of this property is an expression that evaluates to `NO`. Set + this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant Boolean values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable, getter=isTextOptional) MGLStyleValue<NSNumber *> *textOptional; +@property (nonatomic, null_resettable, getter=isTextOptional) NSExpression *textOptional; /** Size of the additional area around the text bounding box used for detecting @@ -1307,48 +1330,59 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `2`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textPadding; +@property (nonatomic, null_resettable) NSExpression *textPadding; /** Orientation of text when map is pitched. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextPitchAlignmentAuto`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `auto`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLTextPitchAlignment` values + * Any of the following constant string values: + * `map`: The text is aligned to the plane of the map. + * `viewport`: The text is aligned to the plane of the viewport. + * `auto`: Automatically matches the value of `text-rotation-alignment`. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textPitchAlignment; +@property (nonatomic, null_resettable) NSExpression *textPitchAlignment; /** Rotates the text clockwise. This property is measured in degrees. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1357,135 +1391,99 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-text-rotate"><code>text-rotate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotation; +@property (nonatomic, null_resettable) NSExpression *textRotation; -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textRotate __attribute__((unavailable("Use textRotation instead."))); +@property (nonatomic, null_resettable) NSExpression *textRotate __attribute__((unavailable("Use textRotation instead."))); /** In combination with `symbolPlacement`, determines the rotation behavior of the individual glyphs forming the text. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextRotationAlignmentAuto`. Set this property - to `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `auto`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLTextRotationAlignment` values + * Any of the following constant string values: + * `map`: When `symbol-placement` is set to `point`, aligns text east-west. + When `symbol-placement` is set to `line`, aligns text x-axes with the line. + * `viewport`: Produces glyphs whose x-axes are aligned with the x-axis of the + viewport, regardless of the value of `symbol-placement`. + * `auto`: When `symbol-placement` is set to `point`, this is equivalent to + `viewport`. When `symbol-placement` is set to `line`, this is equivalent to + `map`. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textRotationAlignment; +@property (nonatomic, null_resettable) NSExpression *textRotationAlignment; /** Specifies how to capitalize text. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextTransformNone`. Set this property to `nil` - to reset it to the default value. + The default value of this property is an expression that evaluates to `none`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant `MGLTextTransform` values + * Any of the following constant string values: + * `none`: The text is not altered. + * `uppercase`: Forces all letters to be displayed in uppercase. + * `lowercase`: Forces all letters to be displayed in lowercase. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTransform; +@property (nonatomic, null_resettable) NSExpression *textTransform; #pragma mark - Accessing the Paint Attributes -#if TARGET_OS_IPHONE /** The tint color to apply to the icon. The `iconImageName` property must be set to a template image. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconColor; -#else -/** - The tint color to apply to the icon. The `iconImageName` property must be set - to a template image. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `iconImageName` is non-`nil`. - Otherwise, it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconColor; -#endif + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *iconColor; /** The transition affecting any changes to this layer’s `iconColor` property. @@ -1499,30 +1497,22 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloBlur; +@property (nonatomic, null_resettable) NSExpression *iconHaloBlur; /** The transition affecting any changes to this layer’s `iconHaloBlur` property. @@ -1531,65 +1521,27 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition iconHaloBlurTransition; -#if TARGET_OS_IPHONE /** The color of the icon’s halo. The `iconImageName` property must be set to a template image. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.clearColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconHaloColor; -#else -/** - The color of the icon’s halo. The `iconImageName` property must be set to a - template image. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.clearColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `iconImageName` is non-`nil`. - Otherwise, it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconHaloColor; -#endif + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *iconHaloColor; /** The transition affecting any changes to this layer’s `iconHaloColor` property. @@ -1603,30 +1555,22 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloWidth; +@property (nonatomic, null_resettable) NSExpression *iconHaloWidth; /** The transition affecting any changes to this layer’s `iconHaloWidth` property. @@ -1638,30 +1582,22 @@ MGL_EXPORT /** The opacity at which the icon will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconOpacity; +@property (nonatomic, null_resettable) NSExpression *iconOpacity; /** The transition affecting any changes to this layer’s `iconOpacity` property. @@ -1676,7 +1612,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -1687,21 +1623,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation; +@property (nonatomic, null_resettable) NSExpression *iconTranslation; #else /** Distance that the icon's anchor is moved from its original placement. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -1712,14 +1652,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate"><code>icon-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslation; +@property (nonatomic, null_resettable) NSExpression *iconTranslation; #endif /** @@ -1729,14 +1673,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition iconTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslate __attribute__((unavailable("Use iconTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *iconTranslate __attribute__((unavailable("Use iconTranslation instead."))); /** Controls the frame of reference for `iconTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLIconTranslationAnchorMap`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`, and `iconTranslation` is non-`nil`. Otherwise, it is ignored. @@ -1745,73 +1688,45 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-icon-translate-anchor"><code>icon-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLIconTranslationAnchor` values + * Any of the following constant string values: + * `map`: Icons are translated relative to the map. + * `viewport`: Icons are translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *iconTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead."))); -#if TARGET_OS_IPHONE /** The color with which the text will be drawn. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.blackColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textColor; -#else -/** - The color with which the text will be drawn. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.blackColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `text` is non-`nil`. Otherwise, - it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textColor; -#endif + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *textColor; /** The transition affecting any changes to this layer’s `textColor` property. @@ -1825,30 +1740,22 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloBlur; +@property (nonatomic, null_resettable) NSExpression *textHaloBlur; /** The transition affecting any changes to this layer’s `textHaloBlur` property. @@ -1857,63 +1764,26 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition textHaloBlurTransition; -#if TARGET_OS_IPHONE /** The color of the text's halo, which helps it stand out from backgrounds. - The default value of this property is an `MGLStyleValue` object containing + The default value of this property is an expression that evaluates to `UIColor.clearColor`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textHaloColor; -#else -/** - The color of the text's halo, which helps it stand out from backgrounds. - - The default value of this property is an `MGLStyleValue` object containing - `NSColor.clearColor`. Set this property to `nil` to reset it to the default - value. - - This property is only applied to the style if `text` is non-`nil`. Otherwise, - it is ignored. + You can set this property to an expression containing any of the following: - You can set this property to an instance of: - - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - */ -@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textHaloColor; -#endif + * Constant `UIColor` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes + */ +@property (nonatomic, null_resettable) NSExpression *textHaloColor; /** The transition affecting any changes to this layer’s `textHaloColor` property. @@ -1928,30 +1798,22 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `0`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloWidth; +@property (nonatomic, null_resettable) NSExpression *textHaloWidth; /** The transition affecting any changes to this layer’s `textHaloWidth` property. @@ -1963,30 +1825,22 @@ MGL_EXPORT /** The opacity at which the text will be drawn. - The default value of this property is an `MGLStyleValue` object containing an - `NSNumber` object containing the float `1`. Set this property to `nil` to reset - it to the default value. + The default value of this property is an expression that evaluates to the float + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLSourceStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` - * `MGLInterpolationModeIdentity` - * `MGLCompositeStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` - * `MGLInterpolationModeCategorical` + * Constant numeric values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable and/or + feature attributes */ -@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textOpacity; +@property (nonatomic, null_resettable) NSExpression *textOpacity; /** The transition affecting any changes to this layer’s `textOpacity` property. @@ -2001,7 +1855,7 @@ MGL_EXPORT This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points downward. Set this property to `nil` to reset it to the default value. @@ -2012,21 +1866,25 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable + + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation; +@property (nonatomic, null_resettable) NSExpression *textTranslation; #else /** Distance that the text's anchor is moved from its original placement. This property is measured in points. - The default value of this property is an `MGLStyleValue` object containing an + The default value of this property is an expression that evaluates to an `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 points upward. Set this property to `nil` to reset it to the default value. @@ -2037,14 +1895,18 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate"><code>text-translate</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `CGVector` values + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Interpolation and step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of: - * `MGLInterpolationModeExponential` - * `MGLInterpolationModeInterval` + This property does not support applying interpolation or step functions to + feature attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslation; +@property (nonatomic, null_resettable) NSExpression *textTranslation; #endif /** @@ -2054,14 +1916,13 @@ MGL_EXPORT */ @property (nonatomic) MGLTransition textTranslationTransition; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslate __attribute__((unavailable("Use textTranslation instead."))); +@property (nonatomic, null_resettable) NSExpression *textTranslate __attribute__((unavailable("Use textTranslation instead."))); /** Controls the frame of reference for `textTranslation`. - The default value of this property is an `MGLStyleValue` object containing an - `NSValue` object containing `MGLTextTranslationAnchorMap`. Set this property to - `nil` to reset it to the default value. + The default value of this property is an expression that evaluates to `map`. + Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and `textTranslation` is non-`nil`. Otherwise, it is ignored. @@ -2070,15 +1931,24 @@ MGL_EXPORT href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-text-translate-anchor"><code>text-translate-anchor</code></a> layout property in the Mapbox Style Specification. - You can set this property to an instance of: + You can set this property to an expression containing any of the following: + + * Constant `MGLTextTranslationAnchor` values + * Any of the following constant string values: + * `map`: The text is translated relative to the map. + * `viewport`: The text is translated relative to the viewport. + * Predefined functions, including mathematical and string operators + * Conditional expressions + * Variable assignments and references to assigned variables + * Step functions applied to the `$zoomLevel` variable - * `MGLConstantStyleValue` - * `MGLCameraStyleFunction` with an interpolation mode of - `MGLInterpolationModeInterval` + This property does not support applying interpolation functions to the + `$zoomLevel` variable or applying interpolation or step functions to feature + attributes. */ -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslationAnchor; +@property (nonatomic, null_resettable) NSExpression *textTranslationAnchor; -@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *textTranslateAnchor __attribute__((unavailable("Use textTranslationAnchor instead."))); +@property (nonatomic, null_resettable) NSExpression *textTranslateAnchor __attribute__((unavailable("Use textTranslationAnchor instead."))); @end diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 3f206aac19..24d6643e6c 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -154,754 +154,754 @@ namespace mbgl { #pragma mark - Accessing the Layout Attributes -- (void)setIconAllowsOverlap:(MGLStyleValue<NSNumber *> *)iconAllowsOverlap { +- (void)setIconAllowsOverlap:(NSExpression *)iconAllowsOverlap { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconAllowsOverlap); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconAllowsOverlap); self.rawLayer->setIconAllowOverlap(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconAllowsOverlap { +- (NSExpression *)iconAllowsOverlap { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconAllowOverlap(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconAllowOverlap()); + propertyValue = self.rawLayer->getDefaultIconAllowOverlap(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setIconAllowOverlap:(MGLStyleValue<NSNumber *> *)iconAllowOverlap { +- (void)setIconAllowOverlap:(NSExpression *)iconAllowOverlap { } -- (MGLStyleValue<NSNumber *> *)iconAllowOverlap { +- (NSExpression *)iconAllowOverlap { return self.iconAllowsOverlap; } -- (void)setIconAnchor:(MGLStyleValue<NSValue *> *)iconAnchor { +- (void)setIconAnchor:(NSExpression *)iconAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenPropertyValue(iconAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType>>(iconAnchor); self.rawLayer->setIconAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconAnchor { +- (NSExpression *)iconAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconAnchor()); + propertyValue = self.rawLayer->getDefaultIconAnchor(); } - return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toExpression(propertyValue); } -- (void)setIconIgnoresPlacement:(MGLStyleValue<NSNumber *> *)iconIgnoresPlacement { +- (void)setIconIgnoresPlacement:(NSExpression *)iconIgnoresPlacement { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconIgnoresPlacement); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconIgnoresPlacement); self.rawLayer->setIconIgnorePlacement(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconIgnoresPlacement { +- (NSExpression *)iconIgnoresPlacement { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconIgnorePlacement(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconIgnorePlacement()); + propertyValue = self.rawLayer->getDefaultIconIgnorePlacement(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setIconIgnorePlacement:(MGLStyleValue<NSNumber *> *)iconIgnorePlacement { +- (void)setIconIgnorePlacement:(NSExpression *)iconIgnorePlacement { } -- (MGLStyleValue<NSNumber *> *)iconIgnorePlacement { +- (NSExpression *)iconIgnorePlacement { return self.iconIgnoresPlacement; } -- (void)setIconImageName:(MGLStyleValue<NSString *> *)iconImageName { +- (void)setIconImageName:(NSExpression *)iconImageName { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenPropertyValue(iconImageName); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(iconImageName); self.rawLayer->setIconImage(mbglValue); } -- (MGLStyleValue<NSString *> *)iconImageName { +- (NSExpression *)iconImageName { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconImage(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconImage()); + propertyValue = self.rawLayer->getDefaultIconImage(); } - return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } -- (void)setIconImage:(MGLStyleValue<NSString *> *)iconImage { +- (void)setIconImage:(NSExpression *)iconImage { } -- (MGLStyleValue<NSString *> *)iconImage { +- (NSExpression *)iconImage { return self.iconImageName; } -- (void)setIconOffset:(MGLStyleValue<NSValue *> *)iconOffset { +- (void)setIconOffset:(NSExpression *)iconOffset { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenPropertyValue(iconOffset); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::array<float, 2>>>(iconOffset); self.rawLayer->setIconOffset(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconOffset { +- (NSExpression *)iconOffset { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconOffset(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOffset()); + propertyValue = self.rawLayer->getDefaultIconOffset(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } -- (void)setIconOptional:(MGLStyleValue<NSNumber *> *)iconOptional { +- (void)setIconOptional:(NSExpression *)iconOptional { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(iconOptional); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(iconOptional); self.rawLayer->setIconOptional(mbglValue); } -- (MGLStyleValue<NSNumber *> *)isIconOptional { +- (NSExpression *)isIconOptional { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconOptional(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconOptional()); + propertyValue = self.rawLayer->getDefaultIconOptional(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setIconPadding:(MGLStyleValue<NSNumber *> *)iconPadding { +- (void)setIconPadding:(NSExpression *)iconPadding { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconPadding); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(iconPadding); self.rawLayer->setIconPadding(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconPadding { +- (NSExpression *)iconPadding { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconPadding(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconPadding()); + propertyValue = self.rawLayer->getDefaultIconPadding(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setIconPitchAlignment:(MGLStyleValue<NSValue *> *)iconPitchAlignment { +- (void)setIconPitchAlignment:(NSExpression *)iconPitchAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumPropertyValue(iconPitchAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(iconPitchAlignment); self.rawLayer->setIconPitchAlignment(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconPitchAlignment { +- (NSExpression *)iconPitchAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconPitchAlignment(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconPitchAlignment()); + propertyValue = self.rawLayer->getDefaultIconPitchAlignment(); } - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toExpression(propertyValue); } -- (void)setIconRotation:(MGLStyleValue<NSNumber *> *)iconRotation { +- (void)setIconRotation:(NSExpression *)iconRotation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconRotation); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconRotation); self.rawLayer->setIconRotate(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconRotation { +- (NSExpression *)iconRotation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconRotate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconRotate()); + propertyValue = self.rawLayer->getDefaultIconRotate(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setIconRotate:(MGLStyleValue<NSNumber *> *)iconRotate { +- (void)setIconRotate:(NSExpression *)iconRotate { } -- (MGLStyleValue<NSNumber *> *)iconRotate { +- (NSExpression *)iconRotate { return self.iconRotation; } -- (void)setIconRotationAlignment:(MGLStyleValue<NSValue *> *)iconRotationAlignment { +- (void)setIconRotationAlignment:(NSExpression *)iconRotationAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumPropertyValue(iconRotationAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(iconRotationAlignment); self.rawLayer->setIconRotationAlignment(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconRotationAlignment { +- (NSExpression *)iconRotationAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconRotationAlignment(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconRotationAlignment()); + propertyValue = self.rawLayer->getDefaultIconRotationAlignment(); } - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconRotationAlignment>().toExpression(propertyValue); } -- (void)setIconScale:(MGLStyleValue<NSNumber *> *)iconScale { +- (void)setIconScale:(NSExpression *)iconScale { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconScale); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconScale); self.rawLayer->setIconSize(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconScale { +- (NSExpression *)iconScale { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconSize(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconSize()); + propertyValue = self.rawLayer->getDefaultIconSize(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setIconSize:(MGLStyleValue<NSNumber *> *)iconSize { +- (void)setIconSize:(NSExpression *)iconSize { } -- (MGLStyleValue<NSNumber *> *)iconSize { +- (NSExpression *)iconSize { return self.iconScale; } -- (void)setIconTextFit:(MGLStyleValue<NSValue *> *)iconTextFit { +- (void)setIconTextFit:(NSExpression *)iconTextFit { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumPropertyValue(iconTextFit); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::IconTextFitType>>(iconTextFit); self.rawLayer->setIconTextFit(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconTextFit { +- (NSExpression *)iconTextFit { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconTextFit(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumStyleValue(self.rawLayer->getDefaultIconTextFit()); + propertyValue = self.rawLayer->getDefaultIconTextFit(); } - return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::IconTextFitType, NSValue *, mbgl::style::IconTextFitType, MGLIconTextFit>().toExpression(propertyValue); } -- (void)setIconTextFitPadding:(MGLStyleValue<NSValue *> *)iconTextFitPadding { +- (void)setIconTextFitPadding:(NSExpression *)iconTextFitPadding { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toInterpolatablePropertyValue(iconTextFitPadding); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 4>>>(iconTextFitPadding); self.rawLayer->setIconTextFitPadding(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconTextFitPadding { +- (NSExpression *)iconTextFitPadding { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconTextFitPadding(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toStyleValue(self.rawLayer->getDefaultIconTextFitPadding()); + propertyValue = self.rawLayer->getDefaultIconTextFitPadding(); } - return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 4>, NSValue *>().toExpression(propertyValue); } -- (void)setKeepsIconUpright:(MGLStyleValue<NSNumber *> *)keepsIconUpright { +- (void)setKeepsIconUpright:(NSExpression *)keepsIconUpright { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(keepsIconUpright); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(keepsIconUpright); self.rawLayer->setIconKeepUpright(mbglValue); } -- (MGLStyleValue<NSNumber *> *)keepsIconUpright { +- (NSExpression *)keepsIconUpright { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconKeepUpright(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconKeepUpright()); + propertyValue = self.rawLayer->getDefaultIconKeepUpright(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setIconKeepUpright:(MGLStyleValue<NSNumber *> *)iconKeepUpright { +- (void)setIconKeepUpright:(NSExpression *)iconKeepUpright { } -- (MGLStyleValue<NSNumber *> *)iconKeepUpright { +- (NSExpression *)iconKeepUpright { return self.keepsIconUpright; } -- (void)setKeepsTextUpright:(MGLStyleValue<NSNumber *> *)keepsTextUpright { +- (void)setKeepsTextUpright:(NSExpression *)keepsTextUpright { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(keepsTextUpright); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(keepsTextUpright); self.rawLayer->setTextKeepUpright(mbglValue); } -- (MGLStyleValue<NSNumber *> *)keepsTextUpright { +- (NSExpression *)keepsTextUpright { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextKeepUpright(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextKeepUpright()); + propertyValue = self.rawLayer->getDefaultTextKeepUpright(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setTextKeepUpright:(MGLStyleValue<NSNumber *> *)textKeepUpright { +- (void)setTextKeepUpright:(NSExpression *)textKeepUpright { } -- (MGLStyleValue<NSNumber *> *)textKeepUpright { +- (NSExpression *)textKeepUpright { return self.keepsTextUpright; } -- (void)setMaximumTextAngle:(MGLStyleValue<NSNumber *> *)maximumTextAngle { +- (void)setMaximumTextAngle:(NSExpression *)maximumTextAngle { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumTextAngle); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(maximumTextAngle); self.rawLayer->setTextMaxAngle(mbglValue); } -- (MGLStyleValue<NSNumber *> *)maximumTextAngle { +- (NSExpression *)maximumTextAngle { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextMaxAngle(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextMaxAngle()); + propertyValue = self.rawLayer->getDefaultTextMaxAngle(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextMaxAngle:(MGLStyleValue<NSNumber *> *)textMaxAngle { +- (void)setTextMaxAngle:(NSExpression *)textMaxAngle { } -- (MGLStyleValue<NSNumber *> *)textMaxAngle { +- (NSExpression *)textMaxAngle { return self.maximumTextAngle; } -- (void)setMaximumTextWidth:(MGLStyleValue<NSNumber *> *)maximumTextWidth { +- (void)setMaximumTextWidth:(NSExpression *)maximumTextWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(maximumTextWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(maximumTextWidth); self.rawLayer->setTextMaxWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)maximumTextWidth { +- (NSExpression *)maximumTextWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextMaxWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextMaxWidth()); + propertyValue = self.rawLayer->getDefaultTextMaxWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextMaxWidth:(MGLStyleValue<NSNumber *> *)textMaxWidth { +- (void)setTextMaxWidth:(NSExpression *)textMaxWidth { } -- (MGLStyleValue<NSNumber *> *)textMaxWidth { +- (NSExpression *)textMaxWidth { return self.maximumTextWidth; } -- (void)setSymbolAvoidsEdges:(MGLStyleValue<NSNumber *> *)symbolAvoidsEdges { +- (void)setSymbolAvoidsEdges:(NSExpression *)symbolAvoidsEdges { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(symbolAvoidsEdges); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(symbolAvoidsEdges); self.rawLayer->setSymbolAvoidEdges(mbglValue); } -- (MGLStyleValue<NSNumber *> *)symbolAvoidsEdges { +- (NSExpression *)symbolAvoidsEdges { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getSymbolAvoidEdges(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultSymbolAvoidEdges()); + propertyValue = self.rawLayer->getDefaultSymbolAvoidEdges(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setSymbolAvoidEdges:(MGLStyleValue<NSNumber *> *)symbolAvoidEdges { +- (void)setSymbolAvoidEdges:(NSExpression *)symbolAvoidEdges { } -- (MGLStyleValue<NSNumber *> *)symbolAvoidEdges { +- (NSExpression *)symbolAvoidEdges { return self.symbolAvoidsEdges; } -- (void)setSymbolPlacement:(MGLStyleValue<NSValue *> *)symbolPlacement { +- (void)setSymbolPlacement:(NSExpression *)symbolPlacement { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumPropertyValue(symbolPlacement); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::SymbolPlacementType>>(symbolPlacement); self.rawLayer->setSymbolPlacement(mbglValue); } -- (MGLStyleValue<NSValue *> *)symbolPlacement { +- (NSExpression *)symbolPlacement { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getSymbolPlacement(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumStyleValue(self.rawLayer->getDefaultSymbolPlacement()); + propertyValue = self.rawLayer->getDefaultSymbolPlacement(); } - return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::SymbolPlacementType, NSValue *, mbgl::style::SymbolPlacementType, MGLSymbolPlacement>().toExpression(propertyValue); } -- (void)setSymbolSpacing:(MGLStyleValue<NSNumber *> *)symbolSpacing { +- (void)setSymbolSpacing:(NSExpression *)symbolSpacing { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(symbolSpacing); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(symbolSpacing); self.rawLayer->setSymbolSpacing(mbglValue); } -- (MGLStyleValue<NSNumber *> *)symbolSpacing { +- (NSExpression *)symbolSpacing { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getSymbolSpacing(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultSymbolSpacing()); + propertyValue = self.rawLayer->getDefaultSymbolSpacing(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setText:(MGLStyleValue<NSString *> *)text { +- (void)setText:(NSExpression *)text { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenPropertyValue(text); + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::string>>(text); self.rawLayer->setTextField(mbglValue); } -- (MGLStyleValue<NSString *> *)text { +- (NSExpression *)text { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextField(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextField()); + propertyValue = self.rawLayer->getDefaultTextField(); } - return MGLStyleValueTransformer<std::string, NSString *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue); } -- (void)setTextField:(MGLStyleValue<NSString *> *)textField { +- (void)setTextField:(NSExpression *)textField { } -- (MGLStyleValue<NSString *> *)textField { +- (NSExpression *)textField { return self.text; } -- (void)setTextAllowsOverlap:(MGLStyleValue<NSNumber *> *)textAllowsOverlap { +- (void)setTextAllowsOverlap:(NSExpression *)textAllowsOverlap { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textAllowsOverlap); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textAllowsOverlap); self.rawLayer->setTextAllowOverlap(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textAllowsOverlap { +- (NSExpression *)textAllowsOverlap { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextAllowOverlap(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextAllowOverlap()); + propertyValue = self.rawLayer->getDefaultTextAllowOverlap(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setTextAllowOverlap:(MGLStyleValue<NSNumber *> *)textAllowOverlap { +- (void)setTextAllowOverlap:(NSExpression *)textAllowOverlap { } -- (MGLStyleValue<NSNumber *> *)textAllowOverlap { +- (NSExpression *)textAllowOverlap { return self.textAllowsOverlap; } -- (void)setTextAnchor:(MGLStyleValue<NSValue *> *)textAnchor { +- (void)setTextAnchor:(NSExpression *)textAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType>>(textAnchor); self.rawLayer->setTextAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)textAnchor { +- (NSExpression *)textAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor()); + propertyValue = self.rawLayer->getDefaultTextAnchor(); } - return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toExpression(propertyValue); } -- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames { +- (void)setTextFontNames:(NSExpression *)textFontNames { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenPropertyValue(textFontNames); + auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::vector<std::string>>>(textFontNames); self.rawLayer->setTextFont(mbglValue); } -- (MGLStyleValue<NSArray<NSString *> *> *)textFontNames { +- (NSExpression *)textFontNames { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextFont(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextFont()); + propertyValue = self.rawLayer->getDefaultTextFont(); } - return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toExpression(propertyValue); } -- (void)setTextFont:(MGLStyleValue<NSArray<NSString *> *> *)textFont { +- (void)setTextFont:(NSExpression *)textFont { } -- (MGLStyleValue<NSArray<NSString *> *> *)textFont { +- (NSExpression *)textFont { return self.textFontNames; } -- (void)setTextFontSize:(MGLStyleValue<NSNumber *> *)textFontSize { +- (void)setTextFontSize:(NSExpression *)textFontSize { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textFontSize); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textFontSize); self.rawLayer->setTextSize(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textFontSize { +- (NSExpression *)textFontSize { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextSize(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextSize()); + propertyValue = self.rawLayer->getDefaultTextSize(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextSize:(MGLStyleValue<NSNumber *> *)textSize { +- (void)setTextSize:(NSExpression *)textSize { } -- (MGLStyleValue<NSNumber *> *)textSize { +- (NSExpression *)textSize { return self.textFontSize; } -- (void)setTextIgnoresPlacement:(MGLStyleValue<NSNumber *> *)textIgnoresPlacement { +- (void)setTextIgnoresPlacement:(NSExpression *)textIgnoresPlacement { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textIgnoresPlacement); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textIgnoresPlacement); self.rawLayer->setTextIgnorePlacement(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textIgnoresPlacement { +- (NSExpression *)textIgnoresPlacement { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextIgnorePlacement(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextIgnorePlacement()); + propertyValue = self.rawLayer->getDefaultTextIgnorePlacement(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setTextIgnorePlacement:(MGLStyleValue<NSNumber *> *)textIgnorePlacement { +- (void)setTextIgnorePlacement:(NSExpression *)textIgnorePlacement { } -- (MGLStyleValue<NSNumber *> *)textIgnorePlacement { +- (NSExpression *)textIgnorePlacement { return self.textIgnoresPlacement; } -- (void)setTextJustification:(MGLStyleValue<NSValue *> *)textJustification { +- (void)setTextJustification:(NSExpression *)textJustification { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenPropertyValue(textJustification); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::TextJustifyType>>(textJustification); self.rawLayer->setTextJustify(mbglValue); } -- (MGLStyleValue<NSValue *> *)textJustification { +- (NSExpression *)textJustification { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextJustify(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextJustify()); + propertyValue = self.rawLayer->getDefaultTextJustify(); } - return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toExpression(propertyValue); } -- (void)setTextJustify:(MGLStyleValue<NSValue *> *)textJustify { +- (void)setTextJustify:(NSExpression *)textJustify { } -- (MGLStyleValue<NSValue *> *)textJustify { +- (NSExpression *)textJustify { return self.textJustification; } -- (void)setTextLetterSpacing:(MGLStyleValue<NSNumber *> *)textLetterSpacing { +- (void)setTextLetterSpacing:(NSExpression *)textLetterSpacing { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textLetterSpacing); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textLetterSpacing); self.rawLayer->setTextLetterSpacing(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textLetterSpacing { +- (NSExpression *)textLetterSpacing { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextLetterSpacing(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextLetterSpacing()); + propertyValue = self.rawLayer->getDefaultTextLetterSpacing(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextLineHeight:(MGLStyleValue<NSNumber *> *)textLineHeight { +- (void)setTextLineHeight:(NSExpression *)textLineHeight { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textLineHeight); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(textLineHeight); self.rawLayer->setTextLineHeight(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textLineHeight { +- (NSExpression *)textLineHeight { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextLineHeight(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextLineHeight()); + propertyValue = self.rawLayer->getDefaultTextLineHeight(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextOffset:(MGLStyleValue<NSValue *> *)textOffset { +- (void)setTextOffset:(NSExpression *)textOffset { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenPropertyValue(textOffset); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<std::array<float, 2>>>(textOffset); self.rawLayer->setTextOffset(mbglValue); } -- (MGLStyleValue<NSValue *> *)textOffset { +- (NSExpression *)textOffset { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextOffset(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOffset()); + propertyValue = self.rawLayer->getDefaultTextOffset(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } -- (void)setTextOptional:(MGLStyleValue<NSNumber *> *)textOptional { +- (void)setTextOptional:(NSExpression *)textOptional { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue(textOptional); + auto mbglValue = MGLStyleValueTransformer<bool, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<bool>>(textOptional); self.rawLayer->setTextOptional(mbglValue); } -- (MGLStyleValue<NSNumber *> *)isTextOptional { +- (NSExpression *)isTextOptional { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextOptional(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextOptional()); + propertyValue = self.rawLayer->getDefaultTextOptional(); } - return MGLStyleValueTransformer<bool, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<bool, NSNumber *>().toExpression(propertyValue); } -- (void)setTextPadding:(MGLStyleValue<NSNumber *> *)textPadding { +- (void)setTextPadding:(NSExpression *)textPadding { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textPadding); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::PropertyValue<float>>(textPadding); self.rawLayer->setTextPadding(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textPadding { +- (NSExpression *)textPadding { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextPadding(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextPadding()); + propertyValue = self.rawLayer->getDefaultTextPadding(); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextPitchAlignment:(MGLStyleValue<NSValue *> *)textPitchAlignment { +- (void)setTextPitchAlignment:(NSExpression *)textPitchAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumPropertyValue(textPitchAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(textPitchAlignment); self.rawLayer->setTextPitchAlignment(mbglValue); } -- (MGLStyleValue<NSValue *> *)textPitchAlignment { +- (NSExpression *)textPitchAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextPitchAlignment(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultTextPitchAlignment()); + propertyValue = self.rawLayer->getDefaultTextPitchAlignment(); } - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextPitchAlignment>().toExpression(propertyValue); } -- (void)setTextRotation:(MGLStyleValue<NSNumber *> *)textRotation { +- (void)setTextRotation:(NSExpression *)textRotation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textRotation); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textRotation); self.rawLayer->setTextRotate(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textRotation { +- (NSExpression *)textRotation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextRotate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextRotate()); + propertyValue = self.rawLayer->getDefaultTextRotate(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } -- (void)setTextRotate:(MGLStyleValue<NSNumber *> *)textRotate { +- (void)setTextRotate:(NSExpression *)textRotate { } -- (MGLStyleValue<NSNumber *> *)textRotate { +- (NSExpression *)textRotate { return self.textRotation; } -- (void)setTextRotationAlignment:(MGLStyleValue<NSValue *> *)textRotationAlignment { +- (void)setTextRotationAlignment:(NSExpression *)textRotationAlignment { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumPropertyValue(textRotationAlignment); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::AlignmentType>>(textRotationAlignment); self.rawLayer->setTextRotationAlignment(mbglValue); } -- (MGLStyleValue<NSValue *> *)textRotationAlignment { +- (NSExpression *)textRotationAlignment { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextRotationAlignment(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumStyleValue(self.rawLayer->getDefaultTextRotationAlignment()); + propertyValue = self.rawLayer->getDefaultTextRotationAlignment(); } - return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLTextRotationAlignment>().toExpression(propertyValue); } -- (void)setTextTransform:(MGLStyleValue<NSValue *> *)textTransform { +- (void)setTextTransform:(NSExpression *)textTransform { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenPropertyValue(textTransform); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::style::TextTransformType>>(textTransform); self.rawLayer->setTextTransform(mbglValue); } -- (MGLStyleValue<NSValue *> *)textTransform { +- (NSExpression *)textTransform { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextTransform(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextTransform()); + propertyValue = self.rawLayer->getDefaultTextTransform(); } - return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TextTransformType, NSValue *, mbgl::style::TextTransformType, MGLTextTransform>().toExpression(propertyValue); } #pragma mark - Accessing the Paint Attributes -- (void)setIconColor:(MGLStyleValue<MGLColor *> *)iconColor { +- (void)setIconColor:(NSExpression *)iconColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(iconColor); self.rawLayer->setIconColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)iconColor { +- (NSExpression *)iconColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconColor()); + propertyValue = self.rawLayer->getDefaultIconColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setIconColorTransition:(MGLTransition )transition { @@ -922,21 +922,21 @@ namespace mbgl { return transition; } -- (void)setIconHaloBlur:(MGLStyleValue<NSNumber *> *)iconHaloBlur { +- (void)setIconHaloBlur:(NSExpression *)iconHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconHaloBlur); self.rawLayer->setIconHaloBlur(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconHaloBlur { +- (NSExpression *)iconHaloBlur { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloBlur()); + propertyValue = self.rawLayer->getDefaultIconHaloBlur(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setIconHaloBlurTransition:(MGLTransition )transition { @@ -957,21 +957,21 @@ namespace mbgl { return transition; } -- (void)setIconHaloColor:(MGLStyleValue<MGLColor *> *)iconHaloColor { +- (void)setIconHaloColor:(NSExpression *)iconHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconHaloColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(iconHaloColor); self.rawLayer->setIconHaloColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)iconHaloColor { +- (NSExpression *)iconHaloColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloColor()); + propertyValue = self.rawLayer->getDefaultIconHaloColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setIconHaloColorTransition:(MGLTransition )transition { @@ -992,21 +992,21 @@ namespace mbgl { return transition; } -- (void)setIconHaloWidth:(MGLStyleValue<NSNumber *> *)iconHaloWidth { +- (void)setIconHaloWidth:(NSExpression *)iconHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconHaloWidth); self.rawLayer->setIconHaloWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconHaloWidth { +- (NSExpression *)iconHaloWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloWidth()); + propertyValue = self.rawLayer->getDefaultIconHaloWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setIconHaloWidthTransition:(MGLTransition )transition { @@ -1027,21 +1027,21 @@ namespace mbgl { return transition; } -- (void)setIconOpacity:(MGLStyleValue<NSNumber *> *)iconOpacity { +- (void)setIconOpacity:(NSExpression *)iconOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(iconOpacity); self.rawLayer->setIconOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)iconOpacity { +- (NSExpression *)iconOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOpacity()); + propertyValue = self.rawLayer->getDefaultIconOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setIconOpacityTransition:(MGLTransition )transition { @@ -1062,21 +1062,21 @@ namespace mbgl { return transition; } -- (void)setIconTranslation:(MGLStyleValue<NSValue *> *)iconTranslation { +- (void)setIconTranslation:(NSExpression *)iconTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(iconTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(iconTranslation); self.rawLayer->setIconTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconTranslation { +- (NSExpression *)iconTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultIconTranslate()); + propertyValue = self.rawLayer->getDefaultIconTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setIconTranslationTransition:(MGLTransition )transition { @@ -1097,52 +1097,52 @@ namespace mbgl { return transition; } -- (void)setIconTranslate:(MGLStyleValue<NSValue *> *)iconTranslate { +- (void)setIconTranslate:(NSExpression *)iconTranslate { } -- (MGLStyleValue<NSValue *> *)iconTranslate { +- (NSExpression *)iconTranslate { return self.iconTranslation; } -- (void)setIconTranslationAnchor:(MGLStyleValue<NSValue *> *)iconTranslationAnchor { +- (void)setIconTranslationAnchor:(NSExpression *)iconTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumPropertyValue(iconTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(iconTranslationAnchor); self.rawLayer->setIconTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)iconTranslationAnchor { +- (NSExpression *)iconTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getIconTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultIconTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultIconTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLIconTranslationAnchor>().toExpression(propertyValue); } -- (void)setIconTranslateAnchor:(MGLStyleValue<NSValue *> *)iconTranslateAnchor { +- (void)setIconTranslateAnchor:(NSExpression *)iconTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)iconTranslateAnchor { +- (NSExpression *)iconTranslateAnchor { return self.iconTranslationAnchor; } -- (void)setTextColor:(MGLStyleValue<MGLColor *> *)textColor { +- (void)setTextColor:(NSExpression *)textColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(textColor); self.rawLayer->setTextColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)textColor { +- (NSExpression *)textColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextColor()); + propertyValue = self.rawLayer->getDefaultTextColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setTextColorTransition:(MGLTransition )transition { @@ -1163,21 +1163,21 @@ namespace mbgl { return transition; } -- (void)setTextHaloBlur:(MGLStyleValue<NSNumber *> *)textHaloBlur { +- (void)setTextHaloBlur:(NSExpression *)textHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textHaloBlur); self.rawLayer->setTextHaloBlur(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textHaloBlur { +- (NSExpression *)textHaloBlur { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloBlur()); + propertyValue = self.rawLayer->getDefaultTextHaloBlur(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setTextHaloBlurTransition:(MGLTransition )transition { @@ -1198,21 +1198,21 @@ namespace mbgl { return transition; } -- (void)setTextHaloColor:(MGLStyleValue<MGLColor *> *)textHaloColor { +- (void)setTextHaloColor:(NSExpression *)textHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textHaloColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<mbgl::Color>>(textHaloColor); self.rawLayer->setTextHaloColor(mbglValue); } -- (MGLStyleValue<MGLColor *> *)textHaloColor { +- (NSExpression *)textHaloColor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloColor()); + propertyValue = self.rawLayer->getDefaultTextHaloColor(); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toExpression(propertyValue); } - (void)setTextHaloColorTransition:(MGLTransition )transition { @@ -1233,21 +1233,21 @@ namespace mbgl { return transition; } -- (void)setTextHaloWidth:(MGLStyleValue<NSNumber *> *)textHaloWidth { +- (void)setTextHaloWidth:(NSExpression *)textHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textHaloWidth); self.rawLayer->setTextHaloWidth(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textHaloWidth { +- (NSExpression *)textHaloWidth { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloWidth()); + propertyValue = self.rawLayer->getDefaultTextHaloWidth(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setTextHaloWidthTransition:(MGLTransition )transition { @@ -1268,21 +1268,21 @@ namespace mbgl { return transition; } -- (void)setTextOpacity:(MGLStyleValue<NSNumber *> *)textOpacity { +- (void)setTextOpacity:(NSExpression *)textOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<float>>(textOpacity); self.rawLayer->setTextOpacity(mbglValue); } -- (MGLStyleValue<NSNumber *> *)textOpacity { +- (NSExpression *)textOpacity { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOpacity()); + propertyValue = self.rawLayer->getDefaultTextOpacity(); } - return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toExpression(propertyValue); } - (void)setTextOpacityTransition:(MGLTransition )transition { @@ -1303,21 +1303,21 @@ namespace mbgl { return transition; } -- (void)setTextTranslation:(MGLStyleValue<NSValue *> *)textTranslation { +- (void)setTextTranslation:(NSExpression *)textTranslation { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(textTranslation); + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toPropertyValue<mbgl::style::PropertyValue<std::array<float, 2>>>(textTranslation); self.rawLayer->setTextTranslate(mbglValue); } -- (MGLStyleValue<NSValue *> *)textTranslation { +- (NSExpression *)textTranslation { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextTranslate(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultTextTranslate()); + propertyValue = self.rawLayer->getDefaultTextTranslate(); } - return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toExpression(propertyValue); } - (void)setTextTranslationTransition:(MGLTransition )transition { @@ -1338,34 +1338,34 @@ namespace mbgl { return transition; } -- (void)setTextTranslate:(MGLStyleValue<NSValue *> *)textTranslate { +- (void)setTextTranslate:(NSExpression *)textTranslate { } -- (MGLStyleValue<NSValue *> *)textTranslate { +- (NSExpression *)textTranslate { return self.textTranslation; } -- (void)setTextTranslationAnchor:(MGLStyleValue<NSValue *> *)textTranslationAnchor { +- (void)setTextTranslationAnchor:(NSExpression *)textTranslationAnchor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumPropertyValue(textTranslationAnchor); + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toPropertyValue<mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType>>(textTranslationAnchor); self.rawLayer->setTextTranslateAnchor(mbglValue); } -- (MGLStyleValue<NSValue *> *)textTranslationAnchor { +- (NSExpression *)textTranslationAnchor { MGLAssertStyleLayerIsValid(); auto propertyValue = self.rawLayer->getTextTranslateAnchor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultTextTranslateAnchor()); + propertyValue = self.rawLayer->getDefaultTextTranslateAnchor(); } - return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toEnumStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLTextTranslationAnchor>().toExpression(propertyValue); } -- (void)setTextTranslateAnchor:(MGLStyleValue<NSValue *> *)textTranslateAnchor { +- (void)setTextTranslateAnchor:(NSExpression *)textTranslateAnchor { } -- (MGLStyleValue<NSValue *> *)textTranslateAnchor { +- (NSExpression *)textTranslateAnchor { return self.textTranslationAnchor; } diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h index 6603570e25..7780a34c7f 100644 --- a/platform/darwin/src/MGLVectorStyleLayer.h +++ b/platform/darwin/src/MGLVectorStyleLayer.h @@ -34,108 +34,9 @@ MGL_EXPORT comes from the style, its predicate corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layer-filter">`filter`</a> property in the style JSON. - - The following comparison operators are supported. - - <ul> - <li><code>NSEqualToPredicateOperatorType</code> (<code>=</code>, <code>==</code>)</li> - <li><code>NSGreaterThanOrEqualToPredicateOperatorType</code> (<code>>=</code>, <code>=></code>)</li> - <li><code>NSLessThanOrEqualToPredicateOperatorType</code> (<code><=</code>, <code>=<</code>)</li> - <li><code>NSGreaterThanPredicateOperatorType</code> (<code>></code>)</li> - <li><code>NSLessThanPredicateOperatorType</code> (<code><</code>)</li> - <li><code>NSNotEqualToPredicateOperatorType</code> (<code>!=</code>, <code><></code>)</li> - <li><code>NSBetweenPredicateOperatorType</code> (<code>BETWEEN</code>)</li> - </ul> - - The following compound operators are supported: - - <ul> - <li><code>NSAndPredicateType</code> (<code>AND</code>, <code>&&</code>)</li> - <li><code>NSOrPredicateType</code> (<code>OR</code>, <code>||</code>)</li> - <li><code>NSNotPredicateType</code> (<code>NOT</code>, <code>!</code>)</li> - </ul> - - The following aggregate operators are supported: - - <ul> - <li><code>NSInPredicateOperatorType</code> (<code>IN</code>)</li> - <li><code>NSContainsPredicateOperatorType</code> (<code>CONTAINS</code>)</li> - </ul> - - To test whether a feature has or lacks a specific attribute, compare the - attribute to `NULL` or `NIL`. Predicates created using the - `+[NSPredicate predicateWithValue:]` method are also supported. String - operators and custom operators are not supported. - - For details about the predicate format string syntax, consult the “Predicate - Format String Syntax” chapter of the - “<a href="https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/">Predicate Programming Guide</a>” - in Apple developer documentation. - - The predicate's left-hand expression must be a string that identifies a feature - attribute or, alternatively, one of the following special attributes: - - <table> - <thead> - <tr><th>Attribute</th><th>Meaning</th></tr> - </thead> - <tbody> - <tr> - <td><code>$id</code></td> - <td> - A value that uniquely identifies the feature in the containing source. - For details on the types of values that may be associated with this key, - consult the documentation for the <code>MGLFeature</code> protocol’s - <code>identifier</code> property. - </td> - </tr> - <tr> - <td><code>$type</code></td> - <td> - The type of geometry represented by the feature. A feature’s type is - guaranteed to be one of the following strings: - <ul> - <li> - <code>Point</code> for point features, corresponding to the - <code>MGLPointAnnotation</code> class - </li> - <li> - <code>LineString</code> for polyline features, corresponding to - the <code>MGLPolyline</code> class - </li> - <li> - <code>Polygon</code> for polygon features, corresponding to the - <code>MGLPolygon</code> class - </li> - </ul> - </td> - </tr> - <tr> - <td><code>point_count</code></td> - <td>The number of point features in a given cluster.</td> - </tr> - </tbody> - </table> - - The predicate’s right-hand expression must be an `NSString` (to match strings) - or `NSNumber` (to match numbers, including Boolean values) or an array of - `NSString`s or `NSNumber`s, depending on the operator and the type of values - expected for the attribute being tested. For floating-point values, use - `-[NSNumber numberWithDouble:]` instead of `-[NSNumber numberWithFloat:]` - to avoid precision issues. - - Automatic type casting is not performed. Therefore, a feature only matches this - predicate if its value for the attribute in question is of the same type as the - value specified in the predicate. Also, operator modifiers such as `c` (for - case insensitivity), `d` (for diacritic insensitivity), and `l` (for locale - sensitivity) are unsupported for comparison and aggregate operators that are - used in the predicate. - - It is possible to create expressions that contain special characters in the - predicate format syntax. This includes the `$` in the `$id` and `$type` special - style attributes and also `hyphen-minus` and `tag:subtag`. However, you must use - `%K` in the format string to represent these variables: - `@"%K == 'LineString'", @"$type"`. + + See the “<a href="../predicates-and-expressions.html">Predicates and Expressions</a>” + guide for details about the predicate syntax supported by this class. ### Example diff --git a/platform/darwin/src/NSArray+MGLAdditions.mm b/platform/darwin/src/NSArray+MGLAdditions.mm index 3cab7ff427..195e96b09d 100644 --- a/platform/darwin/src/NSArray+MGLAdditions.mm +++ b/platform/darwin/src/NSArray+MGLAdditions.mm @@ -1,7 +1,7 @@ #import "NSArray+MGLAdditions.h" #import "NSDictionary+MGLAdditions.h" -#import "NSExpression+MGLAdditions.mm" +#import "NSExpression+MGLPrivateAdditions.h" @implementation NSArray (MGLAdditions) diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index ac2d598d05..5b54d66aeb 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -1,7 +1,7 @@ #import "NSComparisonPredicate+MGLAdditions.h" #import "NSPredicate+MGLAdditions.h" -#import "NSExpression+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" @implementation NSComparisonPredicate (MGLAdditions) @@ -294,3 +294,45 @@ } @end + +@implementation NSComparisonPredicate (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + NSString *op; + switch (self.predicateOperatorType) { + case NSLessThanPredicateOperatorType: + op = @"<"; + break; + case NSLessThanOrEqualToPredicateOperatorType: + op = @"<="; + break; + case NSGreaterThanPredicateOperatorType: + op = @">"; + break; + case NSGreaterThanOrEqualToPredicateOperatorType: + op = @">="; + break; + case NSEqualToPredicateOperatorType: + op = @"=="; + break; + case NSNotEqualToPredicateOperatorType: + op = @"!="; + break; + case NSMatchesPredicateOperatorType: + case NSLikePredicateOperatorType: + case NSBeginsWithPredicateOperatorType: + case NSEndsWithPredicateOperatorType: + case NSInPredicateOperatorType: + case NSCustomSelectorPredicateOperatorType: + case NSContainsPredicateOperatorType: + case NSBetweenPredicateOperatorType: + [NSException raise:NSInvalidArgumentException + format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType]; + } + if (op) { + return @[op, self.leftExpression.mgl_jsonExpressionObject, self.rightExpression.mgl_jsonExpressionObject]; + } + return nil; +} + +@end diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm index 0039b5af82..19568b8033 100644 --- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm @@ -1,7 +1,7 @@ #import "NSCompoundPredicate+MGLAdditions.h" #import "NSPredicate+MGLAdditions.h" -#import "NSExpression+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" @implementation NSCompoundPredicate (MGLAdditions) @@ -71,3 +71,31 @@ } @end + +@implementation NSCompoundPredicate (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + switch (self.compoundPredicateType) { + case NSNotPredicateType: { + NSAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates."); + NSPredicate *subpredicate = self.subpredicates.firstObject; + return @[@"!", subpredicate.mgl_jsonExpressionObject]; + } + + case NSAndPredicateType: { + NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"]; + return [@[@"all"] arrayByAddingObjectsFromArray:subarrays]; + } + + case NSOrPredicateType: { + NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"]; + return [@[@"any"] arrayByAddingObjectsFromArray:subarrays]; + } + } + + [NSException raise:@"Compound predicate type not handled" + format:@""]; + return nil; +} + +@end diff --git a/platform/darwin/src/NSDictionary+MGLAdditions.mm b/platform/darwin/src/NSDictionary+MGLAdditions.mm index aad7fd8810..4bc7ddb3cf 100644 --- a/platform/darwin/src/NSDictionary+MGLAdditions.mm +++ b/platform/darwin/src/NSDictionary+MGLAdditions.mm @@ -1,6 +1,6 @@ #import "NSDictionary+MGLAdditions.h" -#import "NSExpression+MGLAdditions.mm" +#import "NSExpression+MGLPrivateAdditions.h" #import "NSArray+MGLAdditions.h" @implementation NSDictionary (MGLAdditions) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h index 491ed5a536..b4d36bc1b1 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.h +++ b/platform/darwin/src/NSExpression+MGLAdditions.h @@ -1,17 +1,46 @@ #import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE + #import <UIKit/UIKit.h> +#else + #import <Cocoa/Cocoa.h> +#endif -#include <mbgl/style/filter.hpp> +#import "MGLTypes.h" NS_ASSUME_NONNULL_BEGIN @interface NSExpression (MGLAdditions) -@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue; -@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue; -@property (nonatomic, readonly) mbgl::FeatureType mgl_featureType; -@property (nonatomic, readonly) std::vector<mbgl::FeatureType> mgl_aggregateFeatureType; -@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier; -@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier; +/** + Returns an expression equivalent to the given Foundation object deserialized + from JSON data. + + The Foundation object is interpreted according to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSExpression` representation used by this SDK. + + @param object A Foundation object deserialized from JSON data, for example + using `NSJSONSerialization`. + @return An initialized expression equivalent to `object`, suitable for use as + the value of a style layer attribute. + */ ++ (instancetype)mgl_expressionWithJSONObject:(id)object; + +/** + An equivalent Foundation object that can be serialized as JSON. + + The Foundation object conforms to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSExpression` representation used by this SDK. + + You can use `NSJSONSerialization` to serialize the Foundation object as data to + write to a file. + */ +@property (nonatomic, readonly) id mgl_jsonExpressionObject; @end diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index a7759cda9d..5ad565c398 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -1,13 +1,19 @@ -#import "NSExpression+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" #import "MGLTypes.h" #if TARGET_OS_IPHONE #import "UIColor+MGLAdditions.h" + #define MGLEdgeInsets UIEdgeInsets #else #import "NSColor+MGLAdditions.h" + #define MGLEdgeInsets NSEdgeInsets #endif +#import "NSPredicate+MGLAdditions.h" +#import "NSValue+MGLStyleAttributeAdditions.h" -@implementation NSExpression (MGLAdditions) +#import <mbgl/style/expression/expression.hpp> + +@implementation NSExpression (MGLPrivateAdditions) - (std::vector<mbgl::Value>)mgl_aggregateMBGLValue { if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) { @@ -154,3 +160,578 @@ } @end + +@implementation NSObject (MGLExpressionAdditions) + +- (NSNumber *)mgl_number { + return nil; +} + +- (NSNumber *)mgl_numberWithFallbackValues:(id)fallbackValue, ... { + if (self.mgl_number) { + return self.mgl_number; + } + + va_list fallbackValues; + va_start(fallbackValues, fallbackValue); + for (id value = fallbackValue; value; value = va_arg(fallbackValues, id)) { + if ([value mgl_number]) { + return [value mgl_number]; + } + } + + return nil; +} + +@end + +@implementation NSNull (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + return self; +} + +@end + +@implementation NSString (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + return self; +} + +- (NSNumber *)mgl_number { + if (self.doubleValue || ![[NSDecimalNumber decimalNumberWithString:self] isEqual:[NSDecimalNumber notANumber]]) { + return @(self.doubleValue); + } + + return nil; +} + +@end + +@implementation NSNumber (MGLExpressionAdditions) + +- (id)mgl_interpolateWithCurveType:(NSString *)curveType + parameters:(NSArray *)parameters + stops:(NSDictionary<NSNumber *, id> *)stops { + [NSException raise:NSInvalidArgumentException + format:@"Interpolation expressions lack underlying Objective-C implementations."]; + return nil; +} + +- (id)mgl_stepWithMinimum:(id)minimum stops:(NSDictionary<NSNumber *, id> *)stops { + [NSException raise:NSInvalidArgumentException + format:@"Interpolation expressions lack underlying Objective-C implementations."]; + return nil; +} + +- (NSNumber *)mgl_number { + return self; +} + +- (id)mgl_jsonExpressionObject { + if ([self isEqualToNumber:@(M_E)]) { + return @[@"e"]; + } else if ([self isEqualToNumber:@(M_PI)]) { + return @[@"pi"]; + } + return self; +} + +@end + +@implementation MGLColor (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + auto color = [self mgl_color]; + if (color.a == 1) { + return @[@"rgb", @(color.r * 255), @(color.g * 255), @(color.b * 255)]; + } + return @[@"rgba", @(color.r * 255), @(color.g * 255), @(color.b * 255), @(color.a)]; +} + +@end + +@implementation NSArray (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + return [self valueForKeyPath:@"mgl_jsonExpressionObject"]; +} + +@end + +@implementation NSDictionary (MGLExpressionAdditions) + +- (id)mgl_jsonExpressionObject { + NSMutableDictionary *expressionObject = [NSMutableDictionary dictionaryWithCapacity:self.count]; + [self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + expressionObject[[key mgl_jsonExpressionObject]] = [obj mgl_jsonExpressionObject]; + }]; + + return expressionObject; +} + +@end + +@implementation NSExpression (MGLExpressionAdditions) + +- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context { + [NSException raise:NSInternalInconsistencyException + format:@"Assignment expressions lack underlying Objective-C implementations."]; + return self; +} + +@end + +@implementation NSExpression (MGLAdditions) + +static NSDictionary<NSString *, NSString *> *MGLFunctionNamesByExpressionOperator; +static NSDictionary<NSString *, NSString *> *MGLExpressionOperatorsByFunctionNames; + +NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { + NSMutableArray *subexpressions = [NSMutableArray arrayWithCapacity:objects.count]; + for (id object in objects) { + NSExpression *expression = [NSExpression mgl_expressionWithJSONObject:object]; + [subexpressions addObject:expression]; + } + return subexpressions; +} + ++ (instancetype)mgl_expressionWithJSONObject:(id)object { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + MGLFunctionNamesByExpressionOperator = @{ + @"+": @"add:to:", + @"-": @"from:subtract:", + @"*": @"multiply:by:", + @"/": @"divide:by:", + @"%": @"modulus:by:", + @"sqrt": @"sqrt:", + @"log10": @"log:", + @"ln": @"ln:", + @"^": @"raise:toPower:", + @"upcase": @"uppercase:", + @"downcase": @"lowercase:", + }; + }); + + if (!object || object == [NSNull null]) { + return [NSExpression expressionForConstantValue:nil]; + } + + if ([object isKindOfClass:[NSString class]] || + [object isKindOfClass:[NSNumber class]] || + [object isKindOfClass:[NSValue class]] || + [object isKindOfClass:[MGLColor class]]) { + return [NSExpression expressionForConstantValue:object]; + } + + if ([object isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[object count]]; + [object enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + dictionary[key] = [NSExpression mgl_expressionWithJSONObject:obj]; + }]; + return [NSExpression expressionForConstantValue:dictionary]; + } + + if ([object isKindOfClass:[NSArray class]]) { + NSArray *array = (NSArray *)object; + NSString *op = array.firstObject; + + NSArray *argumentObjects = [array subarrayWithRange:NSMakeRange(1, array.count - 1)]; + + NSString *functionName = MGLFunctionNamesByExpressionOperator[op]; + if (functionName) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + if ([op isEqualToString:@"+"] && argumentObjects.count > 2) { + NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions]; + return [NSExpression expressionForFunction:@"sum:" + arguments:@[subexpression]]; + } else if ([op isEqualToString:@"^"] && [argumentObjects.firstObject isEqual:@[@"e"]]) { + functionName = @"exp:"; + subexpressions = [subexpressions subarrayWithRange:NSMakeRange(1, subexpressions.count - 1)]; + } + + return [NSExpression expressionForFunction:functionName + arguments:subexpressions]; + } else if ([op isEqualToString:@"literal"]) { + if ([argumentObjects.firstObject isKindOfClass:[NSArray class]]) { + return [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects.firstObject)]; + } + return [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + } else if ([op isEqualToString:@"to-boolean"]) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + return [NSExpression expressionForFunction:operand selectorName:@"boolValue" arguments:@[]]; + } else if ([op isEqualToString:@"to-number"]) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)]; + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + return [NSExpression expressionForFunction:operand selectorName:@"mgl_numberWithFallbackValues:" arguments:subexpressions]; + } else if ([op isEqualToString:@"to-string"]) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + return [NSExpression expressionForFunction:operand selectorName:@"stringValue" arguments:@[]]; + } else if ([op isEqualToString:@"get"]) { + if (argumentObjects.count == 2) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.lastObject]; + if ([argumentObjects.firstObject isKindOfClass:[NSString class]]) { + return [NSExpression expressionWithFormat:@"%@.%K", operand, argumentObjects.firstObject]; + } + NSExpression *key = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + return [NSExpression expressionWithFormat:@"%@.%@", operand, key]; + } + return [NSExpression expressionForKeyPath:argumentObjects.firstObject]; + } else if ([op isEqualToString:@"length"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + return [NSExpression expressionForFunction:@"count:" arguments:@[subexpressions.firstObject]]; + } else if ([op isEqualToString:@"min"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions]; + return [NSExpression expressionForFunction:@"min:" arguments:@[subexpression]]; + } else if ([op isEqualToString:@"max"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions]; + return [NSExpression expressionForFunction:@"max:" arguments:@[subexpression]]; + } else if ([op isEqualToString:@"e"]) { + return [NSExpression expressionForConstantValue:@(M_E)]; + } else if ([op isEqualToString:@"pi"]) { + return [NSExpression expressionForConstantValue:@(M_PI)]; + } else if ([op isEqualToString:@"concat"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects); + NSExpression *operand = subexpressions.firstObject; + subexpressions = [subexpressions subarrayWithRange:NSMakeRange(1, subexpressions.count - 1)]; + return [NSExpression expressionForFunction:operand selectorName:@"stringByAppendingString:" arguments:subexpressions]; + } else if ([op isEqualToString:@"interpolate"]) { + NSArray *interpolationOptions = argumentObjects.firstObject; + NSString *curveType = interpolationOptions.firstObject; + NSExpression *curveTypeExpression = [NSExpression mgl_expressionWithJSONObject:curveType]; + id curveParameters; + if ([curveType isEqual:@"exponential"]) { + curveParameters = interpolationOptions[1]; + } else if ([curveType isEqualToString:@"cubic-bezier"]) { + curveParameters = @[@"literal", [interpolationOptions subarrayWithRange:NSMakeRange(1, 4)]]; + } + NSExpression *curveParameterExpression = [NSExpression mgl_expressionWithJSONObject:curveParameters]; + argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)]; + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject]; + NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)]; + NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:stopExpressions.count / 2]; + NSEnumerator *stopEnumerator = stopExpressions.objectEnumerator; + while (NSNumber *key = stopEnumerator.nextObject) { + NSExpression *valueExpression = stopEnumerator.nextObject; + stops[key] = [NSExpression mgl_expressionWithJSONObject:valueExpression]; + } + NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops]; + return [NSExpression expressionForFunction:operand + selectorName:@"mgl_interpolateWithCurveType:parameters:stops:" + arguments:@[curveTypeExpression, curveParameterExpression, stopExpression]]; + } else if ([op isEqualToString:@"step"]) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects[0]]; + NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)]; + NSExpression *minimum; + if (stopExpressions.count % 2) { + minimum = [NSExpression mgl_expressionWithJSONObject:stopExpressions.firstObject]; + stopExpressions = [stopExpressions subarrayWithRange:NSMakeRange(1, stopExpressions.count - 1)]; + } + NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:stopExpressions.count / 2]; + NSEnumerator *stopEnumerator = stopExpressions.objectEnumerator; + while (NSNumber *key = stopEnumerator.nextObject) { + NSExpression *valueExpression = stopEnumerator.nextObject; + if (minimum) { + stops[key] = [NSExpression mgl_expressionWithJSONObject:valueExpression]; + } else { + minimum = [NSExpression mgl_expressionWithJSONObject:valueExpression]; + } + } + NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops]; + return [NSExpression expressionForFunction:operand + selectorName:@"mgl_stepWithMinimum:stops:" + arguments:@[minimum, stopExpression]]; + } else if ([op isEqualToString:@"zoom"]) { + return [NSExpression expressionForVariable:@"zoomLevel"]; + } else if ([op isEqualToString:@"heatmap-density"]) { + return [NSExpression expressionForVariable:@"heatmapDensity"]; + } else if ([op isEqualToString:@"let"]) { + NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.lastObject]; + NSArray *bindingObjects = [argumentObjects subarrayWithRange:NSMakeRange(0, argumentObjects.count - 1)]; + NSMutableDictionary *context = [NSMutableDictionary dictionaryWithCapacity:bindingObjects.count / 2]; + NSEnumerator *bindingEnumerator = bindingObjects.objectEnumerator; + while (NSString *key = bindingEnumerator.nextObject) { + context[key] = [NSExpression mgl_expressionWithJSONObject:bindingEnumerator.nextObject]; + } + return [NSExpression expressionForFunction:operand + selectorName:@"mgl_expressionWithContext:" + arguments:@[[NSExpression expressionForConstantValue:context]]]; + } else if ([op isEqualToString:@"var"]) { + return [NSExpression expressionForVariable:argumentObjects.firstObject]; + } else if ([op isEqualToString:@"case"]) { + NSPredicate *conditional = [NSPredicate mgl_predicateWithJSONObject:argumentObjects.firstObject]; + NSExpression *trueExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[1]]; + NSExpression *falseExpression; + if (argumentObjects.count > 3) { + NSArray *falseObjects = [@[@"case"] arrayByAddingObjectsFromArray: + [argumentObjects subarrayWithRange:NSMakeRange(2, argumentObjects.count - 2)]]; + falseExpression = [NSExpression mgl_expressionWithJSONObject:falseObjects]; + } else { + falseExpression = [NSExpression mgl_expressionWithJSONObject:argumentObjects[2]]; + } + return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + } else { + [NSException raise:NSInvalidArgumentException + format:@"Expression operator %@ not yet implemented.", op]; + } + } + + [NSException raise:NSInvalidArgumentException + format:@"Unable to convert JSON object %@ to an NSExpression.", object]; + + return nil; +} + +- (id)mgl_jsonExpressionObject { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + MGLExpressionOperatorsByFunctionNames = @{ + @"add:to:": @"+", + @"from:subtract:": @"-", + @"multiply:by:": @"*", + @"divide:by:": @"/", + @"modulus:by:": @"%", + @"sqrt:": @"sqrt", + @"log:": @"log10", + @"ln:": @"ln", + @"raise:toPower:": @"^", + @"uppercase:": @"upcase", + @"lowercase:": @"downcase", + }; + }); + + switch (self.expressionType) { + case NSVariableExpressionType: { + if ([self.variable isEqualToString:@"heatmapDensity"]) { + return @[@"heatmap-density"]; + } + if ([self.variable isEqualToString:@"zoomLevel"]) { + return @[@"zoom"]; + } + return @[@"var", self.variable]; + } + + case NSConstantValueExpressionType: { + id constantValue = self.constantValue; + if (!constantValue || constantValue == [NSNull null]) { + return [NSNull null]; + } + if ([constantValue isEqual:@(M_E)]) { + return @[@"e"]; + } + if ([constantValue isEqual:@(M_PI)]) { + return @[@"pi"]; + } + if ([constantValue isKindOfClass:[NSArray class]] || + [constantValue isKindOfClass:[NSDictionary class]]) { + NSArray *collection = [constantValue mgl_jsonExpressionObject]; + return @[@"literal", collection]; + } + if ([constantValue isKindOfClass:[MGLColor class]]) { + auto color = [constantValue mgl_color]; + if (color.a == 1) { + return @[@"rgb", @(color.r * 255), @(color.g * 255), @(color.b * 255)]; + } + return @[@"rgba", @(color.r * 255), @(color.g * 255), @(color.b * 255), @(color.a)]; + } + if ([constantValue isKindOfClass:[NSValue class]]) { + const auto boxedValue = (NSValue *)constantValue; + if (strcmp([boxedValue objCType], @encode(CGVector)) == 0) { + // offset [x, y] + std::array<float, 2> mglValue = boxedValue.mgl_offsetArrayValue; + return @[@"literal", @[@(mglValue[0]), @(mglValue[1])]]; + } + if (strcmp([boxedValue objCType], @encode(MGLEdgeInsets)) == 0) { + // padding [x, y] + std::array<float, 4> mglValue = boxedValue.mgl_paddingArrayValue; + return @[@"literal", @[@(mglValue[0]), @(mglValue[1]), @(mglValue[2]), @(mglValue[3])]]; + } + } + return self.constantValue; + } + + case NSKeyPathExpressionType: { + NSArray *expressionObject; + for (NSString *pathComponent in self.keyPath.pathComponents.reverseObjectEnumerator) { + if (expressionObject) { + expressionObject = @[@"get", pathComponent, expressionObject]; + } else { + expressionObject = @[@"get", pathComponent]; + } + } + return expressionObject; + } + + case NSFunctionExpressionType: { + NSString *function = self.function; + NSString *op = MGLExpressionOperatorsByFunctionNames[function]; + if (op) { + NSArray *arguments = self.arguments.mgl_jsonExpressionObject; + return [@[op] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"valueForKeyPath:"]) { + return @[@"get", self.arguments.firstObject.mgl_jsonExpressionObject, self.operand.mgl_jsonExpressionObject]; + } else if ([function isEqualToString:@"average:"]) { + NSExpression *sum = [NSExpression expressionForFunction:@"sum:" arguments:self.arguments]; + NSExpression *count = [NSExpression expressionForFunction:@"count:" arguments:self.arguments]; + return [NSExpression expressionForFunction:@"divide:by:" arguments:@[sum, count]].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"sum:"]) { + NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"]; + return [@[@"+"] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"count:"]) { + NSArray *arguments = self.arguments.firstObject.mgl_jsonExpressionObject; + return @[@"length", arguments]; + } else if ([function isEqualToString:@"min:"]) { + NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"]; + return [@[@"min"] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"max:"]) { + NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"]; + return [@[@"max"] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"exp:"]) { + return [NSExpression expressionForFunction:@"raise:toPower:" arguments:@[@(M_E), self.arguments.firstObject]].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"ceiling:"]) { + return [NSExpression expressionWithFormat:@"trunc:(%@) + TERNARY(modulus:by:(%@, 1) > 0, 1, 0)", + self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"trunc:"]) { + return [NSExpression expressionWithFormat:@"%@ - modulus:by:(%@, 1)", + self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"abs:"]) { + return [NSExpression expressionWithFormat:@"%@ * TERNARY(%@ > 0, 1, -1)", + self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"floor:"]) { + return [NSExpression expressionWithFormat:@"trunc:(%@) - TERNARY(modulus:by:(%@, 1) < 0, 1, 0)", + self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"stringByAppendingString:"]) { + NSArray *arguments = self.arguments.mgl_jsonExpressionObject; + return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"boolValue"]) { + return @[@"to-boolean", self.operand.mgl_jsonExpressionObject]; + } else if ([function isEqualToString:@"mgl_numberWithFallbackValues:"] || + [function isEqualToString:@"decimalValue"] || + [function isEqualToString:@"floatValue"] || + [function isEqualToString:@"doubleValue"]) { + NSArray *arguments = self.arguments.mgl_jsonExpressionObject; + return [@[@"to-number", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments]; + } else if ([function isEqualToString:@"stringValue"]) { + return @[@"to-string", self.operand.mgl_jsonExpressionObject]; + } else if ([function isEqualToString:@"noindex:"]) { + return self.arguments.firstObject.mgl_jsonExpressionObject; + } else if ([function isEqualToString:@"mgl_interpolateWithCurveType:parameters:stops:"]) { + if (self.arguments.count < 3) { + [NSException raise:NSInvalidArgumentException format: + @"Too few arguments to ‘mgl_interpolateWithCurveType:parameters:stops:’ function; expected 3 arguments."]; + } else if (self.arguments.count > 3) { + [NSException raise:NSInvalidArgumentException format: + @"%lu unexpected arguments to ‘mgl_interpolateWithCurveType:parameters:stops:’ function; expected 3 arguments.", + self.arguments.count - 3]; + } + NSString *curveType = self.arguments.firstObject.constantValue; + NSMutableArray *interpolationArray = [NSMutableArray arrayWithObject:curveType]; + if ([curveType isEqualToString:@"exponential"]) { + id base = [self.arguments[1] mgl_jsonExpressionObject]; + [interpolationArray addObject:base]; + } else if ([curveType isEqualToString:@"cubic-bezier"]) { + NSArray *controlPoints = [self.arguments[1].collection mgl_jsonExpressionObject]; + [interpolationArray addObjectsFromArray:controlPoints]; + } + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolationArray, self.operand.mgl_jsonExpressionObject, nil]; + NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[2].constantValue; + for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) { + [expressionObject addObject:key]; + [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]]; + } + return expressionObject; + } else if ([function isEqualToString:@"mgl_stepWithMinimum:stops:"]) { + id minimum = self.arguments.firstObject.mgl_jsonExpressionObject; + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", self.operand.mgl_jsonExpressionObject, minimum, nil]; + NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[1].constantValue; + for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) { + [expressionObject addObject:key]; + [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]]; + } + return expressionObject; + } else if ([function isEqualToString:@"mgl_expressionWithContext:"]) { + id context = self.arguments.firstObject; + if ([context isKindOfClass:[NSExpression class]]) { + context = [context constantValue]; + } + NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"let", nil]; + [context enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, NSExpression * _Nonnull obj, BOOL * _Nonnull stop) { + [expressionObject addObject:key]; + [expressionObject addObject:obj.mgl_jsonExpressionObject]; + }]; + [expressionObject addObject:self.operand.mgl_jsonExpressionObject]; + return expressionObject; + } else if ([function isEqualToString:@"median:"] || + [function isEqualToString:@"mode:"] || + [function isEqualToString:@"stddev:"] || + [function isEqualToString:@"random"] || + [function isEqualToString:@"randomn:"] || + [function isEqualToString:@"now"] || + [function isEqualToString:@"bitwiseAnd:with:"] || + [function isEqualToString:@"bitwiseOr:with:"] || + [function isEqualToString:@"bitwiseXor:with:"] || + [function isEqualToString:@"leftshift:by:"] || + [function isEqualToString:@"rightshift:by:"] || + [function isEqualToString:@"onesComplement:"] || + [function isEqualToString:@"distanceToLocation:fromLocation:"]) { + [NSException raise:NSInvalidArgumentException + format:@"Expression function %@ not yet implemented.", function]; + return nil; + } else { + [NSException raise:NSInvalidArgumentException + format:@"Unrecognized expression function %@.", function]; + return nil; + } + } + + case NSConditionalExpressionType: { + NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, self.trueExpression.mgl_jsonExpressionObject, nil]; + if (self.falseExpression.expressionType == NSConditionalExpressionType) { + // Fold nested conditionals into a single case expression. + NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject; + falseArguments = [falseArguments subarrayWithRange:NSMakeRange(1, falseArguments.count - 1)]; + [arguments addObjectsFromArray:falseArguments]; + } else { + [arguments addObject:self.falseExpression.mgl_jsonExpressionObject]; + } + + [arguments insertObject:@"case" atIndex:0]; + return arguments; + } + + case NSAggregateExpressionType: { + NSArray *collection = [self.collection valueForKeyPath:@"mgl_jsonExpressionObject"]; + return @[@"literal", collection]; + } + + case NSEvaluatedObjectExpressionType: + case NSUnionSetExpressionType: + case NSIntersectSetExpressionType: + case NSMinusSetExpressionType: + case NSSubqueryExpressionType: + case NSAnyKeyExpressionType: + case NSBlockExpressionType: + [NSException raise:NSInvalidArgumentException + format:@"Expression type %lu not yet implemented.", self.expressionType]; + } + + // NSKeyPathSpecifierExpression + if (self.expressionType == 10) { + return self.description; + } + // An assignment expression type is present in the BNF grammar, but the + // corresponding NSExpressionType value and property getters are missing. + if (self.expressionType == 12) { + [NSException raise:NSInvalidArgumentException + format:@"Assignment expressions not yet implemented."]; + } + + return nil; +} + +@end diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h new file mode 100644 index 0000000000..8d1b4d6af5 --- /dev/null +++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h @@ -0,0 +1,79 @@ +#import <Foundation/Foundation.h> +#if TARGET_OS_IPHONE + #import <UIKit/UIKit.h> +#else + #import <Cocoa/Cocoa.h> +#endif + +#import "NSExpression+MGLAdditions.h" + +#include <mbgl/style/filter.hpp> + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (MGLExpressionAdditions) + +- (NSNumber *)mgl_number; +- (NSNumber *)mgl_numberWithFallbackValues:(id)fallbackValue, ... NS_REQUIRES_NIL_TERMINATION; + +@end + +@interface NSExpression (MGLPrivateAdditions) + +@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue; +@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue; +@property (nonatomic, readonly) mbgl::FeatureType mgl_featureType; +@property (nonatomic, readonly) std::vector<mbgl::FeatureType> mgl_aggregateFeatureType; +@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier; +@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier; + +@end + +@interface NSNull (MGLExpressionAdditions) + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface NSString (MGLExpressionAdditions) + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface NSNumber (MGLExpressionAdditions) + +- (id)mgl_interpolateWithCurveType:(NSString *)curveType parameters:(NSArray *)parameters stops:(NSDictionary<NSNumber *, id> *)stops; +- (id)mgl_stepWithMinimum:(id)minimum stops:(NSDictionary<NSNumber *, id> *)stops; + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface NSArray (MGLExpressionAdditions) + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface NSDictionary (MGLExpressionAdditions) + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface MGLColor (MGLExpressionAdditions) + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end + +@interface NSExpression (MGLExpressionAdditions) + +- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context; + +@end + +extern NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects); + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h index fd774dd58b..89e9e65c64 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.h +++ b/platform/darwin/src/NSPredicate+MGLAdditions.h @@ -1,7 +1,6 @@ #import <Foundation/Foundation.h> -#import "NSExpression+MGLAdditions.h" -#include <mbgl/style/filter.hpp> +#import "NSExpression+MGLPrivateAdditions.h" @interface NSPredicate (MGLAdditions) @@ -11,3 +10,10 @@ @end +@interface NSPredicate (MGLExpressionAdditions) + ++ (instancetype)mgl_predicateWithJSONObject:(id)object; + +@property (nonatomic, readonly) id mgl_jsonExpressionObject; + +@end diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index e0511d8740..3ffe200d01 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -228,3 +228,94 @@ public: } @end + +@implementation NSPredicate (MGLExpressionAdditions) + +NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { + NSMutableArray *subpredicates = [NSMutableArray arrayWithCapacity:objects.count]; + for (id object in objects) { + NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:object]; + [subpredicates addObject:predicate]; + } + return subpredicates; +} + ++ (instancetype)mgl_predicateWithJSONObject:(id)object { + if ([object isEqual:@YES]) { + return [NSPredicate predicateWithValue:YES]; + } + if ([object isEqual:@NO]) { + return [NSPredicate predicateWithValue:NO]; + } + + NSAssert([object isKindOfClass:[NSArray class]], @"Condition for case expression should be an expression."); + NSArray *objects = (NSArray *)object; + NSString *op = objects.firstObject; + + if ([op isEqualToString:@"=="]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%@ == %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@"!="]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%@ != %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@"<"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%@ < %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@"<="]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%@ <= %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@">"]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%@ > %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@">="]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSPredicate predicateWithFormat:@"%K >= %@" argumentArray:subexpressions]; + } + if ([op isEqualToString:@"!"]) { + NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + if (subpredicates.count > 1) { + NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; + return [NSCompoundPredicate notPredicateWithSubpredicate:predicate]; + } + if (subpredicates.count) { + return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; + } + return [NSPredicate predicateWithValue:YES]; + } + if ([op isEqualToString:@"all"]) { + NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; + } + if ([op isEqualToString:@"any"]) { + NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]); + return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; + } + + NSAssert(NO, @"Unrecognized expression conditional operator %@.", op); + return nil; +} + +- (id)mgl_jsonExpressionObject { + if ([self isEqual:[NSPredicate predicateWithValue:YES]]) { + return @YES; + } + if ([self isEqual:[NSPredicate predicateWithValue:NO]]) { + return @NO; + } + + if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("]) { + [NSException raise:NSInvalidArgumentException + format:@"Block-based predicates are not supported."]; + } + + [NSException raise:NSInvalidArgumentException + format:@"Unrecognized predicate type."]; + return nil; +} + +@end diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm index c96a4fe7fa..e7c2982413 100644 --- a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm +++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm @@ -31,39 +31,44 @@ { XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(), @"background-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.backgroundColor; + NSExpression *defaultExpression = layer.backgroundColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.backgroundColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.backgroundColor = constantExpression; mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue, - @"Setting backgroundColor to a constant value should update background-color."); - XCTAssertEqualObjects(layer.backgroundColor, constantStyleValue, - @"backgroundColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.backgroundColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting backgroundColor to a constant value expression should update background-color."); + XCTAssertEqualObjects(layer.backgroundColor, constantExpression, + @"backgroundColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.backgroundColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue, - @"Setting backgroundColor to a camera function should update background-color."); - XCTAssertEqualObjects(layer.backgroundColor, functionStyleValue, - @"backgroundColor should round-trip camera functions."); + @"Setting backgroundColor to a camera expression should update background-color."); + XCTAssertEqualObjects(layer.backgroundColor, functionExpression, + @"backgroundColor should round-trip camera expressions."); layer.backgroundColor = nil; XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(), @"Unsetting backgroundColor should return background-color to the default value."); - XCTAssertEqualObjects(layer.backgroundColor, defaultStyleValue, + XCTAssertEqualObjects(layer.backgroundColor, defaultExpression, @"backgroundColor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.backgroundColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.backgroundColorTransition = transitionTest; auto toptions = rawLayer->getBackgroundColorTransition(); @@ -79,39 +84,44 @@ { XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(), @"background-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.backgroundOpacity; + NSExpression *defaultExpression = layer.backgroundOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.backgroundOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.backgroundOpacity = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue, - @"Setting backgroundOpacity to a constant value should update background-opacity."); - XCTAssertEqualObjects(layer.backgroundOpacity, constantStyleValue, - @"backgroundOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.backgroundOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting backgroundOpacity to a constant value expression should update background-opacity."); + XCTAssertEqualObjects(layer.backgroundOpacity, constantExpression, + @"backgroundOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.backgroundOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue, - @"Setting backgroundOpacity to a camera function should update background-opacity."); - XCTAssertEqualObjects(layer.backgroundOpacity, functionStyleValue, - @"backgroundOpacity should round-trip camera functions."); + @"Setting backgroundOpacity to a camera expression should update background-opacity."); + XCTAssertEqualObjects(layer.backgroundOpacity, functionExpression, + @"backgroundOpacity should round-trip camera expressions."); layer.backgroundOpacity = nil; XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(), @"Unsetting backgroundOpacity should return background-opacity to the default value."); - XCTAssertEqualObjects(layer.backgroundOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.backgroundOpacity, defaultExpression, @"backgroundOpacity should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.backgroundOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.backgroundOpacityTransition = transitionTest; auto toptions = rawLayer->getBackgroundOpacityTransition(); @@ -127,39 +137,44 @@ { XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(), @"background-pattern should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.backgroundPattern; + NSExpression *defaultExpression = layer.backgroundPattern; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Background Pattern"]; - layer.backgroundPattern = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Background Pattern'"]; + layer.backgroundPattern = constantExpression; mbgl::style::PropertyValue<std::string> propertyValue = { "Background Pattern" }; XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue, - @"Setting backgroundPattern to a constant value should update background-pattern."); - XCTAssertEqualObjects(layer.backgroundPattern, constantStyleValue, - @"backgroundPattern should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.backgroundPattern = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Background Pattern"}} }; + @"Setting backgroundPattern to a constant value expression should update background-pattern."); + XCTAssertEqualObjects(layer.backgroundPattern, constantExpression, + @"backgroundPattern should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Background Pattern'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.backgroundPattern = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Background Pattern" }, + { 18, "Background Pattern" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue, - @"Setting backgroundPattern to a camera function should update background-pattern."); - XCTAssertEqualObjects(layer.backgroundPattern, functionStyleValue, - @"backgroundPattern should round-trip camera functions."); + @"Setting backgroundPattern to a camera expression should update background-pattern."); + XCTAssertEqualObjects(layer.backgroundPattern, functionExpression, + @"backgroundPattern should round-trip camera expressions."); layer.backgroundPattern = nil; XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(), @"Unsetting backgroundPattern should return background-pattern to the default value."); - XCTAssertEqualObjects(layer.backgroundPattern, defaultStyleValue, + XCTAssertEqualObjects(layer.backgroundPattern, defaultExpression, @"backgroundPattern should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.backgroundPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLBackgroundLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.backgroundPatternTransition = transitionTest; auto toptions = rawLayer->getBackgroundPatternTransition(); diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm index c0c503153a..7677344580 100644 --- a/platform/darwin/test/MGLCircleStyleLayerTests.mm +++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm @@ -52,40 +52,44 @@ { XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(), @"circle-blur should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleBlur; + NSExpression *defaultExpression = layer.circleBlur; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.circleBlur = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.circleBlur = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, - @"Setting circleBlur to a constant value should update circle-blur."); - XCTAssertEqualObjects(layer.circleBlur, constantStyleValue, - @"circleBlur should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleBlur = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting circleBlur to a constant value expression should update circle-blur."); + XCTAssertEqualObjects(layer.circleBlur, constantExpression, + @"circleBlur should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleBlur = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, - @"Setting circleBlur to a camera function should update circle-blur."); - XCTAssertEqualObjects(layer.circleBlur, functionStyleValue, - @"circleBlur should round-trip camera functions."); + @"Setting circleBlur to a camera expression should update circle-blur."); + XCTAssertEqualObjects(layer.circleBlur, functionExpression, + @"circleBlur should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleBlur = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, - @"Setting circleBlur to a source function should update circle-blur."); - XCTAssertEqualObjects(layer.circleBlur, functionStyleValue, - @"circleBlur should round-trip source functions."); + @"Setting circleBlur to a data expression should update circle-blur."); + XCTAssertEqualObjects(layer.circleBlur, functionExpression, + @"circleBlur should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleBlur = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -93,15 +97,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, - @"Setting circleBlur to a composite function should update circle-blur."); - XCTAssertEqualObjects(layer.circleBlur, functionStyleValue, - @"circleBlur should round-trip composite functions."); + @"Setting circleBlur to a camera-data expression should update circle-blur."); + XCTAssertEqualObjects(layer.circleBlur, functionExpression, + @"circleBlur should round-trip camera-data expressions."); layer.circleBlur = nil; XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(), @"Unsetting circleBlur should return circle-blur to the default value."); - XCTAssertEqualObjects(layer.circleBlur, defaultStyleValue, + XCTAssertEqualObjects(layer.circleBlur, defaultExpression, @"circleBlur should return the default value after being unset."); // Transition property test layer.circleBlurTransition = transitionTest; @@ -118,40 +122,44 @@ { XCTAssertTrue(rawLayer->getCircleColor().isUndefined(), @"circle-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleColor; + NSExpression *defaultExpression = layer.circleColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.circleColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.circleColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, - @"Setting circleColor to a constant value should update circle-color."); - XCTAssertEqualObjects(layer.circleColor, constantStyleValue, - @"circleColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting circleColor to a constant value expression should update circle-color."); + XCTAssertEqualObjects(layer.circleColor, constantExpression, + @"circleColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, - @"Setting circleColor to a camera function should update circle-color."); - XCTAssertEqualObjects(layer.circleColor, functionStyleValue, - @"circleColor should round-trip camera functions."); + @"Setting circleColor to a camera expression should update circle-color."); + XCTAssertEqualObjects(layer.circleColor, functionExpression, + @"circleColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, - @"Setting circleColor to a source function should update circle-color."); - XCTAssertEqualObjects(layer.circleColor, functionStyleValue, - @"circleColor should round-trip source functions."); + @"Setting circleColor to a data expression should update circle-color."); + XCTAssertEqualObjects(layer.circleColor, functionExpression, + @"circleColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -159,15 +167,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, - @"Setting circleColor to a composite function should update circle-color."); - XCTAssertEqualObjects(layer.circleColor, functionStyleValue, - @"circleColor should round-trip composite functions."); + @"Setting circleColor to a camera-data expression should update circle-color."); + XCTAssertEqualObjects(layer.circleColor, functionExpression, + @"circleColor should round-trip camera-data expressions."); layer.circleColor = nil; XCTAssertTrue(rawLayer->getCircleColor().isUndefined(), @"Unsetting circleColor should return circle-color to the default value."); - XCTAssertEqualObjects(layer.circleColor, defaultStyleValue, + XCTAssertEqualObjects(layer.circleColor, defaultExpression, @"circleColor should return the default value after being unset."); // Transition property test layer.circleColorTransition = transitionTest; @@ -184,40 +192,44 @@ { XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(), @"circle-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleOpacity; + NSExpression *defaultExpression = layer.circleOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.circleOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.circleOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, - @"Setting circleOpacity to a constant value should update circle-opacity."); - XCTAssertEqualObjects(layer.circleOpacity, constantStyleValue, - @"circleOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting circleOpacity to a constant value expression should update circle-opacity."); + XCTAssertEqualObjects(layer.circleOpacity, constantExpression, + @"circleOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, - @"Setting circleOpacity to a camera function should update circle-opacity."); - XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue, - @"circleOpacity should round-trip camera functions."); + @"Setting circleOpacity to a camera expression should update circle-opacity."); + XCTAssertEqualObjects(layer.circleOpacity, functionExpression, + @"circleOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, - @"Setting circleOpacity to a source function should update circle-opacity."); - XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue, - @"circleOpacity should round-trip source functions."); + @"Setting circleOpacity to a data expression should update circle-opacity."); + XCTAssertEqualObjects(layer.circleOpacity, functionExpression, + @"circleOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -225,15 +237,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, - @"Setting circleOpacity to a composite function should update circle-opacity."); - XCTAssertEqualObjects(layer.circleOpacity, functionStyleValue, - @"circleOpacity should round-trip composite functions."); + @"Setting circleOpacity to a camera-data expression should update circle-opacity."); + XCTAssertEqualObjects(layer.circleOpacity, functionExpression, + @"circleOpacity should round-trip camera-data expressions."); layer.circleOpacity = nil; XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(), @"Unsetting circleOpacity should return circle-opacity to the default value."); - XCTAssertEqualObjects(layer.circleOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.circleOpacity, defaultExpression, @"circleOpacity should return the default value after being unset."); // Transition property test layer.circleOpacityTransition = transitionTest; @@ -250,79 +262,88 @@ { XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(), @"circle-pitch-alignment should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.circlePitchAlignment; + NSExpression *defaultExpression = layer.circlePitchAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCirclePitchAlignment:MGLCirclePitchAlignmentViewport]]; - layer.circlePitchAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.circlePitchAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Viewport }; XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue, - @"Setting circlePitchAlignment to a constant value should update circle-pitch-alignment."); - XCTAssertEqualObjects(layer.circlePitchAlignment, constantStyleValue, - @"circlePitchAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circlePitchAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Viewport}} }; + @"Setting circlePitchAlignment to a constant value expression should update circle-pitch-alignment."); + XCTAssertEqualObjects(layer.circlePitchAlignment, constantExpression, + @"circlePitchAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circlePitchAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{ + { -INFINITY, mbgl::style::AlignmentType::Viewport }, + { 18, mbgl::style::AlignmentType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue, - @"Setting circlePitchAlignment to a camera function should update circle-pitch-alignment."); - XCTAssertEqualObjects(layer.circlePitchAlignment, functionStyleValue, - @"circlePitchAlignment should round-trip camera functions."); + @"Setting circlePitchAlignment to a camera expression should update circle-pitch-alignment."); + XCTAssertEqualObjects(layer.circlePitchAlignment, functionExpression, + @"circlePitchAlignment should round-trip camera expressions."); layer.circlePitchAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(), @"Unsetting circlePitchAlignment should return circle-pitch-alignment to the default value."); - XCTAssertEqualObjects(layer.circlePitchAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.circlePitchAlignment, defaultExpression, @"circlePitchAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.circlePitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // circle-radius { XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(), @"circle-radius should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleRadius; + NSExpression *defaultExpression = layer.circleRadius; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.circleRadius = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.circleRadius = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, - @"Setting circleRadius to a constant value should update circle-radius."); - XCTAssertEqualObjects(layer.circleRadius, constantStyleValue, - @"circleRadius should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleRadius = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting circleRadius to a constant value expression should update circle-radius."); + XCTAssertEqualObjects(layer.circleRadius, constantExpression, + @"circleRadius should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleRadius = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, - @"Setting circleRadius to a camera function should update circle-radius."); - XCTAssertEqualObjects(layer.circleRadius, functionStyleValue, - @"circleRadius should round-trip camera functions."); + @"Setting circleRadius to a camera expression should update circle-radius."); + XCTAssertEqualObjects(layer.circleRadius, functionExpression, + @"circleRadius should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleRadius = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleRadius = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, - @"Setting circleRadius to a source function should update circle-radius."); - XCTAssertEqualObjects(layer.circleRadius, functionStyleValue, - @"circleRadius should round-trip source functions."); + @"Setting circleRadius to a data expression should update circle-radius."); + XCTAssertEqualObjects(layer.circleRadius, functionExpression, + @"circleRadius should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleRadius = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleRadius = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -330,15 +351,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, - @"Setting circleRadius to a composite function should update circle-radius."); - XCTAssertEqualObjects(layer.circleRadius, functionStyleValue, - @"circleRadius should round-trip composite functions."); + @"Setting circleRadius to a camera-data expression should update circle-radius."); + XCTAssertEqualObjects(layer.circleRadius, functionExpression, + @"circleRadius should round-trip camera-data expressions."); layer.circleRadius = nil; XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(), @"Unsetting circleRadius should return circle-radius to the default value."); - XCTAssertEqualObjects(layer.circleRadius, defaultStyleValue, + XCTAssertEqualObjects(layer.circleRadius, defaultExpression, @"circleRadius should return the default value after being unset."); // Transition property test layer.circleRadiusTransition = transitionTest; @@ -355,79 +376,88 @@ { XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(), @"circle-pitch-scale should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleScaleAlignment; + NSExpression *defaultExpression = layer.circleScaleAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport]]; - layer.circleScaleAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.circleScaleAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::CirclePitchScaleType> propertyValue = { mbgl::style::CirclePitchScaleType::Viewport }; XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue, - @"Setting circleScaleAlignment to a constant value should update circle-pitch-scale."); - XCTAssertEqualObjects(layer.circleScaleAlignment, constantStyleValue, - @"circleScaleAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleScaleAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::CirclePitchScaleType> intervalStops = { {{18, mbgl::style::CirclePitchScaleType::Viewport}} }; + @"Setting circleScaleAlignment to a constant value expression should update circle-pitch-scale."); + XCTAssertEqualObjects(layer.circleScaleAlignment, constantExpression, + @"circleScaleAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleScaleAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::CirclePitchScaleType> intervalStops = {{ + { -INFINITY, mbgl::style::CirclePitchScaleType::Viewport }, + { 18, mbgl::style::CirclePitchScaleType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::CirclePitchScaleType> { intervalStops }; XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue, - @"Setting circleScaleAlignment to a camera function should update circle-pitch-scale."); - XCTAssertEqualObjects(layer.circleScaleAlignment, functionStyleValue, - @"circleScaleAlignment should round-trip camera functions."); + @"Setting circleScaleAlignment to a camera expression should update circle-pitch-scale."); + XCTAssertEqualObjects(layer.circleScaleAlignment, functionExpression, + @"circleScaleAlignment should round-trip camera expressions."); layer.circleScaleAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(), @"Unsetting circleScaleAlignment should return circle-pitch-scale to the default value."); - XCTAssertEqualObjects(layer.circleScaleAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.circleScaleAlignment, defaultExpression, @"circleScaleAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.circleScaleAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // circle-stroke-color { XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(), @"circle-stroke-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.circleStrokeColor; + NSExpression *defaultExpression = layer.circleStrokeColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.circleStrokeColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.circleStrokeColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, - @"Setting circleStrokeColor to a constant value should update circle-stroke-color."); - XCTAssertEqualObjects(layer.circleStrokeColor, constantStyleValue, - @"circleStrokeColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleStrokeColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting circleStrokeColor to a constant value expression should update circle-stroke-color."); + XCTAssertEqualObjects(layer.circleStrokeColor, constantExpression, + @"circleStrokeColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleStrokeColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, - @"Setting circleStrokeColor to a camera function should update circle-stroke-color."); - XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue, - @"circleStrokeColor should round-trip camera functions."); + @"Setting circleStrokeColor to a camera expression should update circle-stroke-color."); + XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression, + @"circleStrokeColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleStrokeColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleStrokeColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, - @"Setting circleStrokeColor to a source function should update circle-stroke-color."); - XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue, - @"circleStrokeColor should round-trip source functions."); + @"Setting circleStrokeColor to a data expression should update circle-stroke-color."); + XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression, + @"circleStrokeColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleStrokeColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleStrokeColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -435,15 +465,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, - @"Setting circleStrokeColor to a composite function should update circle-stroke-color."); - XCTAssertEqualObjects(layer.circleStrokeColor, functionStyleValue, - @"circleStrokeColor should round-trip composite functions."); + @"Setting circleStrokeColor to a camera-data expression should update circle-stroke-color."); + XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression, + @"circleStrokeColor should round-trip camera-data expressions."); layer.circleStrokeColor = nil; XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(), @"Unsetting circleStrokeColor should return circle-stroke-color to the default value."); - XCTAssertEqualObjects(layer.circleStrokeColor, defaultStyleValue, + XCTAssertEqualObjects(layer.circleStrokeColor, defaultExpression, @"circleStrokeColor should return the default value after being unset."); // Transition property test layer.circleStrokeColorTransition = transitionTest; @@ -460,40 +490,44 @@ { XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(), @"circle-stroke-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeOpacity; + NSExpression *defaultExpression = layer.circleStrokeOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.circleStrokeOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.circleStrokeOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, - @"Setting circleStrokeOpacity to a constant value should update circle-stroke-opacity."); - XCTAssertEqualObjects(layer.circleStrokeOpacity, constantStyleValue, - @"circleStrokeOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleStrokeOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting circleStrokeOpacity to a constant value expression should update circle-stroke-opacity."); + XCTAssertEqualObjects(layer.circleStrokeOpacity, constantExpression, + @"circleStrokeOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleStrokeOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, - @"Setting circleStrokeOpacity to a camera function should update circle-stroke-opacity."); - XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue, - @"circleStrokeOpacity should round-trip camera functions."); + @"Setting circleStrokeOpacity to a camera expression should update circle-stroke-opacity."); + XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression, + @"circleStrokeOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleStrokeOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleStrokeOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, - @"Setting circleStrokeOpacity to a source function should update circle-stroke-opacity."); - XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue, - @"circleStrokeOpacity should round-trip source functions."); + @"Setting circleStrokeOpacity to a data expression should update circle-stroke-opacity."); + XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression, + @"circleStrokeOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleStrokeOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleStrokeOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -501,15 +535,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, - @"Setting circleStrokeOpacity to a composite function should update circle-stroke-opacity."); - XCTAssertEqualObjects(layer.circleStrokeOpacity, functionStyleValue, - @"circleStrokeOpacity should round-trip composite functions."); + @"Setting circleStrokeOpacity to a camera-data expression should update circle-stroke-opacity."); + XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression, + @"circleStrokeOpacity should round-trip camera-data expressions."); layer.circleStrokeOpacity = nil; XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(), @"Unsetting circleStrokeOpacity should return circle-stroke-opacity to the default value."); - XCTAssertEqualObjects(layer.circleStrokeOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.circleStrokeOpacity, defaultExpression, @"circleStrokeOpacity should return the default value after being unset."); // Transition property test layer.circleStrokeOpacityTransition = transitionTest; @@ -526,40 +560,44 @@ { XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(), @"circle-stroke-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.circleStrokeWidth; + NSExpression *defaultExpression = layer.circleStrokeWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.circleStrokeWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.circleStrokeWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, - @"Setting circleStrokeWidth to a constant value should update circle-stroke-width."); - XCTAssertEqualObjects(layer.circleStrokeWidth, constantStyleValue, - @"circleStrokeWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleStrokeWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting circleStrokeWidth to a constant value expression should update circle-stroke-width."); + XCTAssertEqualObjects(layer.circleStrokeWidth, constantExpression, + @"circleStrokeWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleStrokeWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, - @"Setting circleStrokeWidth to a camera function should update circle-stroke-width."); - XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue, - @"circleStrokeWidth should round-trip camera functions."); + @"Setting circleStrokeWidth to a camera expression should update circle-stroke-width."); + XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression, + @"circleStrokeWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.circleStrokeWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.circleStrokeWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, - @"Setting circleStrokeWidth to a source function should update circle-stroke-width."); - XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue, - @"circleStrokeWidth should round-trip source functions."); + @"Setting circleStrokeWidth to a data expression should update circle-stroke-width."); + XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression, + @"circleStrokeWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.circleStrokeWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.circleStrokeWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -567,15 +605,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, - @"Setting circleStrokeWidth to a composite function should update circle-stroke-width."); - XCTAssertEqualObjects(layer.circleStrokeWidth, functionStyleValue, - @"circleStrokeWidth should round-trip composite functions."); + @"Setting circleStrokeWidth to a camera-data expression should update circle-stroke-width."); + XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression, + @"circleStrokeWidth should round-trip camera-data expressions."); layer.circleStrokeWidth = nil; XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(), @"Unsetting circleStrokeWidth should return circle-stroke-width to the default value."); - XCTAssertEqualObjects(layer.circleStrokeWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.circleStrokeWidth, defaultExpression, @"circleStrokeWidth should return the default value after being unset."); // Transition property test layer.circleStrokeWidthTransition = transitionTest; @@ -592,84 +630,94 @@ { XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(), @"circle-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslation; + NSExpression *defaultExpression = layer.circleTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.circleTranslation = constantStyleValue; + layer.circleTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue, - @"Setting circleTranslation to a constant value should update circle-translate."); - XCTAssertEqualObjects(layer.circleTranslation, constantStyleValue, - @"circleTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting circleTranslation to a constant value expression should update circle-translate."); + XCTAssertEqualObjects(layer.circleTranslation, constantExpression, + @"circleTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue, - @"Setting circleTranslation to a camera function should update circle-translate."); - XCTAssertEqualObjects(layer.circleTranslation, functionStyleValue, - @"circleTranslation should round-trip camera functions."); + @"Setting circleTranslation to a camera expression should update circle-translate."); + XCTAssertEqualObjects(layer.circleTranslation, functionExpression, + @"circleTranslation should round-trip camera expressions."); layer.circleTranslation = nil; XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(), @"Unsetting circleTranslation should return circle-translate to the default value."); - XCTAssertEqualObjects(layer.circleTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.circleTranslation, defaultExpression, @"circleTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.circleTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // circle-translate-anchor { XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(), @"circle-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.circleTranslationAnchor; + NSExpression *defaultExpression = layer.circleTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorViewport]]; - layer.circleTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.circleTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue, - @"Setting circleTranslationAnchor to a constant value should update circle-translate-anchor."); - XCTAssertEqualObjects(layer.circleTranslationAnchor, constantStyleValue, - @"circleTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.circleTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting circleTranslationAnchor to a constant value expression should update circle-translate-anchor."); + XCTAssertEqualObjects(layer.circleTranslationAnchor, constantExpression, + @"circleTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.circleTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue, - @"Setting circleTranslationAnchor to a camera function should update circle-translate-anchor."); - XCTAssertEqualObjects(layer.circleTranslationAnchor, functionStyleValue, - @"circleTranslationAnchor should round-trip camera functions."); + @"Setting circleTranslationAnchor to a camera expression should update circle-translate-anchor."); + XCTAssertEqualObjects(layer.circleTranslationAnchor, functionExpression, + @"circleTranslationAnchor should round-trip camera expressions."); layer.circleTranslationAnchor = nil; XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(), @"Unsetting circleTranslationAnchor should return circle-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.circleTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.circleTranslationAnchor, defaultExpression, @"circleTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.circleTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLCircleLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } } diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 668e5f57f8..d3e2bf91f2 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -137,12 +137,15 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLCircleStyleLayer(identifier: "circles", source: population) layer.sourceLayerIdentifier = "population" - layer.circleColor = MGLStyleValue(rawValue: .green) - layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, - cameraStops: [12: MGLStyleValue(rawValue: 2), - 22: MGLStyleValue(rawValue: 180)], - options: [.interpolationBase: 1.75]) - layer.circleOpacity = MGLStyleValue(rawValue: 0.7) + #if os(macOS) + layer.circleColor = NSExpression(forConstantValue: NSColor.green) + #else + layer.circleColor = NSExpression(forConstantValue: UIColor.green) + #endif + layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)", + [12: 2, + 22: 180]) + layer.circleOpacity = NSExpression(forConstantValue: 0.7) layer.predicate = NSPredicate(format: "%K == %@", "marital-status", "married") mapView.style?.addLayer(layer) //#-end-example-code @@ -157,12 +160,15 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails) layer.sourceLayerIdentifier = "trails" - layer.lineWidth = MGLStyleValue(interpolationMode: .exponential, - cameraStops: [14: MGLStyleValue(rawValue: 2), - 18: MGLStyleValue(rawValue: 20)], - options: [.interpolationBase: 1.5]) - layer.lineColor = MGLStyleValue(rawValue: .brown) - layer.lineCap = MGLStyleValue(rawValue: NSValue(mglLineCap: .round)) + layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)", + [14: 2, + 18: 20]) + #if os(macOS) + layer.lineColor = NSExpression(forConstantValue: NSColor.brown) + #else + layer.lineColor = NSExpression(forConstantValue: UIColor.brown) + #endif + layer.lineCap = NSExpression(forConstantValue: "round") layer.predicate = NSPredicate(format: "%K == %@", "trail-type", "mountain-biking") mapView.style?.addLayer(layer) //#-end-example-code @@ -177,7 +183,11 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLFillStyleLayer(identifier: "parks", source: parks) layer.sourceLayerIdentifier = "parks" - layer.fillColor = MGLStyleValue(rawValue: .green) + #if os(macOS) + layer.fillColor = NSExpression(forConstantValue: NSColor.green) + #else + layer.fillColor = NSExpression(forConstantValue: UIColor.green) + #endif layer.predicate = NSPredicate(format: "type == %@", "national-park") mapView.style?.addLayer(layer) //#-end-example-code @@ -192,8 +202,8 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings) layer.sourceLayerIdentifier = "building" - layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) - layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) + layer.fillExtrusionHeight = NSExpression(forKeyPath: "height") + layer.fillExtrusionBase = NSExpression(forKeyPath: "min_height") layer.predicate = NSPredicate(format: "extrude == 'true'") mapView.style?.addLayer(layer) //#-end-example-code @@ -208,17 +218,17 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLSymbolStyleLayer(identifier: "coffeeshops", source: pois) layer.sourceLayerIdentifier = "pois" - layer.iconImageName = MGLStyleValue(rawValue: "coffee") - layer.iconScale = MGLStyleValue(rawValue: 0.5) - layer.text = MGLStyleValue(rawValue: "{name}") + layer.iconImageName = NSExpression(forConstantValue: "coffee") + layer.iconScale = NSExpression(forConstantValue: 0.5) + layer.text = NSExpression(forKeyPath: "name") #if os(macOS) var vector = CGVector(dx: 10, dy: 0) - layer.textTranslation = MGLStyleValue(rawValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}")) + layer.textTranslation = NSExpression(forConstantValue: NSValue(bytes: &vector, objCType: "{CGVector=dd}")) #else - layer.textTranslation = MGLStyleValue(rawValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) + layer.textTranslation = NSExpression(forConstantValue: NSValue(cgVector: CGVector(dx: 10, dy: 0))) #endif - layer.textJustification = MGLStyleValue(rawValue: NSValue(mglTextJustification: .left)) - layer.textAnchor = MGLStyleValue(rawValue: NSValue(mglTextAnchor: .left)) + layer.textJustification = NSExpression(forConstantValue: "left") + layer.textAnchor = NSExpression(forConstantValue: "left") layer.predicate = NSPredicate(format: "%K == %@", "venue-type", "coffee") mapView.style?.addLayer(layer) //#-end-example-code @@ -239,7 +249,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { //#-example-code let layer = MGLRasterStyleLayer(identifier: "clouds", source: source) - layer.rasterOpacity = MGLStyleValue(rawValue: 0.5) + layer.rasterOpacity = NSExpression(forConstantValue: 0.5) mapView.style?.addLayer(layer) //#-end-example-code diff --git a/platform/darwin/test/MGLDocumentationGuideTests.swift b/platform/darwin/test/MGLDocumentationGuideTests.swift index c71f1b46c7..f939695f32 100644 --- a/platform/darwin/test/MGLDocumentationGuideTests.swift +++ b/platform/darwin/test/MGLDocumentationGuideTests.swift @@ -1,3 +1,4 @@ +import XCTest import Foundation import Mapbox #if os(iOS) @@ -52,25 +53,26 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { func testUsingStyleFunctionsAtRuntime$Stops() { //#-example-code #if os(macOS) - let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] #else - let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] #endif //#-end-example-code - let _ = MGLStyleValue(interpolationMode: .exponential, cameraStops: stops, options: nil) + let _ = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)", + stops[0]!, stops) } func testUsingStyleFunctionsAtRuntime$Linear() { @@ -83,36 +85,32 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { mapView.style?.addSource(source) #if os(macOS) - let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] #else - let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] #endif let layer = MGLCircleStyleLayer(identifier: "circles", source: source) #if os(macOS) - layer.circleColor = MGLStyleValue(interpolationMode: .exponential, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)]) + layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + stops) #else - layer.circleColor = MGLStyleValue(interpolationMode: .exponential, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)]) + layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + stops) #endif - layer.circleRadius = MGLStyleValue(rawValue: 10) + layer.circleRadius = NSExpression(forConstantValue: 10) mapView.style?.insertLayer(layer, below: symbolLayer) //#-end-example-code } @@ -123,14 +121,13 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { //#-example-code let stops = [ - 12: MGLStyleValue<NSNumber>(rawValue: 0.5), - 14: MGLStyleValue(rawValue: 2), - 18: MGLStyleValue(rawValue: 18), + 12: 0.5, + 14: 2, + 18: 18, ] - layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, - cameraStops: stops, - options: [.interpolationBase: 1.5]) + layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)", + stops) //#-end-example-code } @@ -140,31 +137,27 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { //#-example-code #if os(macOS) - let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] - layer.circleColor = MGLStyleValue(interpolationMode: .interval, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)]) + layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)", + NSColor.green, stops) #else - let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), + let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] - layer.circleColor = MGLStyleValue(interpolationMode: .interval, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)]) + layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)", + UIColor.green, stops) #endif //#-end-example-code } @@ -175,28 +168,24 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { //#-example-code #if os(macOS) - let categoricalStops = [ - "earthquake": MGLStyleValue<NSColor>(rawValue: .orange), - "explosion": MGLStyleValue(rawValue: .red), - "quarry blast": MGLStyleValue(rawValue: .yellow), + let colors: [String: NSColor] = [ + "earthquake": .orange, + "explosion": .red, + "quarry blast": .yellow, ] - - layer.circleColor = MGLStyleValue(interpolationMode: .categorical, - sourceStops: categoricalStops, - attributeName: "type", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)]) + let defaultColor = NSColor.blue #else - let categoricalStops = [ - "earthquake": MGLStyleValue<UIColor>(rawValue: .orange), - "explosion": MGLStyleValue(rawValue: .red), - "quarry blast": MGLStyleValue(rawValue: .yellow), + let colors: [String: UIColor] = [ + "earthquake": .orange, + "explosion": .red, + "quarry blast": .yellow, ] - - layer.circleColor = MGLStyleValue(interpolationMode: .categorical, - sourceStops: categoricalStops, - attributeName: "type", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)]) + let defaultColor = UIColor.blue #endif + + layer.circleColor = NSExpression( + format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)", + colors, colors, defaultColor) //#-end-example-code } @@ -205,10 +194,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate { let layer = MGLCircleStyleLayer(identifier: "circles", source: source) //#-example-code - layer.circleRadius = MGLStyleValue(interpolationMode: .identity, - sourceStops: nil, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)]) + layer.circleRadius = NSExpression(forKeyPath: "mag") //#-end-example-code } } diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index ad0833a068..821c5dbdb4 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -1,9 +1,17 @@ #import <XCTest/XCTest.h> +#import "MGLStyleLayerTests.h" + #import <string> #import "MGLTypes.h" -#import "NSExpression+MGLAdditions.h" +#import "NSExpression+MGLPrivateAdditions.h" +#import "NSValue+MGLAdditions.h" +#if TARGET_OS_IPHONE +#import "UIColor+MGLAdditions.h" +#else +#import "NSColor+MGLAdditions.h" +#endif #define MGLAssertEqualValues(actual, expected, ...) \ XCTAssertTrue(actual.is<__typeof__(expected)>()); \ @@ -17,11 +25,14 @@ XCTAssertEqualWithAccuracy(actual.get<__typeof__(expected)>(), expected, accuracy, __VA_ARGS__); \ } +#define MGLConstantExpression(constant) \ + [NSExpression expressionForConstantValue:constant] + #define MGLAssertConstantEqualsValue(constant, value, ...) \ - MGLAssertEqualValues([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, __VA_ARGS__); + MGLAssertEqualValues(MGLConstantExpression(constant).mgl_constantMBGLValue, value, __VA_ARGS__); #define MGLAssertConstantEqualsValueWithAccuracy(constant, value, accuracy, ...) \ - MGLAssertEqualValuesWithAccuracy([NSExpression expressionForConstantValue:constant].mgl_constantMBGLValue, value, accuracy, __VA_ARGS__); + MGLAssertEqualValuesWithAccuracy(MGLConstantExpression(constant).mgl_constantMBGLValue, value, accuracy, __VA_ARGS__); using namespace std::string_literals; @@ -140,4 +151,446 @@ using namespace std::string_literals; XCTAssertEqual([NSExpression expressionForConstantValue:nil].mgl_featureType, mbgl::FeatureType::Unknown); } +#pragma mark - JSON expression object tests + +- (void)testVariableExpressionObject { + { + NSExpression *expression = [NSExpression expressionForVariable:@"zoomLevel"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"zoom"]); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$zoomLevel"].mgl_jsonExpressionObject, @[@"zoom"]); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@[@"zoom"]], expression); + NSMutableDictionary *context = [@{@"zoomLevel": @16} mutableCopy]; + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @16); + } + { + NSExpression *expression = [NSExpression expressionForVariable:@"heatmapDensity"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"heatmap-density"]); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$heatmapDensity"].mgl_jsonExpressionObject, @[@"heatmap-density"]); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@[@"heatmap-density"]], expression); + NSMutableDictionary *context = [@{@"heatmapDensity": @1} mutableCopy]; + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @1); + } + { + NSExpression *expression = [NSExpression expressionForVariable:@"loremIpsum"]; + NSArray *jsonExpression = @[@"var", @"loremIpsum"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$loremIpsum"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + NSMutableDictionary *context = [@{@"loremIpsum": @"Lorem ipsum dolor sit amet"} mutableCopy]; + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @"Lorem ipsum dolor sit amet"); + } + { + NSDictionary *context = @{@"loremIpsum": MGLConstantExpression(@"Lorem ipsum dolor sit amet")}; + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(uppercase($loremIpsum), 'mgl_expressionWithContext:', %@)", context]; + NSArray *jsonExpression = @[@"let", @"loremIpsum", @"Lorem ipsum dolor sit amet", @[@"upcase", @[@"var", @"loremIpsum"]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testConstantValueExpressionObject { + { + NSExpression *expression = [NSExpression expressionForConstantValue:nil]; + XCTAssert(expression.mgl_jsonExpressionObject == [NSNull null]); + XCTAssert([NSExpression expressionWithFormat:@"nil"].mgl_jsonExpressionObject == [NSNull null]); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:[NSNull null]], expression); + XCTAssertNil([expression expressionValueWithObject:nil context:nil]); + } + { + NSExpression *expression = [NSExpression expressionForConstantValue:@1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @1); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1"].mgl_jsonExpressionObject, @1); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@1], expression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); + } + { + NSExpression *expression = [NSExpression expressionForConstantValue:@YES]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @YES); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TRUE"].mgl_jsonExpressionObject, @YES); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@YES], expression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES); + } + { + NSExpression *expression = [NSExpression expressionForConstantValue:nil]; + XCTAssert(expression.mgl_jsonExpressionObject == [NSNull null]); + XCTAssert([NSExpression expressionWithFormat:@"nil"].mgl_jsonExpressionObject == [NSNull null]); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:[NSNull null]], expression); + XCTAssertNil([expression expressionValueWithObject:nil context:nil]); + } + { + CGVector vector = CGVectorMake(1, 2); + NSExpression *expression = [NSExpression expressionForConstantValue:@(vector)]; +#if !TARGET_OS_IPHONE + NSArray *jsonExpression = @[@"literal", @[@1, @-2]]; +#else + NSArray *jsonExpression = @[@"literal", @[@1, @2]]; +#endif + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + // No way to distinguish offsets from ordinary arrays in expressions. + XCTAssertEqualObjects([[NSExpression mgl_expressionWithJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(vector)); + } + { +#if !TARGET_OS_IPHONE + NSEdgeInsets padding = {1, 2, 3, 4}; + NSValue *value = [NSValue valueWithEdgeInsets:padding]; +#else + UIEdgeInsets padding = {1, 2, 3, 4}; + NSValue *value = [NSValue valueWithUIEdgeInsets:padding]; +#endif + NSExpression *expression = [NSExpression expressionForConstantValue:value]; + NSArray *jsonExpression = @[@"literal", @[@1, @4, @3, @2]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + // No way to distinguish offsets from ordinary arrays in expressions. + XCTAssertEqualObjects([[NSExpression mgl_expressionWithJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], value); + } + { + MGLColor *color = [MGLColor mgl_colorWithColor:{ 255.0/255, 239.0/255, 213.0/255, 1 }]; // papayawhip + NSExpression *expression = [NSExpression expressionForConstantValue:color]; + NSArray *jsonExpression = @[@"rgb", @255, @239, @213]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], color); + } + { + MGLColor *color = [MGLColor mgl_colorWithColor:{ 255.0/255, 239.0/255, 213.0/255, 0.5 }]; // papayawhip + NSExpression *expression = [NSExpression expressionForConstantValue:color]; + NSArray *jsonExpression = @[@"rgba", @255, @239, @213, @0.5]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], color); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"noindex(513)"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @513); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @513); + } +} + +- (void)testKeyPathExpressionObject { + { + NSExpression *expression = [NSExpression expressionForKeyPath:@"highway"]; + NSArray *jsonExpression = @[@"get", @"highway"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"highway"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"%@.population", @{@"population": MGLConstantExpression(@12000)}]; + NSArray *jsonExpression = @[@"get", @"population", @[@"literal", @{@"population": @12000}]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"%@.uppercase('population')", @{@"POPULATION": MGLConstantExpression(@12000)}]; + NSArray *jsonExpression = @[@"get", @[@"upcase", @"population"], @[@"literal", @{@"POPULATION": @12000}]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testStatisticalExpressionObject { + { + NSExpression *expression = [NSExpression expressionWithFormat:@"average({1, 2, 2, 3, 4, 7, 9})"]; + NSArray *jsonExpression = @[@"/", @[@"+", @1, @2, @2, @3, @4, @7, @9], @[@"length", @[@"literal", @[@1, @2, @2, @3, @4, @7, @9]]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @4); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"sum({1, 2, 2, 3, 4, 7, 9})"]; + NSArray *jsonExpression = @[@"+", @1, @2, @2, @3, @4, @7, @9]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @28); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"count({1, 2, 2, 3, 4, 7, 9})"]; + NSArray *jsonExpression = @[@"length", @[@"literal", @[@1, @2, @2, @3, @4, @7, @9]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @7); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"min({1, 2, 2, 3, 4, 7, 9})"]; + NSArray *jsonExpression = @[@"min", @1, @2, @2, @3, @4, @7, @9]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"max({1, 2, 2, 3, 4, 7, 9})"]; + NSArray *jsonExpression = @[@"max", @1, @2, @2, @3, @4, @7, @9]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @9); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testArithmeticExpressionObject { + NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1)]; + { + NSExpression *expression = [NSExpression expressionForFunction:@"add:to:" arguments:arguments]; + NSArray *jsonExpression = @[@"+", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 + 1"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"from:subtract:" arguments:arguments]; + NSArray *jsonExpression = @[@"-", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 - 1"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"multiply:by:" arguments:arguments]; + NSArray *jsonExpression = @[@"*", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 * 1"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"divide:by:" arguments:arguments]; + NSArray *jsonExpression = @[@"/", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 / 1"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"modulus:by:" arguments:arguments]; + NSArray *jsonExpression = @[@"%", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + // NSExpression lacks a shorthand operator for modulus. + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@1.5)]]; + NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]]; + NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @1.5, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-1.5)]]; + NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]]; + NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-1.5, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@2)]]; + NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]]; + NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @2, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-2)]]; + NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]]; + NSArray *jsonExpression = @[@"+", jsonTruncation, @[@"case", @[@">", @[@"%", @-2, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"trunc:" arguments:@[MGLConstantExpression(@1.5)]]; + NSArray *jsonExpression = @[@"-", @1.5, @[@"%", @1.5, @1]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"trunc:" arguments:@[MGLConstantExpression(@-1.5)]]; + NSArray *jsonExpression = @[@"-", @-1.5, @[@"%", @-1.5, @1]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@2)]]; + NSArray *jsonExpression = @[@"*", @2, @[@"case", @[@">", @2, @0], @1, @-1]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@-2)]]; + NSArray *jsonExpression = @[@"*", @-2, @[@"case", @[@">", @-2, @0], @1, @-1]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@1.5)]]; + NSArray *jsonTruncation = @[@"-", @1.5, @[@"%", @1.5, @1]]; + NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @1.5, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-1.5)]]; + NSArray *jsonTruncation = @[@"-", @-1.5, @[@"%", @-1.5, @1]]; + NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-1.5, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@2)]]; + NSArray *jsonTruncation = @[@"-", @2, @[@"%", @2, @1]]; + NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @2, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-2)]]; + NSArray *jsonTruncation = @[@"-", @-2, @[@"%", @-2, @1]]; + NSArray *jsonExpression = @[@"-", jsonTruncation, @[@"case", @[@"<", @[@"%", @-2, @1], @0], @1, @0]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2); + } +} + +- (void)testTrigonometricExpressionObject { + NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1)]; + { + NSExpression *expression = [NSExpression expressionForFunction:@"sqrt:" arguments:arguments]; + NSArray *jsonExpression = @[@"sqrt", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"ln:" arguments:arguments]; + NSArray *jsonExpression = @[@"ln", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"raise:toPower:" arguments:arguments]; + NSArray *jsonExpression = @[@"^", @1, @1]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 ** 1"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"exp:" arguments:@[MGLConstantExpression(@0)]]; + NSArray *jsonExpression = @[@"^", @[@"e"], @0]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForConstantValue:@(M_E)]; + NSArray *jsonExpression = @[@"e"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(M_E)); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForConstantValue:@(M_PI)]; + NSArray *jsonExpression = @[@"pi"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(M_PI)); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testStringFormattingExpressionObject { + NSArray *arguments = @[MGLConstantExpression(@"MacDonald")]; + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')"]; + NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald"); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"uppercase:" arguments:arguments]; + NSArray *jsonExpression = @[@"upcase", @"MacDonald"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionForFunction:@"lowercase:" arguments:arguments]; + NSArray *jsonExpression = @[@"downcase", @"MacDonald"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testTypeConversionExpressionObject { + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'boolValue')"]; + NSArray *jsonExpression = @[@"to-boolean", @[@"get", @"number"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + // NSExpression is unable to evaluate -[NSNumber boolValue] by itself + // because it returns a primitive instead of an object. + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_numberWithFallbackValues:', zipCode)"]; + NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"], @[@"get", @"zipCode"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'doubleValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'floatValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'decimalValue', zipCode)"].mgl_jsonExpressionObject, jsonExpression); + // NSExpression is unable to evaluate NSNumber’s -floatValue, + // -doubleValue, or -decimalValue by themselves because they each return + // a primitive instead of an object. + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'stringValue')"]; + NSArray *jsonExpression = @[@"to-string", @[@"get", @"number"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5"); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testInterpolationExpressionObject { + { + NSDictionary *stops = @{@0: MGLConstantExpression(@100), @10: MGLConstantExpression(@200)}; + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", stops]; + NSArray *jsonExpression = @[@"interpolate", @[@"linear"], @[@"get", @"x"], @0, @100, @10, @200]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSDictionary *stops = @{@1: MGLConstantExpression(@2), @3: MGLConstantExpression(@6)}; + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 2, %@)", stops]; + NSArray *jsonExpression = @[@"interpolate", @[@"exponential", @2], @[@"get", @"x"], @1, @2, @3, @6]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSDictionary *stops = @{@0: MGLConstantExpression(@0), @100: MGLConstantExpression(@100)}; + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'cubic-bezier', { 0.42, 0, 0.58, 1 }, %@)", stops]; + NSArray *jsonExpression = @[@"interpolate", @[@"cubic-bezier", @0.42, @0, @0.58, @1], @[@"get", @"x"], @0, @0, @100, @100]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)}; + NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_stepWithMinimum:stops:', 11, %@)", stops]; + NSArray *jsonExpression = @[@"step", @[@"get", @"x"], @11, @0, @111, @1, @1111]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + +- (void)testConditionalExpressionObject { + { + NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; + NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; + NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; + NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; + NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } +} + @end diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm index 5d99c815ea..f5e26381d6 100644 --- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm @@ -52,40 +52,44 @@ { XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), @"fill-extrusion-base should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionBase; + NSExpression *defaultExpression = layer.fillExtrusionBase; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.fillExtrusionBase = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.fillExtrusionBase = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, - @"Setting fillExtrusionBase to a constant value should update fill-extrusion-base."); - XCTAssertEqualObjects(layer.fillExtrusionBase, constantStyleValue, - @"fillExtrusionBase should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionBase = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting fillExtrusionBase to a constant value expression should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, constantExpression, + @"fillExtrusionBase should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionBase = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, - @"Setting fillExtrusionBase to a camera function should update fill-extrusion-base."); - XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, - @"fillExtrusionBase should round-trip camera functions."); + @"Setting fillExtrusionBase to a camera expression should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression, + @"fillExtrusionBase should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillExtrusionBase = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillExtrusionBase = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, - @"Setting fillExtrusionBase to a source function should update fill-extrusion-base."); - XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, - @"fillExtrusionBase should round-trip source functions."); + @"Setting fillExtrusionBase to a data expression should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression, + @"fillExtrusionBase should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillExtrusionBase = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillExtrusionBase = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -93,15 +97,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, - @"Setting fillExtrusionBase to a composite function should update fill-extrusion-base."); - XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, - @"fillExtrusionBase should round-trip composite functions."); + @"Setting fillExtrusionBase to a camera-data expression should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression, + @"fillExtrusionBase should round-trip camera-data expressions."); layer.fillExtrusionBase = nil; XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), @"Unsetting fillExtrusionBase should return fill-extrusion-base to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionBase, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionBase, defaultExpression, @"fillExtrusionBase should return the default value after being unset."); // Transition property test layer.fillExtrusionBaseTransition = transitionTest; @@ -118,40 +122,44 @@ { XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), @"fill-extrusion-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillExtrusionColor; + NSExpression *defaultExpression = layer.fillExtrusionColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.fillExtrusionColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.fillExtrusionColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, - @"Setting fillExtrusionColor to a constant value should update fill-extrusion-color."); - XCTAssertEqualObjects(layer.fillExtrusionColor, constantStyleValue, - @"fillExtrusionColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting fillExtrusionColor to a constant value expression should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, constantExpression, + @"fillExtrusionColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, - @"Setting fillExtrusionColor to a camera function should update fill-extrusion-color."); - XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, - @"fillExtrusionColor should round-trip camera functions."); + @"Setting fillExtrusionColor to a camera expression should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression, + @"fillExtrusionColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillExtrusionColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillExtrusionColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, - @"Setting fillExtrusionColor to a source function should update fill-extrusion-color."); - XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, - @"fillExtrusionColor should round-trip source functions."); + @"Setting fillExtrusionColor to a data expression should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression, + @"fillExtrusionColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillExtrusionColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillExtrusionColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -159,15 +167,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, - @"Setting fillExtrusionColor to a composite function should update fill-extrusion-color."); - XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, - @"fillExtrusionColor should round-trip composite functions."); + @"Setting fillExtrusionColor to a camera-data expression should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression, + @"fillExtrusionColor should round-trip camera-data expressions."); layer.fillExtrusionColor = nil; XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), @"Unsetting fillExtrusionColor should return fill-extrusion-color to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionColor, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionColor, defaultExpression, @"fillExtrusionColor should return the default value after being unset."); // Transition property test layer.fillExtrusionColorTransition = transitionTest; @@ -184,40 +192,44 @@ { XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), @"fill-extrusion-height should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionHeight; + NSExpression *defaultExpression = layer.fillExtrusionHeight; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.fillExtrusionHeight = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.fillExtrusionHeight = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, - @"Setting fillExtrusionHeight to a constant value should update fill-extrusion-height."); - XCTAssertEqualObjects(layer.fillExtrusionHeight, constantStyleValue, - @"fillExtrusionHeight should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionHeight = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting fillExtrusionHeight to a constant value expression should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, constantExpression, + @"fillExtrusionHeight should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionHeight = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, - @"Setting fillExtrusionHeight to a camera function should update fill-extrusion-height."); - XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, - @"fillExtrusionHeight should round-trip camera functions."); + @"Setting fillExtrusionHeight to a camera expression should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression, + @"fillExtrusionHeight should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillExtrusionHeight = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillExtrusionHeight = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, - @"Setting fillExtrusionHeight to a source function should update fill-extrusion-height."); - XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, - @"fillExtrusionHeight should round-trip source functions."); + @"Setting fillExtrusionHeight to a data expression should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression, + @"fillExtrusionHeight should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillExtrusionHeight = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillExtrusionHeight = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -225,15 +237,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, - @"Setting fillExtrusionHeight to a composite function should update fill-extrusion-height."); - XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, - @"fillExtrusionHeight should round-trip composite functions."); + @"Setting fillExtrusionHeight to a camera-data expression should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression, + @"fillExtrusionHeight should round-trip camera-data expressions."); layer.fillExtrusionHeight = nil; XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), @"Unsetting fillExtrusionHeight should return fill-extrusion-height to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultExpression, @"fillExtrusionHeight should return the default value after being unset."); // Transition property test layer.fillExtrusionHeightTransition = transitionTest; @@ -250,39 +262,44 @@ { XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), @"fill-extrusion-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionOpacity; + NSExpression *defaultExpression = layer.fillExtrusionOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.fillExtrusionOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.fillExtrusionOpacity = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, - @"Setting fillExtrusionOpacity to a constant value should update fill-extrusion-opacity."); - XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantStyleValue, - @"fillExtrusionOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting fillExtrusionOpacity to a constant value expression should update fill-extrusion-opacity."); + XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantExpression, + @"fillExtrusionOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, - @"Setting fillExtrusionOpacity to a camera function should update fill-extrusion-opacity."); - XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionStyleValue, - @"fillExtrusionOpacity should round-trip camera functions."); + @"Setting fillExtrusionOpacity to a camera expression should update fill-extrusion-opacity."); + XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionExpression, + @"fillExtrusionOpacity should round-trip camera expressions."); layer.fillExtrusionOpacity = nil; XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), @"Unsetting fillExtrusionOpacity should return fill-extrusion-opacity to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultExpression, @"fillExtrusionOpacity should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.fillExtrusionOpacityTransition = transitionTest; auto toptions = rawLayer->getFillExtrusionOpacityTransition(); @@ -298,39 +315,44 @@ { XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), @"fill-extrusion-pattern should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.fillExtrusionPattern; + NSExpression *defaultExpression = layer.fillExtrusionPattern; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Extrusion Pattern"]; - layer.fillExtrusionPattern = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Fill Extrusion Pattern'"]; + layer.fillExtrusionPattern = constantExpression; mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Extrusion Pattern" }; XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, - @"Setting fillExtrusionPattern to a constant value should update fill-extrusion-pattern."); - XCTAssertEqualObjects(layer.fillExtrusionPattern, constantStyleValue, - @"fillExtrusionPattern should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionPattern = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Extrusion Pattern"}} }; + @"Setting fillExtrusionPattern to a constant value expression should update fill-extrusion-pattern."); + XCTAssertEqualObjects(layer.fillExtrusionPattern, constantExpression, + @"fillExtrusionPattern should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Fill Extrusion Pattern'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionPattern = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Fill Extrusion Pattern" }, + { 18, "Fill Extrusion Pattern" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, - @"Setting fillExtrusionPattern to a camera function should update fill-extrusion-pattern."); - XCTAssertEqualObjects(layer.fillExtrusionPattern, functionStyleValue, - @"fillExtrusionPattern should round-trip camera functions."); + @"Setting fillExtrusionPattern to a camera expression should update fill-extrusion-pattern."); + XCTAssertEqualObjects(layer.fillExtrusionPattern, functionExpression, + @"fillExtrusionPattern should round-trip camera expressions."); layer.fillExtrusionPattern = nil; XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), @"Unsetting fillExtrusionPattern should return fill-extrusion-pattern to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultExpression, @"fillExtrusionPattern should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.fillExtrusionPatternTransition = transitionTest; auto toptions = rawLayer->getFillExtrusionPatternTransition(); @@ -346,84 +368,94 @@ { XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), @"fill-extrusion-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslation; + NSExpression *defaultExpression = layer.fillExtrusionTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.fillExtrusionTranslation = constantStyleValue; + layer.fillExtrusionTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, - @"Setting fillExtrusionTranslation to a constant value should update fill-extrusion-translate."); - XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantStyleValue, - @"fillExtrusionTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting fillExtrusionTranslation to a constant value expression should update fill-extrusion-translate."); + XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantExpression, + @"fillExtrusionTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, - @"Setting fillExtrusionTranslation to a camera function should update fill-extrusion-translate."); - XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionStyleValue, - @"fillExtrusionTranslation should round-trip camera functions."); + @"Setting fillExtrusionTranslation to a camera expression should update fill-extrusion-translate."); + XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionExpression, + @"fillExtrusionTranslation should round-trip camera expressions."); layer.fillExtrusionTranslation = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), @"Unsetting fillExtrusionTranslation should return fill-extrusion-translate to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultExpression, @"fillExtrusionTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // fill-extrusion-translate-anchor { XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), @"fill-extrusion-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslationAnchor; + NSExpression *defaultExpression = layer.fillExtrusionTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport]]; - layer.fillExtrusionTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.fillExtrusionTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, - @"Setting fillExtrusionTranslationAnchor to a constant value should update fill-extrusion-translate-anchor."); - XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantStyleValue, - @"fillExtrusionTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillExtrusionTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting fillExtrusionTranslationAnchor to a constant value expression should update fill-extrusion-translate-anchor."); + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantExpression, + @"fillExtrusionTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillExtrusionTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, - @"Setting fillExtrusionTranslationAnchor to a camera function should update fill-extrusion-translate-anchor."); - XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionStyleValue, - @"fillExtrusionTranslationAnchor should round-trip camera functions."); + @"Setting fillExtrusionTranslationAnchor to a camera expression should update fill-extrusion-translate-anchor."); + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionExpression, + @"fillExtrusionTranslationAnchor should round-trip camera expressions."); layer.fillExtrusionTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), @"Unsetting fillExtrusionTranslationAnchor should return fill-extrusion-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultExpression, @"fillExtrusionTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillExtrusionLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } } diff --git a/platform/darwin/test/MGLFillStyleLayerTests.mm b/platform/darwin/test/MGLFillStyleLayerTests.mm index 85f0b24fa7..25521f3ede 100644 --- a/platform/darwin/test/MGLFillStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillStyleLayerTests.mm @@ -52,79 +52,88 @@ { XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(), @"fill-antialias should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillAntialiased; + NSExpression *defaultExpression = layer.fillAntialiased; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO]; - layer.fillAntialiased = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"false"]; + layer.fillAntialiased = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { false }; XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue, - @"Setting fillAntialiased to a constant value should update fill-antialias."); - XCTAssertEqualObjects(layer.fillAntialiased, constantStyleValue, - @"fillAntialiased should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillAntialiased = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, false}} }; + @"Setting fillAntialiased to a constant value expression should update fill-antialias."); + XCTAssertEqualObjects(layer.fillAntialiased, constantExpression, + @"fillAntialiased should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"false"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillAntialiased = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, false }, + { 18, false }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue, - @"Setting fillAntialiased to a camera function should update fill-antialias."); - XCTAssertEqualObjects(layer.fillAntialiased, functionStyleValue, - @"fillAntialiased should round-trip camera functions."); + @"Setting fillAntialiased to a camera expression should update fill-antialias."); + XCTAssertEqualObjects(layer.fillAntialiased, functionExpression, + @"fillAntialiased should round-trip camera expressions."); layer.fillAntialiased = nil; XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(), @"Unsetting fillAntialiased should return fill-antialias to the default value."); - XCTAssertEqualObjects(layer.fillAntialiased, defaultStyleValue, + XCTAssertEqualObjects(layer.fillAntialiased, defaultExpression, @"fillAntialiased should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillAntialiased = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // fill-color { XCTAssertTrue(rawLayer->getFillColor().isUndefined(), @"fill-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillColor; + NSExpression *defaultExpression = layer.fillColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.fillColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.fillColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getFillColor(), propertyValue, - @"Setting fillColor to a constant value should update fill-color."); - XCTAssertEqualObjects(layer.fillColor, constantStyleValue, - @"fillColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting fillColor to a constant value expression should update fill-color."); + XCTAssertEqualObjects(layer.fillColor, constantExpression, + @"fillColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getFillColor(), propertyValue, - @"Setting fillColor to a camera function should update fill-color."); - XCTAssertEqualObjects(layer.fillColor, functionStyleValue, - @"fillColor should round-trip camera functions."); + @"Setting fillColor to a camera expression should update fill-color."); + XCTAssertEqualObjects(layer.fillColor, functionExpression, + @"fillColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillColor(), propertyValue, - @"Setting fillColor to a source function should update fill-color."); - XCTAssertEqualObjects(layer.fillColor, functionStyleValue, - @"fillColor should round-trip source functions."); + @"Setting fillColor to a data expression should update fill-color."); + XCTAssertEqualObjects(layer.fillColor, functionExpression, + @"fillColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -132,15 +141,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillColor(), propertyValue, - @"Setting fillColor to a composite function should update fill-color."); - XCTAssertEqualObjects(layer.fillColor, functionStyleValue, - @"fillColor should round-trip composite functions."); + @"Setting fillColor to a camera-data expression should update fill-color."); + XCTAssertEqualObjects(layer.fillColor, functionExpression, + @"fillColor should round-trip camera-data expressions."); layer.fillColor = nil; XCTAssertTrue(rawLayer->getFillColor().isUndefined(), @"Unsetting fillColor should return fill-color to the default value."); - XCTAssertEqualObjects(layer.fillColor, defaultStyleValue, + XCTAssertEqualObjects(layer.fillColor, defaultExpression, @"fillColor should return the default value after being unset."); // Transition property test layer.fillColorTransition = transitionTest; @@ -157,40 +166,44 @@ { XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(), @"fill-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillOpacity; + NSExpression *defaultExpression = layer.fillOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.fillOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.fillOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, - @"Setting fillOpacity to a constant value should update fill-opacity."); - XCTAssertEqualObjects(layer.fillOpacity, constantStyleValue, - @"fillOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting fillOpacity to a constant value expression should update fill-opacity."); + XCTAssertEqualObjects(layer.fillOpacity, constantExpression, + @"fillOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, - @"Setting fillOpacity to a camera function should update fill-opacity."); - XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue, - @"fillOpacity should round-trip camera functions."); + @"Setting fillOpacity to a camera expression should update fill-opacity."); + XCTAssertEqualObjects(layer.fillOpacity, functionExpression, + @"fillOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, - @"Setting fillOpacity to a source function should update fill-opacity."); - XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue, - @"fillOpacity should round-trip source functions."); + @"Setting fillOpacity to a data expression should update fill-opacity."); + XCTAssertEqualObjects(layer.fillOpacity, functionExpression, + @"fillOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -198,15 +211,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, - @"Setting fillOpacity to a composite function should update fill-opacity."); - XCTAssertEqualObjects(layer.fillOpacity, functionStyleValue, - @"fillOpacity should round-trip composite functions."); + @"Setting fillOpacity to a camera-data expression should update fill-opacity."); + XCTAssertEqualObjects(layer.fillOpacity, functionExpression, + @"fillOpacity should round-trip camera-data expressions."); layer.fillOpacity = nil; XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(), @"Unsetting fillOpacity should return fill-opacity to the default value."); - XCTAssertEqualObjects(layer.fillOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.fillOpacity, defaultExpression, @"fillOpacity should return the default value after being unset."); // Transition property test layer.fillOpacityTransition = transitionTest; @@ -223,40 +236,44 @@ { XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(), @"fill-outline-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillOutlineColor; + NSExpression *defaultExpression = layer.fillOutlineColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.fillOutlineColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.fillOutlineColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, - @"Setting fillOutlineColor to a constant value should update fill-outline-color."); - XCTAssertEqualObjects(layer.fillOutlineColor, constantStyleValue, - @"fillOutlineColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillOutlineColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting fillOutlineColor to a constant value expression should update fill-outline-color."); + XCTAssertEqualObjects(layer.fillOutlineColor, constantExpression, + @"fillOutlineColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillOutlineColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, - @"Setting fillOutlineColor to a camera function should update fill-outline-color."); - XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue, - @"fillOutlineColor should round-trip camera functions."); + @"Setting fillOutlineColor to a camera expression should update fill-outline-color."); + XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression, + @"fillOutlineColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.fillOutlineColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.fillOutlineColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, - @"Setting fillOutlineColor to a source function should update fill-outline-color."); - XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue, - @"fillOutlineColor should round-trip source functions."); + @"Setting fillOutlineColor to a data expression should update fill-outline-color."); + XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression, + @"fillOutlineColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.fillOutlineColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.fillOutlineColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -264,15 +281,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, - @"Setting fillOutlineColor to a composite function should update fill-outline-color."); - XCTAssertEqualObjects(layer.fillOutlineColor, functionStyleValue, - @"fillOutlineColor should round-trip composite functions."); + @"Setting fillOutlineColor to a camera-data expression should update fill-outline-color."); + XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression, + @"fillOutlineColor should round-trip camera-data expressions."); layer.fillOutlineColor = nil; XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(), @"Unsetting fillOutlineColor should return fill-outline-color to the default value."); - XCTAssertEqualObjects(layer.fillOutlineColor, defaultStyleValue, + XCTAssertEqualObjects(layer.fillOutlineColor, defaultExpression, @"fillOutlineColor should return the default value after being unset."); // Transition property test layer.fillOutlineColorTransition = transitionTest; @@ -289,39 +306,44 @@ { XCTAssertTrue(rawLayer->getFillPattern().isUndefined(), @"fill-pattern should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.fillPattern; + NSExpression *defaultExpression = layer.fillPattern; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Pattern"]; - layer.fillPattern = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Fill Pattern'"]; + layer.fillPattern = constantExpression; mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Pattern" }; XCTAssertEqual(rawLayer->getFillPattern(), propertyValue, - @"Setting fillPattern to a constant value should update fill-pattern."); - XCTAssertEqualObjects(layer.fillPattern, constantStyleValue, - @"fillPattern should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillPattern = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Pattern"}} }; + @"Setting fillPattern to a constant value expression should update fill-pattern."); + XCTAssertEqualObjects(layer.fillPattern, constantExpression, + @"fillPattern should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Fill Pattern'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillPattern = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Fill Pattern" }, + { 18, "Fill Pattern" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getFillPattern(), propertyValue, - @"Setting fillPattern to a camera function should update fill-pattern."); - XCTAssertEqualObjects(layer.fillPattern, functionStyleValue, - @"fillPattern should round-trip camera functions."); + @"Setting fillPattern to a camera expression should update fill-pattern."); + XCTAssertEqualObjects(layer.fillPattern, functionExpression, + @"fillPattern should round-trip camera expressions."); layer.fillPattern = nil; XCTAssertTrue(rawLayer->getFillPattern().isUndefined(), @"Unsetting fillPattern should return fill-pattern to the default value."); - XCTAssertEqualObjects(layer.fillPattern, defaultStyleValue, + XCTAssertEqualObjects(layer.fillPattern, defaultExpression, @"fillPattern should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.fillPatternTransition = transitionTest; auto toptions = rawLayer->getFillPatternTransition(); @@ -337,84 +359,94 @@ { XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(), @"fill-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslation; + NSExpression *defaultExpression = layer.fillTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.fillTranslation = constantStyleValue; + layer.fillTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue, - @"Setting fillTranslation to a constant value should update fill-translate."); - XCTAssertEqualObjects(layer.fillTranslation, constantStyleValue, - @"fillTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting fillTranslation to a constant value expression should update fill-translate."); + XCTAssertEqualObjects(layer.fillTranslation, constantExpression, + @"fillTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue, - @"Setting fillTranslation to a camera function should update fill-translate."); - XCTAssertEqualObjects(layer.fillTranslation, functionStyleValue, - @"fillTranslation should round-trip camera functions."); + @"Setting fillTranslation to a camera expression should update fill-translate."); + XCTAssertEqualObjects(layer.fillTranslation, functionExpression, + @"fillTranslation should round-trip camera expressions."); layer.fillTranslation = nil; XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(), @"Unsetting fillTranslation should return fill-translate to the default value."); - XCTAssertEqualObjects(layer.fillTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.fillTranslation, defaultExpression, @"fillTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // fill-translate-anchor { XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(), @"fill-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillTranslationAnchor; + NSExpression *defaultExpression = layer.fillTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillTranslationAnchor:MGLFillTranslationAnchorViewport]]; - layer.fillTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.fillTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue, - @"Setting fillTranslationAnchor to a constant value should update fill-translate-anchor."); - XCTAssertEqualObjects(layer.fillTranslationAnchor, constantStyleValue, - @"fillTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.fillTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting fillTranslationAnchor to a constant value expression should update fill-translate-anchor."); + XCTAssertEqualObjects(layer.fillTranslationAnchor, constantExpression, + @"fillTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.fillTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue, - @"Setting fillTranslationAnchor to a camera function should update fill-translate-anchor."); - XCTAssertEqualObjects(layer.fillTranslationAnchor, functionStyleValue, - @"fillTranslationAnchor should round-trip camera functions."); + @"Setting fillTranslationAnchor to a camera expression should update fill-translate-anchor."); + XCTAssertEqualObjects(layer.fillTranslationAnchor, functionExpression, + @"fillTranslationAnchor should round-trip camera expressions."); layer.fillTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(), @"Unsetting fillTranslationAnchor should return fill-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.fillTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.fillTranslationAnchor, defaultExpression, @"fillTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.fillTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLFillLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } } diff --git a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm index 283830ccb5..87950e4f24 100644 --- a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm +++ b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm @@ -52,39 +52,44 @@ { XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(), @"hillshade-accent-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.hillshadeAccentColor; + NSExpression *defaultExpression = layer.hillshadeAccentColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.hillshadeAccentColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.hillshadeAccentColor = constantExpression; mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue, - @"Setting hillshadeAccentColor to a constant value should update hillshade-accent-color."); - XCTAssertEqualObjects(layer.hillshadeAccentColor, constantStyleValue, - @"hillshadeAccentColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeAccentColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting hillshadeAccentColor to a constant value expression should update hillshade-accent-color."); + XCTAssertEqualObjects(layer.hillshadeAccentColor, constantExpression, + @"hillshadeAccentColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeAccentColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue, - @"Setting hillshadeAccentColor to a camera function should update hillshade-accent-color."); - XCTAssertEqualObjects(layer.hillshadeAccentColor, functionStyleValue, - @"hillshadeAccentColor should round-trip camera functions."); + @"Setting hillshadeAccentColor to a camera expression should update hillshade-accent-color."); + XCTAssertEqualObjects(layer.hillshadeAccentColor, functionExpression, + @"hillshadeAccentColor should round-trip camera expressions."); layer.hillshadeAccentColor = nil; XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(), @"Unsetting hillshadeAccentColor should return hillshade-accent-color to the default value."); - XCTAssertEqualObjects(layer.hillshadeAccentColor, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeAccentColor, defaultExpression, @"hillshadeAccentColor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeAccentColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.hillshadeAccentColorTransition = transitionTest; auto toptions = rawLayer->getHillshadeAccentColorTransition(); @@ -100,39 +105,44 @@ { XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(), @"hillshade-exaggeration should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.hillshadeExaggeration; + NSExpression *defaultExpression = layer.hillshadeExaggeration; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.hillshadeExaggeration = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.hillshadeExaggeration = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue, - @"Setting hillshadeExaggeration to a constant value should update hillshade-exaggeration."); - XCTAssertEqualObjects(layer.hillshadeExaggeration, constantStyleValue, - @"hillshadeExaggeration should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeExaggeration = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting hillshadeExaggeration to a constant value expression should update hillshade-exaggeration."); + XCTAssertEqualObjects(layer.hillshadeExaggeration, constantExpression, + @"hillshadeExaggeration should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeExaggeration = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue, - @"Setting hillshadeExaggeration to a camera function should update hillshade-exaggeration."); - XCTAssertEqualObjects(layer.hillshadeExaggeration, functionStyleValue, - @"hillshadeExaggeration should round-trip camera functions."); + @"Setting hillshadeExaggeration to a camera expression should update hillshade-exaggeration."); + XCTAssertEqualObjects(layer.hillshadeExaggeration, functionExpression, + @"hillshadeExaggeration should round-trip camera expressions."); layer.hillshadeExaggeration = nil; XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(), @"Unsetting hillshadeExaggeration should return hillshade-exaggeration to the default value."); - XCTAssertEqualObjects(layer.hillshadeExaggeration, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeExaggeration, defaultExpression, @"hillshadeExaggeration should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeExaggeration = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.hillshadeExaggerationTransition = transitionTest; auto toptions = rawLayer->getHillshadeExaggerationTransition(); @@ -148,39 +158,44 @@ { XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(), @"hillshade-highlight-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.hillshadeHighlightColor; + NSExpression *defaultExpression = layer.hillshadeHighlightColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.hillshadeHighlightColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.hillshadeHighlightColor = constantExpression; mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue, - @"Setting hillshadeHighlightColor to a constant value should update hillshade-highlight-color."); - XCTAssertEqualObjects(layer.hillshadeHighlightColor, constantStyleValue, - @"hillshadeHighlightColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeHighlightColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting hillshadeHighlightColor to a constant value expression should update hillshade-highlight-color."); + XCTAssertEqualObjects(layer.hillshadeHighlightColor, constantExpression, + @"hillshadeHighlightColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeHighlightColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue, - @"Setting hillshadeHighlightColor to a camera function should update hillshade-highlight-color."); - XCTAssertEqualObjects(layer.hillshadeHighlightColor, functionStyleValue, - @"hillshadeHighlightColor should round-trip camera functions."); + @"Setting hillshadeHighlightColor to a camera expression should update hillshade-highlight-color."); + XCTAssertEqualObjects(layer.hillshadeHighlightColor, functionExpression, + @"hillshadeHighlightColor should round-trip camera expressions."); layer.hillshadeHighlightColor = nil; XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(), @"Unsetting hillshadeHighlightColor should return hillshade-highlight-color to the default value."); - XCTAssertEqualObjects(layer.hillshadeHighlightColor, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeHighlightColor, defaultExpression, @"hillshadeHighlightColor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeHighlightColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.hillshadeHighlightColorTransition = transitionTest; auto toptions = rawLayer->getHillshadeHighlightColorTransition(); @@ -196,78 +211,88 @@ { XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(), @"hillshade-illumination-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.hillshadeIlluminationAnchor; + NSExpression *defaultExpression = layer.hillshadeIlluminationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLHillshadeIlluminationAnchor:MGLHillshadeIlluminationAnchorViewport]]; - layer.hillshadeIlluminationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.hillshadeIlluminationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::HillshadeIlluminationAnchorType> propertyValue = { mbgl::style::HillshadeIlluminationAnchorType::Viewport }; XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue, - @"Setting hillshadeIlluminationAnchor to a constant value should update hillshade-illumination-anchor."); - XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, constantStyleValue, - @"hillshadeIlluminationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeIlluminationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::HillshadeIlluminationAnchorType> intervalStops = { {{18, mbgl::style::HillshadeIlluminationAnchorType::Viewport}} }; + @"Setting hillshadeIlluminationAnchor to a constant value expression should update hillshade-illumination-anchor."); + XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, constantExpression, + @"hillshadeIlluminationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeIlluminationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::HillshadeIlluminationAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::HillshadeIlluminationAnchorType::Viewport }, + { 18, mbgl::style::HillshadeIlluminationAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::HillshadeIlluminationAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue, - @"Setting hillshadeIlluminationAnchor to a camera function should update hillshade-illumination-anchor."); - XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, functionStyleValue, - @"hillshadeIlluminationAnchor should round-trip camera functions."); + @"Setting hillshadeIlluminationAnchor to a camera expression should update hillshade-illumination-anchor."); + XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, functionExpression, + @"hillshadeIlluminationAnchor should round-trip camera expressions."); layer.hillshadeIlluminationAnchor = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(), @"Unsetting hillshadeIlluminationAnchor should return hillshade-illumination-anchor to the default value."); - XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, defaultExpression, @"hillshadeIlluminationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // hillshade-illumination-direction { XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(), @"hillshade-illumination-direction should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.hillshadeIlluminationDirection; + NSExpression *defaultExpression = layer.hillshadeIlluminationDirection; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.hillshadeIlluminationDirection = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.hillshadeIlluminationDirection = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue, - @"Setting hillshadeIlluminationDirection to a constant value should update hillshade-illumination-direction."); - XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, constantStyleValue, - @"hillshadeIlluminationDirection should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeIlluminationDirection = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting hillshadeIlluminationDirection to a constant value expression should update hillshade-illumination-direction."); + XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, constantExpression, + @"hillshadeIlluminationDirection should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeIlluminationDirection = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue, - @"Setting hillshadeIlluminationDirection to a camera function should update hillshade-illumination-direction."); - XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, functionStyleValue, - @"hillshadeIlluminationDirection should round-trip camera functions."); + @"Setting hillshadeIlluminationDirection to a camera expression should update hillshade-illumination-direction."); + XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, functionExpression, + @"hillshadeIlluminationDirection should round-trip camera expressions."); layer.hillshadeIlluminationDirection = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(), @"Unsetting hillshadeIlluminationDirection should return hillshade-illumination-direction to the default value."); - XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, defaultExpression, @"hillshadeIlluminationDirection should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeIlluminationDirection = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.hillshadeIlluminationDirectionTransition = transitionTest; auto toptions = rawLayer->getHillshadeIlluminationDirectionTransition(); @@ -283,39 +308,44 @@ { XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(), @"hillshade-shadow-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.hillshadeShadowColor; + NSExpression *defaultExpression = layer.hillshadeShadowColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.hillshadeShadowColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.hillshadeShadowColor = constantExpression; mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue, - @"Setting hillshadeShadowColor to a constant value should update hillshade-shadow-color."); - XCTAssertEqualObjects(layer.hillshadeShadowColor, constantStyleValue, - @"hillshadeShadowColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.hillshadeShadowColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting hillshadeShadowColor to a constant value expression should update hillshade-shadow-color."); + XCTAssertEqualObjects(layer.hillshadeShadowColor, constantExpression, + @"hillshadeShadowColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.hillshadeShadowColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue, - @"Setting hillshadeShadowColor to a camera function should update hillshade-shadow-color."); - XCTAssertEqualObjects(layer.hillshadeShadowColor, functionStyleValue, - @"hillshadeShadowColor should round-trip camera functions."); + @"Setting hillshadeShadowColor to a camera expression should update hillshade-shadow-color."); + XCTAssertEqualObjects(layer.hillshadeShadowColor, functionExpression, + @"hillshadeShadowColor should round-trip camera expressions."); layer.hillshadeShadowColor = nil; XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(), @"Unsetting hillshadeShadowColor should return hillshade-shadow-color to the default value."); - XCTAssertEqualObjects(layer.hillshadeShadowColor, defaultStyleValue, + XCTAssertEqualObjects(layer.hillshadeShadowColor, defaultExpression, @"hillshadeShadowColor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.hillshadeShadowColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHillshadeLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.hillshadeShadowColorTransition = transitionTest; auto toptions = rawLayer->getHillshadeShadowColorTransition(); diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm index de64d57851..a51c59c725 100644 --- a/platform/darwin/test/MGLLightTest.mm +++ b/platform/darwin/test/MGLLightTest.mm @@ -27,29 +27,28 @@ { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - auto lightFromMGLlight = [mglLight mbglLight]; + auto lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLlight.getAnchor()); - XCTAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue."); - NSValue *anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue; - XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorViewport); + XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLLight.getAnchor()); + XCTAssertEqual(mglLight.anchor.expressionType, NSConstantValueExpressionType, @"mglLight.anchor isn’t a constant value expression."); + XCTAssertEqualObjects(mglLight.anchor.constantValue, @"viewport"); mbgl::style::PropertyValue<mbgl::style::LightAnchorType> propertyValue = { mbgl::style::LightAnchorType::Viewport }; light.setAnchor(propertyValue); mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - lightFromMGLlight = [mglLight mbglLight]; + lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getAnchor(), lightFromMGLlight.getAnchor()); + XCTAssertEqual(light.getAnchor(), lightFromMGLLight.getAnchor()); } // position { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - auto lightFromMGLlight = [mglLight mbglLight]; + auto lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getDefaultPosition(), lightFromMGLlight.getPosition()); - auto positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssertEqual(light.getDefaultPosition(), lightFromMGLLight.getPosition()); + auto positionTransition = lightFromMGLLight.getPositionTransition(); XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == defaultTransition.delay); XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == defaultTransition.duration); @@ -60,10 +59,10 @@ light.setPositionTransition(transitionOptions); mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - lightFromMGLlight = [mglLight mbglLight]; + lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getPosition(), lightFromMGLlight.getPosition()); - positionTransition = lightFromMGLlight.getPositionTransition(); + XCTAssertEqual(light.getPosition(), lightFromMGLLight.getPosition()); + positionTransition = lightFromMGLLight.getPositionTransition(); XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == transition.delay); XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == transition.duration); @@ -73,10 +72,10 @@ { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - auto lightFromMGLlight = [mglLight mbglLight]; + auto lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getDefaultColor(), lightFromMGLlight.getColor()); - auto colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssertEqual(light.getDefaultColor(), lightFromMGLLight.getColor()); + auto colorTransition = lightFromMGLLight.getColorTransition(); XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == defaultTransition.delay); XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == defaultTransition.duration); @@ -85,10 +84,10 @@ light.setColorTransition(transitionOptions); mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - lightFromMGLlight = [mglLight mbglLight]; + lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getColor(), lightFromMGLlight.getColor()); - colorTransition = lightFromMGLlight.getColorTransition(); + XCTAssertEqual(light.getColor(), lightFromMGLLight.getColor()); + colorTransition = lightFromMGLLight.getColorTransition(); XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == transition.delay); XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == transition.duration); @@ -98,10 +97,10 @@ { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - auto lightFromMGLlight = [mglLight mbglLight]; + auto lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getDefaultIntensity(), lightFromMGLlight.getIntensity()); - auto intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssertEqual(light.getDefaultIntensity(), lightFromMGLLight.getIntensity()); + auto intensityTransition = lightFromMGLLight.getIntensityTransition(); XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == defaultTransition.delay); XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == defaultTransition.duration); @@ -110,10 +109,10 @@ light.setIntensityTransition(transitionOptions); mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - lightFromMGLlight = [mglLight mbglLight]; + lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getIntensity(), lightFromMGLlight.getIntensity()); - intensityTransition = lightFromMGLlight.getIntensityTransition(); + XCTAssertEqual(light.getIntensity(), lightFromMGLLight.getIntensity()); + intensityTransition = lightFromMGLLight.getIntensityTransition(); XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == transition.delay); XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == transition.duration); diff --git a/platform/darwin/test/MGLLightTest.mm.ejs b/platform/darwin/test/MGLLightTest.mm.ejs index 5b1f27d8d1..35ff58b6d5 100644 --- a/platform/darwin/test/MGLLightTest.mm.ejs +++ b/platform/darwin/test/MGLLightTest.mm.ejs @@ -32,19 +32,18 @@ { mbgl::style::Light light; MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - auto lightFromMGLlight = [mglLight mbglLight]; + auto lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.getDefault<%- camelize(property.name) -%>(), lightFromMGLlight.get<%- camelize(property.name) -%>()); + XCTAssertEqual(light.getDefault<%- camelize(property.name) -%>(), lightFromMGLLight.get<%- camelize(property.name) -%>()); <% if (property.transition) { -%> - auto <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLlight.get<%- camelize(property.name) -%>Transition(); + auto <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLLight.get<%- camelize(property.name) -%>Transition(); XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay) == defaultTransition.delay); XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration) == defaultTransition.duration); <% } -%> <% if (property.type == "enum" && property.default) { -%> - XCTAssert([mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isn’t a MGLConstantStyleValue."); - NSValue *<%- camelizeWithLeadingLowercase(property.name) -%>Value = ((MGLConstantStyleValue *)mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>).rawValue; - XCTAssertEqual(<%- camelizeWithLeadingLowercase(property.name) -%>Value.MGLLight<%- camelize(property.name) -%>Value, MGLLight<%- camelize(property.name) -%><%- camelize(property.default) -%>); + XCTAssertEqual(mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>.expressionType, NSConstantValueExpressionType, @"mglLight.<%- camelizeWithLeadingLowercase(property.name) -%> isn’t a constant value expression."); + XCTAssertEqualObjects(mglLight.<%- camelizeWithLeadingLowercase(property.name) -%>.constantValue, @"<%- property.default -%>"); <% } -%> <% if (property.type == "array") { -%> @@ -60,11 +59,11 @@ <% } -%> mglLight = [[MGLLight alloc] initWithMBGLLight:&light]; - lightFromMGLlight = [mglLight mbglLight]; + lightFromMGLLight = mglLight.mbglLight; - XCTAssertEqual(light.get<%- camelize(property.name) -%>(), lightFromMGLlight.get<%- camelize(property.name) -%>()); + XCTAssertEqual(light.get<%- camelize(property.name) -%>(), lightFromMGLLight.get<%- camelize(property.name) -%>()); <% if (property.transition) { -%> - <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLlight.get<%- camelize(property.name) -%>Transition(); + <%- camelizeWithLeadingLowercase(property.name) -%>Transition = lightFromMGLLight.get<%- camelize(property.name) -%>Transition(); XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.delay) == transition.delay); XCTAssert(<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration && MGLTimeIntervalFromDuration(*<%- camelizeWithLeadingLowercase(property.name) -%>Transition.duration) == transition.duration); diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm index 7e7926e22e..bf98e98320 100644 --- a/platform/darwin/test/MGLLineStyleLayerTests.mm +++ b/platform/darwin/test/MGLLineStyleLayerTests.mm @@ -52,72 +52,81 @@ { XCTAssertTrue(rawLayer->getLineCap().isUndefined(), @"line-cap should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineCap; + NSExpression *defaultExpression = layer.lineCap; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapSquare]]; - layer.lineCap = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'square'"]; + layer.lineCap = constantExpression; mbgl::style::PropertyValue<mbgl::style::LineCapType> propertyValue = { mbgl::style::LineCapType::Square }; XCTAssertEqual(rawLayer->getLineCap(), propertyValue, - @"Setting lineCap to a constant value should update line-cap."); - XCTAssertEqualObjects(layer.lineCap, constantStyleValue, - @"lineCap should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineCap = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::LineCapType> intervalStops = { {{18, mbgl::style::LineCapType::Square}} }; + @"Setting lineCap to a constant value expression should update line-cap."); + XCTAssertEqualObjects(layer.lineCap, constantExpression, + @"lineCap should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'square'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineCap = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::LineCapType> intervalStops = {{ + { -INFINITY, mbgl::style::LineCapType::Square }, + { 18, mbgl::style::LineCapType::Square }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::LineCapType> { intervalStops }; XCTAssertEqual(rawLayer->getLineCap(), propertyValue, - @"Setting lineCap to a camera function should update line-cap."); - XCTAssertEqualObjects(layer.lineCap, functionStyleValue, - @"lineCap should round-trip camera functions."); + @"Setting lineCap to a camera expression should update line-cap."); + XCTAssertEqualObjects(layer.lineCap, functionExpression, + @"lineCap should round-trip camera expressions."); layer.lineCap = nil; XCTAssertTrue(rawLayer->getLineCap().isUndefined(), @"Unsetting lineCap should return line-cap to the default value."); - XCTAssertEqualObjects(layer.lineCap, defaultStyleValue, + XCTAssertEqualObjects(layer.lineCap, defaultExpression, @"lineCap should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineCap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineCap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineCap = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineCap = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-join { XCTAssertTrue(rawLayer->getLineJoin().isUndefined(), @"line-join should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineJoin; + NSExpression *defaultExpression = layer.lineJoin; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinMiter]]; - layer.lineJoin = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'miter'"]; + layer.lineJoin = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::style::LineJoinType> propertyValue = { mbgl::style::LineJoinType::Miter }; XCTAssertEqual(rawLayer->getLineJoin(), propertyValue, - @"Setting lineJoin to a constant value should update line-join."); - XCTAssertEqualObjects(layer.lineJoin, constantStyleValue, - @"lineJoin should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineJoin = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::LineJoinType> intervalStops = { {{18, mbgl::style::LineJoinType::Miter}} }; + @"Setting lineJoin to a constant value expression should update line-join."); + XCTAssertEqualObjects(layer.lineJoin, constantExpression, + @"lineJoin should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'miter'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineJoin = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::LineJoinType> intervalStops = {{ + { -INFINITY, mbgl::style::LineJoinType::Miter }, + { 18, mbgl::style::LineJoinType::Miter }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::LineJoinType> { intervalStops }; XCTAssertEqual(rawLayer->getLineJoin(), propertyValue, - @"Setting lineJoin to a camera function should update line-join."); - XCTAssertEqualObjects(layer.lineJoin, functionStyleValue, - @"lineJoin should round-trip camera functions."); + @"Setting lineJoin to a camera expression should update line-join."); + XCTAssertEqualObjects(layer.lineJoin, functionExpression, + @"lineJoin should round-trip camera expressions."); layer.lineJoin = nil; XCTAssertTrue(rawLayer->getLineJoin().isUndefined(), @"Unsetting lineJoin should return line-join to the default value."); - XCTAssertEqualObjects(layer.lineJoin, defaultStyleValue, + XCTAssertEqualObjects(layer.lineJoin, defaultExpression, @"lineJoin should return the default value after being unset."); } @@ -125,118 +134,132 @@ { XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(), @"line-miter-limit should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineMiterLimit; + NSExpression *defaultExpression = layer.lineMiterLimit; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineMiterLimit = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineMiterLimit = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue, - @"Setting lineMiterLimit to a constant value should update line-miter-limit."); - XCTAssertEqualObjects(layer.lineMiterLimit, constantStyleValue, - @"lineMiterLimit should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineMiterLimit = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineMiterLimit to a constant value expression should update line-miter-limit."); + XCTAssertEqualObjects(layer.lineMiterLimit, constantExpression, + @"lineMiterLimit should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineMiterLimit = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue, - @"Setting lineMiterLimit to a camera function should update line-miter-limit."); - XCTAssertEqualObjects(layer.lineMiterLimit, functionStyleValue, - @"lineMiterLimit should round-trip camera functions."); + @"Setting lineMiterLimit to a camera expression should update line-miter-limit."); + XCTAssertEqualObjects(layer.lineMiterLimit, functionExpression, + @"lineMiterLimit should round-trip camera expressions."); layer.lineMiterLimit = nil; XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(), @"Unsetting lineMiterLimit should return line-miter-limit to the default value."); - XCTAssertEqualObjects(layer.lineMiterLimit, defaultStyleValue, + XCTAssertEqualObjects(layer.lineMiterLimit, defaultExpression, @"lineMiterLimit should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineMiterLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-round-limit { XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(), @"line-round-limit should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineRoundLimit; + NSExpression *defaultExpression = layer.lineRoundLimit; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineRoundLimit = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineRoundLimit = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue, - @"Setting lineRoundLimit to a constant value should update line-round-limit."); - XCTAssertEqualObjects(layer.lineRoundLimit, constantStyleValue, - @"lineRoundLimit should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineRoundLimit = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineRoundLimit to a constant value expression should update line-round-limit."); + XCTAssertEqualObjects(layer.lineRoundLimit, constantExpression, + @"lineRoundLimit should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineRoundLimit = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue, - @"Setting lineRoundLimit to a camera function should update line-round-limit."); - XCTAssertEqualObjects(layer.lineRoundLimit, functionStyleValue, - @"lineRoundLimit should round-trip camera functions."); + @"Setting lineRoundLimit to a camera expression should update line-round-limit."); + XCTAssertEqualObjects(layer.lineRoundLimit, functionExpression, + @"lineRoundLimit should round-trip camera expressions."); layer.lineRoundLimit = nil; XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(), @"Unsetting lineRoundLimit should return line-round-limit to the default value."); - XCTAssertEqualObjects(layer.lineRoundLimit, defaultStyleValue, + XCTAssertEqualObjects(layer.lineRoundLimit, defaultExpression, @"lineRoundLimit should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineRoundLimit = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-blur { XCTAssertTrue(rawLayer->getLineBlur().isUndefined(), @"line-blur should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineBlur; + NSExpression *defaultExpression = layer.lineBlur; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineBlur = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineBlur = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, - @"Setting lineBlur to a constant value should update line-blur."); - XCTAssertEqualObjects(layer.lineBlur, constantStyleValue, - @"lineBlur should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineBlur = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineBlur to a constant value expression should update line-blur."); + XCTAssertEqualObjects(layer.lineBlur, constantExpression, + @"lineBlur should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineBlur = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, - @"Setting lineBlur to a camera function should update line-blur."); - XCTAssertEqualObjects(layer.lineBlur, functionStyleValue, - @"lineBlur should round-trip camera functions."); + @"Setting lineBlur to a camera expression should update line-blur."); + XCTAssertEqualObjects(layer.lineBlur, functionExpression, + @"lineBlur should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineBlur = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, - @"Setting lineBlur to a source function should update line-blur."); - XCTAssertEqualObjects(layer.lineBlur, functionStyleValue, - @"lineBlur should round-trip source functions."); + @"Setting lineBlur to a data expression should update line-blur."); + XCTAssertEqualObjects(layer.lineBlur, functionExpression, + @"lineBlur should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineBlur = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -244,15 +267,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, - @"Setting lineBlur to a composite function should update line-blur."); - XCTAssertEqualObjects(layer.lineBlur, functionStyleValue, - @"lineBlur should round-trip composite functions."); + @"Setting lineBlur to a camera-data expression should update line-blur."); + XCTAssertEqualObjects(layer.lineBlur, functionExpression, + @"lineBlur should round-trip camera-data expressions."); layer.lineBlur = nil; XCTAssertTrue(rawLayer->getLineBlur().isUndefined(), @"Unsetting lineBlur should return line-blur to the default value."); - XCTAssertEqualObjects(layer.lineBlur, defaultStyleValue, + XCTAssertEqualObjects(layer.lineBlur, defaultExpression, @"lineBlur should return the default value after being unset."); // Transition property test layer.lineBlurTransition = transitionTest; @@ -269,40 +292,44 @@ { XCTAssertTrue(rawLayer->getLineColor().isUndefined(), @"line-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.lineColor; + NSExpression *defaultExpression = layer.lineColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.lineColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.lineColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getLineColor(), propertyValue, - @"Setting lineColor to a constant value should update line-color."); - XCTAssertEqualObjects(layer.lineColor, constantStyleValue, - @"lineColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting lineColor to a constant value expression should update line-color."); + XCTAssertEqualObjects(layer.lineColor, constantExpression, + @"lineColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getLineColor(), propertyValue, - @"Setting lineColor to a camera function should update line-color."); - XCTAssertEqualObjects(layer.lineColor, functionStyleValue, - @"lineColor should round-trip camera functions."); + @"Setting lineColor to a camera expression should update line-color."); + XCTAssertEqualObjects(layer.lineColor, functionExpression, + @"lineColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineColor(), propertyValue, - @"Setting lineColor to a source function should update line-color."); - XCTAssertEqualObjects(layer.lineColor, functionStyleValue, - @"lineColor should round-trip source functions."); + @"Setting lineColor to a data expression should update line-color."); + XCTAssertEqualObjects(layer.lineColor, functionExpression, + @"lineColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -310,15 +337,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineColor(), propertyValue, - @"Setting lineColor to a composite function should update line-color."); - XCTAssertEqualObjects(layer.lineColor, functionStyleValue, - @"lineColor should round-trip composite functions."); + @"Setting lineColor to a camera-data expression should update line-color."); + XCTAssertEqualObjects(layer.lineColor, functionExpression, + @"lineColor should round-trip camera-data expressions."); layer.lineColor = nil; XCTAssertTrue(rawLayer->getLineColor().isUndefined(), @"Unsetting lineColor should return line-color to the default value."); - XCTAssertEqualObjects(layer.lineColor, defaultStyleValue, + XCTAssertEqualObjects(layer.lineColor, defaultExpression, @"lineColor should return the default value after being unset."); // Transition property test layer.lineColorTransition = transitionTest; @@ -335,79 +362,88 @@ { XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(), @"line-dasharray should be unset initially."); - MGLStyleValue<NSArray<NSNumber *> *> *defaultStyleValue = layer.lineDashPattern; + NSExpression *defaultExpression = layer.lineDashPattern; - MGLStyleValue<NSArray<NSNumber *> *> *constantStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithRawValue:@[@1, @2]]; - layer.lineDashPattern = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{1, 2}"]; + layer.lineDashPattern = constantExpression; mbgl::style::PropertyValue<std::vector<float>> propertyValue = { {1, 2} }; XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue, - @"Setting lineDashPattern to a constant value should update line-dasharray."); - XCTAssertEqualObjects(layer.lineDashPattern, constantStyleValue, - @"lineDashPattern should round-trip constant values."); - - MGLStyleValue<NSArray<NSNumber *> *> * functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineDashPattern = functionStyleValue; - - mbgl::style::IntervalStops<std::vector<float>> intervalStops = { {{18, {1, 2}}} }; + @"Setting lineDashPattern to a constant value expression should update line-dasharray."); + XCTAssertEqualObjects(layer.lineDashPattern, constantExpression, + @"lineDashPattern should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 2}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineDashPattern = functionExpression; + + mbgl::style::IntervalStops<std::vector<float>> intervalStops = {{ + { -INFINITY, {1, 2} }, + { 18, {1, 2} }, + }}; propertyValue = mbgl::style::CameraFunction<std::vector<float>> { intervalStops }; XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue, - @"Setting lineDashPattern to a camera function should update line-dasharray."); - XCTAssertEqualObjects(layer.lineDashPattern, functionStyleValue, - @"lineDashPattern should round-trip camera functions."); + @"Setting lineDashPattern to a camera expression should update line-dasharray."); + XCTAssertEqualObjects(layer.lineDashPattern, functionExpression, + @"lineDashPattern should round-trip camera expressions."); layer.lineDashPattern = nil; XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(), @"Unsetting lineDashPattern should return line-dasharray to the default value."); - XCTAssertEqualObjects(layer.lineDashPattern, defaultStyleValue, + XCTAssertEqualObjects(layer.lineDashPattern, defaultExpression, @"lineDashPattern should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSArray<NSNumber *> *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineDashPattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-gap-width { XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(), @"line-gap-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineGapWidth; + NSExpression *defaultExpression = layer.lineGapWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineGapWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineGapWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, - @"Setting lineGapWidth to a constant value should update line-gap-width."); - XCTAssertEqualObjects(layer.lineGapWidth, constantStyleValue, - @"lineGapWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineGapWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineGapWidth to a constant value expression should update line-gap-width."); + XCTAssertEqualObjects(layer.lineGapWidth, constantExpression, + @"lineGapWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineGapWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, - @"Setting lineGapWidth to a camera function should update line-gap-width."); - XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue, - @"lineGapWidth should round-trip camera functions."); + @"Setting lineGapWidth to a camera expression should update line-gap-width."); + XCTAssertEqualObjects(layer.lineGapWidth, functionExpression, + @"lineGapWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineGapWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineGapWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, - @"Setting lineGapWidth to a source function should update line-gap-width."); - XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue, - @"lineGapWidth should round-trip source functions."); + @"Setting lineGapWidth to a data expression should update line-gap-width."); + XCTAssertEqualObjects(layer.lineGapWidth, functionExpression, + @"lineGapWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineGapWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineGapWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -415,15 +451,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, - @"Setting lineGapWidth to a composite function should update line-gap-width."); - XCTAssertEqualObjects(layer.lineGapWidth, functionStyleValue, - @"lineGapWidth should round-trip composite functions."); + @"Setting lineGapWidth to a camera-data expression should update line-gap-width."); + XCTAssertEqualObjects(layer.lineGapWidth, functionExpression, + @"lineGapWidth should round-trip camera-data expressions."); layer.lineGapWidth = nil; XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(), @"Unsetting lineGapWidth should return line-gap-width to the default value."); - XCTAssertEqualObjects(layer.lineGapWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.lineGapWidth, defaultExpression, @"lineGapWidth should return the default value after being unset."); // Transition property test layer.lineGapWidthTransition = transitionTest; @@ -440,40 +476,44 @@ { XCTAssertTrue(rawLayer->getLineOffset().isUndefined(), @"line-offset should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOffset; + NSExpression *defaultExpression = layer.lineOffset; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineOffset = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineOffset = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, - @"Setting lineOffset to a constant value should update line-offset."); - XCTAssertEqualObjects(layer.lineOffset, constantStyleValue, - @"lineOffset should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineOffset = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineOffset to a constant value expression should update line-offset."); + XCTAssertEqualObjects(layer.lineOffset, constantExpression, + @"lineOffset should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineOffset = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, - @"Setting lineOffset to a camera function should update line-offset."); - XCTAssertEqualObjects(layer.lineOffset, functionStyleValue, - @"lineOffset should round-trip camera functions."); + @"Setting lineOffset to a camera expression should update line-offset."); + XCTAssertEqualObjects(layer.lineOffset, functionExpression, + @"lineOffset should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineOffset = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, - @"Setting lineOffset to a source function should update line-offset."); - XCTAssertEqualObjects(layer.lineOffset, functionStyleValue, - @"lineOffset should round-trip source functions."); + @"Setting lineOffset to a data expression should update line-offset."); + XCTAssertEqualObjects(layer.lineOffset, functionExpression, + @"lineOffset should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineOffset = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -481,15 +521,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, - @"Setting lineOffset to a composite function should update line-offset."); - XCTAssertEqualObjects(layer.lineOffset, functionStyleValue, - @"lineOffset should round-trip composite functions."); + @"Setting lineOffset to a camera-data expression should update line-offset."); + XCTAssertEqualObjects(layer.lineOffset, functionExpression, + @"lineOffset should round-trip camera-data expressions."); layer.lineOffset = nil; XCTAssertTrue(rawLayer->getLineOffset().isUndefined(), @"Unsetting lineOffset should return line-offset to the default value."); - XCTAssertEqualObjects(layer.lineOffset, defaultStyleValue, + XCTAssertEqualObjects(layer.lineOffset, defaultExpression, @"lineOffset should return the default value after being unset."); // Transition property test layer.lineOffsetTransition = transitionTest; @@ -506,40 +546,44 @@ { XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(), @"line-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineOpacity; + NSExpression *defaultExpression = layer.lineOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, - @"Setting lineOpacity to a constant value should update line-opacity."); - XCTAssertEqualObjects(layer.lineOpacity, constantStyleValue, - @"lineOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineOpacity to a constant value expression should update line-opacity."); + XCTAssertEqualObjects(layer.lineOpacity, constantExpression, + @"lineOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, - @"Setting lineOpacity to a camera function should update line-opacity."); - XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue, - @"lineOpacity should round-trip camera functions."); + @"Setting lineOpacity to a camera expression should update line-opacity."); + XCTAssertEqualObjects(layer.lineOpacity, functionExpression, + @"lineOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, - @"Setting lineOpacity to a source function should update line-opacity."); - XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue, - @"lineOpacity should round-trip source functions."); + @"Setting lineOpacity to a data expression should update line-opacity."); + XCTAssertEqualObjects(layer.lineOpacity, functionExpression, + @"lineOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -547,15 +591,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, - @"Setting lineOpacity to a composite function should update line-opacity."); - XCTAssertEqualObjects(layer.lineOpacity, functionStyleValue, - @"lineOpacity should round-trip composite functions."); + @"Setting lineOpacity to a camera-data expression should update line-opacity."); + XCTAssertEqualObjects(layer.lineOpacity, functionExpression, + @"lineOpacity should round-trip camera-data expressions."); layer.lineOpacity = nil; XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(), @"Unsetting lineOpacity should return line-opacity to the default value."); - XCTAssertEqualObjects(layer.lineOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.lineOpacity, defaultExpression, @"lineOpacity should return the default value after being unset."); // Transition property test layer.lineOpacityTransition = transitionTest; @@ -572,39 +616,44 @@ { XCTAssertTrue(rawLayer->getLinePattern().isUndefined(), @"line-pattern should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.linePattern; + NSExpression *defaultExpression = layer.linePattern; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Line Pattern"]; - layer.linePattern = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"]; + layer.linePattern = constantExpression; mbgl::style::PropertyValue<std::string> propertyValue = { "Line Pattern" }; XCTAssertEqual(rawLayer->getLinePattern(), propertyValue, - @"Setting linePattern to a constant value should update line-pattern."); - XCTAssertEqualObjects(layer.linePattern, constantStyleValue, - @"linePattern should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.linePattern = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Line Pattern"}} }; + @"Setting linePattern to a constant value expression should update line-pattern."); + XCTAssertEqualObjects(layer.linePattern, constantExpression, + @"linePattern should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.linePattern = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Line Pattern" }, + { 18, "Line Pattern" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getLinePattern(), propertyValue, - @"Setting linePattern to a camera function should update line-pattern."); - XCTAssertEqualObjects(layer.linePattern, functionStyleValue, - @"linePattern should round-trip camera functions."); + @"Setting linePattern to a camera expression should update line-pattern."); + XCTAssertEqualObjects(layer.linePattern, functionExpression, + @"linePattern should round-trip camera expressions."); layer.linePattern = nil; XCTAssertTrue(rawLayer->getLinePattern().isUndefined(), @"Unsetting linePattern should return line-pattern to the default value."); - XCTAssertEqualObjects(layer.linePattern, defaultStyleValue, + XCTAssertEqualObjects(layer.linePattern, defaultExpression, @"linePattern should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.linePattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.linePattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.linePattern = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.linePatternTransition = transitionTest; auto toptions = rawLayer->getLinePatternTransition(); @@ -620,124 +669,138 @@ { XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(), @"line-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslation; + NSExpression *defaultExpression = layer.lineTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.lineTranslation = constantStyleValue; + layer.lineTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue, - @"Setting lineTranslation to a constant value should update line-translate."); - XCTAssertEqualObjects(layer.lineTranslation, constantStyleValue, - @"lineTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting lineTranslation to a constant value expression should update line-translate."); + XCTAssertEqualObjects(layer.lineTranslation, constantExpression, + @"lineTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue, - @"Setting lineTranslation to a camera function should update line-translate."); - XCTAssertEqualObjects(layer.lineTranslation, functionStyleValue, - @"lineTranslation should round-trip camera functions."); + @"Setting lineTranslation to a camera expression should update line-translate."); + XCTAssertEqualObjects(layer.lineTranslation, functionExpression, + @"lineTranslation should round-trip camera expressions."); layer.lineTranslation = nil; XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(), @"Unsetting lineTranslation should return line-translate to the default value."); - XCTAssertEqualObjects(layer.lineTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.lineTranslation, defaultExpression, @"lineTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-translate-anchor { XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(), @"line-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.lineTranslationAnchor; + NSExpression *defaultExpression = layer.lineTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineTranslationAnchor:MGLLineTranslationAnchorViewport]]; - layer.lineTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.lineTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue, - @"Setting lineTranslationAnchor to a constant value should update line-translate-anchor."); - XCTAssertEqualObjects(layer.lineTranslationAnchor, constantStyleValue, - @"lineTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting lineTranslationAnchor to a constant value expression should update line-translate-anchor."); + XCTAssertEqualObjects(layer.lineTranslationAnchor, constantExpression, + @"lineTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue, - @"Setting lineTranslationAnchor to a camera function should update line-translate-anchor."); - XCTAssertEqualObjects(layer.lineTranslationAnchor, functionStyleValue, - @"lineTranslationAnchor should round-trip camera functions."); + @"Setting lineTranslationAnchor to a camera expression should update line-translate-anchor."); + XCTAssertEqualObjects(layer.lineTranslationAnchor, functionExpression, + @"lineTranslationAnchor should round-trip camera expressions."); layer.lineTranslationAnchor = nil; XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(), @"Unsetting lineTranslationAnchor should return line-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.lineTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.lineTranslationAnchor, defaultExpression, @"lineTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.lineTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLLineLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // line-width { XCTAssertTrue(rawLayer->getLineWidth().isUndefined(), @"line-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.lineWidth; + NSExpression *defaultExpression = layer.lineWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.lineWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.lineWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, - @"Setting lineWidth to a constant value should update line-width."); - XCTAssertEqualObjects(layer.lineWidth, constantStyleValue, - @"lineWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.lineWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting lineWidth to a constant value expression should update line-width."); + XCTAssertEqualObjects(layer.lineWidth, constantExpression, + @"lineWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.lineWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, - @"Setting lineWidth to a camera function should update line-width."); - XCTAssertEqualObjects(layer.lineWidth, functionStyleValue, - @"lineWidth should round-trip camera functions."); + @"Setting lineWidth to a camera expression should update line-width."); + XCTAssertEqualObjects(layer.lineWidth, functionExpression, + @"lineWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.lineWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.lineWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, - @"Setting lineWidth to a source function should update line-width."); - XCTAssertEqualObjects(layer.lineWidth, functionStyleValue, - @"lineWidth should round-trip source functions."); + @"Setting lineWidth to a data expression should update line-width."); + XCTAssertEqualObjects(layer.lineWidth, functionExpression, + @"lineWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.lineWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.lineWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -745,15 +808,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, - @"Setting lineWidth to a composite function should update line-width."); - XCTAssertEqualObjects(layer.lineWidth, functionStyleValue, - @"lineWidth should round-trip composite functions."); + @"Setting lineWidth to a camera-data expression should update line-width."); + XCTAssertEqualObjects(layer.lineWidth, functionExpression, + @"lineWidth should round-trip camera-data expressions."); layer.lineWidth = nil; XCTAssertTrue(rawLayer->getLineWidth().isUndefined(), @"Unsetting lineWidth should return line-width to the default value."); - XCTAssertEqualObjects(layer.lineWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.lineWidth, defaultExpression, @"lineWidth should return the default value after being unset."); // Transition property test layer.lineWidthTransition = transitionTest; diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index 6e6951dcdd..d8cad0b166 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -571,4 +571,11 @@ namespace mbgl { } } +- (void)testComparisonExpressionArray { + { + NSArray *expected = @[@"==", @1, @2]; + XCTAssertEqualObjects([NSPredicate predicateWithFormat:@"1 = 2"].mgl_jsonExpressionObject, expected); + } +} + @end diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm index 26526e3a6c..5ded23ee3c 100644 --- a/platform/darwin/test/MGLRasterStyleLayerTests.mm +++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm @@ -34,117 +34,132 @@ { XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(), @"raster-brightness-max should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumRasterBrightness; + NSExpression *defaultExpression = layer.maximumRasterBrightness; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.maximumRasterBrightness = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.maximumRasterBrightness = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue, - @"Setting maximumRasterBrightness to a constant value should update raster-brightness-max."); - XCTAssertEqualObjects(layer.maximumRasterBrightness, constantStyleValue, - @"maximumRasterBrightness should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.maximumRasterBrightness = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting maximumRasterBrightness to a constant value expression should update raster-brightness-max."); + XCTAssertEqualObjects(layer.maximumRasterBrightness, constantExpression, + @"maximumRasterBrightness should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.maximumRasterBrightness = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue, - @"Setting maximumRasterBrightness to a camera function should update raster-brightness-max."); - XCTAssertEqualObjects(layer.maximumRasterBrightness, functionStyleValue, - @"maximumRasterBrightness should round-trip camera functions."); + @"Setting maximumRasterBrightness to a camera expression should update raster-brightness-max."); + XCTAssertEqualObjects(layer.maximumRasterBrightness, functionExpression, + @"maximumRasterBrightness should round-trip camera expressions."); layer.maximumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(), @"Unsetting maximumRasterBrightness should return raster-brightness-max to the default value."); - XCTAssertEqualObjects(layer.maximumRasterBrightness, defaultStyleValue, + XCTAssertEqualObjects(layer.maximumRasterBrightness, defaultExpression, @"maximumRasterBrightness should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.maximumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // raster-brightness-min { XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(), @"raster-brightness-min should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.minimumRasterBrightness; + NSExpression *defaultExpression = layer.minimumRasterBrightness; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.minimumRasterBrightness = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.minimumRasterBrightness = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue, - @"Setting minimumRasterBrightness to a constant value should update raster-brightness-min."); - XCTAssertEqualObjects(layer.minimumRasterBrightness, constantStyleValue, - @"minimumRasterBrightness should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.minimumRasterBrightness = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting minimumRasterBrightness to a constant value expression should update raster-brightness-min."); + XCTAssertEqualObjects(layer.minimumRasterBrightness, constantExpression, + @"minimumRasterBrightness should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.minimumRasterBrightness = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue, - @"Setting minimumRasterBrightness to a camera function should update raster-brightness-min."); - XCTAssertEqualObjects(layer.minimumRasterBrightness, functionStyleValue, - @"minimumRasterBrightness should round-trip camera functions."); + @"Setting minimumRasterBrightness to a camera expression should update raster-brightness-min."); + XCTAssertEqualObjects(layer.minimumRasterBrightness, functionExpression, + @"minimumRasterBrightness should round-trip camera expressions."); layer.minimumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(), @"Unsetting minimumRasterBrightness should return raster-brightness-min to the default value."); - XCTAssertEqualObjects(layer.minimumRasterBrightness, defaultStyleValue, + XCTAssertEqualObjects(layer.minimumRasterBrightness, defaultExpression, @"minimumRasterBrightness should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.minimumRasterBrightness = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // raster-contrast { XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(), @"raster-contrast should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterContrast; + NSExpression *defaultExpression = layer.rasterContrast; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.rasterContrast = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.rasterContrast = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue, - @"Setting rasterContrast to a constant value should update raster-contrast."); - XCTAssertEqualObjects(layer.rasterContrast, constantStyleValue, - @"rasterContrast should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.rasterContrast = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting rasterContrast to a constant value expression should update raster-contrast."); + XCTAssertEqualObjects(layer.rasterContrast, constantExpression, + @"rasterContrast should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.rasterContrast = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue, - @"Setting rasterContrast to a camera function should update raster-contrast."); - XCTAssertEqualObjects(layer.rasterContrast, functionStyleValue, - @"rasterContrast should round-trip camera functions."); + @"Setting rasterContrast to a camera expression should update raster-contrast."); + XCTAssertEqualObjects(layer.rasterContrast, functionExpression, + @"rasterContrast should round-trip camera expressions."); layer.rasterContrast = nil; XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(), @"Unsetting rasterContrast should return raster-contrast to the default value."); - XCTAssertEqualObjects(layer.rasterContrast, defaultStyleValue, + XCTAssertEqualObjects(layer.rasterContrast, defaultExpression, @"rasterContrast should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.rasterContrast = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.rasterContrastTransition = transitionTest; auto toptions = rawLayer->getRasterContrastTransition(); @@ -160,117 +175,132 @@ { XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(), @"raster-fade-duration should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterFadeDuration; + NSExpression *defaultExpression = layer.rasterFadeDuration; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.rasterFadeDuration = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.rasterFadeDuration = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue, - @"Setting rasterFadeDuration to a constant value should update raster-fade-duration."); - XCTAssertEqualObjects(layer.rasterFadeDuration, constantStyleValue, - @"rasterFadeDuration should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.rasterFadeDuration = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting rasterFadeDuration to a constant value expression should update raster-fade-duration."); + XCTAssertEqualObjects(layer.rasterFadeDuration, constantExpression, + @"rasterFadeDuration should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.rasterFadeDuration = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue, - @"Setting rasterFadeDuration to a camera function should update raster-fade-duration."); - XCTAssertEqualObjects(layer.rasterFadeDuration, functionStyleValue, - @"rasterFadeDuration should round-trip camera functions."); + @"Setting rasterFadeDuration to a camera expression should update raster-fade-duration."); + XCTAssertEqualObjects(layer.rasterFadeDuration, functionExpression, + @"rasterFadeDuration should round-trip camera expressions."); layer.rasterFadeDuration = nil; XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(), @"Unsetting rasterFadeDuration should return raster-fade-duration to the default value."); - XCTAssertEqualObjects(layer.rasterFadeDuration, defaultStyleValue, + XCTAssertEqualObjects(layer.rasterFadeDuration, defaultExpression, @"rasterFadeDuration should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.rasterFadeDuration = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // raster-hue-rotate { XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(), @"raster-hue-rotate should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterHueRotation; + NSExpression *defaultExpression = layer.rasterHueRotation; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.rasterHueRotation = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.rasterHueRotation = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue, - @"Setting rasterHueRotation to a constant value should update raster-hue-rotate."); - XCTAssertEqualObjects(layer.rasterHueRotation, constantStyleValue, - @"rasterHueRotation should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.rasterHueRotation = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting rasterHueRotation to a constant value expression should update raster-hue-rotate."); + XCTAssertEqualObjects(layer.rasterHueRotation, constantExpression, + @"rasterHueRotation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.rasterHueRotation = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue, - @"Setting rasterHueRotation to a camera function should update raster-hue-rotate."); - XCTAssertEqualObjects(layer.rasterHueRotation, functionStyleValue, - @"rasterHueRotation should round-trip camera functions."); + @"Setting rasterHueRotation to a camera expression should update raster-hue-rotate."); + XCTAssertEqualObjects(layer.rasterHueRotation, functionExpression, + @"rasterHueRotation should round-trip camera expressions."); layer.rasterHueRotation = nil; XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(), @"Unsetting rasterHueRotation should return raster-hue-rotate to the default value."); - XCTAssertEqualObjects(layer.rasterHueRotation, defaultStyleValue, + XCTAssertEqualObjects(layer.rasterHueRotation, defaultExpression, @"rasterHueRotation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.rasterHueRotation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // raster-opacity { XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(), @"raster-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterOpacity; + NSExpression *defaultExpression = layer.rasterOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.rasterOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.rasterOpacity = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue, - @"Setting rasterOpacity to a constant value should update raster-opacity."); - XCTAssertEqualObjects(layer.rasterOpacity, constantStyleValue, - @"rasterOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.rasterOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting rasterOpacity to a constant value expression should update raster-opacity."); + XCTAssertEqualObjects(layer.rasterOpacity, constantExpression, + @"rasterOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.rasterOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue, - @"Setting rasterOpacity to a camera function should update raster-opacity."); - XCTAssertEqualObjects(layer.rasterOpacity, functionStyleValue, - @"rasterOpacity should round-trip camera functions."); + @"Setting rasterOpacity to a camera expression should update raster-opacity."); + XCTAssertEqualObjects(layer.rasterOpacity, functionExpression, + @"rasterOpacity should round-trip camera expressions."); layer.rasterOpacity = nil; XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(), @"Unsetting rasterOpacity should return raster-opacity to the default value."); - XCTAssertEqualObjects(layer.rasterOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.rasterOpacity, defaultExpression, @"rasterOpacity should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.rasterOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.rasterOpacityTransition = transitionTest; auto toptions = rawLayer->getRasterOpacityTransition(); @@ -286,39 +316,44 @@ { XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(), @"raster-saturation should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.rasterSaturation; + NSExpression *defaultExpression = layer.rasterSaturation; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.rasterSaturation = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.rasterSaturation = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue, - @"Setting rasterSaturation to a constant value should update raster-saturation."); - XCTAssertEqualObjects(layer.rasterSaturation, constantStyleValue, - @"rasterSaturation should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.rasterSaturation = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting rasterSaturation to a constant value expression should update raster-saturation."); + XCTAssertEqualObjects(layer.rasterSaturation, constantExpression, + @"rasterSaturation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.rasterSaturation = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue, - @"Setting rasterSaturation to a camera function should update raster-saturation."); - XCTAssertEqualObjects(layer.rasterSaturation, functionStyleValue, - @"rasterSaturation should round-trip camera functions."); + @"Setting rasterSaturation to a camera expression should update raster-saturation."); + XCTAssertEqualObjects(layer.rasterSaturation, functionExpression, + @"rasterSaturation should round-trip camera expressions."); layer.rasterSaturation = nil; XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(), @"Unsetting rasterSaturation should return raster-saturation to the default value."); - XCTAssertEqualObjects(layer.rasterSaturation, defaultStyleValue, + XCTAssertEqualObjects(layer.rasterSaturation, defaultExpression, @"rasterSaturation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.rasterSaturation = functionExpression, NSException, NSInvalidArgumentException, @"MGLRasterLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); // Transition property test layer.rasterSaturationTransition = transitionTest; auto toptions = rawLayer->getRasterSaturationTransition(); diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift index f21041782e..576e3da86b 100644 --- a/platform/darwin/test/MGLSDKTestHelpers.swift +++ b/platform/darwin/test/MGLSDKTestHelpers.swift @@ -1,3 +1,4 @@ +import XCTest import Foundation class MGLSDKTestHelpers { diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm index 60500959b6..868dca730a 100644 --- a/platform/darwin/test/MGLShapeSourceTests.mm +++ b/platform/darwin/test/MGLShapeSourceTests.mm @@ -298,7 +298,10 @@ // when a shape is included in the features array MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:coordinates count:sizeof(coordinates)/sizeof(coordinates[0]) interiorPolygons:nil]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-literal-conversion" XCTAssertThrowsSpecificNamed([[MGLShapeSource alloc] initWithIdentifier:@"source-id-invalid" features:@[polygon] options:nil], NSException, NSInvalidArgumentException, @"Shape source should raise an exception if a shape is sent to the features initializer"); +#pragma clang diagnostic pop } - (void)testMGLShapeSourceWithShapesConvenienceInitializer { diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h index f0b889f022..28c316c8ba 100644 --- a/platform/darwin/test/MGLStyleLayerTests.h +++ b/platform/darwin/test/MGLStyleLayerTests.h @@ -1,6 +1,9 @@ #import <Mapbox/Mapbox.h> #import <XCTest/XCTest.h> +#define MGLConstantExpression(constant) \ + [NSExpression expressionForConstantValue:constant] + @interface MGLStyleLayerTests : XCTestCase <MGLMapViewDelegate> @property (nonatomic, copy, readonly, class) NSString *layerType; diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index 5fdfc3d44e..0487066255 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -64,45 +64,49 @@ { XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(), @"<%- originalPropertyName(property) %> should be unset initially."); - MGLStyleValue<<%- propertyType(property) %>> *defaultStyleValue = layer.<%- objCName(property) %>; + NSExpression *defaultExpression = layer.<%- objCName(property) %>; - MGLStyleValue<<%- propertyType(property) %>> *constantStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithRawValue:<%- objCTestValue(property, type, 3) %>]; - layer.<%- objCName(property) %> = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, true, 3) %>]; + layer.<%- objCName(property) %> = constantExpression; <% if (property["property-function"]) { -%> mbgl::style::DataDrivenPropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; <% } else { -%> mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; <% } -%> XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, - @"Setting <%- objCName(property) %> to a constant value should update <%- originalPropertyName(property) %>."); - XCTAssertEqualObjects(layer.<%- objCName(property) %>, constantStyleValue, - @"<%- objCName(property) %> should round-trip constant values."); - - MGLStyleValue<<%- propertyType(property) %>> * functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.<%- objCName(property) %> = functionStyleValue; - - mbgl::style::IntervalStops<<%- mbglType(property) %>> intervalStops = { {{18, <%- mbglTestValue(property, type) %>}} }; + @"Setting <%- objCName(property) %> to a constant value expression should update <%- originalPropertyName(property) %>."); + XCTAssertEqualObjects(layer.<%- objCName(property) %>, constantExpression, + @"<%- objCName(property) %> should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, false, 3) %>]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.<%- objCName(property) %> = functionExpression; + + mbgl::style::IntervalStops<<%- mbglType(property) %>> intervalStops = {{ + { -INFINITY, <%- mbglTestValue(property, type) %> }, + { 18, <%- mbglTestValue(property, type) %> }, + }}; propertyValue = mbgl::style::CameraFunction<<%- mbglType(property) %>> { intervalStops }; XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, - @"Setting <%- objCName(property) %> to a camera function should update <%- originalPropertyName(property) %>."); - XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue, - @"<%- objCName(property) %> should round-trip camera functions."); + @"Setting <%- objCName(property) %> to a camera expression should update <%- originalPropertyName(property) %>."); + XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression, + @"<%- objCName(property) %> should round-trip camera expressions."); <% if (property["property-function"] && isInterpolatable(property)) { -%> - functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.<%- objCName(property) %> = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.<%- objCName(property) %> = functionExpression; mbgl::style::ExponentialStops<<%- mbglType(property) %>> exponentialStops = { {{18, <%- mbglTestValue(property, type) %>}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<<%- mbglType(property) %>> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, - @"Setting <%- objCName(property) %> to a source function should update <%- originalPropertyName(property) %>."); - XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue, - @"<%- objCName(property) %> should round-trip source functions."); + @"Setting <%- objCName(property) %> to a data expression should update <%- originalPropertyName(property) %>."); + XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression, + @"<%- objCName(property) %> should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.<%- objCName(property) %> = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.<%- objCName(property) %> = functionExpression; std::map<float, <%- mbglType(property) %>> innerStops { {18, <%- mbglTestValue(property, type) %>} }; mbgl::style::CompositeExponentialStops<<%- mbglType(property) %>> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -110,24 +114,25 @@ propertyValue = mbgl::style::CompositeFunction<<%- mbglType(property) %>> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, - @"Setting <%- objCName(property) %> to a composite function should update <%- originalPropertyName(property) %>."); - XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionStyleValue, - @"<%- objCName(property) %> should round-trip composite functions."); + @"Setting <%- objCName(property) %> to a camera-data expression should update <%- originalPropertyName(property) %>."); + XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression, + @"<%- objCName(property) %> should round-trip camera-data expressions."); <% } -%> <% if (!property.required) { -%> layer.<%- objCName(property) %> = nil; XCTAssertTrue(rawLayer->get<%- camelize(originalPropertyName(property)) %>().isUndefined(), @"Unsetting <%- objCName(property) %> should return <%- originalPropertyName(property) %> to the default value."); - XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultStyleValue, + XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultExpression, @"<%- objCName(property) %> should return the default value after being unset."); <% } -%> <% if (!property["property-function"]) { -%> - functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<<%- propertyType(property) %>> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); <% } -%> <% if (property["transition"] && !property.original) { -%> // Transition property test diff --git a/platform/darwin/test/MGLStyleValueTests.h b/platform/darwin/test/MGLStyleValueTests.h deleted file mode 100644 index a563de39f0..0000000000 --- a/platform/darwin/test/MGLStyleValueTests.h +++ /dev/null @@ -1,4 +0,0 @@ -#import <XCTest/XCTest.h> - -@interface MGLStyleValueTests : XCTestCase -@end diff --git a/platform/darwin/test/MGLStyleValueTests.m b/platform/darwin/test/MGLStyleValueTests.m deleted file mode 100644 index cd6eec8324..0000000000 --- a/platform/darwin/test/MGLStyleValueTests.m +++ /dev/null @@ -1,113 +0,0 @@ -#import <XCTest/XCTest.h> -#import <Mapbox/Mapbox.h> - -@interface MGLStyleValueTests : XCTestCase -@end - -@implementation MGLStyleValueTests - -- (void)testStoplessFunction { - XCTAssertThrowsSpecificNamed([MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{} options:nil], NSException, NSInvalidArgumentException, @"Stopless function should raise an exception"); -} - -- (void)testDeprecatedFunctions { - MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"test" - shape:nil - options:nil]; - MGLSymbolStyleLayer *symbolStyleLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer" - source:shapeSource]; - MGLCircleStyleLayer *circleStyleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circleLayer" - source:shapeSource]; - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // deprecated function, stops with float values - NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *stops = @{ - @1: [MGLStyleValue<NSNumber *> valueWithRawValue:@0], - @2: [MGLStyleValue<NSNumber *> valueWithRawValue:@1], - @3: [MGLStyleValue<NSNumber *> valueWithRawValue:@2], - @4: [MGLStyleValue<NSNumber *> valueWithRawValue:@0], - }; - MGLStyleValue<NSNumber *> *iconHaloBlurStyleValue = - [MGLStyleValue<NSNumber *> valueWithInterpolationBase:1.0 stops:stops]; - symbolStyleLayer.iconHaloBlur = iconHaloBlurStyleValue; - XCTAssertEqualObjects(symbolStyleLayer.iconHaloBlur, iconHaloBlurStyleValue); - - // deprecated function, stops with boolean values - stops = @{ - @1: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES], - @2: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO], - @3: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES], - @4: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO], - }; - MGLStyleValue<NSNumber *> *iconAllowsOverlapStyleValue = - [MGLStyleValue<NSNumber *> valueWithInterpolationBase:1.0 stops:stops]; - symbolStyleLayer.iconAllowsOverlap = iconAllowsOverlapStyleValue; - // iconAllowsOverlap is boolean so mgl and mbgl conversions will coerce the developers stops into interval stops - MGLStyleValue<NSNumber *> *expectedIconAllowsOverlapStyleValue = - [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval - cameraStops:stops - options:nil]; - XCTAssertEqualObjects(symbolStyleLayer.iconAllowsOverlap, expectedIconAllowsOverlapStyleValue); - - /// - // creating and using MGLStyleFunctions directly - /// - - NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *circleRadiusStops = @{ - @0: [MGLStyleValue<NSNumber *> valueWithRawValue:@10], - @20: [MGLStyleValue<NSNumber *> valueWithRawValue:@5], - }; - MGLStyleFunction<NSNumber *> *circleRadiusFunction = - [MGLStyleFunction<NSNumber *> functionWithInterpolationBase:1.0 - stops:circleRadiusStops]; - circleStyleLayer.circleRadius = circleRadiusFunction; - MGLStyleValue<NSNumber *> *expectedCircleRadiusFunction = - [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:circleRadiusStops - options:nil]; - // setting a data driven property to an MGLStyleFunction should return an exponential camera function - XCTAssertEqualObjects(circleStyleLayer.circleRadius, expectedCircleRadiusFunction); - - CGVector circleTranslationOne = CGVectorMake(100, 0); - CGVector circleTranslationTwo = CGVectorMake(0, 0); -#if TARGET_OS_IPHONE - NSValue *circleTranslationValueOne = [NSValue valueWithCGVector:circleTranslationOne]; - NSValue *circleTranslationValueTwo = [NSValue valueWithCGVector:circleTranslationTwo]; -#else - NSValue *circleTranslationValueOne = [NSValue value:&circleTranslationOne withObjCType:@encode(CGVector)]; - NSValue *circleTranslationValueTwo = [NSValue value:&circleTranslationTwo withObjCType:@encode(CGVector)]; -#endif - - NSDictionary<NSNumber *, MGLStyleValue<NSValue *> *> *circleTranslationStops = @{ - @0: [MGLStyleValue<NSValue *> valueWithRawValue:circleTranslationValueOne], - @10: [MGLStyleValue<NSValue *> valueWithRawValue:circleTranslationValueTwo], - }; - MGLStyleFunction<NSValue *> *circleTranslationFunction = - [MGLStyleFunction<NSValue *> functionWithInterpolationBase:1.0 - stops:circleTranslationStops]; - circleStyleLayer.circleTranslation = circleTranslationFunction; - MGLStyleValue<NSValue *> *expectedCircleTranslationFunction = - [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:circleTranslationStops - options:nil]; - // setting a non-data driven, interpolatable property to an MGLStyleFunction should return an exponential camera function - XCTAssertEqualObjects(circleStyleLayer.circleTranslation, expectedCircleTranslationFunction); - - NSDictionary<NSNumber *, MGLStyleValue<NSNumber *> *> *iconOptionalStops = @{ - @0: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO], - @20: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES], - }; - MGLStyleFunction<NSNumber *> *iconOptionalFunction = - [MGLStyleFunction<NSNumber *> valueWithInterpolationBase:1.0 - stops:iconOptionalStops]; - symbolStyleLayer.iconOptional = iconOptionalFunction; - MGLStyleValue<NSNumber *> *expectedIconOptionalFunction = - [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval - cameraStops:iconOptionalStops - options:nil]; - XCTAssertEqualObjects(symbolStyleLayer.iconOptional, expectedIconOptionalFunction); -#pragma clang diagnostic pop -} - -@end diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift deleted file mode 100644 index c559037588..0000000000 --- a/platform/darwin/test/MGLStyleValueTests.swift +++ /dev/null @@ -1,362 +0,0 @@ -import XCTest -import Mapbox - -#if os(iOS) || os(watchOS) || os(tvOS) -typealias MGLColor = UIColor -#elseif os(macOS) -typealias MGLColor = NSColor -#endif - -#if swift(>=3.2) -#else -func XCTAssertEqual<T: FloatingPoint>(_ lhs: @autoclosure () throws -> T, _ rhs: @autoclosure () throws -> T, accuracy: T) { - XCTAssertEqualWithAccuracy(lhs, rhs, accuracy: accuracy) -} -#endif - -extension MGLStyleValueTests { - - struct Color { - var red: CGFloat = 0 - var green: CGFloat = 0 - var blue: CGFloat = 0 - var alpha: CGFloat = 0 - } - - func assertColorsEqualWithAccuracy(_ actual: MGLColor, _ expected: MGLColor, accuracy: Float = 1/255) { - var actualColor = Color() - var expectedColor = Color() - - actual.getRed(&actualColor.red, green: &actualColor.green, blue: &actualColor.blue, alpha: &actualColor.alpha) - expected.getRed(&expectedColor.red, green: &expectedColor.green, blue: &expectedColor.blue, alpha: &expectedColor.alpha) - - XCTAssertEqual(Float(actualColor.red), Float(expectedColor.red), accuracy: accuracy) - XCTAssertEqual(Float(actualColor.green), Float(expectedColor.green), accuracy: accuracy) - XCTAssertEqual(Float(actualColor.blue), Float(expectedColor.blue), accuracy: accuracy) - XCTAssertEqual(Float(actualColor.alpha), Float(expectedColor.alpha), accuracy: accuracy) - } - - func assertColorValuesEqual(_ actual: MGLStyleValue<MGLColor>, _ expected: MGLStyleValue<MGLColor>) { - guard type(of: actual) == type(of: expected) else { - XCTFail("Expected \(type(of: expected)), but found \(type(of: actual)) instead.") - return - } - - if let actualConstant = actual as? MGLConstantStyleValue<MGLColor> { - assertColorsEqualWithAccuracy(actualConstant.rawValue, (expected as! MGLConstantStyleValue<MGLColor>).rawValue) - } else if let actualFunction = actual as? MGLStyleFunction<MGLColor>, - let expectedFunction = expected as? MGLStyleFunction<MGLColor> { - - // unless we have stops, there's no need for a custom comparison - default to plain == assertion - guard let actualStops = actualFunction.stops, let expectedStops = expectedFunction.stops else { - XCTAssertEqual(actualFunction, expectedFunction) - return - } - - guard expectedStops is [String: Any] || expectedStops is [Float:Any] else { - XCTFail("Stop levels must be String or Float.") - return - } - - XCTAssertEqual(actualFunction.interpolationBase, expectedFunction.interpolationBase) - XCTAssertEqual(actualFunction.interpolationMode, expectedFunction.interpolationMode) - if let actualFunction = actualFunction as? MGLSourceStyleFunction<MGLColor>, - let expectedFunction = expectedFunction as? MGLSourceStyleFunction<MGLColor> { - XCTAssertEqual(actualFunction.defaultValue, expectedFunction.defaultValue) - } else if let actualFunction = actualFunction as? MGLCompositeStyleFunction<MGLColor>, - let expectedFunction = expectedFunction as? MGLCompositeStyleFunction<MGLColor> { - XCTAssertEqual(actualFunction.defaultValue, expectedFunction.defaultValue) - } - - func assertStopEqual (_ actualValue: Any?, _ expectedValue: Any?) { - guard type(of: actualValue) == type(of: expectedValue) else { - XCTFail("Expected stop value of type \(type(of: expectedValue)), but found \(type(of: actualValue)) instead.") - return - } - if let actualValue = actualValue as? MGLConstantStyleValue<MGLColor>, - let expectedValue = expectedValue as? MGLConstantStyleValue<MGLColor> { - assertColorsEqualWithAccuracy(actualValue.rawValue, expectedValue.rawValue) - } else if let actualValue = actualValue as? MGLConstantStyleValue<AnyObject>, - let expectedValue = expectedValue as? MGLConstantStyleValue<AnyObject> { - XCTAssertEqual(actualValue, expectedValue) - } else { - XCTFail("Unsupported stop value type \(type(of: actualValue)).") - } - } - - XCTAssertEqual(actualStops.count, expectedStops.count) - if let actualStops = actualStops as? [String:Any], let expectedStops = expectedStops as? [String: Any] { - for (key, value) in actualStops { - assertStopEqual(value, expectedStops[key]) - } - } else if let actualStops = actualStops as? [Float:Any], let expectedStops = expectedStops as? [Float:Any] { - for (key, value) in actualStops { - assertStopEqual(value, expectedStops[key]) - } - } else { - XCTFail("Expected stops of type \(type(of: Array(expectedStops.keys)).Index.self), but found \(type(of: Array(actualStops.keys)).Index.self) instead.") - return - } - } else { - XCTFail("MGLStyleValue<MGLColor> must be either a constant or a style function.") - } - } - - func testConstantValues() { - let shapeSource = MGLShapeSource(identifier: "source", shape: nil, options: nil) - let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "symbolLayer", source: shapeSource) - let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) - - // Boolean - symbolStyleLayer.iconAllowsOverlap = MGLConstantStyleValue(rawValue: true) - XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLConstantStyleValue<NSNumber>).rawValue, true) - - // Number - symbolStyleLayer.iconHaloWidth = MGLConstantStyleValue(rawValue: 3) - XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLConstantStyleValue<NSNumber>).rawValue, 3) - - // String - symbolStyleLayer.text = MGLConstantStyleValue(rawValue: "{name}") - XCTAssertEqual((symbolStyleLayer.text as! MGLConstantStyleValue<NSString>).rawValue, "{name}") - - var circleTranslationOne = CGVector(dx: 100, dy: 0) - let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}") - - // non-data-driven (interpolatable property value), set to constant style value - let expectedCircleTranslationValue = MGLStyleValue<NSValue>(rawValue: circleTranslationValueOne) - circleStyleLayer.circleTranslation = expectedCircleTranslationValue - XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue) - - // non-data-driven (enumeration property value), set to constant style value - let expectedCircleScaleAlignmentValue = MGLStyleValue<NSValue>(rawValue: NSValue(mglCircleScaleAlignment: .map)) - circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue - XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue) - } - - func testFunctionsWithNonDataDrivenProperties() { - let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil) - let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) - - var circleTranslationOne = CGVector(dx: 100, dy: 0) - let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}") - var circleTranslationTwo = CGVector(dx: 0, dy: 0) - let circleTranslationValueTwo = NSValue(bytes: &circleTranslationTwo, objCType: "{CGVector=dd}") - - let circleTranslationStops : [Float:MGLStyleValue<NSValue>] = [ - 0: MGLStyleValue<NSValue>(rawValue: circleTranslationValueOne), - 10: MGLStyleValue<NSValue>(rawValue: circleTranslationValueTwo) - ] - - // non-data-driven (interpolatable property value), camera function with CGVector (NSValue) stop values - let expectedCircleTranslationValue = MGLStyleValue<NSValue>( - interpolationMode: .interval, - cameraStops: circleTranslationStops, - options: nil - ) - circleStyleLayer.circleTranslation = expectedCircleTranslationValue - XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue) - - // non-data-driven (enumeration property value), camera function with MGLCircleScaleAlignment enum (NSValue) stop values - let scaleAlignmentStops : [Float:MGLStyleValue<NSValue>] = [ - 0: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .map)), - 10: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .viewport)) - ] - let expectedCircleScaleAlignmentValue = MGLStyleValue<NSValue>( - interpolationMode: .interval, - cameraStops: scaleAlignmentStops, - options: nil - ) - circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue - XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue) - } - - func testFunctionsWithDataDrivenProperties() { - let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil) - let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) - - // data-driven, camera function with exponential color stop values - let redGreenStops : [Float:MGLStyleValue<MGLColor>] = [ - 0: MGLStyleValue<MGLColor>(rawValue: .red), - 10: MGLStyleValue<MGLColor>(rawValue: .red), - 15: MGLStyleValue<MGLColor>(rawValue: .green) - ] - let expectedCircleColorValue = MGLStyleValue<MGLColor>( - interpolationMode: .exponential, - cameraStops: redGreenStops, - options: [.interpolationBase: 10.0] - ) - circleStyleLayer.circleColor = expectedCircleColorValue - assertColorValuesEqual(circleStyleLayer.circleColor as! MGLStyleFunction<MGLColor>, expectedCircleColorValue as! MGLStyleFunction<MGLColor>) - - // data-driven, source function with categorical color stop values with string attribute keys - let redOnlyStops = [ - "red": MGLStyleValue<MGLColor>(rawValue: .red) - ] - let expectedRedCategoricalValue = MGLStyleValue<MGLColor>( - interpolationMode: .categorical, - sourceStops: redOnlyStops, - attributeName: "red", - options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .cyan)] - ) - circleStyleLayer.circleColor = expectedRedCategoricalValue - assertColorValuesEqual(circleStyleLayer.circleColor, expectedRedCategoricalValue) - - // data-driven, source function with categorical color stop values with integer attribute keys - let greenOrangeStops : [Float:MGLStyleValue<MGLColor>] = [ - 0: MGLStyleValue<MGLColor>(rawValue: .green), - 100: MGLStyleValue<MGLColor>(rawValue: .orange) - ] - let expectedGreenOrangeCategoricalValue = MGLStyleValue<MGLColor>( - interpolationMode: .categorical, - sourceStops: greenOrangeStops, - attributeName: "temp", - options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .red)] - ) - circleStyleLayer.circleColor = expectedGreenOrangeCategoricalValue - assertColorValuesEqual(circleStyleLayer.circleColor, expectedGreenOrangeCategoricalValue) - - // data-driven, source function with exponential color stop values - let expectedRedGreenSourceExponentialValue = MGLStyleValue<MGLColor>( - interpolationMode: .exponential, - sourceStops: redGreenStops, - attributeName: "temp", - options: nil - ) - circleStyleLayer.circleColor = expectedRedGreenSourceExponentialValue - assertColorValuesEqual(circleStyleLayer.circleColor, expectedRedGreenSourceExponentialValue) - - // data-driven, identity source function - let expectedSourceIdentityValue = MGLStyleValue<MGLColor>( - interpolationMode: .identity, - sourceStops: nil, - attributeName: "size", - options: [.defaultValue: MGLStyleValue<MGLColor>(rawValue: .green)] - ) - circleStyleLayer.circleColor = expectedSourceIdentityValue - assertColorValuesEqual(circleStyleLayer.circleColor, expectedSourceIdentityValue) - - // data-driven, source function with categorical color stop values with boolean attribute keys - let booleanCategoricalStops = [ - false: MGLStyleValue<NSNumber>(rawValue: 0), - true: MGLStyleValue<NSNumber>(rawValue: 2) - ] - let expectedCircleBlurCategoricalValue = MGLStyleValue<NSNumber>( - interpolationMode: .categorical, - sourceStops: booleanCategoricalStops, - attributeName: "fuzzy", - options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 42)] - ) - circleStyleLayer.circleBlur = expectedCircleBlurCategoricalValue - XCTAssertEqual(circleStyleLayer.circleBlur, expectedCircleBlurCategoricalValue) - - // data-driven, composite function with inner categorical color stop values with string attribute keys nested in outer camera stops - let smallRadius = MGLStyleValue<NSNumber>(rawValue: 5) - let mediumRadius = MGLStyleValue<NSNumber>(rawValue: 10) - let largeRadius = MGLStyleValue<NSNumber>(rawValue: 20) - let radiusCompositeCategoricalStops: [Float: [String: MGLStyleValue<NSNumber>]] = [ - 0: ["green": smallRadius], - 10: ["green": smallRadius], - 15: ["green": largeRadius], - 20: ["green": largeRadius] - ] - let defaultRadius = MGLStyleValue<NSNumber>(rawValue: 2) - let expectedCompositeCategoricalValue = MGLStyleValue<NSNumber>( - interpolationMode: .categorical, - compositeStops: radiusCompositeCategoricalStops, - attributeName: "color", - options: [.defaultValue: defaultRadius] - ) - circleStyleLayer.circleRadius = expectedCompositeCategoricalValue - - var compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction - var expectedCompositeValue = expectedCompositeCategoricalValue as! MGLCompositeStyleFunction - XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName) - XCTAssertEqual(compositeValue.stops as NSDictionary, radiusCompositeCategoricalStops as NSDictionary) - XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode) - XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue) - - // data-driven, composite function with inner exponential color stop values nested in outer camera stops - let radiusCompositeExponentialOrIntervalStops: [Float: [Float: MGLStyleValue<NSNumber>]] = [ - 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)], - 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)], - 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)] - ] - - let expectedStops = [ - 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)], - 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)], - 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)] - ] - circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>( - interpolationMode: .exponential, - compositeStops: [ - 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)], - 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)], - 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)] - ], - attributeName: "temp", - options: [.defaultValue: mediumRadius] - ) - - let expectedCompositeExponentialValue = MGLStyleValue<NSNumber>( - interpolationMode: .exponential, - compositeStops: radiusCompositeExponentialOrIntervalStops, - attributeName: "temp", - options: [.defaultValue: mediumRadius] - ) - - compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction - expectedCompositeValue = expectedCompositeExponentialValue as! MGLCompositeStyleFunction - XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName) - XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary) - XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode) - XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue) - - // get a value back - if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLCompositeStyleFunction<NSNumber> { - if let returnedStops = returnedCircleRadius.stops as NSDictionary? as? [NSNumber: [NSNumber: MGLStyleValue<NSNumber>]] { - let lhs: MGLStyleValue<NSNumber> = returnedStops[0]!.values.first! - let rhs: MGLStyleValue<NSNumber> = radiusCompositeExponentialOrIntervalStops[0]!.values.first! - XCTAssertEqual(lhs, rhs) - } - } - - // get value back as base class - if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLStyleFunction<NSNumber> { - if let returnedStops = returnedCircleRadius.stops as NSDictionary? as? [NSNumber: [NSNumber: MGLStyleValue<NSNumber>]] { - let lhs: MGLStyleValue<NSNumber> = returnedStops[0]!.values.first! - let rhs: MGLStyleValue<NSNumber> = radiusCompositeExponentialOrIntervalStops[0]!.values.first! - XCTAssertEqual(lhs, rhs) - } - } - - // data-driven, composite function with inner interval color stop values nested in outer camera stops - let expectedCompositeIntervalValue = MGLStyleValue<NSNumber>( - interpolationMode: .interval, - compositeStops: [ - - 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)], - 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)] - ], - attributeName: "temp", - options: nil - ) - circleStyleLayer.circleRadius = MGLStyleValue<NSNumber>( - interpolationMode: .interval, - compositeStops: [ - 0: [0: MGLStyleValue<NSNumber>(rawValue: 5)], - 10: [200: MGLStyleValue<NSNumber>(rawValue: 5)], - 20: [200: MGLStyleValue<NSNumber>(rawValue: 20)] - ], - attributeName: "temp", - options: nil - ) - - compositeValue = circleStyleLayer.circleRadius as! MGLCompositeStyleFunction - expectedCompositeValue = expectedCompositeIntervalValue as! MGLCompositeStyleFunction - XCTAssertEqual(compositeValue.attributeName, expectedCompositeValue.attributeName) - XCTAssertEqual(compositeValue.stops as NSDictionary, expectedStops as NSDictionary) - XCTAssertEqual(compositeValue.interpolationMode, expectedCompositeValue.interpolationMode) - XCTAssertEqual(compositeValue.defaultValue, expectedCompositeValue.defaultValue) - } -} diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index 60d674f2ed..05e091b5c8 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -52,72 +52,81 @@ { XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(), @"icon-allow-overlap should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconAllowsOverlap; + NSExpression *defaultExpression = layer.iconAllowsOverlap; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.iconAllowsOverlap = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.iconAllowsOverlap = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue, - @"Setting iconAllowsOverlap to a constant value should update icon-allow-overlap."); - XCTAssertEqualObjects(layer.iconAllowsOverlap, constantStyleValue, - @"iconAllowsOverlap should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconAllowsOverlap = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting iconAllowsOverlap to a constant value expression should update icon-allow-overlap."); + XCTAssertEqualObjects(layer.iconAllowsOverlap, constantExpression, + @"iconAllowsOverlap should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconAllowsOverlap = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue, - @"Setting iconAllowsOverlap to a camera function should update icon-allow-overlap."); - XCTAssertEqualObjects(layer.iconAllowsOverlap, functionStyleValue, - @"iconAllowsOverlap should round-trip camera functions."); + @"Setting iconAllowsOverlap to a camera expression should update icon-allow-overlap."); + XCTAssertEqualObjects(layer.iconAllowsOverlap, functionExpression, + @"iconAllowsOverlap should round-trip camera expressions."); layer.iconAllowsOverlap = nil; XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(), @"Unsetting iconAllowsOverlap should return icon-allow-overlap to the default value."); - XCTAssertEqualObjects(layer.iconAllowsOverlap, defaultStyleValue, + XCTAssertEqualObjects(layer.iconAllowsOverlap, defaultExpression, @"iconAllowsOverlap should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-anchor { XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(), @"icon-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconAnchor; + NSExpression *defaultExpression = layer.iconAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight]]; - layer.iconAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"]; + layer.iconAnchor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight }; XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue, - @"Setting iconAnchor to a constant value should update icon-anchor."); - XCTAssertEqualObjects(layer.iconAnchor, constantStyleValue, - @"iconAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} }; + @"Setting iconAnchor to a constant value expression should update icon-anchor."); + XCTAssertEqualObjects(layer.iconAnchor, constantExpression, + @"iconAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::SymbolAnchorType::BottomRight }, + { 18, mbgl::style::SymbolAnchorType::BottomRight }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue, - @"Setting iconAnchor to a camera function should update icon-anchor."); - XCTAssertEqualObjects(layer.iconAnchor, functionStyleValue, - @"iconAnchor should round-trip camera functions."); + @"Setting iconAnchor to a camera expression should update icon-anchor."); + XCTAssertEqualObjects(layer.iconAnchor, functionExpression, + @"iconAnchor should round-trip camera expressions."); layer.iconAnchor = nil; XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(), @"Unsetting iconAnchor should return icon-anchor to the default value."); - XCTAssertEqualObjects(layer.iconAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.iconAnchor, defaultExpression, @"iconAnchor should return the default value after being unset."); } @@ -125,72 +134,81 @@ { XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(), @"icon-ignore-placement should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconIgnoresPlacement; + NSExpression *defaultExpression = layer.iconIgnoresPlacement; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.iconIgnoresPlacement = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.iconIgnoresPlacement = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue, - @"Setting iconIgnoresPlacement to a constant value should update icon-ignore-placement."); - XCTAssertEqualObjects(layer.iconIgnoresPlacement, constantStyleValue, - @"iconIgnoresPlacement should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconIgnoresPlacement = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting iconIgnoresPlacement to a constant value expression should update icon-ignore-placement."); + XCTAssertEqualObjects(layer.iconIgnoresPlacement, constantExpression, + @"iconIgnoresPlacement should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconIgnoresPlacement = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue, - @"Setting iconIgnoresPlacement to a camera function should update icon-ignore-placement."); - XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionStyleValue, - @"iconIgnoresPlacement should round-trip camera functions."); + @"Setting iconIgnoresPlacement to a camera expression should update icon-ignore-placement."); + XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionExpression, + @"iconIgnoresPlacement should round-trip camera expressions."); layer.iconIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(), @"Unsetting iconIgnoresPlacement should return icon-ignore-placement to the default value."); - XCTAssertEqualObjects(layer.iconIgnoresPlacement, defaultStyleValue, + XCTAssertEqualObjects(layer.iconIgnoresPlacement, defaultExpression, @"iconIgnoresPlacement should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-image { XCTAssertTrue(rawLayer->getIconImage().isUndefined(), @"icon-image should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.iconImageName; + NSExpression *defaultExpression = layer.iconImageName; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Icon Image"]; - layer.iconImageName = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Icon Image'"]; + layer.iconImageName = constantExpression; mbgl::style::DataDrivenPropertyValue<std::string> propertyValue = { "Icon Image" }; XCTAssertEqual(rawLayer->getIconImage(), propertyValue, - @"Setting iconImageName to a constant value should update icon-image."); - XCTAssertEqualObjects(layer.iconImageName, constantStyleValue, - @"iconImageName should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconImageName = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Icon Image"}} }; + @"Setting iconImageName to a constant value expression should update icon-image."); + XCTAssertEqualObjects(layer.iconImageName, constantExpression, + @"iconImageName should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Icon Image'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconImageName = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Icon Image" }, + { 18, "Icon Image" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getIconImage(), propertyValue, - @"Setting iconImageName to a camera function should update icon-image."); - XCTAssertEqualObjects(layer.iconImageName, functionStyleValue, - @"iconImageName should round-trip camera functions."); + @"Setting iconImageName to a camera expression should update icon-image."); + XCTAssertEqualObjects(layer.iconImageName, functionExpression, + @"iconImageName should round-trip camera expressions."); layer.iconImageName = nil; XCTAssertTrue(rawLayer->getIconImage().isUndefined(), @"Unsetting iconImageName should return icon-image to the default value."); - XCTAssertEqualObjects(layer.iconImageName, defaultStyleValue, + XCTAssertEqualObjects(layer.iconImageName, defaultExpression, @"iconImageName should return the default value after being unset."); } @@ -198,46 +216,50 @@ { XCTAssertTrue(rawLayer->getIconOffset().isUndefined(), @"icon-offset should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconOffset; + NSExpression *defaultExpression = layer.iconOffset; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.iconOffset = constantStyleValue; + layer.iconOffset = constantExpression; mbgl::style::DataDrivenPropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, - @"Setting iconOffset to a constant value should update icon-offset."); - XCTAssertEqualObjects(layer.iconOffset, constantStyleValue, - @"iconOffset should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconOffset = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting iconOffset to a constant value expression should update icon-offset."); + XCTAssertEqualObjects(layer.iconOffset, constantExpression, + @"iconOffset should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconOffset = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, - @"Setting iconOffset to a camera function should update icon-offset."); - XCTAssertEqualObjects(layer.iconOffset, functionStyleValue, - @"iconOffset should round-trip camera functions."); + @"Setting iconOffset to a camera expression should update icon-offset."); + XCTAssertEqualObjects(layer.iconOffset, functionExpression, + @"iconOffset should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconOffset = functionExpression; mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<std::array<float, 2>> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, - @"Setting iconOffset to a source function should update icon-offset."); - XCTAssertEqualObjects(layer.iconOffset, functionStyleValue, - @"iconOffset should round-trip source functions."); + @"Setting iconOffset to a data expression should update icon-offset."); + XCTAssertEqualObjects(layer.iconOffset, functionExpression, + @"iconOffset should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconOffset = functionExpression; std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} }; mbgl::style::CompositeExponentialStops<std::array<float, 2>> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -245,15 +267,15 @@ propertyValue = mbgl::style::CompositeFunction<std::array<float, 2>> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, - @"Setting iconOffset to a composite function should update icon-offset."); - XCTAssertEqualObjects(layer.iconOffset, functionStyleValue, - @"iconOffset should round-trip composite functions."); + @"Setting iconOffset to a camera-data expression should update icon-offset."); + XCTAssertEqualObjects(layer.iconOffset, functionExpression, + @"iconOffset should round-trip camera-data expressions."); layer.iconOffset = nil; XCTAssertTrue(rawLayer->getIconOffset().isUndefined(), @"Unsetting iconOffset should return icon-offset to the default value."); - XCTAssertEqualObjects(layer.iconOffset, defaultStyleValue, + XCTAssertEqualObjects(layer.iconOffset, defaultExpression, @"iconOffset should return the default value after being unset."); } @@ -261,157 +283,176 @@ { XCTAssertTrue(rawLayer->getIconOptional().isUndefined(), @"icon-optional should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOptional; + NSExpression *defaultExpression = layer.iconOptional; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.iconOptional = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.iconOptional = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getIconOptional(), propertyValue, - @"Setting iconOptional to a constant value should update icon-optional."); - XCTAssertEqualObjects(layer.iconOptional, constantStyleValue, - @"iconOptional should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconOptional = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting iconOptional to a constant value expression should update icon-optional."); + XCTAssertEqualObjects(layer.iconOptional, constantExpression, + @"iconOptional should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconOptional = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getIconOptional(), propertyValue, - @"Setting iconOptional to a camera function should update icon-optional."); - XCTAssertEqualObjects(layer.iconOptional, functionStyleValue, - @"iconOptional should round-trip camera functions."); + @"Setting iconOptional to a camera expression should update icon-optional."); + XCTAssertEqualObjects(layer.iconOptional, functionExpression, + @"iconOptional should round-trip camera expressions."); layer.iconOptional = nil; XCTAssertTrue(rawLayer->getIconOptional().isUndefined(), @"Unsetting iconOptional should return icon-optional to the default value."); - XCTAssertEqualObjects(layer.iconOptional, defaultStyleValue, + XCTAssertEqualObjects(layer.iconOptional, defaultExpression, @"iconOptional should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-padding { XCTAssertTrue(rawLayer->getIconPadding().isUndefined(), @"icon-padding should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconPadding; + NSExpression *defaultExpression = layer.iconPadding; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconPadding = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconPadding = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconPadding(), propertyValue, - @"Setting iconPadding to a constant value should update icon-padding."); - XCTAssertEqualObjects(layer.iconPadding, constantStyleValue, - @"iconPadding should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconPadding = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconPadding to a constant value expression should update icon-padding."); + XCTAssertEqualObjects(layer.iconPadding, constantExpression, + @"iconPadding should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconPadding = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconPadding(), propertyValue, - @"Setting iconPadding to a camera function should update icon-padding."); - XCTAssertEqualObjects(layer.iconPadding, functionStyleValue, - @"iconPadding should round-trip camera functions."); + @"Setting iconPadding to a camera expression should update icon-padding."); + XCTAssertEqualObjects(layer.iconPadding, functionExpression, + @"iconPadding should round-trip camera expressions."); layer.iconPadding = nil; XCTAssertTrue(rawLayer->getIconPadding().isUndefined(), @"Unsetting iconPadding should return icon-padding to the default value."); - XCTAssertEqualObjects(layer.iconPadding, defaultStyleValue, + XCTAssertEqualObjects(layer.iconPadding, defaultExpression, @"iconPadding should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-pitch-alignment { XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), @"icon-pitch-alignment should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconPitchAlignment; + NSExpression *defaultExpression = layer.iconPitchAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto]]; - layer.iconPitchAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + layer.iconPitchAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto }; XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, - @"Setting iconPitchAlignment to a constant value should update icon-pitch-alignment."); - XCTAssertEqualObjects(layer.iconPitchAlignment, constantStyleValue, - @"iconPitchAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconPitchAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} }; + @"Setting iconPitchAlignment to a constant value expression should update icon-pitch-alignment."); + XCTAssertEqualObjects(layer.iconPitchAlignment, constantExpression, + @"iconPitchAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconPitchAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{ + { -INFINITY, mbgl::style::AlignmentType::Auto }, + { 18, mbgl::style::AlignmentType::Auto }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, - @"Setting iconPitchAlignment to a camera function should update icon-pitch-alignment."); - XCTAssertEqualObjects(layer.iconPitchAlignment, functionStyleValue, - @"iconPitchAlignment should round-trip camera functions."); + @"Setting iconPitchAlignment to a camera expression should update icon-pitch-alignment."); + XCTAssertEqualObjects(layer.iconPitchAlignment, functionExpression, + @"iconPitchAlignment should round-trip camera expressions."); layer.iconPitchAlignment = nil; XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), @"Unsetting iconPitchAlignment should return icon-pitch-alignment to the default value."); - XCTAssertEqualObjects(layer.iconPitchAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.iconPitchAlignment, defaultExpression, @"iconPitchAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-rotate { XCTAssertTrue(rawLayer->getIconRotate().isUndefined(), @"icon-rotate should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconRotation; + NSExpression *defaultExpression = layer.iconRotation; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconRotation = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconRotation = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, - @"Setting iconRotation to a constant value should update icon-rotate."); - XCTAssertEqualObjects(layer.iconRotation, constantStyleValue, - @"iconRotation should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconRotation = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconRotation to a constant value expression should update icon-rotate."); + XCTAssertEqualObjects(layer.iconRotation, constantExpression, + @"iconRotation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconRotation = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, - @"Setting iconRotation to a camera function should update icon-rotate."); - XCTAssertEqualObjects(layer.iconRotation, functionStyleValue, - @"iconRotation should round-trip camera functions."); + @"Setting iconRotation to a camera expression should update icon-rotate."); + XCTAssertEqualObjects(layer.iconRotation, functionExpression, + @"iconRotation should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconRotation = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconRotation = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, - @"Setting iconRotation to a source function should update icon-rotate."); - XCTAssertEqualObjects(layer.iconRotation, functionStyleValue, - @"iconRotation should round-trip source functions."); + @"Setting iconRotation to a data expression should update icon-rotate."); + XCTAssertEqualObjects(layer.iconRotation, functionExpression, + @"iconRotation should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconRotation = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconRotation = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -419,15 +460,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, - @"Setting iconRotation to a composite function should update icon-rotate."); - XCTAssertEqualObjects(layer.iconRotation, functionStyleValue, - @"iconRotation should round-trip composite functions."); + @"Setting iconRotation to a camera-data expression should update icon-rotate."); + XCTAssertEqualObjects(layer.iconRotation, functionExpression, + @"iconRotation should round-trip camera-data expressions."); layer.iconRotation = nil; XCTAssertTrue(rawLayer->getIconRotate().isUndefined(), @"Unsetting iconRotation should return icon-rotate to the default value."); - XCTAssertEqualObjects(layer.iconRotation, defaultStyleValue, + XCTAssertEqualObjects(layer.iconRotation, defaultExpression, @"iconRotation should return the default value after being unset."); } @@ -435,79 +476,88 @@ { XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(), @"icon-rotation-alignment should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconRotationAlignment; + NSExpression *defaultExpression = layer.iconRotationAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto]]; - layer.iconRotationAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + layer.iconRotationAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto }; XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue, - @"Setting iconRotationAlignment to a constant value should update icon-rotation-alignment."); - XCTAssertEqualObjects(layer.iconRotationAlignment, constantStyleValue, - @"iconRotationAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconRotationAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} }; + @"Setting iconRotationAlignment to a constant value expression should update icon-rotation-alignment."); + XCTAssertEqualObjects(layer.iconRotationAlignment, constantExpression, + @"iconRotationAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconRotationAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{ + { -INFINITY, mbgl::style::AlignmentType::Auto }, + { 18, mbgl::style::AlignmentType::Auto }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue, - @"Setting iconRotationAlignment to a camera function should update icon-rotation-alignment."); - XCTAssertEqualObjects(layer.iconRotationAlignment, functionStyleValue, - @"iconRotationAlignment should round-trip camera functions."); + @"Setting iconRotationAlignment to a camera expression should update icon-rotation-alignment."); + XCTAssertEqualObjects(layer.iconRotationAlignment, functionExpression, + @"iconRotationAlignment should round-trip camera expressions."); layer.iconRotationAlignment = nil; XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(), @"Unsetting iconRotationAlignment should return icon-rotation-alignment to the default value."); - XCTAssertEqualObjects(layer.iconRotationAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.iconRotationAlignment, defaultExpression, @"iconRotationAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-size { XCTAssertTrue(rawLayer->getIconSize().isUndefined(), @"icon-size should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconScale; + NSExpression *defaultExpression = layer.iconScale; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconScale = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconScale = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconSize(), propertyValue, - @"Setting iconScale to a constant value should update icon-size."); - XCTAssertEqualObjects(layer.iconScale, constantStyleValue, - @"iconScale should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconScale = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconScale to a constant value expression should update icon-size."); + XCTAssertEqualObjects(layer.iconScale, constantExpression, + @"iconScale should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconScale = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconSize(), propertyValue, - @"Setting iconScale to a camera function should update icon-size."); - XCTAssertEqualObjects(layer.iconScale, functionStyleValue, - @"iconScale should round-trip camera functions."); + @"Setting iconScale to a camera expression should update icon-size."); + XCTAssertEqualObjects(layer.iconScale, functionExpression, + @"iconScale should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconScale = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconScale = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconSize(), propertyValue, - @"Setting iconScale to a source function should update icon-size."); - XCTAssertEqualObjects(layer.iconScale, functionStyleValue, - @"iconScale should round-trip source functions."); + @"Setting iconScale to a data expression should update icon-size."); + XCTAssertEqualObjects(layer.iconScale, functionExpression, + @"iconScale should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconScale = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconScale = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -515,15 +565,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconSize(), propertyValue, - @"Setting iconScale to a composite function should update icon-size."); - XCTAssertEqualObjects(layer.iconScale, functionStyleValue, - @"iconScale should round-trip composite functions."); + @"Setting iconScale to a camera-data expression should update icon-size."); + XCTAssertEqualObjects(layer.iconScale, functionExpression, + @"iconScale should round-trip camera-data expressions."); layer.iconScale = nil; XCTAssertTrue(rawLayer->getIconSize().isUndefined(), @"Unsetting iconScale should return icon-size to the default value."); - XCTAssertEqualObjects(layer.iconScale, defaultStyleValue, + XCTAssertEqualObjects(layer.iconScale, defaultExpression, @"iconScale should return the default value after being unset."); } @@ -531,241 +581,270 @@ { XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(), @"icon-text-fit should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFit; + NSExpression *defaultExpression = layer.iconTextFit; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTextFit:MGLIconTextFitBoth]]; - layer.iconTextFit = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'both'"]; + layer.iconTextFit = constantExpression; mbgl::style::PropertyValue<mbgl::style::IconTextFitType> propertyValue = { mbgl::style::IconTextFitType::Both }; XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue, - @"Setting iconTextFit to a constant value should update icon-text-fit."); - XCTAssertEqualObjects(layer.iconTextFit, constantStyleValue, - @"iconTextFit should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconTextFit = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::IconTextFitType> intervalStops = { {{18, mbgl::style::IconTextFitType::Both}} }; + @"Setting iconTextFit to a constant value expression should update icon-text-fit."); + XCTAssertEqualObjects(layer.iconTextFit, constantExpression, + @"iconTextFit should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'both'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconTextFit = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::IconTextFitType> intervalStops = {{ + { -INFINITY, mbgl::style::IconTextFitType::Both }, + { 18, mbgl::style::IconTextFitType::Both }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::IconTextFitType> { intervalStops }; XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue, - @"Setting iconTextFit to a camera function should update icon-text-fit."); - XCTAssertEqualObjects(layer.iconTextFit, functionStyleValue, - @"iconTextFit should round-trip camera functions."); + @"Setting iconTextFit to a camera expression should update icon-text-fit."); + XCTAssertEqualObjects(layer.iconTextFit, functionExpression, + @"iconTextFit should round-trip camera expressions."); layer.iconTextFit = nil; XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(), @"Unsetting iconTextFit should return icon-text-fit to the default value."); - XCTAssertEqualObjects(layer.iconTextFit, defaultStyleValue, + XCTAssertEqualObjects(layer.iconTextFit, defaultExpression, @"iconTextFit should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconTextFit = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-text-fit-padding { XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(), @"icon-text-fit-padding should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTextFitPadding; + NSExpression *defaultExpression = layer.iconTextFitPadding; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(1, 1, 1, 1)] #else [NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 1, 1, 1)] #endif ]; - layer.iconTextFitPadding = constantStyleValue; + layer.iconTextFitPadding = constantExpression; mbgl::style::PropertyValue<std::array<float, 4>> propertyValue = { { 1, 1, 1, 1 } }; XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue, - @"Setting iconTextFitPadding to a constant value should update icon-text-fit-padding."); - XCTAssertEqualObjects(layer.iconTextFitPadding, constantStyleValue, - @"iconTextFitPadding should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconTextFitPadding = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 4>> intervalStops = { {{18, { 1, 1, 1, 1 }}} }; + @"Setting iconTextFitPadding to a constant value expression should update icon-text-fit-padding."); + XCTAssertEqualObjects(layer.iconTextFitPadding, constantExpression, + @"iconTextFitPadding should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1, 1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconTextFitPadding = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 4>> intervalStops = {{ + { -INFINITY, { 1, 1, 1, 1 } }, + { 18, { 1, 1, 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 4>> { intervalStops }; XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue, - @"Setting iconTextFitPadding to a camera function should update icon-text-fit-padding."); - XCTAssertEqualObjects(layer.iconTextFitPadding, functionStyleValue, - @"iconTextFitPadding should round-trip camera functions."); + @"Setting iconTextFitPadding to a camera expression should update icon-text-fit-padding."); + XCTAssertEqualObjects(layer.iconTextFitPadding, functionExpression, + @"iconTextFitPadding should round-trip camera expressions."); layer.iconTextFitPadding = nil; XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(), @"Unsetting iconTextFitPadding should return icon-text-fit-padding to the default value."); - XCTAssertEqualObjects(layer.iconTextFitPadding, defaultStyleValue, + XCTAssertEqualObjects(layer.iconTextFitPadding, defaultExpression, @"iconTextFitPadding should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconTextFitPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-keep-upright { XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(), @"icon-keep-upright should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsIconUpright; + NSExpression *defaultExpression = layer.keepsIconUpright; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.keepsIconUpright = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.keepsIconUpright = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue, - @"Setting keepsIconUpright to a constant value should update icon-keep-upright."); - XCTAssertEqualObjects(layer.keepsIconUpright, constantStyleValue, - @"keepsIconUpright should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.keepsIconUpright = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting keepsIconUpright to a constant value expression should update icon-keep-upright."); + XCTAssertEqualObjects(layer.keepsIconUpright, constantExpression, + @"keepsIconUpright should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.keepsIconUpright = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue, - @"Setting keepsIconUpright to a camera function should update icon-keep-upright."); - XCTAssertEqualObjects(layer.keepsIconUpright, functionStyleValue, - @"keepsIconUpright should round-trip camera functions."); + @"Setting keepsIconUpright to a camera expression should update icon-keep-upright."); + XCTAssertEqualObjects(layer.keepsIconUpright, functionExpression, + @"keepsIconUpright should round-trip camera expressions."); layer.keepsIconUpright = nil; XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(), @"Unsetting keepsIconUpright should return icon-keep-upright to the default value."); - XCTAssertEqualObjects(layer.keepsIconUpright, defaultStyleValue, + XCTAssertEqualObjects(layer.keepsIconUpright, defaultExpression, @"keepsIconUpright should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.keepsIconUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-keep-upright { XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(), @"text-keep-upright should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.keepsTextUpright; + NSExpression *defaultExpression = layer.keepsTextUpright; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@NO]; - layer.keepsTextUpright = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"false"]; + layer.keepsTextUpright = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { false }; XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue, - @"Setting keepsTextUpright to a constant value should update text-keep-upright."); - XCTAssertEqualObjects(layer.keepsTextUpright, constantStyleValue, - @"keepsTextUpright should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.keepsTextUpright = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, false}} }; + @"Setting keepsTextUpright to a constant value expression should update text-keep-upright."); + XCTAssertEqualObjects(layer.keepsTextUpright, constantExpression, + @"keepsTextUpright should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"false"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.keepsTextUpright = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, false }, + { 18, false }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue, - @"Setting keepsTextUpright to a camera function should update text-keep-upright."); - XCTAssertEqualObjects(layer.keepsTextUpright, functionStyleValue, - @"keepsTextUpright should round-trip camera functions."); + @"Setting keepsTextUpright to a camera expression should update text-keep-upright."); + XCTAssertEqualObjects(layer.keepsTextUpright, functionExpression, + @"keepsTextUpright should round-trip camera expressions."); layer.keepsTextUpright = nil; XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(), @"Unsetting keepsTextUpright should return text-keep-upright to the default value."); - XCTAssertEqualObjects(layer.keepsTextUpright, defaultStyleValue, + XCTAssertEqualObjects(layer.keepsTextUpright, defaultExpression, @"keepsTextUpright should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.keepsTextUpright = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-max-angle { XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(), @"text-max-angle should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextAngle; + NSExpression *defaultExpression = layer.maximumTextAngle; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.maximumTextAngle = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.maximumTextAngle = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue, - @"Setting maximumTextAngle to a constant value should update text-max-angle."); - XCTAssertEqualObjects(layer.maximumTextAngle, constantStyleValue, - @"maximumTextAngle should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.maximumTextAngle = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting maximumTextAngle to a constant value expression should update text-max-angle."); + XCTAssertEqualObjects(layer.maximumTextAngle, constantExpression, + @"maximumTextAngle should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.maximumTextAngle = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue, - @"Setting maximumTextAngle to a camera function should update text-max-angle."); - XCTAssertEqualObjects(layer.maximumTextAngle, functionStyleValue, - @"maximumTextAngle should round-trip camera functions."); + @"Setting maximumTextAngle to a camera expression should update text-max-angle."); + XCTAssertEqualObjects(layer.maximumTextAngle, functionExpression, + @"maximumTextAngle should round-trip camera expressions."); layer.maximumTextAngle = nil; XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(), @"Unsetting maximumTextAngle should return text-max-angle to the default value."); - XCTAssertEqualObjects(layer.maximumTextAngle, defaultStyleValue, + XCTAssertEqualObjects(layer.maximumTextAngle, defaultExpression, @"maximumTextAngle should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.maximumTextAngle = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-max-width { XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(), @"text-max-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.maximumTextWidth; + NSExpression *defaultExpression = layer.maximumTextWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.maximumTextWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.maximumTextWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, - @"Setting maximumTextWidth to a constant value should update text-max-width."); - XCTAssertEqualObjects(layer.maximumTextWidth, constantStyleValue, - @"maximumTextWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.maximumTextWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting maximumTextWidth to a constant value expression should update text-max-width."); + XCTAssertEqualObjects(layer.maximumTextWidth, constantExpression, + @"maximumTextWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.maximumTextWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, - @"Setting maximumTextWidth to a camera function should update text-max-width."); - XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue, - @"maximumTextWidth should round-trip camera functions."); + @"Setting maximumTextWidth to a camera expression should update text-max-width."); + XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression, + @"maximumTextWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.maximumTextWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.maximumTextWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, - @"Setting maximumTextWidth to a source function should update text-max-width."); - XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue, - @"maximumTextWidth should round-trip source functions."); + @"Setting maximumTextWidth to a data expression should update text-max-width."); + XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression, + @"maximumTextWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.maximumTextWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.maximumTextWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -773,15 +852,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, - @"Setting maximumTextWidth to a composite function should update text-max-width."); - XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue, - @"maximumTextWidth should round-trip composite functions."); + @"Setting maximumTextWidth to a camera-data expression should update text-max-width."); + XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression, + @"maximumTextWidth should round-trip camera-data expressions."); layer.maximumTextWidth = nil; XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(), @"Unsetting maximumTextWidth should return text-max-width to the default value."); - XCTAssertEqualObjects(layer.maximumTextWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.maximumTextWidth, defaultExpression, @"maximumTextWidth should return the default value after being unset."); } @@ -789,150 +868,169 @@ { XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(), @"symbol-avoid-edges should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolAvoidsEdges; + NSExpression *defaultExpression = layer.symbolAvoidsEdges; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.symbolAvoidsEdges = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.symbolAvoidsEdges = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue, - @"Setting symbolAvoidsEdges to a constant value should update symbol-avoid-edges."); - XCTAssertEqualObjects(layer.symbolAvoidsEdges, constantStyleValue, - @"symbolAvoidsEdges should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.symbolAvoidsEdges = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting symbolAvoidsEdges to a constant value expression should update symbol-avoid-edges."); + XCTAssertEqualObjects(layer.symbolAvoidsEdges, constantExpression, + @"symbolAvoidsEdges should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.symbolAvoidsEdges = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue, - @"Setting symbolAvoidsEdges to a camera function should update symbol-avoid-edges."); - XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionStyleValue, - @"symbolAvoidsEdges should round-trip camera functions."); + @"Setting symbolAvoidsEdges to a camera expression should update symbol-avoid-edges."); + XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionExpression, + @"symbolAvoidsEdges should round-trip camera expressions."); layer.symbolAvoidsEdges = nil; XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(), @"Unsetting symbolAvoidsEdges should return symbol-avoid-edges to the default value."); - XCTAssertEqualObjects(layer.symbolAvoidsEdges, defaultStyleValue, + XCTAssertEqualObjects(layer.symbolAvoidsEdges, defaultExpression, @"symbolAvoidsEdges should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.symbolAvoidsEdges = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // symbol-placement { XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(), @"symbol-placement should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.symbolPlacement; + NSExpression *defaultExpression = layer.symbolPlacement; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSymbolPlacement:MGLSymbolPlacementLine]]; - layer.symbolPlacement = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'line'"]; + layer.symbolPlacement = constantExpression; mbgl::style::PropertyValue<mbgl::style::SymbolPlacementType> propertyValue = { mbgl::style::SymbolPlacementType::Line }; XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue, - @"Setting symbolPlacement to a constant value should update symbol-placement."); - XCTAssertEqualObjects(layer.symbolPlacement, constantStyleValue, - @"symbolPlacement should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.symbolPlacement = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::SymbolPlacementType> intervalStops = { {{18, mbgl::style::SymbolPlacementType::Line}} }; + @"Setting symbolPlacement to a constant value expression should update symbol-placement."); + XCTAssertEqualObjects(layer.symbolPlacement, constantExpression, + @"symbolPlacement should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'line'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.symbolPlacement = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::SymbolPlacementType> intervalStops = {{ + { -INFINITY, mbgl::style::SymbolPlacementType::Line }, + { 18, mbgl::style::SymbolPlacementType::Line }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolPlacementType> { intervalStops }; XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue, - @"Setting symbolPlacement to a camera function should update symbol-placement."); - XCTAssertEqualObjects(layer.symbolPlacement, functionStyleValue, - @"symbolPlacement should round-trip camera functions."); + @"Setting symbolPlacement to a camera expression should update symbol-placement."); + XCTAssertEqualObjects(layer.symbolPlacement, functionExpression, + @"symbolPlacement should round-trip camera expressions."); layer.symbolPlacement = nil; XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(), @"Unsetting symbolPlacement should return symbol-placement to the default value."); - XCTAssertEqualObjects(layer.symbolPlacement, defaultStyleValue, + XCTAssertEqualObjects(layer.symbolPlacement, defaultExpression, @"symbolPlacement should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.symbolPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // symbol-spacing { XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(), @"symbol-spacing should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.symbolSpacing; + NSExpression *defaultExpression = layer.symbolSpacing; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.symbolSpacing = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.symbolSpacing = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue, - @"Setting symbolSpacing to a constant value should update symbol-spacing."); - XCTAssertEqualObjects(layer.symbolSpacing, constantStyleValue, - @"symbolSpacing should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.symbolSpacing = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting symbolSpacing to a constant value expression should update symbol-spacing."); + XCTAssertEqualObjects(layer.symbolSpacing, constantExpression, + @"symbolSpacing should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.symbolSpacing = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue, - @"Setting symbolSpacing to a camera function should update symbol-spacing."); - XCTAssertEqualObjects(layer.symbolSpacing, functionStyleValue, - @"symbolSpacing should round-trip camera functions."); + @"Setting symbolSpacing to a camera expression should update symbol-spacing."); + XCTAssertEqualObjects(layer.symbolSpacing, functionExpression, + @"symbolSpacing should round-trip camera expressions."); layer.symbolSpacing = nil; XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(), @"Unsetting symbolSpacing should return symbol-spacing to the default value."); - XCTAssertEqualObjects(layer.symbolSpacing, defaultStyleValue, + XCTAssertEqualObjects(layer.symbolSpacing, defaultExpression, @"symbolSpacing should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.symbolSpacing = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-field { XCTAssertTrue(rawLayer->getTextField().isUndefined(), @"text-field should be unset initially."); - MGLStyleValue<NSString *> *defaultStyleValue = layer.text; + NSExpression *defaultExpression = layer.text; - MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Text Field"]; - layer.text = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'Text Field'"]; + layer.text = constantExpression; mbgl::style::DataDrivenPropertyValue<std::string> propertyValue = { "Text Field" }; XCTAssertEqual(rawLayer->getTextField(), propertyValue, - @"Setting text to a constant value should update text-field."); - XCTAssertEqualObjects(layer.text, constantStyleValue, - @"text should round-trip constant values."); - - MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.text = functionStyleValue; - - mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Text Field"}} }; + @"Setting text to a constant value expression should update text-field."); + XCTAssertEqualObjects(layer.text, constantExpression, + @"text should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'Text Field'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.text = functionExpression; + + mbgl::style::IntervalStops<std::string> intervalStops = {{ + { -INFINITY, "Text Field" }, + { 18, "Text Field" }, + }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; XCTAssertEqual(rawLayer->getTextField(), propertyValue, - @"Setting text to a camera function should update text-field."); - XCTAssertEqualObjects(layer.text, functionStyleValue, - @"text should round-trip camera functions."); + @"Setting text to a camera expression should update text-field."); + XCTAssertEqualObjects(layer.text, functionExpression, + @"text should round-trip camera expressions."); layer.text = nil; XCTAssertTrue(rawLayer->getTextField().isUndefined(), @"Unsetting text should return text-field to the default value."); - XCTAssertEqualObjects(layer.text, defaultStyleValue, + XCTAssertEqualObjects(layer.text, defaultExpression, @"text should return the default value after being unset."); } @@ -940,72 +1038,81 @@ { XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(), @"text-allow-overlap should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textAllowsOverlap; + NSExpression *defaultExpression = layer.textAllowsOverlap; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.textAllowsOverlap = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.textAllowsOverlap = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue, - @"Setting textAllowsOverlap to a constant value should update text-allow-overlap."); - XCTAssertEqualObjects(layer.textAllowsOverlap, constantStyleValue, - @"textAllowsOverlap should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textAllowsOverlap = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting textAllowsOverlap to a constant value expression should update text-allow-overlap."); + XCTAssertEqualObjects(layer.textAllowsOverlap, constantExpression, + @"textAllowsOverlap should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textAllowsOverlap = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue, - @"Setting textAllowsOverlap to a camera function should update text-allow-overlap."); - XCTAssertEqualObjects(layer.textAllowsOverlap, functionStyleValue, - @"textAllowsOverlap should round-trip camera functions."); + @"Setting textAllowsOverlap to a camera expression should update text-allow-overlap."); + XCTAssertEqualObjects(layer.textAllowsOverlap, functionExpression, + @"textAllowsOverlap should round-trip camera expressions."); layer.textAllowsOverlap = nil; XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(), @"Unsetting textAllowsOverlap should return text-allow-overlap to the default value."); - XCTAssertEqualObjects(layer.textAllowsOverlap, defaultStyleValue, + XCTAssertEqualObjects(layer.textAllowsOverlap, defaultExpression, @"textAllowsOverlap should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textAllowsOverlap = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-anchor { XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(), @"text-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textAnchor; + NSExpression *defaultExpression = layer.textAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]]; - layer.textAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"]; + layer.textAnchor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::style::SymbolAnchorType> propertyValue = { mbgl::style::SymbolAnchorType::BottomRight }; XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue, - @"Setting textAnchor to a constant value should update text-anchor."); - XCTAssertEqualObjects(layer.textAnchor, constantStyleValue, - @"textAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{18, mbgl::style::SymbolAnchorType::BottomRight}} }; + @"Setting textAnchor to a constant value expression should update text-anchor."); + XCTAssertEqualObjects(layer.textAnchor, constantExpression, + @"textAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::SymbolAnchorType::BottomRight }, + { 18, mbgl::style::SymbolAnchorType::BottomRight }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue, - @"Setting textAnchor to a camera function should update text-anchor."); - XCTAssertEqualObjects(layer.textAnchor, functionStyleValue, - @"textAnchor should round-trip camera functions."); + @"Setting textAnchor to a camera expression should update text-anchor."); + XCTAssertEqualObjects(layer.textAnchor, functionExpression, + @"textAnchor should round-trip camera expressions."); layer.textAnchor = nil; XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(), @"Unsetting textAnchor should return text-anchor to the default value."); - XCTAssertEqualObjects(layer.textAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.textAnchor, defaultExpression, @"textAnchor should return the default value after being unset."); } @@ -1013,33 +1120,37 @@ { XCTAssertTrue(rawLayer->getTextFont().isUndefined(), @"text-font should be unset initially."); - MGLStyleValue<NSArray<NSString *> *> *defaultStyleValue = layer.textFontNames; + NSExpression *defaultExpression = layer.textFontNames; - MGLStyleValue<NSArray<NSString *> *> *constantStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithRawValue:@[@"Text Font", @"Tnof Txet"]]; - layer.textFontNames = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"{'Text Font', 'Tnof Txet'}"]; + layer.textFontNames = constantExpression; mbgl::style::DataDrivenPropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } }; XCTAssertEqual(rawLayer->getTextFont(), propertyValue, - @"Setting textFontNames to a constant value should update text-font."); - XCTAssertEqualObjects(layer.textFontNames, constantStyleValue, - @"textFontNames should round-trip constant values."); - - MGLStyleValue<NSArray<NSString *> *> * functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textFontNames = functionStyleValue; - - mbgl::style::IntervalStops<std::vector<std::string>> intervalStops = { {{18, { "Text Font", "Tnof Txet" }}} }; + @"Setting textFontNames to a constant value expression should update text-font."); + XCTAssertEqualObjects(layer.textFontNames, constantExpression, + @"textFontNames should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{'Text Font', 'Tnof Txet'}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textFontNames = functionExpression; + + mbgl::style::IntervalStops<std::vector<std::string>> intervalStops = {{ + { -INFINITY, { "Text Font", "Tnof Txet" } }, + { 18, { "Text Font", "Tnof Txet" } }, + }}; propertyValue = mbgl::style::CameraFunction<std::vector<std::string>> { intervalStops }; XCTAssertEqual(rawLayer->getTextFont(), propertyValue, - @"Setting textFontNames to a camera function should update text-font."); - XCTAssertEqualObjects(layer.textFontNames, functionStyleValue, - @"textFontNames should round-trip camera functions."); + @"Setting textFontNames to a camera expression should update text-font."); + XCTAssertEqualObjects(layer.textFontNames, functionExpression, + @"textFontNames should round-trip camera expressions."); layer.textFontNames = nil; XCTAssertTrue(rawLayer->getTextFont().isUndefined(), @"Unsetting textFontNames should return text-font to the default value."); - XCTAssertEqualObjects(layer.textFontNames, defaultStyleValue, + XCTAssertEqualObjects(layer.textFontNames, defaultExpression, @"textFontNames should return the default value after being unset."); } @@ -1047,40 +1158,44 @@ { XCTAssertTrue(rawLayer->getTextSize().isUndefined(), @"text-size should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textFontSize; + NSExpression *defaultExpression = layer.textFontSize; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textFontSize = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textFontSize = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextSize(), propertyValue, - @"Setting textFontSize to a constant value should update text-size."); - XCTAssertEqualObjects(layer.textFontSize, constantStyleValue, - @"textFontSize should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textFontSize = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textFontSize to a constant value expression should update text-size."); + XCTAssertEqualObjects(layer.textFontSize, constantExpression, + @"textFontSize should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textFontSize = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextSize(), propertyValue, - @"Setting textFontSize to a camera function should update text-size."); - XCTAssertEqualObjects(layer.textFontSize, functionStyleValue, - @"textFontSize should round-trip camera functions."); + @"Setting textFontSize to a camera expression should update text-size."); + XCTAssertEqualObjects(layer.textFontSize, functionExpression, + @"textFontSize should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textFontSize = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textFontSize = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextSize(), propertyValue, - @"Setting textFontSize to a source function should update text-size."); - XCTAssertEqualObjects(layer.textFontSize, functionStyleValue, - @"textFontSize should round-trip source functions."); + @"Setting textFontSize to a data expression should update text-size."); + XCTAssertEqualObjects(layer.textFontSize, functionExpression, + @"textFontSize should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textFontSize = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textFontSize = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1088,15 +1203,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextSize(), propertyValue, - @"Setting textFontSize to a composite function should update text-size."); - XCTAssertEqualObjects(layer.textFontSize, functionStyleValue, - @"textFontSize should round-trip composite functions."); + @"Setting textFontSize to a camera-data expression should update text-size."); + XCTAssertEqualObjects(layer.textFontSize, functionExpression, + @"textFontSize should round-trip camera-data expressions."); layer.textFontSize = nil; XCTAssertTrue(rawLayer->getTextSize().isUndefined(), @"Unsetting textFontSize should return text-size to the default value."); - XCTAssertEqualObjects(layer.textFontSize, defaultStyleValue, + XCTAssertEqualObjects(layer.textFontSize, defaultExpression, @"textFontSize should return the default value after being unset."); } @@ -1104,72 +1219,81 @@ { XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(), @"text-ignore-placement should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textIgnoresPlacement; + NSExpression *defaultExpression = layer.textIgnoresPlacement; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.textIgnoresPlacement = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.textIgnoresPlacement = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue, - @"Setting textIgnoresPlacement to a constant value should update text-ignore-placement."); - XCTAssertEqualObjects(layer.textIgnoresPlacement, constantStyleValue, - @"textIgnoresPlacement should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textIgnoresPlacement = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting textIgnoresPlacement to a constant value expression should update text-ignore-placement."); + XCTAssertEqualObjects(layer.textIgnoresPlacement, constantExpression, + @"textIgnoresPlacement should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textIgnoresPlacement = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue, - @"Setting textIgnoresPlacement to a camera function should update text-ignore-placement."); - XCTAssertEqualObjects(layer.textIgnoresPlacement, functionStyleValue, - @"textIgnoresPlacement should round-trip camera functions."); + @"Setting textIgnoresPlacement to a camera expression should update text-ignore-placement."); + XCTAssertEqualObjects(layer.textIgnoresPlacement, functionExpression, + @"textIgnoresPlacement should round-trip camera expressions."); layer.textIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(), @"Unsetting textIgnoresPlacement should return text-ignore-placement to the default value."); - XCTAssertEqualObjects(layer.textIgnoresPlacement, defaultStyleValue, + XCTAssertEqualObjects(layer.textIgnoresPlacement, defaultExpression, @"textIgnoresPlacement should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textIgnoresPlacement = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-justify { XCTAssertTrue(rawLayer->getTextJustify().isUndefined(), @"text-justify should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textJustification; + NSExpression *defaultExpression = layer.textJustification; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationRight]]; - layer.textJustification = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'right'"]; + layer.textJustification = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::style::TextJustifyType> propertyValue = { mbgl::style::TextJustifyType::Right }; XCTAssertEqual(rawLayer->getTextJustify(), propertyValue, - @"Setting textJustification to a constant value should update text-justify."); - XCTAssertEqualObjects(layer.textJustification, constantStyleValue, - @"textJustification should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textJustification = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TextJustifyType> intervalStops = { {{18, mbgl::style::TextJustifyType::Right}} }; + @"Setting textJustification to a constant value expression should update text-justify."); + XCTAssertEqualObjects(layer.textJustification, constantExpression, + @"textJustification should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'right'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textJustification = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TextJustifyType> intervalStops = {{ + { -INFINITY, mbgl::style::TextJustifyType::Right }, + { 18, mbgl::style::TextJustifyType::Right }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TextJustifyType> { intervalStops }; XCTAssertEqual(rawLayer->getTextJustify(), propertyValue, - @"Setting textJustification to a camera function should update text-justify."); - XCTAssertEqualObjects(layer.textJustification, functionStyleValue, - @"textJustification should round-trip camera functions."); + @"Setting textJustification to a camera expression should update text-justify."); + XCTAssertEqualObjects(layer.textJustification, functionExpression, + @"textJustification should round-trip camera expressions."); layer.textJustification = nil; XCTAssertTrue(rawLayer->getTextJustify().isUndefined(), @"Unsetting textJustification should return text-justify to the default value."); - XCTAssertEqualObjects(layer.textJustification, defaultStyleValue, + XCTAssertEqualObjects(layer.textJustification, defaultExpression, @"textJustification should return the default value after being unset."); } @@ -1177,40 +1301,44 @@ { XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(), @"text-letter-spacing should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLetterSpacing; + NSExpression *defaultExpression = layer.textLetterSpacing; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textLetterSpacing = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textLetterSpacing = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, - @"Setting textLetterSpacing to a constant value should update text-letter-spacing."); - XCTAssertEqualObjects(layer.textLetterSpacing, constantStyleValue, - @"textLetterSpacing should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textLetterSpacing = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textLetterSpacing to a constant value expression should update text-letter-spacing."); + XCTAssertEqualObjects(layer.textLetterSpacing, constantExpression, + @"textLetterSpacing should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textLetterSpacing = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, - @"Setting textLetterSpacing to a camera function should update text-letter-spacing."); - XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue, - @"textLetterSpacing should round-trip camera functions."); + @"Setting textLetterSpacing to a camera expression should update text-letter-spacing."); + XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression, + @"textLetterSpacing should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textLetterSpacing = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textLetterSpacing = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, - @"Setting textLetterSpacing to a source function should update text-letter-spacing."); - XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue, - @"textLetterSpacing should round-trip source functions."); + @"Setting textLetterSpacing to a data expression should update text-letter-spacing."); + XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression, + @"textLetterSpacing should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textLetterSpacing = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textLetterSpacing = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1218,15 +1346,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, - @"Setting textLetterSpacing to a composite function should update text-letter-spacing."); - XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue, - @"textLetterSpacing should round-trip composite functions."); + @"Setting textLetterSpacing to a camera-data expression should update text-letter-spacing."); + XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression, + @"textLetterSpacing should round-trip camera-data expressions."); layer.textLetterSpacing = nil; XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(), @"Unsetting textLetterSpacing should return text-letter-spacing to the default value."); - XCTAssertEqualObjects(layer.textLetterSpacing, defaultStyleValue, + XCTAssertEqualObjects(layer.textLetterSpacing, defaultExpression, @"textLetterSpacing should return the default value after being unset."); } @@ -1234,85 +1362,94 @@ { XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(), @"text-line-height should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textLineHeight; + NSExpression *defaultExpression = layer.textLineHeight; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textLineHeight = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textLineHeight = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue, - @"Setting textLineHeight to a constant value should update text-line-height."); - XCTAssertEqualObjects(layer.textLineHeight, constantStyleValue, - @"textLineHeight should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textLineHeight = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textLineHeight to a constant value expression should update text-line-height."); + XCTAssertEqualObjects(layer.textLineHeight, constantExpression, + @"textLineHeight should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textLineHeight = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue, - @"Setting textLineHeight to a camera function should update text-line-height."); - XCTAssertEqualObjects(layer.textLineHeight, functionStyleValue, - @"textLineHeight should round-trip camera functions."); + @"Setting textLineHeight to a camera expression should update text-line-height."); + XCTAssertEqualObjects(layer.textLineHeight, functionExpression, + @"textLineHeight should round-trip camera expressions."); layer.textLineHeight = nil; XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(), @"Unsetting textLineHeight should return text-line-height to the default value."); - XCTAssertEqualObjects(layer.textLineHeight, defaultStyleValue, + XCTAssertEqualObjects(layer.textLineHeight, defaultExpression, @"textLineHeight should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textLineHeight = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-offset { XCTAssertTrue(rawLayer->getTextOffset().isUndefined(), @"text-offset should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textOffset; + NSExpression *defaultExpression = layer.textOffset; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.textOffset = constantStyleValue; + layer.textOffset = constantExpression; mbgl::style::DataDrivenPropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, - @"Setting textOffset to a constant value should update text-offset."); - XCTAssertEqualObjects(layer.textOffset, constantStyleValue, - @"textOffset should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textOffset = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting textOffset to a constant value expression should update text-offset."); + XCTAssertEqualObjects(layer.textOffset, constantExpression, + @"textOffset should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textOffset = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, - @"Setting textOffset to a camera function should update text-offset."); - XCTAssertEqualObjects(layer.textOffset, functionStyleValue, - @"textOffset should round-trip camera functions."); + @"Setting textOffset to a camera expression should update text-offset."); + XCTAssertEqualObjects(layer.textOffset, functionExpression, + @"textOffset should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textOffset = functionExpression; mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<std::array<float, 2>> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, - @"Setting textOffset to a source function should update text-offset."); - XCTAssertEqualObjects(layer.textOffset, functionStyleValue, - @"textOffset should round-trip source functions."); + @"Setting textOffset to a data expression should update text-offset."); + XCTAssertEqualObjects(layer.textOffset, functionExpression, + @"textOffset should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textOffset = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textOffset = functionExpression; std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} }; mbgl::style::CompositeExponentialStops<std::array<float, 2>> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1320,15 +1457,15 @@ propertyValue = mbgl::style::CompositeFunction<std::array<float, 2>> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, - @"Setting textOffset to a composite function should update text-offset."); - XCTAssertEqualObjects(layer.textOffset, functionStyleValue, - @"textOffset should round-trip composite functions."); + @"Setting textOffset to a camera-data expression should update text-offset."); + XCTAssertEqualObjects(layer.textOffset, functionExpression, + @"textOffset should round-trip camera-data expressions."); layer.textOffset = nil; XCTAssertTrue(rawLayer->getTextOffset().isUndefined(), @"Unsetting textOffset should return text-offset to the default value."); - XCTAssertEqualObjects(layer.textOffset, defaultStyleValue, + XCTAssertEqualObjects(layer.textOffset, defaultExpression, @"textOffset should return the default value after being unset."); } @@ -1336,157 +1473,176 @@ { XCTAssertTrue(rawLayer->getTextOptional().isUndefined(), @"text-optional should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOptional; + NSExpression *defaultExpression = layer.textOptional; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]; - layer.textOptional = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"true"]; + layer.textOptional = constantExpression; mbgl::style::PropertyValue<bool> propertyValue = { true }; XCTAssertEqual(rawLayer->getTextOptional(), propertyValue, - @"Setting textOptional to a constant value should update text-optional."); - XCTAssertEqualObjects(layer.textOptional, constantStyleValue, - @"textOptional should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textOptional = functionStyleValue; - - mbgl::style::IntervalStops<bool> intervalStops = { {{18, true}} }; + @"Setting textOptional to a constant value expression should update text-optional."); + XCTAssertEqualObjects(layer.textOptional, constantExpression, + @"textOptional should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"true"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textOptional = functionExpression; + + mbgl::style::IntervalStops<bool> intervalStops = {{ + { -INFINITY, true }, + { 18, true }, + }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; XCTAssertEqual(rawLayer->getTextOptional(), propertyValue, - @"Setting textOptional to a camera function should update text-optional."); - XCTAssertEqualObjects(layer.textOptional, functionStyleValue, - @"textOptional should round-trip camera functions."); + @"Setting textOptional to a camera expression should update text-optional."); + XCTAssertEqualObjects(layer.textOptional, functionExpression, + @"textOptional should round-trip camera expressions."); layer.textOptional = nil; XCTAssertTrue(rawLayer->getTextOptional().isUndefined(), @"Unsetting textOptional should return text-optional to the default value."); - XCTAssertEqualObjects(layer.textOptional, defaultStyleValue, + XCTAssertEqualObjects(layer.textOptional, defaultExpression, @"textOptional should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textOptional = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textOptional = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-padding { XCTAssertTrue(rawLayer->getTextPadding().isUndefined(), @"text-padding should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textPadding; + NSExpression *defaultExpression = layer.textPadding; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textPadding = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textPadding = constantExpression; mbgl::style::PropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextPadding(), propertyValue, - @"Setting textPadding to a constant value should update text-padding."); - XCTAssertEqualObjects(layer.textPadding, constantStyleValue, - @"textPadding should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textPadding = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textPadding to a constant value expression should update text-padding."); + XCTAssertEqualObjects(layer.textPadding, constantExpression, + @"textPadding should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textPadding = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextPadding(), propertyValue, - @"Setting textPadding to a camera function should update text-padding."); - XCTAssertEqualObjects(layer.textPadding, functionStyleValue, - @"textPadding should round-trip camera functions."); + @"Setting textPadding to a camera expression should update text-padding."); + XCTAssertEqualObjects(layer.textPadding, functionExpression, + @"textPadding should round-trip camera expressions."); layer.textPadding = nil; XCTAssertTrue(rawLayer->getTextPadding().isUndefined(), @"Unsetting textPadding should return text-padding to the default value."); - XCTAssertEqualObjects(layer.textPadding, defaultStyleValue, + XCTAssertEqualObjects(layer.textPadding, defaultExpression, @"textPadding should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textPadding = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-pitch-alignment { XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(), @"text-pitch-alignment should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textPitchAlignment; + NSExpression *defaultExpression = layer.textPitchAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextPitchAlignment:MGLTextPitchAlignmentAuto]]; - layer.textPitchAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + layer.textPitchAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto }; XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue, - @"Setting textPitchAlignment to a constant value should update text-pitch-alignment."); - XCTAssertEqualObjects(layer.textPitchAlignment, constantStyleValue, - @"textPitchAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textPitchAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} }; + @"Setting textPitchAlignment to a constant value expression should update text-pitch-alignment."); + XCTAssertEqualObjects(layer.textPitchAlignment, constantExpression, + @"textPitchAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textPitchAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{ + { -INFINITY, mbgl::style::AlignmentType::Auto }, + { 18, mbgl::style::AlignmentType::Auto }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue, - @"Setting textPitchAlignment to a camera function should update text-pitch-alignment."); - XCTAssertEqualObjects(layer.textPitchAlignment, functionStyleValue, - @"textPitchAlignment should round-trip camera functions."); + @"Setting textPitchAlignment to a camera expression should update text-pitch-alignment."); + XCTAssertEqualObjects(layer.textPitchAlignment, functionExpression, + @"textPitchAlignment should round-trip camera expressions."); layer.textPitchAlignment = nil; XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(), @"Unsetting textPitchAlignment should return text-pitch-alignment to the default value."); - XCTAssertEqualObjects(layer.textPitchAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.textPitchAlignment, defaultExpression, @"textPitchAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textPitchAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-rotate { XCTAssertTrue(rawLayer->getTextRotate().isUndefined(), @"text-rotate should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textRotation; + NSExpression *defaultExpression = layer.textRotation; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textRotation = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textRotation = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, - @"Setting textRotation to a constant value should update text-rotate."); - XCTAssertEqualObjects(layer.textRotation, constantStyleValue, - @"textRotation should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textRotation = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textRotation to a constant value expression should update text-rotate."); + XCTAssertEqualObjects(layer.textRotation, constantExpression, + @"textRotation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textRotation = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, - @"Setting textRotation to a camera function should update text-rotate."); - XCTAssertEqualObjects(layer.textRotation, functionStyleValue, - @"textRotation should round-trip camera functions."); + @"Setting textRotation to a camera expression should update text-rotate."); + XCTAssertEqualObjects(layer.textRotation, functionExpression, + @"textRotation should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textRotation = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textRotation = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, - @"Setting textRotation to a source function should update text-rotate."); - XCTAssertEqualObjects(layer.textRotation, functionStyleValue, - @"textRotation should round-trip source functions."); + @"Setting textRotation to a data expression should update text-rotate."); + XCTAssertEqualObjects(layer.textRotation, functionExpression, + @"textRotation should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textRotation = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textRotation = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1494,15 +1650,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, - @"Setting textRotation to a composite function should update text-rotate."); - XCTAssertEqualObjects(layer.textRotation, functionStyleValue, - @"textRotation should round-trip composite functions."); + @"Setting textRotation to a camera-data expression should update text-rotate."); + XCTAssertEqualObjects(layer.textRotation, functionExpression, + @"textRotation should round-trip camera-data expressions."); layer.textRotation = nil; XCTAssertTrue(rawLayer->getTextRotate().isUndefined(), @"Unsetting textRotation should return text-rotate to the default value."); - XCTAssertEqualObjects(layer.textRotation, defaultStyleValue, + XCTAssertEqualObjects(layer.textRotation, defaultExpression, @"textRotation should return the default value after being unset."); } @@ -1510,72 +1666,81 @@ { XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(), @"text-rotation-alignment should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textRotationAlignment; + NSExpression *defaultExpression = layer.textRotationAlignment; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextRotationAlignment:MGLTextRotationAlignmentAuto]]; - layer.textRotationAlignment = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + layer.textRotationAlignment = constantExpression; mbgl::style::PropertyValue<mbgl::style::AlignmentType> propertyValue = { mbgl::style::AlignmentType::Auto }; XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue, - @"Setting textRotationAlignment to a constant value should update text-rotation-alignment."); - XCTAssertEqualObjects(layer.textRotationAlignment, constantStyleValue, - @"textRotationAlignment should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textRotationAlignment = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} }; + @"Setting textRotationAlignment to a constant value expression should update text-rotation-alignment."); + XCTAssertEqualObjects(layer.textRotationAlignment, constantExpression, + @"textRotationAlignment should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'auto'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textRotationAlignment = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{ + { -INFINITY, mbgl::style::AlignmentType::Auto }, + { 18, mbgl::style::AlignmentType::Auto }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue, - @"Setting textRotationAlignment to a camera function should update text-rotation-alignment."); - XCTAssertEqualObjects(layer.textRotationAlignment, functionStyleValue, - @"textRotationAlignment should round-trip camera functions."); + @"Setting textRotationAlignment to a camera expression should update text-rotation-alignment."); + XCTAssertEqualObjects(layer.textRotationAlignment, functionExpression, + @"textRotationAlignment should round-trip camera expressions."); layer.textRotationAlignment = nil; XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(), @"Unsetting textRotationAlignment should return text-rotation-alignment to the default value."); - XCTAssertEqualObjects(layer.textRotationAlignment, defaultStyleValue, + XCTAssertEqualObjects(layer.textRotationAlignment, defaultExpression, @"textRotationAlignment should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textRotationAlignment = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-transform { XCTAssertTrue(rawLayer->getTextTransform().isUndefined(), @"text-transform should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTransform; + NSExpression *defaultExpression = layer.textTransform; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTransform:MGLTextTransformLowercase]]; - layer.textTransform = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'lowercase'"]; + layer.textTransform = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::style::TextTransformType> propertyValue = { mbgl::style::TextTransformType::Lowercase }; XCTAssertEqual(rawLayer->getTextTransform(), propertyValue, - @"Setting textTransform to a constant value should update text-transform."); - XCTAssertEqualObjects(layer.textTransform, constantStyleValue, - @"textTransform should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textTransform = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TextTransformType> intervalStops = { {{18, mbgl::style::TextTransformType::Lowercase}} }; + @"Setting textTransform to a constant value expression should update text-transform."); + XCTAssertEqualObjects(layer.textTransform, constantExpression, + @"textTransform should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'lowercase'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textTransform = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TextTransformType> intervalStops = {{ + { -INFINITY, mbgl::style::TextTransformType::Lowercase }, + { 18, mbgl::style::TextTransformType::Lowercase }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TextTransformType> { intervalStops }; XCTAssertEqual(rawLayer->getTextTransform(), propertyValue, - @"Setting textTransform to a camera function should update text-transform."); - XCTAssertEqualObjects(layer.textTransform, functionStyleValue, - @"textTransform should round-trip camera functions."); + @"Setting textTransform to a camera expression should update text-transform."); + XCTAssertEqualObjects(layer.textTransform, functionExpression, + @"textTransform should round-trip camera expressions."); layer.textTransform = nil; XCTAssertTrue(rawLayer->getTextTransform().isUndefined(), @"Unsetting textTransform should return text-transform to the default value."); - XCTAssertEqualObjects(layer.textTransform, defaultStyleValue, + XCTAssertEqualObjects(layer.textTransform, defaultExpression, @"textTransform should return the default value after being unset."); } @@ -1583,40 +1748,44 @@ { XCTAssertTrue(rawLayer->getIconColor().isUndefined(), @"icon-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconColor; + NSExpression *defaultExpression = layer.iconColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.iconColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.iconColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, - @"Setting iconColor to a constant value should update icon-color."); - XCTAssertEqualObjects(layer.iconColor, constantStyleValue, - @"iconColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting iconColor to a constant value expression should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, constantExpression, + @"iconColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, - @"Setting iconColor to a camera function should update icon-color."); - XCTAssertEqualObjects(layer.iconColor, functionStyleValue, - @"iconColor should round-trip camera functions."); + @"Setting iconColor to a camera expression should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionExpression, + @"iconColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, - @"Setting iconColor to a source function should update icon-color."); - XCTAssertEqualObjects(layer.iconColor, functionStyleValue, - @"iconColor should round-trip source functions."); + @"Setting iconColor to a data expression should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionExpression, + @"iconColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1624,15 +1793,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, - @"Setting iconColor to a composite function should update icon-color."); - XCTAssertEqualObjects(layer.iconColor, functionStyleValue, - @"iconColor should round-trip composite functions."); + @"Setting iconColor to a camera-data expression should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionExpression, + @"iconColor should round-trip camera-data expressions."); layer.iconColor = nil; XCTAssertTrue(rawLayer->getIconColor().isUndefined(), @"Unsetting iconColor should return icon-color to the default value."); - XCTAssertEqualObjects(layer.iconColor, defaultStyleValue, + XCTAssertEqualObjects(layer.iconColor, defaultExpression, @"iconColor should return the default value after being unset."); // Transition property test layer.iconColorTransition = transitionTest; @@ -1649,40 +1818,44 @@ { XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(), @"icon-halo-blur should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloBlur; + NSExpression *defaultExpression = layer.iconHaloBlur; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconHaloBlur = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconHaloBlur = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, - @"Setting iconHaloBlur to a constant value should update icon-halo-blur."); - XCTAssertEqualObjects(layer.iconHaloBlur, constantStyleValue, - @"iconHaloBlur should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconHaloBlur = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconHaloBlur to a constant value expression should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, constantExpression, + @"iconHaloBlur should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconHaloBlur = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, - @"Setting iconHaloBlur to a camera function should update icon-halo-blur."); - XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, - @"iconHaloBlur should round-trip camera functions."); + @"Setting iconHaloBlur to a camera expression should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression, + @"iconHaloBlur should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconHaloBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconHaloBlur = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, - @"Setting iconHaloBlur to a source function should update icon-halo-blur."); - XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, - @"iconHaloBlur should round-trip source functions."); + @"Setting iconHaloBlur to a data expression should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression, + @"iconHaloBlur should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconHaloBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconHaloBlur = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1690,15 +1863,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, - @"Setting iconHaloBlur to a composite function should update icon-halo-blur."); - XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, - @"iconHaloBlur should round-trip composite functions."); + @"Setting iconHaloBlur to a camera-data expression should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression, + @"iconHaloBlur should round-trip camera-data expressions."); layer.iconHaloBlur = nil; XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(), @"Unsetting iconHaloBlur should return icon-halo-blur to the default value."); - XCTAssertEqualObjects(layer.iconHaloBlur, defaultStyleValue, + XCTAssertEqualObjects(layer.iconHaloBlur, defaultExpression, @"iconHaloBlur should return the default value after being unset."); // Transition property test layer.iconHaloBlurTransition = transitionTest; @@ -1715,40 +1888,44 @@ { XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(), @"icon-halo-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.iconHaloColor; + NSExpression *defaultExpression = layer.iconHaloColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.iconHaloColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.iconHaloColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, - @"Setting iconHaloColor to a constant value should update icon-halo-color."); - XCTAssertEqualObjects(layer.iconHaloColor, constantStyleValue, - @"iconHaloColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconHaloColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting iconHaloColor to a constant value expression should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, constantExpression, + @"iconHaloColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconHaloColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, - @"Setting iconHaloColor to a camera function should update icon-halo-color."); - XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, - @"iconHaloColor should round-trip camera functions."); + @"Setting iconHaloColor to a camera expression should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionExpression, + @"iconHaloColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconHaloColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconHaloColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, - @"Setting iconHaloColor to a source function should update icon-halo-color."); - XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, - @"iconHaloColor should round-trip source functions."); + @"Setting iconHaloColor to a data expression should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionExpression, + @"iconHaloColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconHaloColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconHaloColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1756,15 +1933,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, - @"Setting iconHaloColor to a composite function should update icon-halo-color."); - XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, - @"iconHaloColor should round-trip composite functions."); + @"Setting iconHaloColor to a camera-data expression should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionExpression, + @"iconHaloColor should round-trip camera-data expressions."); layer.iconHaloColor = nil; XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(), @"Unsetting iconHaloColor should return icon-halo-color to the default value."); - XCTAssertEqualObjects(layer.iconHaloColor, defaultStyleValue, + XCTAssertEqualObjects(layer.iconHaloColor, defaultExpression, @"iconHaloColor should return the default value after being unset."); // Transition property test layer.iconHaloColorTransition = transitionTest; @@ -1781,40 +1958,44 @@ { XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(), @"icon-halo-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconHaloWidth; + NSExpression *defaultExpression = layer.iconHaloWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconHaloWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconHaloWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, - @"Setting iconHaloWidth to a constant value should update icon-halo-width."); - XCTAssertEqualObjects(layer.iconHaloWidth, constantStyleValue, - @"iconHaloWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconHaloWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconHaloWidth to a constant value expression should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, constantExpression, + @"iconHaloWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconHaloWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, - @"Setting iconHaloWidth to a camera function should update icon-halo-width."); - XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, - @"iconHaloWidth should round-trip camera functions."); + @"Setting iconHaloWidth to a camera expression should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression, + @"iconHaloWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconHaloWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconHaloWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, - @"Setting iconHaloWidth to a source function should update icon-halo-width."); - XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, - @"iconHaloWidth should round-trip source functions."); + @"Setting iconHaloWidth to a data expression should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression, + @"iconHaloWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconHaloWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconHaloWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1822,15 +2003,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, - @"Setting iconHaloWidth to a composite function should update icon-halo-width."); - XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, - @"iconHaloWidth should round-trip composite functions."); + @"Setting iconHaloWidth to a camera-data expression should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression, + @"iconHaloWidth should round-trip camera-data expressions."); layer.iconHaloWidth = nil; XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(), @"Unsetting iconHaloWidth should return icon-halo-width to the default value."); - XCTAssertEqualObjects(layer.iconHaloWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.iconHaloWidth, defaultExpression, @"iconHaloWidth should return the default value after being unset."); // Transition property test layer.iconHaloWidthTransition = transitionTest; @@ -1847,40 +2028,44 @@ { XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(), @"icon-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.iconOpacity; + NSExpression *defaultExpression = layer.iconOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.iconOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.iconOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, - @"Setting iconOpacity to a constant value should update icon-opacity."); - XCTAssertEqualObjects(layer.iconOpacity, constantStyleValue, - @"iconOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting iconOpacity to a constant value expression should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, constantExpression, + @"iconOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, - @"Setting iconOpacity to a camera function should update icon-opacity."); - XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, - @"iconOpacity should round-trip camera functions."); + @"Setting iconOpacity to a camera expression should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionExpression, + @"iconOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.iconOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.iconOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, - @"Setting iconOpacity to a source function should update icon-opacity."); - XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, - @"iconOpacity should round-trip source functions."); + @"Setting iconOpacity to a data expression should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionExpression, + @"iconOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.iconOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.iconOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -1888,15 +2073,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, - @"Setting iconOpacity to a composite function should update icon-opacity."); - XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, - @"iconOpacity should round-trip composite functions."); + @"Setting iconOpacity to a camera-data expression should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionExpression, + @"iconOpacity should round-trip camera-data expressions."); layer.iconOpacity = nil; XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(), @"Unsetting iconOpacity should return icon-opacity to the default value."); - XCTAssertEqualObjects(layer.iconOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.iconOpacity, defaultExpression, @"iconOpacity should return the default value after being unset."); // Transition property test layer.iconOpacityTransition = transitionTest; @@ -1913,124 +2098,138 @@ { XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(), @"icon-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslation; + NSExpression *defaultExpression = layer.iconTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.iconTranslation = constantStyleValue; + layer.iconTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue, - @"Setting iconTranslation to a constant value should update icon-translate."); - XCTAssertEqualObjects(layer.iconTranslation, constantStyleValue, - @"iconTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting iconTranslation to a constant value expression should update icon-translate."); + XCTAssertEqualObjects(layer.iconTranslation, constantExpression, + @"iconTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue, - @"Setting iconTranslation to a camera function should update icon-translate."); - XCTAssertEqualObjects(layer.iconTranslation, functionStyleValue, - @"iconTranslation should round-trip camera functions."); + @"Setting iconTranslation to a camera expression should update icon-translate."); + XCTAssertEqualObjects(layer.iconTranslation, functionExpression, + @"iconTranslation should round-trip camera expressions."); layer.iconTranslation = nil; XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(), @"Unsetting iconTranslation should return icon-translate to the default value."); - XCTAssertEqualObjects(layer.iconTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.iconTranslation, defaultExpression, @"iconTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // icon-translate-anchor { XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(), @"icon-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconTranslationAnchor; + NSExpression *defaultExpression = layer.iconTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconTranslationAnchor:MGLIconTranslationAnchorViewport]]; - layer.iconTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.iconTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue, - @"Setting iconTranslationAnchor to a constant value should update icon-translate-anchor."); - XCTAssertEqualObjects(layer.iconTranslationAnchor, constantStyleValue, - @"iconTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.iconTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting iconTranslationAnchor to a constant value expression should update icon-translate-anchor."); + XCTAssertEqualObjects(layer.iconTranslationAnchor, constantExpression, + @"iconTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.iconTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue, - @"Setting iconTranslationAnchor to a camera function should update icon-translate-anchor."); - XCTAssertEqualObjects(layer.iconTranslationAnchor, functionStyleValue, - @"iconTranslationAnchor should round-trip camera functions."); + @"Setting iconTranslationAnchor to a camera expression should update icon-translate-anchor."); + XCTAssertEqualObjects(layer.iconTranslationAnchor, functionExpression, + @"iconTranslationAnchor should round-trip camera expressions."); layer.iconTranslationAnchor = nil; XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(), @"Unsetting iconTranslationAnchor should return icon-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.iconTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.iconTranslationAnchor, defaultExpression, @"iconTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.iconTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-color { XCTAssertTrue(rawLayer->getTextColor().isUndefined(), @"text-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textColor; + NSExpression *defaultExpression = layer.textColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.textColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.textColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, - @"Setting textColor to a constant value should update text-color."); - XCTAssertEqualObjects(layer.textColor, constantStyleValue, - @"textColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting textColor to a constant value expression should update text-color."); + XCTAssertEqualObjects(layer.textColor, constantExpression, + @"textColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, - @"Setting textColor to a camera function should update text-color."); - XCTAssertEqualObjects(layer.textColor, functionStyleValue, - @"textColor should round-trip camera functions."); + @"Setting textColor to a camera expression should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionExpression, + @"textColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, - @"Setting textColor to a source function should update text-color."); - XCTAssertEqualObjects(layer.textColor, functionStyleValue, - @"textColor should round-trip source functions."); + @"Setting textColor to a data expression should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionExpression, + @"textColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -2038,15 +2237,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, - @"Setting textColor to a composite function should update text-color."); - XCTAssertEqualObjects(layer.textColor, functionStyleValue, - @"textColor should round-trip composite functions."); + @"Setting textColor to a camera-data expression should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionExpression, + @"textColor should round-trip camera-data expressions."); layer.textColor = nil; XCTAssertTrue(rawLayer->getTextColor().isUndefined(), @"Unsetting textColor should return text-color to the default value."); - XCTAssertEqualObjects(layer.textColor, defaultStyleValue, + XCTAssertEqualObjects(layer.textColor, defaultExpression, @"textColor should return the default value after being unset."); // Transition property test layer.textColorTransition = transitionTest; @@ -2063,40 +2262,44 @@ { XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(), @"text-halo-blur should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloBlur; + NSExpression *defaultExpression = layer.textHaloBlur; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textHaloBlur = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textHaloBlur = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, - @"Setting textHaloBlur to a constant value should update text-halo-blur."); - XCTAssertEqualObjects(layer.textHaloBlur, constantStyleValue, - @"textHaloBlur should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textHaloBlur = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textHaloBlur to a constant value expression should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, constantExpression, + @"textHaloBlur should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textHaloBlur = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, - @"Setting textHaloBlur to a camera function should update text-halo-blur."); - XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, - @"textHaloBlur should round-trip camera functions."); + @"Setting textHaloBlur to a camera expression should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionExpression, + @"textHaloBlur should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textHaloBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textHaloBlur = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, - @"Setting textHaloBlur to a source function should update text-halo-blur."); - XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, - @"textHaloBlur should round-trip source functions."); + @"Setting textHaloBlur to a data expression should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionExpression, + @"textHaloBlur should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textHaloBlur = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textHaloBlur = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -2104,15 +2307,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, - @"Setting textHaloBlur to a composite function should update text-halo-blur."); - XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, - @"textHaloBlur should round-trip composite functions."); + @"Setting textHaloBlur to a camera-data expression should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionExpression, + @"textHaloBlur should round-trip camera-data expressions."); layer.textHaloBlur = nil; XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(), @"Unsetting textHaloBlur should return text-halo-blur to the default value."); - XCTAssertEqualObjects(layer.textHaloBlur, defaultStyleValue, + XCTAssertEqualObjects(layer.textHaloBlur, defaultExpression, @"textHaloBlur should return the default value after being unset."); // Transition property test layer.textHaloBlurTransition = transitionTest; @@ -2129,40 +2332,44 @@ { XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(), @"text-halo-color should be unset initially."); - MGLStyleValue<MGLColor *> *defaultStyleValue = layer.textHaloColor; + NSExpression *defaultExpression = layer.textHaloColor; - MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; - layer.textHaloColor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + layer.textHaloColor = constantExpression; mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, - @"Setting textHaloColor to a constant value should update text-halo-color."); - XCTAssertEqualObjects(layer.textHaloColor, constantStyleValue, - @"textHaloColor should round-trip constant values."); - - MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textHaloColor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + @"Setting textHaloColor to a constant value expression should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, constantExpression, + @"textHaloColor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textHaloColor = functionExpression; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{ + { -INFINITY, { 1, 0, 0, 1 } }, + { 18, { 1, 0, 0, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, - @"Setting textHaloColor to a camera function should update text-halo-color."); - XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, - @"textHaloColor should round-trip camera functions."); + @"Setting textHaloColor to a camera expression should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionExpression, + @"textHaloColor should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textHaloColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textHaloColor = functionExpression; mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, - @"Setting textHaloColor to a source function should update text-halo-color."); - XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, - @"textHaloColor should round-trip source functions."); + @"Setting textHaloColor to a data expression should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionExpression, + @"textHaloColor should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textHaloColor = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textHaloColor = functionExpression; std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -2170,15 +2377,15 @@ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, - @"Setting textHaloColor to a composite function should update text-halo-color."); - XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, - @"textHaloColor should round-trip composite functions."); + @"Setting textHaloColor to a camera-data expression should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionExpression, + @"textHaloColor should round-trip camera-data expressions."); layer.textHaloColor = nil; XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(), @"Unsetting textHaloColor should return text-halo-color to the default value."); - XCTAssertEqualObjects(layer.textHaloColor, defaultStyleValue, + XCTAssertEqualObjects(layer.textHaloColor, defaultExpression, @"textHaloColor should return the default value after being unset."); // Transition property test layer.textHaloColorTransition = transitionTest; @@ -2195,40 +2402,44 @@ { XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(), @"text-halo-width should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textHaloWidth; + NSExpression *defaultExpression = layer.textHaloWidth; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textHaloWidth = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textHaloWidth = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, - @"Setting textHaloWidth to a constant value should update text-halo-width."); - XCTAssertEqualObjects(layer.textHaloWidth, constantStyleValue, - @"textHaloWidth should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textHaloWidth = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textHaloWidth to a constant value expression should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, constantExpression, + @"textHaloWidth should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textHaloWidth = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, - @"Setting textHaloWidth to a camera function should update text-halo-width."); - XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, - @"textHaloWidth should round-trip camera functions."); + @"Setting textHaloWidth to a camera expression should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionExpression, + @"textHaloWidth should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textHaloWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textHaloWidth = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, - @"Setting textHaloWidth to a source function should update text-halo-width."); - XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, - @"textHaloWidth should round-trip source functions."); + @"Setting textHaloWidth to a data expression should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionExpression, + @"textHaloWidth should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textHaloWidth = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textHaloWidth = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -2236,15 +2447,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, - @"Setting textHaloWidth to a composite function should update text-halo-width."); - XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, - @"textHaloWidth should round-trip composite functions."); + @"Setting textHaloWidth to a camera-data expression should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionExpression, + @"textHaloWidth should round-trip camera-data expressions."); layer.textHaloWidth = nil; XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(), @"Unsetting textHaloWidth should return text-halo-width to the default value."); - XCTAssertEqualObjects(layer.textHaloWidth, defaultStyleValue, + XCTAssertEqualObjects(layer.textHaloWidth, defaultExpression, @"textHaloWidth should return the default value after being unset."); // Transition property test layer.textHaloWidthTransition = transitionTest; @@ -2261,40 +2472,44 @@ { XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(), @"text-opacity should be unset initially."); - MGLStyleValue<NSNumber *> *defaultStyleValue = layer.textOpacity; + NSExpression *defaultExpression = layer.textOpacity; - MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; - layer.textOpacity = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + layer.textOpacity = constantExpression; mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, - @"Setting textOpacity to a constant value should update text-opacity."); - XCTAssertEqualObjects(layer.textOpacity, constantStyleValue, - @"textOpacity should round-trip constant values."); - - MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textOpacity = functionStyleValue; - - mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + @"Setting textOpacity to a constant value expression should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, constantExpression, + @"textOpacity should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"0xff"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textOpacity = functionExpression; + + mbgl::style::IntervalStops<float> intervalStops = {{ + { -INFINITY, 0xff }, + { 18, 0xff }, + }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, - @"Setting textOpacity to a camera function should update text-opacity."); - XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, - @"textOpacity should round-trip camera functions."); + @"Setting textOpacity to a camera expression should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionExpression, + @"textOpacity should round-trip camera expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; - layer.textOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}]; + layer.textOpacity = functionExpression; mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, - @"Setting textOpacity to a source function should update text-opacity."); - XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, - @"textOpacity should round-trip source functions."); + @"Setting textOpacity to a data expression should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionExpression, + @"textOpacity should round-trip data expressions."); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; - layer.textOpacity = functionStyleValue; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + layer.textOpacity = functionExpression; std::map<float, float> innerStops { {18, 0xff} }; mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; @@ -2302,15 +2517,15 @@ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, - @"Setting textOpacity to a composite function should update text-opacity."); - XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, - @"textOpacity should round-trip composite functions."); + @"Setting textOpacity to a camera-data expression should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionExpression, + @"textOpacity should round-trip camera-data expressions."); layer.textOpacity = nil; XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(), @"Unsetting textOpacity should return text-opacity to the default value."); - XCTAssertEqualObjects(layer.textOpacity, defaultStyleValue, + XCTAssertEqualObjects(layer.textOpacity, defaultExpression, @"textOpacity should return the default value after being unset."); // Transition property test layer.textOpacityTransition = transitionTest; @@ -2327,84 +2542,94 @@ { XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(), @"text-translate should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslation; + NSExpression *defaultExpression = layer.textTranslation; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"%@", #if TARGET_OS_IPHONE [NSValue valueWithCGVector:CGVectorMake(1, 1)] #else [NSValue valueWithMGLVector:CGVectorMake(1, -1)] #endif ]; - layer.textTranslation = constantStyleValue; + layer.textTranslation = constantExpression; mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue, - @"Setting textTranslation to a constant value should update text-translate."); - XCTAssertEqualObjects(layer.textTranslation, constantStyleValue, - @"textTranslation should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textTranslation = functionStyleValue; - - mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + @"Setting textTranslation to a constant value expression should update text-translate."); + XCTAssertEqualObjects(layer.textTranslation, constantExpression, + @"textTranslation should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textTranslation = functionExpression; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{ + { -INFINITY, { 1, 1 } }, + { 18, { 1, 1 } }, + }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue, - @"Setting textTranslation to a camera function should update text-translate."); - XCTAssertEqualObjects(layer.textTranslation, functionStyleValue, - @"textTranslation should round-trip camera functions."); + @"Setting textTranslation to a camera expression should update text-translate."); + XCTAssertEqualObjects(layer.textTranslation, functionExpression, + @"textTranslation should round-trip camera expressions."); layer.textTranslation = nil; XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(), @"Unsetting textTranslation should return text-translate to the default value."); - XCTAssertEqualObjects(layer.textTranslation, defaultStyleValue, + XCTAssertEqualObjects(layer.textTranslation, defaultExpression, @"textTranslation should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textTranslation = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } // text-translate-anchor { XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(), @"text-translate-anchor should be unset initially."); - MGLStyleValue<NSValue *> *defaultStyleValue = layer.textTranslationAnchor; + NSExpression *defaultExpression = layer.textTranslationAnchor; - MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextTranslationAnchor:MGLTextTranslationAnchorViewport]]; - layer.textTranslationAnchor = constantStyleValue; + NSExpression *constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + layer.textTranslationAnchor = constantExpression; mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue, - @"Setting textTranslationAnchor to a constant value should update text-translate-anchor."); - XCTAssertEqualObjects(layer.textTranslationAnchor, constantStyleValue, - @"textTranslationAnchor should round-trip constant values."); - - MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; - layer.textTranslationAnchor = functionStyleValue; - - mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + @"Setting textTranslationAnchor to a constant value expression should update text-translate-anchor."); + XCTAssertEqualObjects(layer.textTranslationAnchor, constantExpression, + @"textTranslationAnchor should round-trip constant value expressions."); + + constantExpression = [NSExpression expressionWithFormat:@"'viewport'"]; + NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + layer.textTranslationAnchor = functionExpression; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{ + { -INFINITY, mbgl::style::TranslateAnchorType::Viewport }, + { 18, mbgl::style::TranslateAnchorType::Viewport }, + }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue, - @"Setting textTranslationAnchor to a camera function should update text-translate-anchor."); - XCTAssertEqualObjects(layer.textTranslationAnchor, functionStyleValue, - @"textTranslationAnchor should round-trip camera functions."); + @"Setting textTranslationAnchor to a camera expression should update text-translate-anchor."); + XCTAssertEqualObjects(layer.textTranslationAnchor, functionExpression, + @"textTranslationAnchor should round-trip camera expressions."); layer.textTranslationAnchor = nil; XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(), @"Unsetting textTranslationAnchor should return text-translate-anchor to the default value."); - XCTAssertEqualObjects(layer.textTranslationAnchor, defaultStyleValue, + XCTAssertEqualObjects(layer.textTranslationAnchor, defaultExpression, @"textTranslationAnchor should return the default value after being unset."); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; + XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}]; + functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}]; + XCTAssertThrowsSpecificNamed(layer.textTranslationAnchor = functionExpression, NSException, NSInvalidArgumentException, @"MGLSymbolLayer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); } } diff --git a/platform/darwin/test/test-Bridging-Header.h b/platform/darwin/test/test-Bridging-Header.h index 5d23e9d6c5..1b2cb5d6d0 100644 --- a/platform/darwin/test/test-Bridging-Header.h +++ b/platform/darwin/test/test-Bridging-Header.h @@ -1,4 +1,4 @@ // // Use this file to import your target's public headers that you would like to expose to Swift. // -#import "MGLStyleValueTests.h" + diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 1bd8bb1cf0..0301a78efc 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -10,6 +10,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles and rendering +* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added `MGLComputedShapeSource` source class that allows applications to supply vector data on a per-tile basis. * Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 4306354030..0f617188b9 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -897,8 +897,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { MGLFillExtrusionStyleLayer* layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"extrudedBuildings" source:source]; layer.sourceLayerIdentifier = @"building"; layer.predicate = [NSPredicate predicateWithFormat:@"extrude == 'true' AND height > 0"]; - layer.fillExtrusionBase = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"min_height" options:nil]; - layer.fillExtrusionHeight = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"height" options:nil]; + layer.fillExtrusionBase = [NSExpression expressionForKeyPath:@"min_height"]; + layer.fillExtrusionHeight = [NSExpression expressionForKeyPath:@"height"]; // Set the fill color to that of the existing building footprint layer, if it exists. MGLFillStyleLayer* buildingLayer = (MGLFillStyleLayer*)[self.mapView.style layerWithIdentifier:@"building"]; @@ -906,10 +906,10 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { if (buildingLayer.fillColor) { layer.fillExtrusionColor = buildingLayer.fillColor; } else { - layer.fillExtrusionColor = [MGLStyleValue valueWithRawValue:[UIColor whiteColor]]; + layer.fillExtrusionColor = [NSExpression expressionForConstantValue:[UIColor whiteColor]]; } - layer.fillExtrusionOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.75]; + layer.fillExtrusionOpacity = [NSExpression expressionForConstantValue:@0.75]; } MGLStyleLayer* labelLayer = [self.mapView.style layerWithIdentifier:@"waterway-label"]; @@ -924,48 +924,45 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - (void)styleWaterLayer { MGLFillStyleLayer *waterLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"water"]; - NSDictionary *waterColorStops = @{@6.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor yellowColor]], - @8.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]], - @10.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]], - @12.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]], - @14.0f: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]]}; - MGLStyleValue *waterColorFunction = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:waterColorStops - options: nil]; - waterLayer.fillColor = waterColorFunction; - - NSDictionary *fillAntialiasedStops = @{@11: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES], - @12: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO], - @13: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES], - @14: [MGLStyleValue<NSNumber *> valueWithRawValue:@NO], - @15: [MGLStyleValue<NSNumber *> valueWithRawValue:@YES]}; - MGLStyleValue *fillAntialiasedFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval - cameraStops:fillAntialiasedStops - options:nil]; - waterLayer.fillAntialiased = fillAntialiasedFunction; + NSDictionary *waterColorStops = @{@6.0f: [UIColor yellowColor], + @8.0f: [UIColor blueColor], + @10.0f: [UIColor redColor], + @12.0f: [UIColor greenColor], + @14.0f: [UIColor blueColor]}; + waterLayer.fillColor = [NSExpression expressionWithFormat: + @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + waterColorStops]; + + NSDictionary *fillAntialiasedStops = @{@11: @YES, + @12: @NO, + @13: @YES, + @14: @NO, + @15: @YES}; + waterLayer.fillAntialiased = [NSExpression expressionWithFormat: + @"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', false, %@)", + fillAntialiasedStops]; } - (void)styleRoadLayer { MGLLineStyleLayer *roadLayer = (MGLLineStyleLayer *)[self.mapView.style layerWithIdentifier:@"road-primary"]; - roadLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blackColor]]; - - NSDictionary *lineWidthStops = @{@5: [MGLStyleValue<NSNumber *> valueWithRawValue:@5], - @10: [MGLStyleValue<NSNumber *> valueWithRawValue:@15], - @15: [MGLStyleValue<NSNumber *> valueWithRawValue:@30]}; - MGLStyleValue *lineWidthFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:lineWidthStops - options:nil]; - roadLayer.lineWidth = lineWidthFunction; - roadLayer.lineGapWidth = lineWidthFunction; - - NSDictionary *roadLineColorStops = @{@10: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]], - @13: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor yellowColor]], - @16: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor cyanColor]]}; - MGLStyleValue *roadLineColor = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:roadLineColorStops - options: nil]; - roadLayer.lineColor = roadLineColor; + roadLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor blackColor]]; + + NSDictionary *lineWidthStops = @{@5: @5, + @10: @15, + @15: @30}; + NSExpression *lineWidthExpression = [NSExpression expressionWithFormat: + @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + lineWidthStops]; + roadLayer.lineWidth = lineWidthExpression; + roadLayer.lineGapWidth = lineWidthExpression; + + NSDictionary *roadLineColorStops = @{@10: [UIColor purpleColor], + @13: [UIColor yellowColor], + @16: [UIColor cyanColor]}; + roadLayer.lineColor = [NSExpression expressionWithFormat: + @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + roadLineColorStops]; roadLayer.visible = YES; roadLayer.maximumZoomLevel = 15; @@ -979,11 +976,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:rasterSource]; MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"my-raster-layer" source:rasterSource]; - MGLStyleValue *opacityFunction = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential - cameraStops:@{@20.0f: [MGLStyleValue<NSNumber *> valueWithRawValue:@1.0f], - @5.0f: [MGLStyleValue<NSNumber *> valueWithRawValue:@0.0f]} - options:nil]; - rasterLayer.rasterOpacity = opacityFunction; + NSDictionary *opacityStops = @{@20.0f: @1.0f, + @5.0f: @0.0f}; + rasterLayer.rasterOpacity = [NSExpression expressionWithFormat: + @"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + opacityStops]; [self.mapView.style addLayer:rasterLayer]; } @@ -995,7 +992,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:source]; MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"test" source:source]; - fillLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]]; + fillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor purpleColor]]; [self.mapView.style addLayer:fillLayer]; } @@ -1003,7 +1000,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { - (void)styleSymbolLayer { MGLSymbolStyleLayer *stateLayer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:@"state-label-lg"]; - stateLayer.textColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; + stateLayer.textColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; } - (void)styleBuildingLayer @@ -1011,13 +1008,13 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { MGLTransition transition = { 5, 1 }; self.mapView.style.transition = transition; MGLFillStyleLayer *buildingLayer = (MGLFillStyleLayer *)[self.mapView.style layerWithIdentifier:@"building"]; - buildingLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor purpleColor]]; + buildingLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor purpleColor]]; } - (void)styleFerryLayer { MGLLineStyleLayer *ferryLineLayer = (MGLLineStyleLayer *)[self.mapView.style layerWithIdentifier:@"ferry"]; - ferryLineLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; + ferryLineLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; } - (void)removeParkLayer @@ -1041,8 +1038,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { statesLayer.predicate = [NSPredicate predicateWithFormat:@"name == 'Texas'"]; // paint properties - statesLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; - statesLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.25]; + statesLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; + statesLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.25]; }); } @@ -1061,9 +1058,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { countiesLayer.predicate = [NSPredicate predicateWithFormat:@"NAME10 == 'Washington'"]; // paint properties - countiesLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; - countiesLayer.lineOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.75]; - countiesLayer.lineWidth = [MGLStyleValue<NSNumber *> valueWithRawValue:@5]; + countiesLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; + countiesLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.75]; + countiesLayer.lineWidth = [NSExpression expressionForConstantValue:@5]; }); } @@ -1082,8 +1079,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { regionsLayer.predicate = [NSPredicate predicateWithFormat:@"HRRNUM >= %@ AND HRRNUM < 300", @(200)]; // paint properties - regionsLayer.fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]]; - regionsLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithRawValue:@0.5]; + regionsLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor blueColor]]; + regionsLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; }); } @@ -1113,8 +1110,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:source]; MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:queryLayerID source:source]; - fillLayer.fillColor = [MGLConstantStyleValue<UIColor *> valueWithRawValue:[UIColor blueColor]]; - fillLayer.fillOpacity = [MGLConstantStyleValue<NSNumber *> valueWithRawValue:@0.5]; + fillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor blueColor]]; + fillLayer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; [self.mapView.style addLayer:fillLayer]; }); } @@ -1167,8 +1164,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"leaf-fill-layer" source:source]; layer.predicate = [NSPredicate predicateWithFormat:@"color = 'red'"]; - MGLStyleValue *fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; - layer.fillColor = fillColor; + layer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; [self.mapView.style addLayer:layer]; NSString *geoJSON = @"{\"type\": \"Feature\", \"properties\": {\"color\": \"green\"}, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ -114.06847000122069, 51.050459433092655 ] }}"; @@ -1179,7 +1175,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:pointSource]; MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"leaf-circle-layer" source:pointSource]; - circleLayer.circleColor = [MGLStyleValue valueWithRawValue:[UIColor greenColor]]; + circleLayer.circleColor = [NSExpression expressionForConstantValue:[UIColor greenColor]]; circleLayer.predicate = [NSPredicate predicateWithFormat:@"color = 'green'"]; [self.mapView.style addLayer:circleLayer]; @@ -1196,7 +1192,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:plainShapeSource]; MGLFillStyleLayer *plainFillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"leaf-plain-fill-layer" source:plainShapeSource]; - plainFillLayer.fillColor = [MGLStyleValue valueWithRawValue:[UIColor yellowColor]]; + plainFillLayer.fillColor = [NSExpression expressionForConstantValue:[UIColor yellowColor]]; [self.mapView.style addLayer:plainFillLayer]; } @@ -1273,8 +1269,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:source]; MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"mutable-data-layer-features-id" source:source]; - MGLStyleValue *fillColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]; - layer.fillColor = fillColor; + layer.fillColor = [NSExpression expressionForConstantValue:[UIColor redColor]]; [self.mapView.style addLayer:layer]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @@ -1306,16 +1301,14 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:vectorSource]; MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"style-vector-background-layer-id"]; - backgroundLayer.backgroundColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor blackColor]]; + backgroundLayer.backgroundColor = [NSExpression expressionForConstantValue:[UIColor blackColor]]; [self.mapView.style addLayer:backgroundLayer]; MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-vector-line-layer-id" source:vectorSource]; lineLayer.sourceLayerIdentifier = @"contour"; - NSUInteger lineJoinValue = MGLLineJoinRound; - lineLayer.lineJoin = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue value:&lineJoinValue withObjCType:@encode(MGLLineJoin)]]; - NSUInteger lineCapValue = MGLLineCapRound; - lineLayer.lineCap = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue value:&lineCapValue withObjCType:@encode(MGLLineCap)]]; - lineLayer.lineColor = [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]]; + lineLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"]; + lineLayer.lineCap = [NSExpression expressionForConstantValue:@"round"]; + lineLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor greenColor]]; [self.mapView.style addLayer:lineLayer]; } @@ -1393,19 +1386,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addSource:routeSource]; MGLLineStyleLayer *baseRouteLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-base-route-layer" source:routeSource]; - baseRouteLayer.lineColor = [MGLConstantStyleValue valueWithRawValue:[UIColor orangeColor]]; - baseRouteLayer.lineWidth = [MGLConstantStyleValue valueWithRawValue:@20]; - baseRouteLayer.lineOpacity = [MGLConstantStyleValue valueWithRawValue:@0.5]; - baseRouteLayer.lineCap = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapRound]]; - baseRouteLayer.lineJoin = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinRound]]; + baseRouteLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor orangeColor]]; + baseRouteLayer.lineWidth = [NSExpression expressionForConstantValue:@20]; + baseRouteLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.5]; + baseRouteLayer.lineCap = [NSExpression expressionForConstantValue:@"round"]; + baseRouteLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"]; [self.mapView.style addLayer:baseRouteLayer]; MGLLineStyleLayer *routeLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-route-layer" source:routeSource]; - routeLayer.lineColor = [MGLConstantStyleValue valueWithRawValue:[UIColor whiteColor]]; - routeLayer.lineWidth = [MGLConstantStyleValue valueWithRawValue:@15]; - routeLayer.lineOpacity = [MGLConstantStyleValue valueWithRawValue:@0.8]; - routeLayer.lineCap = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineCap:MGLLineCapRound]]; - routeLayer.lineJoin = [MGLConstantStyleValue valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinRound]]; + routeLayer.lineColor = [NSExpression expressionForConstantValue:[UIColor whiteColor]]; + routeLayer.lineWidth = [NSExpression expressionForConstantValue:@15]; + routeLayer.lineOpacity = [NSExpression expressionForConstantValue:@0.8]; + routeLayer.lineCap = [NSExpression expressionForConstantValue:@"round"]; + routeLayer.lineJoin = [NSExpression expressionForConstantValue:@"round"]; [self.mapView.style addLayer:routeLayer]; } @@ -1435,19 +1428,10 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { // source, categorical function that sets any feature with a "fill" attribute value of true to red color and anything without to green MGLFillStyleLayer *fillStyleLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:shapeSource]; - NSDictionary *stops = @{@(YES): [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor greenColor]]}; - NSDictionary *fillColorOptions = @{MGLStyleFunctionOptionDefaultValue: [MGLStyleValue<UIColor *> valueWithRawValue:[UIColor redColor]]}; - fillStyleLayer.fillColor = [MGLStyleValue<UIColor *> valueWithInterpolationMode:MGLInterpolationModeCategorical - sourceStops:stops - attributeName:@"fill" - options:fillColorOptions]; + fillStyleLayer.fillColor = [NSExpression expressionWithFormat:@"TERNARY(fill, %@, %@)", [UIColor greenColor], [UIColor redColor]]; // source, identity function that sets any feature with an "opacity" attribute to use that value and anything without to 1.0 - NSDictionary *fillOpacityOptions = @{MGLStyleFunctionOptionDefaultValue: [MGLStyleValue<NSNumber *> valueWithRawValue:@(1.0)]}; - fillStyleLayer.fillOpacity = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity - sourceStops:nil - attributeName:@"opacity" - options:fillOpacityOptions]; + fillStyleLayer.fillOpacity = [NSExpression expressionWithFormat:@"TERNARY(opacity != nil, opacity, 1.0)"]; [self.mapView.style addLayer:fillStyleLayer]; } @@ -1462,43 +1446,10 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:lineLayer]; MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"latlonlabels" source:source]; - labelLayer.text = [MGLStyleValue valueWithRawValue:@"{value}"]; + labelLayer.text = [NSExpression expressionForKeyPath:@"value"]; [self.mapView.style addLayer:labelLayer]; } -- (void)styleLabelLanguageForLayersNamed:(NSArray<NSString *> *)layers -{ - _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels; - NSString *bestLanguageForUser = [NSString stringWithFormat:@"{name_%@}", [self bestLanguageForUser]]; - NSString *language = _usingLocaleBasedCountryLabels ? bestLanguageForUser : @"{name}"; - - for (NSString *layerName in layers) { - MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName]; - - if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) { - if ([layer.text isKindOfClass:[MGLStyleConstantValue class]]) { - MGLStyleConstantValue *label = (MGLStyleConstantValue<NSString *> *)layer.text; - if ([label.rawValue hasPrefix:@"{name"]) { - layer.text = [MGLStyleValue valueWithRawValue:language]; - } - } - else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) { - MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text; - NSMutableDictionary *stops = function.stops.mutableCopy; - [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue<NSString *> *stop, BOOL *done) { - if ([stop.rawValue hasPrefix:@"{name"]) { - stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:language]; - } - }]; - function.stops = stops; - layer.text = function; - } - } else { - NSLog(@"%@ is not a symbol style layer", layerName); - } - } -} - - (NSString *)bestLanguageForUser { // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 00dba95419..51cd87a766 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -270,12 +270,16 @@ In style JSON | In Objective-C | In Swift ## Setting attribute values Each property representing a layout or paint attribute is set to an -`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for style functions). The -style value object is a container for the raw value or function parameters that -you want the attribute to be set to. +`NSExpression` object. `NSExpression` objects play the same role as +[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions), +but you create the former using a very different syntax. `NSExpression`’s format +string syntax is reminiscent of a spreadsheet formula or an expression in a +database query. See the +“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +overview of the expression support in this SDK. This SDK no longer supports +style functions; use expressions instead. -### Constant style values +### Constant values in expressions In contrast to the JSON type that the style specification defines for each layout or paint property, the style value object often contains a more specific @@ -286,10 +290,10 @@ or set. In style JSON | In Objective-C | In Swift --------------|-----------------------|--------- Color | `UIColor` | `UIColor` -Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`) +Enum | `NSString` | `String` String | `NSString` | `String` -Boolean | `NSNumber.boolValue` | `Bool` -Number | `NSNumber.floatValue` | `Float` +Boolean | `NSNumber.boolValue` | `NSNumber.boolValue` +Number | `NSNumber.floatValue` | `NSNumber.floatValue` Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]` Array (`-font`) | `NSArray<NSString>` | `[String]` Array (`-offset`, `-translate`) | `NSValue.CGVectorValue` | `NSValue.cgVectorValue` @@ -301,38 +305,73 @@ in Swift are specified in counterclockwise order, in contrast to the clockwise order defined by the style specification. -### Style functions - -A _style function_ allows you to vary the value of a layout or paint attribute -based on the zoom level, data provided by content sources, or both. For more -information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. - -Each kind of style function is represented by a distinct class, but you -typically create style functions as you create any other style value, using -class methods on `MGLStyleValue`: - -In style specification | SDK class | SDK factory method ----------------------------|-----------------------------|------------------- -zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` -property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` -zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` - -The documentation for each individual style layer property indicates the kinds -of style functions that are enabled for that property. - -When you create a style function, you specify an _interpolation mode_ and a -series of _stops_. Each stop determines the effective value displayed at a -particular zoom level (for camera functions) or the effective value on features -with a particular attribute value in the content source (for source functions). -The interpolation mode tells the SDK how to calculate the effective value -between any two stops: - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` +### Expression operators + +In style specification | Method, function, or predicate type | Format string syntax +-----------------------|-------------------------------------|--------------------- +`array` | | +`boolean` | | +`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary` +`number` | | +`string` | | +`to-boolean` | `boolValue` | +`to-color` | | +`to-number` | `mgl_numberWithFallbackValues:` | +`to-string` | `stringValue` | +`typeof` | | +`geometry-type` | | +`id` | | +`properties` | | +`at` | | +`get` | `+[NSExpression expressionForKeyPath:]` | Key path +`has` | | +`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})` +`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)` +`!=` | `NSNotEqualToPredicateOperatorType` | `key != value` +`<` | `NSLessThanPredicateOperatorType` | `key < value` +`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value` +`==` | `NSEqualToPredicateOperatorType` | `key == value` +`>` | `NSGreaterThanPredicateOperatorType` | `key > value` +`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value` +`all` | `NSAndPredicateType` | `p0 AND … AND pn` +`any` | `NSOrPredicateType` | `p0 OR … OR pn` +`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)` +`coalesce` | | +`match` | | +`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` | +`step` | `mgl_stepWithMinimum:stops:` | +`let` | `mgl_expressionWithContext:` | +`var` | `+[NSExpression expressionForVariable:]` | `$variable` +`concat` | `stringByAppendingString:` | +`downcase` | `lowercase:` | `lowercase('DOWNTOWN')` +`upcase` | `uppercase:` | `uppercase('Elysian Fields')` + +`rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | +`rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | +`to-rgba` | | +`-` | `from:subtract:` | `2 - 1` +`*` | `multiply:by:` | `1 * 2` +`/` | `divide:by:` | `1 / 2` +`%` | `modulus:by:` | +`^` | `raise:toPower:` | `2 ** 2` +`+` | `add:to:` | `1 + 2` +`acos` | | +`asin` | | +`atan` | | +`cos` | | +`e` | | `%@` representing `NSNumber` containing `M_E` +`ln` | `ln:` | `ln(2)` +`ln2` | | `%@` representing `NSNumber` containing `M_LN2` +`log10` | `log:` | `log(1)` +`log2` | | +`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` +`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` +`pi` | | `%@` representing `NSNumber` containing `M_PI` +`sin` | | +`sqrt` | `sqrt:` | `sqrt(2)` +`tan` | | +`zoom` | | `$zoom` +`heatmap-density` | | `$heatmapDensity` ## Filtering sources @@ -357,5 +396,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the `MGLVectorStyleLayer.predicate` documentation for a full description of -the supported operators and operand types. +See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +a full description of the supported operators and operand types. diff --git a/platform/ios/docs/guides/Using Style Functions at Runtime.md b/platform/ios/docs/guides/Using Style Functions at Runtime.md index c1fcaa00e9..0b4e842e0e 100644 --- a/platform/ios/docs/guides/Using Style Functions at Runtime.md +++ b/platform/ios/docs/guides/Using Style Functions at Runtime.md @@ -27,15 +27,15 @@ The documentation for each individual style layer property notes which style fun ## Stops -Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. +Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys. ```swift -let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] ``` @@ -57,20 +57,18 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil) mapView.style?.addSource(source) -let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] let layer = MGLCircleStyleLayer(identifier: "circles", source: source) -layer.circleColor = MGLStyleValue(interpolationMode: .exponential, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)]) -layer.circleRadius = MGLStyleValue(rawValue: 10) +layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + stops) +layer.circleRadius = NSExpression(forConstantValue: 10) mapView.style?.insertLayer(layer, below: symbolLayer) ``` @@ -89,14 +87,13 @@ The example below increases a layer’s `circleRadius` exponentially based on a ```swift let stops = [ - 12: MGLStyleValue<NSNumber>(rawValue: 0.5), - 14: MGLStyleValue(rawValue: 2), - 18: MGLStyleValue(rawValue: 18), + 12: 0.5, + 14: 2, + 18: 18, ] -layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, - cameraStops: stops, - options: [.interpolationBase: 1.5]) +layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)", + stops) ``` ### Interval @@ -106,18 +103,16 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on. ```swift -let stops = [ - 0: MGLStyleValue<UIColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: UIColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] -layer.circleColor = MGLStyleValue(interpolationMode: .interval, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)]) +layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)", + UIColor.green, stops) ``` ![interval mode](img/data-driven-styling/interval.png) @@ -129,16 +124,16 @@ At each stop, `MGLInterpolationModeCategorical` produces an output value equal t There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories. ```swift -let categoricalStops = [ - "earthquake": MGLStyleValue<UIColor>(rawValue: .orange), - "explosion": MGLStyleValue(rawValue: .red), - "quarry blast": MGLStyleValue(rawValue: .yellow), +let colors: [String: UIColor] = [ + "earthquake": .orange, + "explosion": .red, + "quarry blast": .yellow, ] +let defaultColor = UIColor.blue -layer.circleColor = MGLStyleValue(interpolationMode: .categorical, - sourceStops: categoricalStops, - attributeName: "type", - options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)]) +layer.circleColor = NSExpression( + format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)", + colors, colors, defaultColor) ``` ![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png) @@ -148,10 +143,7 @@ layer.circleColor = MGLStyleValue(interpolationMode: .categorical, `MGLInterpolationModeIdentity` uses the attribute’s value as the style value. For example, you can set the `circleRadius` to the earthquake’s magnitude. Since the attribute value itself will be used as the style value, `sourceStops` should be set to `nil`. ```swift -layer.circleRadius = MGLStyleValue(interpolationMode: .identity, - sourceStops: nil, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)]) +layer.circleRadius = NSExpression(forKeyPath: "mag") ``` ![identity mode](img/data-driven-styling/identity.png) diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index d655708a26..f9ac67a203 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; }; 0778DD431F67556700A73B34 /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; }; - 16376B491FFEED010000563E /* MGLMapViewLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */; }; 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; }; 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; }; 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */; }; @@ -27,6 +26,7 @@ 16376B3E1FFDB4B40000563E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 16376B3C1FFDB4B40000563E /* LaunchScreen.storyboard */; }; 16376B411FFDB4B40000563E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B401FFDB4B40000563E /* main.m */; }; 16376B471FFDB92B0000563E /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; }; + 16376B491FFEED010000563E /* MGLMapViewLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */; }; 165D0CE720005419009A3C66 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; 1753ED421E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */; }; @@ -54,7 +54,7 @@ 3510FFEB1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFE81D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h */; }; 3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; - 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; }; + 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; }; 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; @@ -132,7 +132,6 @@ 357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; 3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */; }; - 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */; }; 359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; }; 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; @@ -315,7 +314,6 @@ DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC99A1CB6E064006E619F /* MBXViewController.m */; }; DA1DC99F1CB6E088006E619F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99E1CB6E088006E619F /* Assets.xcassets */; }; DA1F8F3D1EBD287B00367E42 /* MGLDocumentationGuideTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */; }; - DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */; }; DA25D5C01CCD9F8400607828 /* Root.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5BF1CCD9F8400607828 /* Root.plist */; }; DA25D5C61CCDA06800607828 /* Root.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5C41CCDA06800607828 /* Root.strings */; }; DA25D5CD1CCDA11500607828 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5B91CCD9EDE00607828 /* Settings.bundle */; }; @@ -507,6 +505,8 @@ DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DAC25FCC200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */; }; + DAC25FCD200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */; }; DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; }; DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; }; DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165691CF41981001FF4B9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -641,7 +641,6 @@ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; }; 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; }; 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLComputedShapeSourceTests.m; path = ../../darwin/test/MGLComputedShapeSourceTests.m; sourceTree = "<group>"; }; - 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewLayoutTests.m; sourceTree = "<group>"; }; 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; }; 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; }; 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; }; @@ -655,6 +654,7 @@ 16376B3D1FFDB4B40000563E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 16376B3F1FFDB4B40000563E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 16376B401FFDB4B40000563E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + 16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewLayoutTests.m; sourceTree = "<group>"; }; 1753ED411E53CE6F00A9FD90 /* MGLConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLConversion.h; sourceTree = "<group>"; }; 1F0666881EC64F8E001C16D7 /* MGLLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight.h; sourceTree = "<group>"; }; 1F0666891EC64F8E001C16D7 /* MGLLight.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLight.mm; sourceTree = "<group>"; }; @@ -716,11 +716,9 @@ 357579861D502AFE000B822E /* MGLLineStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLineStyleLayerTests.mm; path = ../../darwin/test/MGLLineStyleLayerTests.mm; sourceTree = "<group>"; }; 357579881D502B06000B822E /* MGLCircleStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLCircleStyleLayerTests.mm; path = ../../darwin/test/MGLCircleStyleLayerTests.mm; sourceTree = "<group>"; }; 3575798A1D502B0C000B822E /* MGLBackgroundStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLBackgroundStyleLayerTests.mm; path = ../../darwin/test/MGLBackgroundStyleLayerTests.mm; sourceTree = "<group>"; }; - 357F09091DF84F3800941873 /* MGLStyleValueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLStyleValueTests.h; path = ../../darwin/test/MGLStyleValueTests.h; sourceTree = "<group>"; }; 357FE2DB1E02D2B20068B753 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCoder+MGLAdditions.h"; path = "../../darwin/src/NSCoder+MGLAdditions.h"; sourceTree = "<group>"; }; 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "NSCoder+MGLAdditions.mm"; path = "../../darwin/src/NSCoder+MGLAdditions.mm"; sourceTree = "<group>"; }; 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLDistanceFormatterTests.m; path = ../../darwin/test/MGLDistanceFormatterTests.m; sourceTree = "<group>"; }; - 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLStyleValueTests.m; path = ../../darwin/test/MGLStyleValueTests.m; sourceTree = "<group>"; }; 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView_Private.h; sourceTree = "<group>"; }; 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLAdditions.h"; sourceTree = "<group>"; }; 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSPredicate+MGLAdditions.mm"; sourceTree = "<group>"; }; @@ -845,7 +843,6 @@ DA1DC99A1CB6E064006E619F /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = "<group>"; }; DA1DC99E1CB6E088006E619F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationGuideTests.swift; path = ../../darwin/test/MGLDocumentationGuideTests.swift; sourceTree = "<group>"; }; - DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLStyleValueTests.swift; path = ../../darwin/test/MGLStyleValueTests.swift; sourceTree = "<group>"; }; DA25D5B91CCD9EDE00607828 /* Settings.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Settings.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; DA25D5BF1CCD9F8400607828 /* Root.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Root.plist; sourceTree = "<group>"; }; DA25D5C51CCDA06800607828 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Root.strings; sourceTree = "<group>"; }; @@ -1047,6 +1044,7 @@ DABCABBB1CB80692000A7C39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; DABCABBF1CB80717000A7C39 /* locations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = locations.cpp; sourceTree = "<group>"; }; DABCABC01CB80717000A7C39 /* locations.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locations.hpp; sourceTree = "<group>"; }; + DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; DAC49C621CD07D74009E1AA3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; DACBC60B20118ABE00C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; }; DACBC60C20118AD000C4D7E2 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Foundation.strings; sourceTree = "<group>"; }; @@ -1274,9 +1272,6 @@ 3575798F1D513EF1000B822E /* Layers */, 40CFA64E1D78754A008103BD /* Sources */, 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */, - 357F09091DF84F3800941873 /* MGLStyleValueTests.h */, - 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */, - DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */, ); name = Styling; sourceTree = "<group>"; @@ -1775,6 +1770,7 @@ 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */, DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */, 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */, + DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */, 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */, 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */, 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */, @@ -1954,6 +1950,7 @@ DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */, 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */, 3557F7B01E1D27D300CCA5E6 /* MGLDistanceFormatter.h in Headers */, + DAC25FCC200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */, DA72620B1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */, 404C26E71D89C55D000AA13D /* MGLTileSource_Private.h in Headers */, DA88485C1CBAFB9800AB86E3 /* MGLFaux3DUserLocationAnnotationView.h in Headers */, @@ -2071,6 +2068,7 @@ DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */, 357FE2DE1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */, 1753ED431E53CE6F00A9FD90 /* MGLConversion.h in Headers */, + DAC25FCD200FD83F009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */, 354B83971D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */, DAF0D8111DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */, 96E516FF20005A4F00A02306 /* MGLMapboxEvents.h in Headers */, @@ -2507,7 +2505,6 @@ files = ( 6407D6701E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift in Sources */, DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */, - 3599A3E61DF708BC00E77FB2 /* MGLStyleValueTests.m in Sources */, 409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */, DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */, DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */, @@ -2525,7 +2522,6 @@ 357579851D502AF5000B822E /* MGLSymbolStyleLayerTests.mm in Sources */, 357579871D502AFE000B822E /* MGLLineStyleLayerTests.mm in Sources */, 357579891D502B06000B822E /* MGLCircleStyleLayerTests.mm in Sources */, - DA2207BF1DC0805F0002F84D /* MGLStyleValueTests.swift in Sources */, 40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */, DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */, 35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */, diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index f7e1049c9c..005e7698ab 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -22,6 +22,7 @@ custom_categories: - Using Style Functions at Runtime - Working with Mapbox Studio - Working with GeoJSON Data + - Predicates and Expressions - For Style Authors - Tile URL Templates - Info.plist Keys @@ -62,7 +63,6 @@ custom_categories: - name: Styling the Map children: - MGLStyle - - MGLStyleValue - MGLLight - name: Style Primitives children: diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index ce9c4965d7..204d495aef 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -63,3 +63,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" +#import "NSExpression+MGLAdditions.h" diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 6cc8ad9301..4e26aa443a 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -4,6 +4,7 @@ ### Styles and rendering +* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) * Added `MGLComputedShapeSource` source class that allows applications to supply vector data on a per-tile basis. * Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) * Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index eb20063996..94b0cffcdc 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -716,7 +716,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self.mapView.style addLayer:lineLayer]; MGLSymbolStyleLayer *labelLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"graticule.labels" source:source]; - labelLayer.text = [MGLStyleValue valueWithRawValue:@"{value}"]; + labelLayer.text = [NSExpression expressionWithFormat:@"value"]; [self.mapView.style addLayer:labelLayer]; } @@ -787,16 +787,16 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio self.mapView.style.transition = transition; MGLStyleLayer *waterLayer = [self.mapView.style layerWithIdentifier:@"water"]; - MGLStyleValue *colorFunction = [MGLStyleValue<NSColor *> valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ - @0.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor redColor]], - @10.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor yellowColor]], - @20.0: [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor blackColor]], - } options:nil]; + NSExpression *colorExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{ + @0.0: [NSColor redColor], + @10.0: [NSColor yellowColor], + @20.0: [NSColor blackColor], + }]; if ([waterLayer respondsToSelector:@selector(fillColor)]) { - [waterLayer setValue:colorFunction forKey:@"fillColor"]; + [waterLayer setValue:colorExpression forKey:@"fillColor"]; } else if ([waterLayer respondsToSelector:@selector(lineColor)]) { - [waterLayer setValue:colorFunction forKey:@"lineColor"]; + [waterLayer setValue:colorExpression forKey:@"lineColor"]; } NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam" ofType:@"geojson"]; @@ -805,8 +805,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self.mapView.style addSource:source]; MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"test" source:source]; - circleLayer.circleColor = [MGLStyleValue<NSColor *> valueWithRawValue:[NSColor greenColor]]; - circleLayer.circleRadius = [MGLStyleValue<NSNumber *> valueWithRawValue:[NSNumber numberWithInteger:40]]; + circleLayer.circleColor = [NSExpression expressionForConstantValue:[NSColor greenColor]]; + circleLayer.circleRadius = [NSExpression expressionForConstantValue:@40]; // fillLayer.predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"type", @"park"]; [self.mapView.style addLayer:circleLayer]; @@ -818,13 +818,13 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource]; theaterLayer.sourceLayerIdentifier = @"poi_label"; theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"]; - theaterLayer.iconImageName = [MGLStyleValue valueWithRawValue:NSImageNameIChatTheaterTemplate]; - theaterLayer.iconScale = [MGLStyleValue valueWithRawValue:@2]; - theaterLayer.iconColor = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential cameraStops:@{ - @16.0: [MGLStyleValue valueWithRawValue:[NSColor redColor]], - @18.0: [MGLStyleValue valueWithRawValue:[NSColor yellowColor]], - @20.0: [MGLStyleValue valueWithRawValue:[NSColor blackColor]], - } options:nil]; + theaterLayer.iconImageName = [NSExpression expressionForConstantValue:NSImageNameIChatTheaterTemplate]; + theaterLayer.iconScale = [NSExpression expressionForConstantValue:@2]; + theaterLayer.iconColor = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{ + @16.0: [NSColor redColor], + @18.0: [NSColor yellowColor], + @20.0: [NSColor blackColor], + }]; [self.mapView.style addLayer:theaterLayer]; } diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 9eeb159b75..4a16066a2a 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -257,12 +257,16 @@ In style JSON | In Objective-C | In Swift ## Setting attribute values Each property representing a layout or paint attribute is set to an -`MGLStyleValue` object, which is either an `MGLConstantStyleValue` object (for -constant values) or an `MGLStyleFunction` object (for style functions). The -style value object is a container for the raw value or function parameters that -you want the attribute to be set to. +`NSExpression` object. `NSExpression` objects play the same role as +[expressions in the Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions), +but you create the former using a very different syntax. `NSExpression`’s format +string syntax is reminiscent of a spreadsheet formula or an expression in a +database query. See the +“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +overview of the expression support in this SDK. This SDK no longer supports +style functions; use expressions instead. -### Constant style values +### Constant values in expressions In contrast to the JSON type that the style specification defines for each layout or paint property, the style value object often contains a more specific @@ -273,10 +277,10 @@ or set. In style JSON | In Objective-C | In Swift --------------|-----------------------|--------- Color | `NSColor` | `NSColor` -Enum | `NSValue` (see `NSValue(MGLAdditions)`) | `NSValue` (see `NSValue(MGLAdditions)`) +Enum | `NSString` | `String` String | `NSString` | `String` -Boolean | `NSNumber.boolValue` | `Bool` -Number | `NSNumber.floatValue` | `Float` +Boolean | `NSNumber.boolValue` | `NSNumber.boolValue` +Number | `NSNumber.floatValue` | `NSNumber.floatValue` Array (`-dasharray`) | `NSArray<NSNumber>` | `[Float]` Array (`-font`) | `NSArray<NSString>` | `[String]` Array (`-offset`, `-translate`) | `NSValue` containing `CGVector` | `NSValue` containing `CGVector` @@ -294,38 +298,72 @@ offset or translation upward, while a negative `CGVector.dy` means an offset or translation downward. This is the reverse of how `CGVector` is interpreted on iOS. -### Style functions - -A _style function_ allows you to vary the value of a layout or paint attribute -based on the zoom level, data provided by content sources, or both. For more -information about style functions, see “[Using Style Functions at Runtime](using-style-functions-at-runtime.html)”. - -Each kind of style function is represented by a distinct class, but you -typically create style functions as you create any other style value, using -class methods on `MGLStyleValue`: - -In style specification | SDK class | SDK factory method ----------------------------|-----------------------------|------------------- -zoom function | `MGLCameraStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:cameraStops:options:]` -property function | `MGLSourceStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:sourceStops:attributeName:options:]` -zoom-and-property function | `MGLCompositeStyleFunction` | `+[MGLStyleValue valueWithInterpolationMode:compositeStops:attributeName:options:]` - -The documentation for each individual style layer property indicates the kinds -of style functions that are enabled for that property. - -When you create a style function, you specify an _interpolation mode_ and a -series of _stops_. Each stop determines the effective value displayed at a -particular zoom level (for camera functions) or the effective value on features -with a particular attribute value in the content source (for source functions). -The interpolation mode tells the SDK how to calculate the effective value -between any two stops: - -In style specification | In the SDK ------------------------------|----------- -`exponential` | `MGLInterpolationModeExponential` -`interval` | `MGLInterpolationModeInterval` -`categorical` | `MGLInterpolationModeCategorical` -`identity` | `MGLInterpolationModeIdentity` +### Expression operators + +In style specification | Method, function, or predicate type | Format string syntax +-----------------------|-------------------------------------|--------------------- +`array` | | +`boolean` | | +`literal` | `+[NSExpression expressionForConstantValue:]` | `%@` representing `NSArray` or `NSDictionary` +`number` | | +`string` | | +`to-boolean` | `boolValue` | +`to-color` | | +`to-number` | `mgl_numberWithFallbackValues:` | +`to-string` | `stringValue` | +`typeof` | | +`geometry-type` | | +`id` | | +`properties` | | +`at` | | +`get` | `+[NSExpression expressionForKeyPath:]` | Key path +`has` | | +`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})` +`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)` +`!=` | `NSNotEqualToPredicateOperatorType` | `key != value` +`<` | `NSLessThanPredicateOperatorType` | `key < value` +`<=` | `NSLessThanOrEqualToPredicateOperatorType` | `key <= value` +`==` | `NSEqualToPredicateOperatorType` | `key == value` +`>` | `NSGreaterThanPredicateOperatorType` | `key > value` +`>=` | `NSGreaterThanOrEqualToPredicateOperatorType` | `key >= value` +`all` | `NSAndPredicateType` | `p0 AND … AND pn` +`any` | `NSOrPredicateType` | `p0 OR … OR pn` +`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` | `TERNARY(condition, trueExpression, falseExpression)` +`coalesce` | | +`match` | | +`interpolate` | `mgl_interpolateWithCurveType:parameters:stops:` | +`step` | `mgl_stepWithMinimum:stops:` | +`let` | `mgl_expressionWithContext:` | +`var` | `+[NSExpression expressionForVariable:]` | `$variable` +`concat` | `stringByAppendingString:` | +`downcase` | `lowercase:` | `lowercase('DOWNTOWN')` +`upcase` | `uppercase:` | `uppercase('Elysian Fields')` +`rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | +`rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | +`to-rgba` | | +`-` | `from:subtract:` | `2 - 1` +`*` | `multiply:by:` | `1 * 2` +`/` | `divide:by:` | `1 / 2` +`%` | `modulus:by:` | +`^` | `raise:toPower:` | `2 ** 2` +`+` | `add:to:` | `1 + 2` +`acos` | | +`asin` | | +`atan` | | +`cos` | | +`e` | | `%@` representing `NSNumber` containing `M_E` +`ln` | `ln:` | `ln(2)` +`ln2` | | `%@` representing `NSNumber` containing `M_LN2` +`log10` | `log:` | `log(1)` +`log2` | | +`max` | `max:` | `max({1, 2, 2, 3, 4, 7, 9})` +`min` | `min:` | `min({1, 2, 2, 3, 4, 7, 9})` +`pi` | | `%@` representing `NSNumber` containing `M_PI` +`sin` | | +`sqrt` | `sqrt:` | `sqrt(2)` +`tan` | | +`zoom` | | `$zoom` +`heatmap-density` | | `$heatmapDensity` ## Filtering sources @@ -350,5 +388,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the `MGLVectorStyleLayer.predicate` documentation for a full description of -the supported operators and operand types. +See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +a full description of the supported operators and operand types. diff --git a/platform/macos/docs/guides/Using Style Functions at Runtime.md b/platform/macos/docs/guides/Using Style Functions at Runtime.md index ea772a93a2..4e854aaaa0 100644 --- a/platform/macos/docs/guides/Using Style Functions at Runtime.md +++ b/platform/macos/docs/guides/Using Style Functions at Runtime.md @@ -27,15 +27,15 @@ The documentation for each individual style layer property notes which style fun ## Stops -Stops are key-value pairs that that determine a style value. With a `MGLCameraSourceFunction` stop, you can use a dictionary with a zoom level for a key and a `MGLStyleValue` for the value. For example, you can use a stops dictionary with zoom levels 0, 10, and 20 as keys, and yellow, orange, and red as the values. A `MGLSourceStyleFunction` uses the relevant attribute value as the key. +Stops are dictionary keys that are associated with layer attribute values. With feature attribute values as stops, you can use a dictionary with a zoom level for a key and an expression or constant value for the value. For example, you can use a stop dictionary with the zoom levels 0, 10, and 20 as keys and the colors yellow, orange, and red as the values. Alternatively, attribute values can be the keys. ```swift -let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] ``` @@ -57,20 +57,18 @@ let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbo let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil) mapView.style?.addSource(source) -let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] let layer = MGLCircleStyleLayer(identifier: "circles", source: source) -layer.circleColor = MGLStyleValue(interpolationMode: .exponential, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)]) -layer.circleRadius = MGLStyleValue(rawValue: 10) +layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", + stops) +layer.circleRadius = NSExpression(forConstantValue: 10) mapView.style?.insertLayer(layer, below: symbolLayer) ``` @@ -89,14 +87,13 @@ The example below increases a layer’s `circleRadius` exponentially based on a ```swift let stops = [ - 12: MGLStyleValue<NSNumber>(rawValue: 0.5), - 14: MGLStyleValue(rawValue: 2), - 18: MGLStyleValue(rawValue: 18), + 12: 0.5, + 14: 2, + 18: 18, ] -layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, - cameraStops: stops, - options: [.interpolationBase: 1.5]) +layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)", + stops) ``` ### Interval @@ -106,18 +103,16 @@ layer.circleRadius = MGLStyleValue(interpolationMode: .exponential, When we use the stops dictionary given above with an interval interpolation mode, we create ranges where earthquakes with a magnitude of 0 to just less than 2.5 would be yellow, 2.5 to just less than 5 would be orange, and so on. ```swift -let stops = [ - 0: MGLStyleValue<NSColor>(rawValue: .yellow), - 2.5: MGLStyleValue(rawValue: .orange), - 5: MGLStyleValue(rawValue: .red), - 7.5: MGLStyleValue(rawValue: .blue), - 10: MGLStyleValue(rawValue: .white), +let stops: [Float: NSColor] = [ + 0: .yellow, + 2.5: .orange, + 5: .red, + 7.5: .blue, + 10: .white, ] -layer.circleColor = MGLStyleValue(interpolationMode: .interval, - sourceStops: stops, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)]) +layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)", + NSColor.green, stops) ``` ![interval mode](img/data-driven-styling/interval.png) @@ -129,16 +124,16 @@ At each stop, `MGLInterpolationModeCategorical` produces an output value equal t There are three main types of events in the dataset: earthquakes, explosions, and quarry blasts. In this case, the color of the circle layer will be determined by the type of event, with a default value of blue to catch any events that do not fall into any of those categories. ```swift -let categoricalStops = [ - "earthquake": MGLStyleValue<NSColor>(rawValue: .orange), - "explosion": MGLStyleValue(rawValue: .red), - "quarry blast": MGLStyleValue(rawValue: .yellow), +let colors: [String: NSColor] = [ + "earthquake": .orange, + "explosion": .red, + "quarry blast": .yellow, ] +let defaultColor = NSColor.blue -layer.circleColor = MGLStyleValue(interpolationMode: .categorical, - sourceStops: categoricalStops, - attributeName: "type", - options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)]) +layer.circleColor = NSExpression( + format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)", + colors, colors, defaultColor) ``` ![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png) @@ -148,10 +143,7 @@ layer.circleColor = MGLStyleValue(interpolationMode: .categorical, `MGLInterpolationModeIdentity` uses the attribute’s value as the style value. For example, you can set the `circleRadius` to the earthquake’s magnitude. Since the attribute value itself will be used as the style value, `sourceStops` should be set to `nil`. ```swift -layer.circleRadius = MGLStyleValue(interpolationMode: .identity, - sourceStops: nil, - attributeName: "mag", - options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)]) +layer.circleRadius = NSExpression(forKeyPath: "mag") ``` ![identity mode](img/data-driven-styling/identity.png) diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index bdb29a890c..99f1119411 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -18,6 +18,7 @@ custom_categories: - name: Guides children: - Working with GeoJSON Data + - Predicates and Expressions - For Style Authors - Using Style Functions at Runtime - Tile URL Templates @@ -48,7 +49,6 @@ custom_categories: - name: Styling the Map children: - MGLStyle - - MGLStyleValue - MGLLight - name: Content Primitives children: diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index e33a8fdc82..c232216814 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -26,7 +26,7 @@ 1FCDF1421F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */; }; 1FCDF1431F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */; }; 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; - 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; }; + 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; 3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3526EABC1DF9B19800006B43 /* MGLCodingTests.m */; }; 352742781D4C220900A1ECE6 /* MGLStyleValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742771D4C220900A1ECE6 /* MGLStyleValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -55,7 +55,6 @@ 35724FC41D630502002A4AB4 /* amsterdam.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */; }; 359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */; }; 3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */; }; - 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */; }; 35C5D8471D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */; }; 35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */; }; 35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */; }; @@ -98,7 +97,6 @@ DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC881D5EEAC3009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; DA00FC8B1D5EEAC3009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC891D5EEAC3009AABC8 /* MGLAttributionInfo.mm */; }; DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */; }; - DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */; }; DA2784FE1DF03060001D5B8D /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA2784FD1DF03060001D5B8D /* Media.xcassets */; }; DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */; }; DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -164,6 +162,7 @@ DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; }; DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; }; DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; }; + DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */; }; DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; }; DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */; }; DACC22141CF3D3E200D220D9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22121CF3D3E200D220D9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -321,7 +320,6 @@ 3527429E1D4C25BD00A1ECE6 /* MGLStyleValue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleValue.mm; sourceTree = "<group>"; }; 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLAdditions.h"; sourceTree = "<group>"; }; 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSPredicate+MGLAdditions.mm"; sourceTree = "<group>"; }; - 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValueTests.h; sourceTree = "<group>"; }; 3537CA731D3F93A600380318 /* MGLStyle_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyle_Private.h; sourceTree = "<group>"; }; 3538AA211D542685008EC33D /* MGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer.h; sourceTree = "<group>"; }; 3538AA221D542685008EC33D /* MGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyleLayer.mm; sourceTree = "<group>"; }; @@ -335,7 +333,6 @@ 358EB3AE1D61F0DB00E46D9C /* amsterdam.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = amsterdam.geojson; path = ../../darwin/test/amsterdam.geojson; sourceTree = "<group>"; }; 359819571E02F611008FC139 /* NSCoder+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCoder+MGLAdditions.h"; sourceTree = "<group>"; }; 359819581E02F611008FC139 /* NSCoder+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSCoder+MGLAdditions.mm"; sourceTree = "<group>"; }; - 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLStyleValueTests.m; sourceTree = "<group>"; }; 35C5D8431D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSComparisonPredicate+MGLAdditions.h"; sourceTree = "<group>"; }; 35C5D8441D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSComparisonPredicate+MGLAdditions.mm"; sourceTree = "<group>"; }; 35C5D8451D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCompoundPredicate+MGLAdditions.h"; sourceTree = "<group>"; }; @@ -396,7 +393,6 @@ DA1AC01E1E5B8826006DF1D6 /* lt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; }; DA1AC01F1E5B8904006DF1D6 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Foundation.stringsdict; sourceTree = "<group>"; }; DA2207BA1DC076930002F84D /* test-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "test-Bridging-Header.h"; sourceTree = "<group>"; }; - DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLStyleValueTests.swift; sourceTree = "<group>"; }; DA2784FD1DF03060001D5B8D /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Media.xcassets; path = ../../darwin/test/Media.xcassets; sourceTree = "<group>"; }; DA2987591E1A4290002299F5 /* MGLDocumentationExampleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLDocumentationExampleTests.swift; path = ../../darwin/test/MGLDocumentationExampleTests.swift; sourceTree = "<group>"; }; DA3389601FA3EAC4001EA329 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Foundation.strings"; sourceTree = "<group>"; }; @@ -513,6 +509,7 @@ DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; }; DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = "<group>"; }; DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = "<group>"; }; + DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; }; DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLStyle+MBXAdditions.h"; sourceTree = "<group>"; }; DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLStyle+MBXAdditions.m"; sourceTree = "<group>"; }; @@ -888,9 +885,6 @@ DA8F257C1D51C5F40010E6B5 /* Layers */, DA87A99A1DC9D88800810D09 /* Sources */, 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */, - 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */, - 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */, - DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */, ); name = Styling; path = ../../darwin/test; @@ -1003,6 +997,7 @@ 408AA85E1DAEED3300022900 /* NSDictionary+MGLAdditions.mm */, DAE6C37F1CC31E2A00DB3429 /* NSException+MGLAdditions.h */, 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */, + DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */, 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */, 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */, 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */, @@ -1202,6 +1197,7 @@ 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */, DAE6C3661CC31E0400DB3429 /* MGLShape.h in Headers */, DA551B831DB496AC0009AFAF /* MGLTileSource_Private.h in Headers */, + DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */, DA7262071DEEDD460043BB89 /* MGLOpenGLStyleLayer.h in Headers */, 352742811D4C243B00A1ECE6 /* MGLSource.h in Headers */, DAE6C3C21CC31F4500DB3429 /* Mapbox.h in Headers */, @@ -1574,12 +1570,10 @@ 55E2AD111E5B0A6900E8C587 /* MGLOfflineStorageTests.mm in Sources */, 3526EABD1DF9B19800006B43 /* MGLCodingTests.m in Sources */, DA87A9A21DC9DCF100810D09 /* MGLFillStyleLayerTests.mm in Sources */, - 3599A3E81DF70E2000E77FB2 /* MGLStyleValueTests.m in Sources */, DA57D4B11EBC699800793288 /* MGLDocumentationGuideTests.swift in Sources */, DAEDC4321D6033F1000224FF /* MGLAttributionInfoTests.m in Sources */, DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */, 556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */, - DA2207BC1DC076940002F84D /* MGLStyleValueTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 26c67d5550..402c556998 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -59,3 +59,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLStyleValue.h" #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" +#import "NSExpression+MGLAdditions.h" diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp index 614a2addad..34537d48ae 100644 --- a/src/mbgl/style/expression/step.cpp +++ b/src/mbgl/style/expression/step.cpp @@ -40,6 +40,12 @@ void Step::eachChild(const std::function<void(const Expression&)>& visit) const } } +void Step::eachStop(const std::function<void(double, const Expression&)>& visit) const { + for (const auto &stop : stops) { + visit(stop.first, *stop.second); + } +} + bool Step::operator==(const Expression& e) const { if (auto rhs = dynamic_cast<const Step*>(&e)) { return *input == *(rhs->input) && Expression::childrenEqual(stops, rhs->stops); |