diff options
author | Minh Nguyễn <mxn@1ec5.org> | 2018-01-24 00:04:02 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-24 00:04:02 -0800 |
commit | fb5b8d34f20b696319cfc16838243265143ba972 (patch) | |
tree | bdbb9a02e89c84e26cdabd38add1a6d6f805b4d0 | |
parent | d4ed8d1a4474e43241e42610001403261353466f (diff) | |
download | qtlocation-mapboxgl-fb5b8d34f20b696319cfc16838243265143ba972.tar.gz |
Reimplement style values atop NSExpression (#10726)
* [ios, macos] Import headers, not implementation files
* [core] Added accessors for various expression parameters
Added missing parameter accessors to various expression operator classes, as well as a method on InterpolatorBase and Step that enumerates the stops and their values.
* [ios, macos] Silenced warning in test of error condition
* [ios, macos] Made MGLSphericalPosition boxable
* [ios, macos] Implemented array enumeration during conversion
* [ios, macos] Temporarily ignore heatmap layer type
* [ios, macos] Migrated MGLSymbolStyleLayer.text to NSExpression
MGLSymbolStyleLayer.text is now of type NSExpression instead of MGLStyleValue, as a first step toward migrating the entire layer API from style values to expressions.
Implemented conversions from NSExpression to JSON arrays and vice versa. The most common NSExpression functions are now converted into style expressions, but not all of the most common style expression operators are supported yet.
* [ios, macos] Implemented string coercion
* [ios, macos] Color literals
* [ios, macos] Null constant expressions
* [ios, macos] Convert dictionary literals
* [ios, macos] Interpolation expressions
* [ios, macos] to-boolean, to-number, get from object
* [ios, macos] Variable expressions
Implemented custom expression functions for assigning and referring to variables within the context of an expression. Variables are assigned via a “context dictionary” and applied to an subexpression that is given as another argument to the same expression. Also implemented built-in variable expressions for zoom level and heatmap density.
* [ios, macos] Convert colors, offsets, padding in expressions to JSON objects
* [ios, macos] Expression-based style property getters
Implemented a conversion from mbgl::style::PropertyValues to Objective-C JSON objects, which are then converted to NSExpressions.
* [ios, macos] Consolidated property value–expression conversion in MGLStyleValueTransformer
* [ios, macos] Predicate and expression guide
Extracted documentation about predicates from a documentation comment in MGLVectorStyleLayer.h to a new jazzy guide. Added details about NSExpression support as well. Began updating the “For Style Authors” guide to reflect the transition from style values to expressions.
* [ios, macos] Updated style authoring guide
Updated the Information for Style Authors guide to discuss expressions instead of style functions. Included a table mapping style specification expression operators to NSExpression syntaxes.
* [ios, macos] Migrated codegen templates to expressions
* [ios, macos] Applied expression changes via codegen
Ran make darwin-style-code.
* [macos] Migrated macosapp to expressions
* [ios, macos] Updated style function guide
This guide needs to be thoroughly rewritten, but for now the example code has been migrated to expressions.
* [ios, macos] Eviscerated style function tests
* [ios, macos] Updated changelogs
* [ios] Migrated iosapp to expressions
* [ios, macos] Exposed JSON conversion methods publicly
* [ios, macos] Removed MGLStyleValue, MGLStyleFunction
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); |