summaryrefslogtreecommitdiff
path: root/platform/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin')
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs73
-rw-r--r--platform/darwin/docs/guides/Migrating to Expressions.md.ejs212
-rw-r--r--platform/darwin/docs/guides/Predicates and Expressions.md979
-rw-r--r--platform/darwin/docs/guides/Tile URL Templates.md.ejs14
-rw-r--r--platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs99
-rw-r--r--platform/darwin/docs/img/data-driven-styling/cast.pngbin0 -> 49974 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/categorical1.pngbin105968 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/categorical2.pngbin125698 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/citibikes.pngbin152843 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential-function-1.pngbin69991 -> 34835 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential-function.pngbin70066 -> 34933 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential.pngbin86910 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/identity.pngbin68837 -> 70278 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/interval.pngbin83927 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/multiply.pngbin0 -> 50375 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/polylineExample.pngbin156800 -> 0 bytes
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss8
-rw-r--r--platform/darwin/resources/da.lproj/Foundation.strings297
-rw-r--r--platform/darwin/resources/da.lproj/Foundation.stringsdict54
-rw-r--r--platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict54
-rwxr-xr-xplatform/darwin/scripts/generate-style-code.js42
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json22
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.h124
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.mm136
-rw-r--r--platform/darwin/src/MGLAccountManager.h26
-rw-r--r--platform/darwin/src/MGLAccountManager.m9
-rw-r--r--platform/darwin/src/MGLAccountManager_Private.h2
-rw-r--r--platform/darwin/src/MGLAttributionInfo.mm7
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.h29
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h67
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.h70
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm60
-rw-r--r--platform/darwin/src/MGLComputedShapeSource_Private.h (renamed from platform/darwin/src/MGLAbstractShapeSource_Private.h)12
-rw-r--r--platform/darwin/src/MGLFeature.h27
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.h45
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.h54
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLGeometry.h32
-rw-r--r--platform/darwin/src/MGLGeometry.mm16
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h2
-rw-r--r--platform/darwin/src/MGLHeatmapStyleLayer.h58
-rw-r--r--platform/darwin/src/MGLHillshadeStyleLayer.h77
-rw-r--r--platform/darwin/src/MGLLight.h29
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.h56
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm3
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h2
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.h2
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h10
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm4
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.h3
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.mm92
-rw-r--r--platform/darwin/src/MGLPointAnnotation.h5
-rw-r--r--platform/darwin/src/MGLPointCollection.h5
-rw-r--r--platform/darwin/src/MGLPolygon.h9
-rw-r--r--platform/darwin/src/MGLPolyline.h9
-rw-r--r--platform/darwin/src/MGLPolyline.mm8
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.h17
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.mm2
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.h28
-rw-r--r--platform/darwin/src/MGLRasterTileSource.h (renamed from platform/darwin/src/MGLRasterSource.h)45
-rw-r--r--platform/darwin/src/MGLRasterTileSource.mm (renamed from platform/darwin/src/MGLRasterSource.mm)14
-rw-r--r--platform/darwin/src/MGLRasterTileSource_Private.h (renamed from platform/darwin/src/MGLRasterSource_Private.h)4
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.h2
-rw-r--r--platform/darwin/src/MGLShape.h18
-rw-r--r--platform/darwin/src/MGLShapeCollection.h5
-rw-r--r--platform/darwin/src/MGLShapeSource.h131
-rw-r--r--platform/darwin/src/MGLShapeSource.mm69
-rw-r--r--platform/darwin/src/MGLShapeSource_Private.h4
-rw-r--r--platform/darwin/src/MGLSource.h9
-rw-r--r--platform/darwin/src/MGLStyle.h109
-rw-r--r--platform/darwin/src/MGLStyle.mm182
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs10
-rw-r--r--platform/darwin/src/MGLStyleValue.h14
-rw-r--r--platform/darwin/src/MGLStyleValue.mm87
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h8
-rw-r--r--platform/darwin/src/MGLStyle_Private.h4
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h183
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm6
-rw-r--r--platform/darwin/src/MGLTileSource.h6
-rw-r--r--platform/darwin/src/MGLTileSource.mm1
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.h4
-rw-r--r--platform/darwin/src/MGLVectorTileSource.h (renamed from platform/darwin/src/MGLVectorSource.h)41
-rw-r--r--platform/darwin/src/MGLVectorTileSource.mm (renamed from platform/darwin/src/MGLVectorSource.mm)51
-rw-r--r--platform/darwin/src/MGLVectorTileSource_Private.h (renamed from platform/darwin/src/MGLVectorSource_Private.h)7
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm260
-rw-r--r--platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm60
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h160
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm942
-rw-r--r--platform/darwin/src/NSExpression+MGLPrivateAdditions.h10
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.h4
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm109
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m6
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.h13
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.m10
-rw-r--r--platform/darwin/test/MGLBackgroundStyleLayerTests.mm18
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm112
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift46
-rw-r--r--platform/darwin/test/MGLDocumentationGuideTests.swift138
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm639
-rw-r--r--platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm64
-rw-r--r--platform/darwin/test/MGLFillStyleLayerTests.mm64
-rw-r--r--platform/darwin/test/MGLGeometryTests.mm9
-rw-r--r--platform/darwin/test/MGLHeatmapColorTests.mm11
-rw-r--r--platform/darwin/test/MGLHeatmapStyleLayerTests.mm40
-rw-r--r--platform/darwin/test/MGLHillshadeStyleLayerTests.mm36
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.mm120
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm703
-rw-r--r--platform/darwin/test/MGLRasterStyleLayerTests.mm42
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift17
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm1
-rw-r--r--platform/darwin/test/MGLSourceQueryTests.m4
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs20
-rw-r--r--platform/darwin/test/MGLStyleTests.mm154
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm384
-rw-r--r--platform/darwin/test/MGLTileSetTests.mm12
117 files changed, 5481 insertions, 2741 deletions
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
index 7648d6f7ac..2ae6602224 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -178,21 +178,22 @@ source object is a member of one of the following subclasses of `MGLSource`:
In style JSON | In the SDK
--------------|-----------
+`vector` | `MGLVectorTileSource`
+`raster` | `MGLRasterTileSource`
+`raster-dem` | `MGLRasterDEMSource`
`geojson` | `MGLShapeSource`
-`raster` | `MGLRasterSource`
-`vector` | `MGLVectorSource`
`image` | `MGLImageSource`
`canvas` and `video` sources are not supported.
### Tile sources
-Raster and vector sources may be defined in TileJSON configuration files. This
-SDK supports the properties defined in the style specification, which are a
+Raster and vector tile sources may be defined in TileJSON configuration files.
+This SDK supports the properties defined in the style specification, which are a
subset of the keys defined in version 2.1.0 of the
[TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
specification. As an alternative to authoring a custom TileJSON file, you may
-supply various tile source options when creating a raster or vector source.
+supply various tile source options when creating a raster or vector tile source.
These options are detailed in the `MGLTileSourceOption` documentation:
In style JSON | In TileJSON | In the SDK
@@ -321,6 +322,11 @@ iOS.
<% } -%>
### Expression operators
+Many expression operators defined in the style specification have corresponding
+symbols to be used with the `+[NSExpression expressionWithFormat:]`,
+`+[NSExpression expressionForFunction:arguments:]`, or
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method:
+
In style specification | Method, function, or predicate type | Format string syntax
-----------------------|-------------------------------------|---------------------
`array` | |
@@ -330,15 +336,15 @@ In style specification | Method, function, or predicate type | Format string syn
`string` | |
`to-boolean` | `boolValue` |
`to-color` | |
-`to-number` | `mgl_numberWithFallbackValues:` |
-`to-string` | `stringValue` |
+`to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')`
+`to-string` | `stringValue` | `CAST(ele, 'NSString')`
`typeof` | |
-`geometry-type` | |
-`id` | |
-`properties` | |
-`at` | |
+`geometry-type` | `NSExpression.geometryTypeVariableExpression` | `$geometryType`
+`id` | `NSExpression.featureIdentifierVariableExpression` | `$featureIdentifier`
+`properties` | `NSExpression.featureAttributesVariableExpression` | `$featureAttributes`
+`at` | `objectFrom:withIndex:` | `array[n]`
`get` | `+[NSExpression expressionForKeyPath:]` | Key path
-`has` | |
+`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
`length` | `count:` | `count({1, 2, 2, 3, 4, 7, 9})`
`!` | `NSNotPredicateType` | `NOT (p0 OR … OR pn)`
`!=` | `NSNotEqualToPredicateOperatorType` | `key != value`
@@ -349,20 +355,20 @@ In style specification | Method, function, or predicate type | Format string syn
`>=` | `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:` |
+`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` or `MGL_IF` or `+[NSExpression mgl_expressionForConditional:trueExpression:falseExpresssion:]` | `TERNARY(1 = 2, YES, NO)` or `MGL_IF(1 = 2, YES, 2 = 2, YES, NO)`
+`coalesce` | `mgl_coalesce:` | `mgl_coalesce({x, y, z})`
+`match` | `MGL_MATCH` or `+[NSExpression mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')`
+`interpolate` | `mgl_interpolate:withCurveType:parameters:stops:` or `+[NSExpression mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` |
+`step` | `mgl_step:withMinimum:stops:` or `+[NSExpression mgl_expressionForSteppingExpression:fromExpression:stops:]` |
+`let` | `mgl_expressionWithContext:` | `MGL_LET('ios', 11, 'macos', 10.13, $ios + $macos)`
`var` | `+[NSExpression expressionForVariable:]` | `$variable`
-`concat` | `stringByAppendingString:` |
+`concat` | `mgl_join:` or `-[NSExpression mgl_expressionByAppendingExpression:]` | `mgl_join({'Old', ' ', 'MacDonald'})`
`downcase` | `lowercase:` | `lowercase('DOWNTOWN')`
`upcase` | `uppercase:` | `uppercase('Elysian Fields')`
<% if (macOS) { -%>
`rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
`rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` |
-<% } else { %>
+<% } else { -%>
`rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` |
`rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` |
<% } -%>
@@ -373,27 +379,34 @@ In style specification | Method, function, or predicate type | Format string syn
`%` | `modulus:by:` |
`^` | `raise:toPower:` | `2 ** 2`
`+` | `add:to:` | `1 + 2`
-`acos` | |
-`asin` | |
-`atan` | |
-`cos` | |
+`abs` | `abs:` | `abs(-1)`
+`acos` | `mgl_acos:` | `mgl_acos(1)`
+`asin` | `mgl_asin:` | `mgl_asin(0)`
+`atan` | `mgl_atan:` | `mgl_atan(20)`
+`ceil` | `ceiling:` | `ceiling(0.99999)`
+`cos` | `mgl_cos:` | `mgl_cos(0)`
`e` | | `%@` representing `NSNumber` containing `M_E`
+`floor` | `floor:` | `floor(-0.99999)`
`ln` | `ln:` | `ln(2)`
`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
`log10` | `log:` | `log(1)`
-`log2` | |
+`log2` | `mgl_log2:` | `mgl_log2(1024)`
`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` | |
+`round` | `mgl_round:` | `mgl_round(1.5)`
+`sin` | `mgl_sin:` | `mgl_sin(0)`
`sqrt` | `sqrt:` | `sqrt(2)`
-`tan` | |
-`zoom` | | `$zoom`
-`heatmap-density` | | `$heatmapDensity`
+`tan` | `mgl_tan:` | `mgl_tan(0)`
+`zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoom`
+`heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity`
+
+For operators that have no corresponding `NSExpression` symbol, use the
+`MGL_FUNCTION()` format string syntax.
## Filtering sources
-You can filter a shape or vector source by setting the
+You can filter a shape or vector tile source by setting the
`MGLVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a
table of style JSON operators and the corresponding operators used in the
predicate format string:
diff --git a/platform/darwin/docs/guides/Migrating to Expressions.md.ejs b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs
new file mode 100644
index 0000000000..addbf6940e
--- /dev/null
+++ b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs
@@ -0,0 +1,212 @@
+<%
+ const os = locals.os;
+ const iOS = os === 'iOS';
+ const macOS = os === 'macOS';
+ const cocoaPrefix = iOS ? 'UI' : 'NS';
+ const guide = 'MigratingToExpressions';
+-%>
+<!--
+ This file is generated.
+ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+-->
+
+# Migrating from Style Functions to Expressions
+
+[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
+
+With Mapbox Maps SDK for <%- iOS ? 'iOS v4.0.0' : 'macOS v0.7.0' %>, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions.
+
+An expression is represented at runtime by the `NSExpression` class. Expressions can be used to style paint and layout properties based on zoom level, data attributes, or a combination of the two.
+
+A constant expression can also be assigned to a style property. For example, the opacity of a fill style layer can be set to a constant value between 0 and 1.
+
+The documentation for each individual style layer property notes which non-constant expressions are enabled for that property. Style functions supported four interpolation modes: exponential, interval, categorical, and identity.
+
+This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php). Under each interpolation mode, the style function implementation will be shown, followed by the current syntax.
+
+For more information about how to work with GeoJSON data in our <%- os %> SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. To learn more about supported expressions, see our ["Predicates and Expressions"](predicates-and-expressions.html) guide. The "Predicates and Expressions" guide also outlines Mapbox custom functions that can be used to dynamically style a map.
+
+## Stops
+Stops are dictionary keys that are associated with layer attribute values. Constant values no longer need to be wrapped as style values when they are values in a stops dictionary.
+
+
+Style function syntax:
+
+```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),
+]
+```
+
+Current syntax:
+<%- guideExample(guide, 'Stops', os) %>
+
+
+## Interpolation mode
+
+Style functions supported four interpolation modes: exponential/linear, interval, categorical, and identity. For more information about supported custom expressions, please see the "Predicates and Expressions" guide.
+
+### Linear
+
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` takes the interpolation type as a parameter. If you previously used the default interpolation base, use the curve type `MGLExpressionInterpolationMode.linear`. See the [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code) documentation for more details.
+
+The stops dictionary below, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
+
+Style function syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+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 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)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+Current syntax:
+
+<%- guideExample(guide, 'Linear', os) %>
+
+### Exponential
+
+If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect.
+
+Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key.
+
+<img src="img/data-driven-styling/exponential-function.png" height=344/>
+<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
+
+The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The interpolation base is `1.5`.
+
+Style function syntax:
+
+```swift
+let stops = [
+ 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 18),
+]
+
+layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
+ cameraStops: stops,
+ options: [.interpolationBase: 1.5])
+```
+
+Current syntax:
+
+<%- guideExample(guide, 'Exponential', os) %>
+
+### Interval
+
+Steps, or intervals, create a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the layout or paint value assigned to that key. You can use the `+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]` method or the custom function [`mgl_step:from:stops:`](predicates-and-expressions.html#code-mgl_step-from-stops-code) for cases where you previously used interval interpolation mode. The first parameter takes the feature attribute name and the second parameter (`from:`) optionally takes the default or fallback value for that function. The final parameter takes a stops dictionary as an argument.
+
+When we use the stops dictionary given above with an `'mgl_step:from:stops:'`, 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.
+
+Style function syntax:
+
+```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),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .interval,
+ sourceStops: stops,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+````
+
+Current syntax:
+
+<%- guideExample(guide, 'Interval', os) %>
+
+### Categorical
+
+Categorical interpolation mode took a stops dictionary. If the value for a specified feature attribute name matched one in that stops dictionary, the style value for that attribute value would be used. Categorical style functions can now be replaced with `MGL_MATCH`.
+
+`MGL_MATCH` takes an initial condition, which in this case is an attribute key. This is followed by possible matches for that key and the value to assign to the layer property if there is a match. The final argument can be a default style value that is to be used if none of the specified values match.
+
+There are three main types of events in the USGS 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.
+
+Style function syntax:
+
+```swift
+let categoricalStops = [
+ "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
+ "explosion": MGLStyleValue(rawValue: .red),
+ "quarry blast": MGLStyleValue(rawValue: .yellow),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
+ sourceStops: categoricalStops,
+ attributeName: "type",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+```
+
+Current syntax:
+<%- guideExample(guide, 'Categorical', os) %>
+
+If your use case does not require a default value, you can either apply a predicate to your layer prior to styling it, or use the format string `"valueForKeyPath:"`.
+
+### Identity
+
+Identity interpolation mode used the attribute’s value as the style layer property value. In this example, you might set the `circleRadius` to the earthquake’s magnitude. In order to use a feature attribute value to style a layer property, set the property value to `[NSExpression expressionForKeyPath:]`, which take the feature attribute name as an argument.
+
+Style function syntax:
+
+```swift
+layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
+ sourceStops: nil,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+```
+
+Current syntax:
+<%- guideExample(guide, 'Identity', os) %>
+
+![identity mode](img/data-driven-styling/identity.png)
+
+Some built-in functions can be applied to attribute values to style layer property values. To set the circle radius to three times the earthquake’s magnitude, create a `multiply:by:` function that takes the attribute value and the multiplier as arguments, or use a format string.
+
+<%- guideExample(guide, 'Multiply', os) %>
+
+![multiply magnitude](img/data-driven-styling/multiply.png)
+
+You can also cast attribute values in order to use them. One example is to cast an integer as an `NSString` and use it as a text value.
+
+<%- guideExample(guide, 'Cast', os) %>
+
+![cast a value](img/data-driven-styling/cast.png)
+
+### Constant Values
+
+For constant values that do not necessarily change based on camera or attribute values, use `[NSExpression expressionForConstantValue:]` (previously `[MGLStyleValue valueWithRawValue:]`).
+
+## Resources
+
+* [USGS Earthquake Feed](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
+* [For Style Authors](for-style-authors.html)
+* [Predicates and Expressions](predicates-and-expressions.html)
diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md
index 19d98fd4c1..c3b3d39a52 100644
--- a/platform/darwin/docs/guides/Predicates and Expressions.md
+++ b/platform/darwin/docs/guides/Predicates and Expressions.md
@@ -19,6 +19,8 @@ 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.
+### Operators
+
The following comparison operators are supported:
`NSPredicateOperatorType` | Format string syntax
@@ -31,12 +33,17 @@ The following comparison operators are supported:
`NSNotEqualToPredicateOperatorType` | `key != value`<br />`key <> value`
`NSBetweenPredicateOperatorType` | `key BETWEEN { 32, 212 }`
+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.
+
The following compound operators are supported:
`NSCompoundPredicateType` | Format string syntax
--------------------------|---------------------
`NSAndPredicateType` | `predicate1 AND predicate2`<br />`predicate1 && predicate2`
-`NSOrPredicateType` | `predicate1 OR predicate2`<br />`predicate1 || predicate2`
+`NSOrPredicateType` | `predicate1 OR predicate2`<br /><code>predicate1 &vert;&vert; predicate2</code>
`NSNotPredicateType` | `NOT predicate`<br />`!predicate`
The following aggregate operators are supported:
@@ -46,97 +53,101 @@ The following aggregate operators are supported:
`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.
+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.
+
+### Operands
+
+Operands in predicates can be [variables](#variables), [key paths](#key-paths),
+or almost anything else that can appear
+[inside an expression](#using-expressions-to-configure-layout-and-paint-attributes).
+
+Automatic type casting is not performed. Therefore, a feature only matches a
+predicate if its value for the attribute in question is of the same type as the
+value specified in the predicate. Use the `CAST()` operator to convert a key
+path or variable into a matching type:
+
+* To cast a value to a number, use `CAST(key, 'NSNumber')`.
+* To cast a value to a string, use `CAST(key, 'NSString')`.
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:
+## Using expressions to configure layout and paint attributes
+
+An expression can contain subexpressions of various types. Each of the supported
+types of expressions is discussed below.
+
+### Constant values
+
+A constant value can be of any of the following types:
+
+In Objective-C | In Swift
+----------------------|---------
+`NSColor` (macOS)<br>`UIColor` (iOS) | `NSColor` (macOS)<br>`UIColor` (iOS)
+`NSString` | `String`
+`NSString` | `String`
+`NSNumber.boolValue` | `NSNumber.boolValue`
+`NSNumber.doubleValue` | `NSNumber.doubleValue`
+`NSArray<NSNumber>` | `[Float]`
+`NSArray<NSString>` | `[String]`
+`NSValue.CGVectorValue` (iOS)<br>`NSValue` containing `CGVector` (macOS) | `NSValue.cgVectorValue` (iOS)<br>`NSValue` containing `CGVector` (macOS)
+`NSValue.UIEdgeInsetsValue` (iOS)<br>`NSValue.edgeInsetsValue` (macOS) | `NSValue.uiEdgeInsetsValue` (iOS)<br>`NSValue.edgeInsetsValue` (macOS)
+
+For literal floating-point values, use `-[NSNumber numberWithDouble:]` instead
+of `-[NSNumber numberWithFloat:]` to avoid precision issues.
+
+### 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.
+
+The following special attribute is also available on features that are produced
+as a result of clustering multiple point features together in a shape source:
<table>
<thead>
-<tr><th>Attribute</th><th>Meaning</th></tr>
+<tr><th>Attribute</th><th>Type</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>Number</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.
+Some characters may not be used directly as part of a key path in a format
+string. For example, if a feature’s attribute is named `ISO 3166-1:2006`, an
+expression format string of `lowercase(ISO 3166-1:2006)` or a predicate format
+string of `ISO 3166-1:2006 == 'US-OH'` would raise an exception. Instead, use a
+`%K` placeholder or the `+[NSExpression expressionForKeyPath:]` initializer:
-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"`.
+```objc
+[NSPredicate predicateWithFormat:@"%K == 'US-OH'", @"ISO 3166-1:2006"];
+[NSExpression expressionForFunction:@"lowercase:"
+ arguments:@[[NSExpression expressionForKeyPath:@"ISO 3166-1:2006"]]]
+```
-## Using expressions to configure layout and paint attributes
+```swift
+NSPredicate(format: "%K == 'US-OH'", "ISO 3166-1:2006")
+NSExpression(forFunction: "lowercase:",
+ arguments: [NSExpression(forKeyPath: "ISO 3166-1:2006")])
+```
### 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),
+[functions predefined](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init#discussion)
+by the
+[`+[NSExpression expressionForFunction:arguments:]` method](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init),
the following subset is supported in layer attribute values:
Initializer parameter | Format string syntax
@@ -163,8 +174,13 @@ Initializer parameter | Format string syntax
`uppercase:` | `uppercase('Elysian Fields')`
`lowercase:` | `lowercase('DOWNTOWN')`
`noindex:` | `noindex(0 + 2 + c)`
+`length:` | `length('Wapakoneta')`
+`castObject:toType:` | `CAST(ele, 'NSString')`<br>`CAST(ele, 'NSNumber')`
-The following predefined functions are not supported:
+A number of [Mapbox-specific functions](#mapbox-specific-functions) are also
+available.
+
+The following predefined functions are **not** supported:
Initializer parameter | Format string syntax
----------------------|---------------------
@@ -182,199 +198,65 @@ Initializer parameter | Format string syntax
`onesComplement:` | `onesComplement(255)`
`distanceToLocation:fromLocation:` | `distanceToLocation:fromLocation:(there, here)`
-#### Mapbox-specific functions
+### Conditionals
-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.
+Conditionals are supported via the built-in
+`+[NSExpression expressionForConditional:trueExpression:falseExpression:]`
+method and `TERNARY()` operator. If you need to express multiple cases
+(“else-if”), you can either nest a conditional within a conditional or use the
+[`MGL_IF()`](#code-mgl_if-code) or [`MGL_MATCH()`](#code-mgl_match-code) function.
+
+### Aggregates
+
+Aggregate expressions can contain arrays of expressions. In some cases, it is
+possible to use the array itself instead of wrapping the array in an aggregate
+expression.
+
+### Variables
+
+The following variables are defined by this SDK for use with style layers:
<table>
<thead>
-<tr><th>Selector name</th><th>Target</th><th>Arguments</th><th>Returns</th></tr>
+<tr><th>Variable</th><th>Type</th><th>Meaning</th></tr>
</thead>
<tbody>
<tr>
- <td><code>boolValue</code></td>
+ <td><code>$featureIdentifier</code></td>
<td>
- An `NSExpression` that evaluates to a number or string.
+ Any
+ <a href="working-with-geojson-data.html#about-geojson-deserialization">GeoJSON data type</a>
</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`.
+ A value that uniquely identifies the feature in the containing source.
+ This variable corresponds to the
+ <code>NSExpression.featureIdentifierVariableExpression</code> property.
</td>
</tr>
<tr>
- <td><code>mgl_expressionWithContext:</code></td>
+ <td><code>$geometryType</code></td>
+ <td>String</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>
+ The type of geometry represented by the feature. A feature’s type is 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>
+ This variable corresponds to the
+ <code>NSExpression.geometryTypeVariableExpression</code> property.
</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>
@@ -383,7 +265,9 @@ The following variables are defined by this SDK for use with style layers:
<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.
+ variable can only be used with the <code>heatmapColor</code> property.
+ This variable corresponds to the
+ <code>NSExpression.heatmapDensityVariableExpression</code> property.
</td>
</tr>
<tr>
@@ -392,7 +276,8 @@ The following variables are defined by this SDK for use with style layers:
<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.
+ step expression. This variable corresponds to the
+ <code>NSExpression.zoomLevelVariableExpression</code> property.
</td>
</tr>
</tbody>
@@ -404,11 +289,601 @@ 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}];
+[NSExpression expressionWithFormat:@"MGL_LET('floorCount', 2, $floorCount + 1)"];
```
```swift
-NSExpression(format: "FUNCTION($floorCount + 1, 'mgl_expressionWithContext:', %@)",
- ["floorCount": 2])
+NSExpression(format: "MGL_LET(floorCount, 2, $floorCount + 1)")
```
+
+## Mapbox-specific functions
+
+For compatibility with the Mapbox Style Specification, the following functions
+are defined by this SDK. When setting a style layer property, you can call these
+functions just like the predefined functions above, using either the
+`+[NSExpression expressionForFunction:arguments:]` method or a convenient format
+string syntax:
+
+### `mgl_does:have:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_does:have:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_does:have:(SELF, '🧀🍔')</code> or <code>mgl_does:have:(%@, '🧀🍔')</code></dd>
+</dl>
+
+Returns a Boolean value indicating whether the dictionary has a value for the
+key or whether the evaluated object (`SELF`) has a value for the feature
+attribute. Compared to the [`mgl_has:`](#code-mgl_has-code) custom function,
+that function’s target is instead passed in as the first argument to this
+function. Both functions are equivalent to the syntax `key != NIL` or
+`%@[key] != NIL` but can be used outside of a predicate.
+
+### `mgl_interpolate:withCurveType:parameters:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_interpolate:withCurveType:parameters:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)</code></dd>
+</dl>
+
+Produces continuous, smooth results by interpolating between pairs of input and
+output values (“stops”). Compared to the
+[`mgl_interpolateWithCurveType:parameters:stops:`](#code-mgl_interpolatewithcurvetype-parameters-stops-code)
+custom function, the input expression (that function’s target) is instead passed
+in as the first argument to this function.
+
+### `mgl_step:from:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_step:from:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_step:from:stops:(x, 11, %@)</code></dd>
+</dl>
+
+Produces discrete, stepped results by evaluating a piecewise-constant function
+defined by pairs of input and output values ("stops"). Compared to the
+[`mgl_stepWithMinimum:stops:`](#code-mgl_stepwithminimum-stops-code) custom
+function, the input expression (that function’s target) is instead passed in as
+the first argument to this function.
+
+### `mgl_join:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_join:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_join({'Old', 'MacDonald'})</code></dd>
+</dl>
+
+Returns the result of concatenating together all the elements of an array in
+order. Compared to the
+[`stringByAppendingString:`](#code-stringbyappendingstring-code) custom
+function, this function takes only one argument, which is an aggregate
+expression containing the strings to concatenate.
+
+### `mgl_acos:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_acos:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_acos(1)</code></dd>
+</dl>
+
+Returns the arccosine of the number.
+
+This function corresponds to the
+[`acos`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-acos)
+operator in the Mapbox Style Specification.
+
+### `mgl_asin:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_asin:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_asin(0)</code></dd>
+</dl>
+
+Returns the arcsine of the number.
+
+This function corresponds to the
+[`asin`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-asin)
+operator in the Mapbox Style Specification.
+
+### `mgl_atan:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_atan:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_atan(20)</code></dd>
+</dl>
+
+Returns the arctangent of the number.
+
+This function corresponds to the
+[`atan`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-atan)
+operator in the Mapbox Style Specification.
+
+### `mgl_cos:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_cos:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_cos(0)</code></dd>
+</dl>
+
+Returns the cosine of the number.
+
+This function corresponds to the
+[`cos`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-cos)
+operator in the Mapbox Style Specification.
+
+### `mgl_log2:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_log2:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_log2(1024)</code></dd>
+</dl>
+
+Returns the base-2 logarithm of the number.
+
+This function corresponds to the
+[`log2`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log2)
+operator in the Mapbox Style Specification.
+
+### `mgl_round:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_round:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_round(1.5)</code></dd>
+</dl>
+
+Returns the number rounded to the nearest integer. If the number is halfway
+between two integers, this function rounds it away from zero.
+
+This function corresponds to the
+[`round`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-round)
+operator in the Mapbox Style Specification.
+
+### `mgl_sin:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_sin:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_sin(0)</code></dd>
+</dl>
+
+Returns the sine of the number.
+
+This function corresponds to the
+[`sin`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sin)
+operator in the Mapbox Style Specification.
+
+### `mgl_tan:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_tan:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_tan(0)</code></dd>
+</dl>
+
+Returns the tangent of the number.
+
+This function corresponds to the
+[`tan`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-tan)
+operator in the Mapbox Style Specification.
+
+### `mgl_coalesce:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_coalesce:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_coalesce({x, y, z})</code></dd>
+</dl>
+
+Returns the first non-`nil` value from an array of expressions.
+
+This function corresponds to the
+[`coalesce`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-coalesce)
+operator in the Mapbox Style Specification.
+
+### `MGL_LET`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_LET:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_LET('age', uppercase('old'), 'name', uppercase('MacDonald'), mgl_join({$age, $name}))</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Any number of variable names interspersed with their assigned
+ <code>NSExpression</code> values, followed by an <code>NSExpression</code>
+ that may contain references to those variables.
+</dd>
+</dl>
+
+Returns the result of evaluating an expression with the given variable values.
+Compared to the
+[`mgl_expressionWithContext:`](#code-mgl_expressionwithcontext-code) custom
+function, this function takes the variable names and values inline before the
+expression that contains references to those variables.
+
+### `MGL_MATCH`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_MATCH:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ An input expression, then any number of argument pairs, followed by a default
+ expression. Each argument pair consists of a constant value followed by an
+ expression to produce as a result of matching that constant value.
+</dd>
+</dl>
+
+Returns the result of matching the input expression against the given constant
+values.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]`
+method and the
+[`match`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-match)
+operator in the Mapbox Style Specification.
+
+### `MGL_IF`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_IF:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_IF(1 = 2, YES, 2 = 2, YES, NO)</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Alternating <code>NSPredicate</code> conditionals and resulting expressions,
+ followed by a default expression.
+</dd>
+</dl>
+
+Returns the first expression that meets the condition; otherwise, the default
+value. Unlike
+`+[NSExpression expressionForConditional:trueExpression:falseExpression:]` or
+the `TERNARY()` syntax, this function can accept multiple “if else” conditions
+and is supported on iOS 8._x_ and macOS 10.10._x_; however, each conditional
+passed into this function must be wrapped in a constant expression.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForConditional:trueExpression:falseExpresssion:]`
+method and the
+[`case`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-case)
+operator in the Mapbox Style Specification.
+
+### `MGL_FUNCTION`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_FUNCTION:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_FUNCTION('typeof', mystery)</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Any arguments required by the expression operator.
+</dd>
+</dl>
+
+An expression exactly as defined by the
+[Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions).
+
+## Custom functions
+
+The following custom functions are also available with the
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method or the
+`FUNCTION()` format string syntax.
+
+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.
+
+The Mapbox Style Specification defines some operators for which no custom
+function is available. To use these operators in an `NSExpression`, call the
+[`MGL_FUNCTION()`](#code-mgl_function-code) function with the same arguments
+that the operator expects.
+
+### `boolValue`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>boolValue</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(1, 'boolValue')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number or string.
+</dd>
+<dt>Arguments:</dt>
+<dd>None.</dd>
+</dl>
+
+A Boolean representation of the target: <code>FALSE</code> when then input is an
+empty string, 0, <code>FALSE</code>, <code>NIL</code>, or <code>NaN</code>,
+otherwise <code>TRUE</code>.
+
+### `mgl_has:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_has:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION($featureAttributes, 'mgl_has:', '🧀🍔')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to an <code>NSDictionary</code>
+ or the evaluated object (<code>SELF</code>).
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to an <code>NSString</code>
+ representing the key to look up in the dictionary or the feature attribute to
+ look up in the evaluated object (see <code>MGLFeature.attributes</code>).
+</dd>
+</dl>
+
+<code>true</code> if the dictionary has a value for the key or if the evaluated
+object has a value for the feature attribute.
+
+This function corresponds to the
+[`has`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has)
+operator in the Mapbox Style Specification. See also the
+[`mgl_does:have:`](#code-mgl_does-have-code) function, which is used on its own
+without the `FUNCTION()` operator. You can also check whether an object has an
+attribute by comparing the key path to `NIL`, for example `cheeseburger != NIL`
+or `burger.cheese != NIL`
+
+### `mgl_expressionWithContext:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_expressionWithContext:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($ios + $macos, 'mgl_expressionWithContext:', %@)</code> with
+ a dictionary containing <code>ios</code> and <code>macos</code> keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that may contain references to the variables
+ defined in the context dictionary.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ An <code>NSDictionary</code> with <code>NSString</code>s as keys and
+ <code>NSExpression</code>s as values. Each key is a variable name and each
+ value is the variable’s value within the target expression.
+</dd>
+</dl>
+
+The target expression with variable subexpressions replaced with the values
+defined in the context dictionary.
+
+This function corresponds to the
+[`let`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-let)
+operator in the Mapbox Style Specification. See also the
+[`MGL_LET`](#code-mgl_let-code) function, which is used on its own without the
+`FUNCTION()` operator.
+
+### `mgl_interpolateWithCurveType:parameters:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_interpolateWithCurveType:parameters:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', NIL, %@)</code>
+ with a dictionary containing zoom levels or other constant values as keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number and contains a
+ variable or key path expression.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ The first argument is one of the following strings denoting curve types:
+ <code>linear</code>, <code>exponential</code>, or <code>cubic-bezier</code>.
+
+ The second argument is an expression providing parameters for the curve:
+
+ <ul>
+ <li>If the curve type is <code>linear</code>, the argument is <code>NIL</code>.</li>
+ <li>
+ If the curve type is <code>exponential</code>, the argument is an
+ expression that evaluates to a number, specifying the base of the
+ exponential interpolation.
+ </li>
+ <li>
+ If the curve type is <code>cubic-bezier</code>, 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 <code>NSDictionary</code> object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+</dd>
+</dl>
+
+A value interpolated along the continuous mathematical function defined by the
+arguments, with the target as the input to the function.
+
+The input expression is matched against the keys in the stop dictionary. The
+keys may be feature attribute values, zoom levels, or heatmap densities. The
+values may be constant values or `NSExpression` objects. 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.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]`
+method and the
+[`interpolate`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate)
+operator in the Mapbox Style Specification. See also the
+[`mgl_interpolate:withCurveType:parameters:stops:`](#code-mgl_interpolate-withcurvetype-parameters-stops-code)
+function, which is used on its own without the `FUNCTION()` operator.
+
+### `mgl_numberWithFallbackValues:`
+
+<dl>
+<dt>Selector:</dt>
+<dd>
+ <code>mgl_numberWithFallbackValues:</code>,
+ <code>doubleValue</code>,
+ <code>floatValue</code>, or
+ <code>decimalValue</code>
+</dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(ele, 'mgl_numberWithFallbackValues:', 0)</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a Boolean value, number, or
+ string.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ Zero or more <code>NSExpression</code>s, each evaluating to a Boolean value
+ or string.
+</dd>
+</dl>
+
+A numeric representation of the target:
+
+* If the target is <code>NIL</code> or <code>FALSE</code>, the result is 0.
+* If the target is true, the result is 1.
+ * If the target is a string, it is converted to a number as specified by the
+ “[ToNumber Applied to the String Type](https://tc39.github.io/ecma262/#sec-tonumber-applied-to-the-string-type)”
+ algorithm of the ECMAScript Language Specification.
+ * If multiple values are provided, each one is evaluated in order until the
+ first successful conversion is obtained.
+
+This function corresponds to the
+[`to-number`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-number)
+operator in the Mapbox Style Specification. You can also cast a value to a
+number by passing the value and the string `NSNumber` into the `CAST()`
+operator.
+
+### `mgl_stepWithMinimum:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_stepWithMinimum:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', 0, %@)</code> with
+ a dictionary with zoom levels or other constant values as keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number and contains a
+ variable or key path expression.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ 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 <code>NSDictionary</code> object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+</dd>
+</dl>
+
+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.
+
+The input expression is matched against the keys in the stop dictionary. The
+keys may be feature attribute values, zoom levels, or heatmap densities. The
+values may be constant values or `NSExpression` objects. 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.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]`
+method and the
+[`step`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step)
+operator in the Mapbox Style Specification.
+
+### `stringByAppendingString:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>stringByAppendingString:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')</code></dd>
+<dt>Target:</dt>
+<dd>An <code>NSExpression</code> that evaluates to a string.</dd>
+<dt>Arguments:</dt>
+<dd>One or more <code>NSExpression</code>s, each evaluating to a string.</dd>
+</dl>
+
+The target string with each of the argument strings appended in order.
+
+This function corresponds to the
+`-[NSExpression(MGLAdditions) mgl_expressionByAppendingExpression:]`
+method and is similar to the
+[`concat`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-concat)
+operator in the Mapbox Style Specification. See also the
+[`mgl_join:`](#code-mgl_join-code) function, which concatenates multiple
+expressions and is used on its own without the `FUNCTION()` operator.
+
+### `stringValue`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>stringValue</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(ele, 'stringValue')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a Boolean value, number, or
+ string.
+</dd>
+<dt>Arguments:</dt>
+<dd>None.</dd>
+</dl>
+
+A string representation of the target:
+
+* If the target is <code>NIL</code>, the result is the empty string.
+* If the target is a Boolean value, the result is the string `true` or `false`.
+* If the target is a number, it is converted to a string as specified by the
+ “[NumberToString](https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type)”
+ algorithm of the ECMAScript Language Specification.
+* 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.
+* 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.
+
+This function corresponds to the
+[`to-string`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-string)
+operator in the Mapbox Style Specification. You can also cast a value to a
+string by passing the value and the string `NSString` into the `CAST()`
+operator.
diff --git a/platform/darwin/docs/guides/Tile URL Templates.md.ejs b/platform/darwin/docs/guides/Tile URL Templates.md.ejs
index 78fb297138..2b1de65b42 100644
--- a/platform/darwin/docs/guides/Tile URL Templates.md.ejs
+++ b/platform/darwin/docs/guides/Tile URL Templates.md.ejs
@@ -10,12 +10,12 @@
-->
# Tile URL Templates
-`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource`
-objects, can be created using an initializer that accepts an array of tile URL
-templates. Tile URL templates are strings that specify the URLs of the vector
-tiles or raster tile images to load. A template resembles an absolute URL, but
-with any number of placeholder strings that the source evaluates based on the
-tile it needs to load. For example:
+`MGLTileSource` objects, specifically `MGLRasterTileSource` and
+`MGLVectorTileSource` objects, can be created using an initializer that accepts
+an array of tile URL templates. Tile URL templates are strings that specify the
+URLs of the vector tiles or raster tile images to load. A template resembles an
+absolute URL, but with any number of placeholder strings that the source
+evaluates based on the tile it needs to load. For example:
* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be
evaluated as `http://www.example.com/tiles/14/6/9.pbf`.
@@ -62,7 +62,7 @@ all of which are optional:
<td>The tile’s zoom level. At zoom level 0, each tile covers the entire
world map; at zoom level 1, it covers ¼ of the world; at zoom level 2,
<sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by
- a <code>MGLRasterSource</code> object, whether the tile zoom level
+ a <code>MGLRasterTileSource</code> object, whether the tile zoom level
matches the map’s current zoom level depends on the value of the
source’s tile size as specified in the
<code>MGLTileSourceOptionTileSize</code> key of the <code>options</code>
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
deleted file mode 100644
index 61034a674f..0000000000
--- a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs
+++ /dev/null
@@ -1,99 +0,0 @@
-<%
- const os = locals.os;
- const iOS = os === 'iOS';
- const macOS = os === 'macOS';
- const cocoaPrefix = iOS ? 'UI' : 'NS';
- const guide = 'UsingStyleFunctionsAtRuntime';
--%>
-<!--
- This file is generated.
- Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
--->
-
-# Using Style Functions at Runtime
-
-[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
-
-Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price.
-
-_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source.
-
-You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details.
-
-![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png)
-
-This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide.
-
-A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`:
-
-* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level.
-* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake.
-* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake.
-
-The documentation for each individual style layer property notes which style functions are enabled for that property.
-
-## Stops
-
-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) %>
-
-## Interpolation mode
-
-The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function.
-
-### Linear
-
-`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary.
-
-The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
-
-<%- guideExample(guide, 'Linear', os) %>
-
-![exponential mode](img/data-driven-styling/exponential.png)
-
-### Exponential
-
-By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect.
-
-Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom.
-
-<img src="img/data-driven-styling/exponential-function.png" height=344/>
-<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
-
-The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The `MGLStyleFunctionOptionInterpolationBase` is `1.5`.
-
-<%- guideExample(guide, 'Exponential', os) %>
-
-### Interval
-
-`MGLInterpolationModeInterval` creates a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the style value assigned to that key.
-
-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.
-
-<%- guideExample(guide, 'Interval', os) %>
-
-![interval mode](img/data-driven-styling/interval.png)
-
-### Categorical
-
-At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes.
-
-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.
-
-<%- guideExample(guide, 'Categorical', os) %>
-
-![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
-
-### Identity
-
-`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`.
-
-<%- guideExample(guide, 'Identity', os) %>
-
-![identity mode](img/data-driven-styling/identity.png)
-
-##Resources
-
-* [USGS](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
-* [For Style Authors](for-style-authors.html)
diff --git a/platform/darwin/docs/img/data-driven-styling/cast.png b/platform/darwin/docs/img/data-driven-styling/cast.png
new file mode 100644
index 0000000000..e5b40b4ffa
--- /dev/null
+++ b/platform/darwin/docs/img/data-driven-styling/cast.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/categorical1.png b/platform/darwin/docs/img/data-driven-styling/categorical1.png
deleted file mode 100644
index 969846b41b..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/categorical1.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/categorical2.png b/platform/darwin/docs/img/data-driven-styling/categorical2.png
deleted file mode 100644
index 5008c522ed..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/categorical2.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/citibikes.png b/platform/darwin/docs/img/data-driven-styling/citibikes.png
deleted file mode 100644
index a616672a32..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/citibikes.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
index 6aa129a305..39a8a2a1e4 100644
--- a/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
+++ b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential-function.png b/platform/darwin/docs/img/data-driven-styling/exponential-function.png
index c14969f0a8..f9824fc5e4 100644
--- a/platform/darwin/docs/img/data-driven-styling/exponential-function.png
+++ b/platform/darwin/docs/img/data-driven-styling/exponential-function.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential.png b/platform/darwin/docs/img/data-driven-styling/exponential.png
deleted file mode 100644
index 87ddc1350e..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/exponential.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/identity.png b/platform/darwin/docs/img/data-driven-styling/identity.png
index 632ccdf3d5..3355694ec9 100644
--- a/platform/darwin/docs/img/data-driven-styling/identity.png
+++ b/platform/darwin/docs/img/data-driven-styling/identity.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/interval.png b/platform/darwin/docs/img/data-driven-styling/interval.png
deleted file mode 100644
index d15aff2025..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/interval.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/multiply.png b/platform/darwin/docs/img/data-driven-styling/multiply.png
new file mode 100644
index 0000000000..df42f243e1
--- /dev/null
+++ b/platform/darwin/docs/img/data-driven-styling/multiply.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/polylineExample.png b/platform/darwin/docs/img/data-driven-styling/polylineExample.png
deleted file mode 100644
index cd9b39bae4..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/polylineExample.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
index 103ba601dc..ff6d2a374c 100644
--- a/platform/darwin/docs/theme/assets/css/jazzy.css.scss
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -113,7 +113,12 @@ h2 {
}
h3 {
- @include heading(14px, 1em 0 0.3em);
+ @include heading(1.2rem, 1em 0 0.3em);
+}
+
+h3 > code {
+ font-weight: bold;
+ font-size: 1.2rem;
}
h4 {
@@ -386,7 +391,6 @@ pre code {
.nav-group-task[data-name="MGLStyleFunction"],
.nav-group-task[data-name="MGLStyleLayer"],
.nav-group-task[data-name="MGLTileSource"],
-.nav-group-task[data-name="MGLAbstractShapeSource"],
.nav-group-task[data-name="MGLVectorStyleLayer"] {
.nav-group-task-link::after {
@extend %nav-group-task-gloss;
diff --git a/platform/darwin/resources/da.lproj/Foundation.strings b/platform/darwin/resources/da.lproj/Foundation.strings
new file mode 100644
index 0000000000..d8d18f453c
--- /dev/null
+++ b/platform/darwin/resources/da.lproj/Foundation.strings
@@ -0,0 +1,297 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ klokken";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ klokken";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "øst";
+
+/* East, short */
+"COMPASS_E_SHORT" = "Ø";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "øst via nord";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "ØvN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "øst via syd";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ØvS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "øst-nordøst";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ØNØ";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "øst-sydøst";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ØSØ";
+
+/* North, long */
+"COMPASS_N_LONG" = "nord";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "nord via øst";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NvØ";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "nord via vest";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NvV";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "nordøst";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NØ";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "nordøst via øst";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NØvØ";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "nordøst via nord";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NØvN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "nord-nordøst";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNØ";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "nord-nordvest";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNV";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "nordvest";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NV";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "nordvest via nord";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NVvN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "nordvest via vest";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NVvW";
+
+/* South, long */
+"COMPASS_S_LONG" = "syd";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "syd via øst";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SvØ";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "syd via vest";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SvV";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "sydøst";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SØ";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "sydøst via øst";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SØvØ";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "sydøst via syd";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SØvS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "syd-sydøst";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSØ";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "syd-sydvest";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSV";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "sydvest";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SV";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "sydvest via syd";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SVvS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "sydvest via vest";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SVvV";
+
+/* West, long */
+"COMPASS_W_LONG" = "vest";
+
+/* West, short */
+"COMPASS_W_SHORT" = "V";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "vest via nord";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "VvN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "vest via syd";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "VvS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "vest-nordvest";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "VNV";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "vest-sydvest";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "VSV";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d grad(er)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ og %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@, og %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@ øst";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ øst";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@Ø";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@ via %2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d minut(ter)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ nord";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ nord";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ syd";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ syd";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d sekunde(r)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ vest";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ vest";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@V";
+
+/* OpenStreetMap full name attribution */
+"OSM_FULL_NAME" = "OpenStreetMap";
+
+/* OpenStreetMap short name attribution */
+"OSM_SHORT_NAME" = "OSM";
+
diff --git a/platform/darwin/resources/da.lproj/Foundation.stringsdict b/platform/darwin/resources/da.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..17983b8aa8
--- /dev/null
+++ b/platform/darwin/resources/da.lproj/Foundation.stringsdict
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d grad</string>
+ <key>other</key>
+ <string>%d grader</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d minut</string>
+ <key>other</key>
+ <string>%d minutter</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d sekund</string>
+ <key>other</key>
+ <string>%d sekunder</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict b/platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..e99e86d98d
--- /dev/null
+++ b/platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d grau</string>
+ <key>other</key>
+ <string>%d graus</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d minuto</string>
+ <key>other</key>
+ <string>%d minutos</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d segundo</string>
+ <key>other</key>
+ <string>%d segundos</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 53a668d10b..c7b54b326a 100755
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -300,7 +300,17 @@ 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 expression containing any of the following:\n\n';
- doc += `* Constant ${describeType(property)} values\n`;
+ doc += `* Constant ${describeType(property)} values`;
+ if ('minimum' in property) {
+ if ('maximum' in property) {
+ doc += ` between ${formatNumber(property.minimum)} and ${formatNumber(property.maximum)} inclusive`;
+ } else {
+ doc += ` no less than ${formatNumber(property.minimum)}`;
+ }
+ } else if ('maximum' in property) {
+ doc += ` no greater than ${formatNumber(property.maximum)}`;
+ }
+ doc += '\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';
@@ -393,7 +403,7 @@ global.describeValue = function (value, property, layerType) {
case 'boolean':
return value ? '`YES`' : '`NO`';
case 'number':
- return 'the float `' + value + '`';
+ return 'the float ' + formatNumber(value);
case 'string':
if (value === '') {
return 'the empty string';
@@ -428,7 +438,7 @@ global.describeValue = function (value, property, layerType) {
if (color.r === 1 && color.g === 1 && color.b === 1 && color.a === 1) {
return '`UIColor.whiteColor`';
}
- return 'a `UIColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`;
+ return 'a `UIColor`' + ` object whose RGB value is ${formatNumber(color.r)}, ${formatNumber(color.g)}, ${formatNumber(color.b)} and whose alpha value is ${formatNumber(color.a)}`;
case 'array':
let units = property.units || '';
if (units) {
@@ -439,12 +449,12 @@ global.describeValue = function (value, property, layerType) {
if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) {
return 'an `NSValue` object containing `UIEdgeInsetsZero`';
}
- return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`;
+ return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${formatNumber(value[0])}${units} on the top, ${formatNumber(value[3])}${units} on the left, ${formatNumber(value[2])}${units} on the bottom, and ${formatNumber(value[1])}${units} on the right`;
case 'offset':
case 'translate':
- return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`;
+ return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${formatNumber(value[0])}${units} rightward and ${formatNumber(value[1])}${units} downward`;
case 'position':
- return 'an `MGLSphericalPosition` struct set to' + ` ${value[0]} radial, ${value[1]} azimuthal and ${value[2]} polar`;
+ return 'an `MGLSphericalPosition` struct set to' + ` ${formatNumber(value[0])} radial, ${formatNumber(value[1])} azimuthal and ${formatNumber(value[2])} polar`;
default:
return 'the array `' + value.join('`, `') + '`';
}
@@ -453,6 +463,10 @@ global.describeValue = function (value, property, layerType) {
}
};
+global.formatNumber = function (num) {
+ return num.toLocaleString().replace('-', '\u2212');
+}
+
global.propertyDefault = function (property, layerType) {
if (property.name === 'heatmap-color') {
return 'an expression that evaluates to a rainbow color scale from blue to red';
@@ -612,7 +626,7 @@ const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.
const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true});
const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true});
const forStyleAuthorsMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true });
-const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs', 'utf8'), { strict: true });
+const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Migrating to Expressions.md.ejs', 'utf8'), { strict: true });
const templatesMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Tile URL Templates.md.ejs', 'utf8'), { strict: true });
const lightH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLLight.h.ejs', 'utf8'), {strict: true});
@@ -650,13 +664,13 @@ function duplicatePlatformDecls(src) {
// Look for a documentation comment that contains “MGLColor” or “UIColor”
// and the subsequent function, method, or property declaration. Try not to
// match greedily.
- return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\*\/)(\s*[^;]+?\b(?:MGL|NS|UI)(?:Color|EdgeInsets(?:Zero)?)\b[^;]+?;)/g,
+ return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\b(?:MGL|NS|UI)Color\b[\s\S]*?\*\/)(\s*.+?;)/g,
(match, comment, decl) => {
- let macosComment = comment.replace(/\b(?:MGL|UI)(Color|EdgeInsets(?:Zero)?)\b/g, 'NS$1')
+ let macosComment = comment.replace(/\b(?:MGL|UI)Color\b/g, 'NSColor')
// Use the correct indefinite article.
- .replace(/\ba(\s+`?NS(?:Color|EdgeInsets))\b/gi, 'an$1');
- let iosDecl = decl.replace(/\bMGL(Color|EdgeInsets)\b/g, 'UI$1');
- let macosDecl = decl.replace(/\b(?:MGL|UI)(Color|EdgeInsets)\b/g, 'NS$1');
+ .replace(/\ba(\s+`?NSColor)\b/gi, 'an$1');
+ let iosDecl = decl.replace(/\bMGLColor\b/g, 'UIColor');
+ let macosDecl = decl.replace(/\b(?:MGL|UI)Color\b/g, 'NSColor');
return `\
#if TARGET_OS_IPHONE
${comment}${iosDecl}
@@ -743,10 +757,10 @@ writeIfModified(`platform/macos/docs/guides/For Style Authors.md`, forStyleAutho
renamedProperties: renamedPropertiesByLayerType,
layers: layers,
}));
-writeIfModified(`platform/ios/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
+writeIfModified(`platform/ios/docs/guides/Migrating to Expressions.md`, ddsGuideMD({
os: 'iOS',
}));
-writeIfModified(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
+writeIfModified(`platform/macos/docs/guides/Migrating to Expressions.md`, ddsGuideMD({
os: 'macOS',
}));
writeIfModified(`platform/ios/docs/guides/Tile URL Templates.md`, templatesMD({
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json
index b0c50a06f8..d47b13cdb2 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -9,28 +9,28 @@
"type": {
"values": {
"fill": {
- "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"fill-extrusion": {
- "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"line": {
- "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"symbol": {
- "doc": "An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at points or along lines on the map.\n\nUse a symbol style layer to configure the visual appearance of labels for features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or `MGLFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at points or along lines on the map.\n\nUse a symbol style layer to configure the visual appearance of feature labels. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLShape` or `MGLFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"circle": {
- "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object."
+ "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object."
},
"heatmap": {
- "doc": "An `MGLHeatmapStyleLayer` is a style layer that renders a <a href=\"https://en.wikipedia.org/wiki/Heat_map\">heatmap</a>.\n\nA heatmap visualizes the spatial distribution of a large, dense set of point data, using color to avoid cluttering the map with individual points at low zoom levels. The points are weighted by an attribute you specify. Use a heatmap style layer in conjunction with point or point collection features in vector tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.\n\nConsider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can alternatively cluster the source using the `MGLShapeSourceOptionClustered` option and render the data using an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer`."
+ "doc": "An `MGLHeatmapStyleLayer` is a style layer that renders a <a href=\"https://en.wikipedia.org/wiki/Heat_map\">heatmap</a>.\n\nA heatmap visualizes the spatial distribution of a large, dense set of point data, using color to avoid cluttering the map with individual points at low zoom levels. The points are weighted by an attribute you specify. Use a heatmap style layer in conjunction with point or point collection features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.\n\nConsider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point data in an `MGLShapeSource` is dense enough to warrant a heatmap, you can alternatively cluster the source using the `MGLShapeSourceOptionClustered` option and render the data using an `MGLCircleStyleLayer` or `MGLSymbolStyleLayer`."
},
"raster": {
- "doc": "An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster imagery on the map, especially raster tiles.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterSource` object or raster images loaded by an `MGLImageSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic.\n\nRaster images may also be used as icons or patterns in a style layer. To register an image for use as an icon or pattern, use the `-[MGLStyle setImage:forName:]` method. To configure a point annotation’s image, use the `MGLAnnotationImage` class."
+ "doc": "An `MGLRasterStyleLayer` is a style layer that renders georeferenced raster imagery on the map, especially raster tiles.\n\nUse a raster style layer to configure the color parameters of raster tiles loaded by an `MGLRasterTileSource` object or raster images loaded by an `MGLImageSource` object. For example, you could use a raster style layer to render <a href=\"https://www.mapbox.com/satellite/\">Mapbox Satellite</a> imagery, a <a href=\"https://www.mapbox.com/help/define-tileset/#raster-tilesets\">raster tile set</a> uploaded to Mapbox Studio, or a raster map authored in <a href=\"https://tilemill-project.github.io/tilemill/\">TileMill</a>, the classic Mapbox Editor, or Mapbox Studio Classic.\n\nRaster images may also be used as icons or patterns in a style layer. To register an image for use as an icon or pattern, use the `-[MGLStyle setImage:forName:]` method. To configure a point annotation’s image, use the `MGLAnnotationImage` class."
},
"hillshade": {
- "doc": "An `MGLHillshadeStyleLayer` is a style layer that renders raster <a href=\"https://en.wikipedia.org/wiki/Digital_elevation_model\">digital elevation model</a> (DEM) tiles on the map.\n\nUse a hillshade style layer to configure the color parameters of raster tiles loaded by an `MGLRasterDEMSource` object. For example, you could use a hillshade style layer to render <a href=\"https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb\">Mapbox Terrain-RGB</a> data.\n\nTo display posterized hillshading based on vector shapes, as with the <a href=\"https://www.mapbox.com/vector-tiles/mapbox-terrain/\">Mapbox Terrain</a> source, use an `MGLVectorSource` object in conjunction with several `MGLFillStyleLayer` objects."
+ "doc": "An `MGLHillshadeStyleLayer` is a style layer that renders raster <a href=\"https://en.wikipedia.org/wiki/Digital_elevation_model\">digital elevation model</a> (DEM) tiles on the map.\n\nUse a hillshade style layer to configure the color parameters of raster tiles loaded by an `MGLRasterDEMSource` object. For example, you could use a hillshade style layer to render <a href=\"https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb\">Mapbox Terrain-RGB</a> data.\n\nTo display posterized hillshading based on vector shapes, as with the <a href=\"https://www.mapbox.com/vector-tiles/mapbox-terrain/\">Mapbox Terrain</a> source, use an `MGLVectorTileSource` object in conjunction with several `MGLFillStyleLayer` objects."
},
"background": {
"doc": "An `MGLBackgroundStyleLayer` is a style layer that covers the entire map. Use a background style layer to configure a color or pattern to show below all other map content. If the style’s other layers use the Mapbox Streets source, the background style layer is responsible for drawing land, whereas the oceans and other bodies of water are drawn by `MGLFillStyleLayer` objects.\n\nA background style layer is typically the bottommost layer in a style, because it covers the entire map and can occlude any layers below it. You can therefore access it by getting the last item in the `MGLStyle.layers` array.\n\nIf the background style layer is transparent or omitted from the style, any portion of the map view that does not show another style layer is transparent."
@@ -46,10 +46,10 @@
"doc": "Offset distance of icon from its anchor."
},
"icon-image": {
- "doc": "Name of image in sprite to use for drawing an image background. Within literal values, attribute names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named attribute. Expressions do not support this syntax; for equivalent functionality in expressions, use `stringByAppendingString:` and key path expressions."
+ "doc": "Name of a style image to use for drawing an image background.\n\nUse the `+[MGLStyle setImage:forName:]` method to associate an image with a name that you can set this property to.\n\nWithin a constant string value, a feature attribute name enclosed in curly braces (e.g., `{token}`) is replaced with the value of the named attribute. Tokens inside non-constant expressions are ignored; instead, use `mgl_join:` and key path expressions."
},
"text-field": {
- "doc": "Value to use for a text label. Within literal values, attribute names enclosed in curly brackets (e.g. `{token}`) are replaced with the value of the named attribute. Expressions do not support this syntax; for equivalent functionality in expressions, use `stringByAppendingString:` and key path expressions."
+ "doc": "Value to use for a text label.\n\nWithin a constant string value, a feature attribute name enclosed in curly braces (e.g., `{token}`) is replaced with the value of the named attribute. Tokens inside non-constant expressions are ignored; instead, use `mgl_join:` and key path expressions."
},
"text-font": {
"doc": "An array of font face names used to display the text.\n\nEach font name must be included in the `{fontstack}` portion of the JSON stylesheet’s <a href=\"https://www.mapbox.com/mapbox-gl-style-spec/#glyphs\"><code>glyphs</code></a> property. You can register a custom font when designing the style in Mapbox Studio. Fonts installed on the system are not used.\n\nThe first font named in the array is applied to the text. For each character in the text, if the first font lacks a glyph for the character, the next font is applied as a fallback, and so on."
@@ -84,7 +84,7 @@
},
"paint_heatmap": {
"heatmap-color": {
- "doc": "Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `$heatmapDensity` as input."
+ "doc": "The color of each screen point based on its density value in a heatmap. This property is normally set to an interpolation or step expression with the `$heatmapDensity` value as its input."
}
},
"paint_line": {
diff --git a/platform/darwin/src/MGLAbstractShapeSource.h b/platform/darwin/src/MGLAbstractShapeSource.h
deleted file mode 100644
index d61f41fbf6..0000000000
--- a/platform/darwin/src/MGLAbstractShapeSource.h
+++ /dev/null
@@ -1,124 +0,0 @@
-#import "MGLSource.h"
-
-/**
- Options for `MGLShapeSource` objects.
- */
-typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
-
-/**
- An `NSNumber` object containing a Boolean enabling or disabling clustering.
- If the `shape` property contains point shapes, setting this option to
- `YES` clusters the points by radius into groups. The default value is `NO`.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
- source property in the Mapbox Style Specification.
-
- This option only affects point features within a shape source.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
-
-/**
- An `NSNumber` object containing an integer; specifies the radius of each
- cluster if clustering is enabled. A value of 512 produces a radius equal to
- the width of a tile. The default value is 50.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to cluster points if clustering is enabled. Defaults to one zoom level
- less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
- maximum zoom level, the shapes are not clustered.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
-
-/**
- An `NSNumber` object containing an integer; specifies the minimum zoom level at
- which to create vector tiles. The default value is 0.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-minzoom"><code>minzoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to create vector tiles. A greater value produces greater detail at high
- zoom levels. The default value is 18.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
-
-/**
- An `NSNumber` object containing an integer; specifies the size of the tile
- buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
- buffer as wide as the tile itself. Larger values produce fewer rendering
- artifacts near tile edges and slower performance. The default value is 128.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
-
-/**
- An `NSNumber` object containing a double; specifies the Douglas-Peucker
- simplification tolerance. A greater value produces simpler geometries and
- improves performance. The default value is 0.375.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
-
-/**
- An `NSNumber` object containing a Boolean value; specifies whether the shape of
- an `MGLComputedShapeSource` should be wrapped to accomodate coordinates with
- longitudes beyond −180 and 180. The default value is `NO`.
-
- Setting this option to `YES` affects rendering performance.
-
- This option is ignored when creating an instance of a class besides
- `MGLComputedShapeSource`.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates;
-
-/**
- An `NSNumber` object containing a Boolean value; specifies whether the shape of
- an `MGLComputedShapeSource` should be clipped at the edge of each tile. The
- default value is `NO`.
-
- Setting this option to `YES` affects rendering performance. Use this option to
- clip `MGLPolyline`s and `MGLPolygon`s at tile boundaries without artifacts.
-
- This option is ignored when creating an instance of a class besides
- `MGLComputedShapeSource`.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates;
-
-/**
- `MGLAbstractShapeSource` is an abstract base class for map content sources that
- supply vector shapes to be shown on the map. A shape source is added to an
- `MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
- layer defines the appearance of any content supplied by the shape source.
-
-
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of `MGLShapeSource` or
- `MGLComputedShapeSource`.
- */
-MGL_EXPORT
-@interface MGLAbstractShapeSource : MGLSource
-
-
-@end
diff --git a/platform/darwin/src/MGLAbstractShapeSource.mm b/platform/darwin/src/MGLAbstractShapeSource.mm
deleted file mode 100644
index eb0807cee0..0000000000
--- a/platform/darwin/src/MGLAbstractShapeSource.mm
+++ /dev/null
@@ -1,136 +0,0 @@
-#import "MGLAbstractShapeSource.h"
-#import "MGLAbstractShapeSource_Private.h"
-
-const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
-const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
-const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
-const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
-const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
-const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates";
-const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates";
-
-@interface MGLAbstractShapeSource ()
-
-@end
-
-@implementation MGLAbstractShapeSource
-
-@end
-
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
- auto geoJSONOptions = mbgl::style::GeoJSONOptions();
-
- if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
- }
- geoJSONOptions.minzoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
- }
- geoJSONOptions.maxzoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
- }
- geoJSONOptions.buffer = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
- }
- geoJSONOptions.tolerance = value.doubleValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
- }
- geoJSONOptions.clusterRadius = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
- }
- geoJSONOptions.clusterMaxZoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
- }
- geoJSONOptions.cluster = value.boolValue;
- }
-
- return geoJSONOptions;
-}
-
-mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
- mbgl::style::CustomGeometrySource::Options sourceOptions;
-
- if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
- }
- sourceOptions.zoomRange.min = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
- }
- sourceOptions.zoomRange.max = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
- }
- sourceOptions.tileOptions.buffer = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
- }
- sourceOptions.tileOptions.tolerance = value.doubleValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionWrapsCoordinates]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionWrapsCoordinates must be an NSNumber."];
- }
- sourceOptions.tileOptions.wrap = value.boolValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClipsCoordinates]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClipsCoordinates must be an NSNumber."];
- }
- sourceOptions.tileOptions.clip = value.boolValue;
- }
-
- return sourceOptions;
-}
diff --git a/platform/darwin/src/MGLAccountManager.h b/platform/darwin/src/MGLAccountManager.h
index 741cc323cb..436e45ca9b 100644
--- a/platform/darwin/src/MGLAccountManager.h
+++ b/platform/darwin/src/MGLAccountManager.h
@@ -5,7 +5,7 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The MGLAccountManager object provides a global way to set a Mapbox API access
+ The `MGLAccountManager` object provides a global way to set a Mapbox API access
token.
*/
MGL_EXPORT
@@ -14,9 +14,9 @@ MGL_EXPORT
#pragma mark Authorizing Access
/**
- Set the
+ The
<a href="https://www.mapbox.com/help/define-access-token/">Mapbox access token</a>
- to be used by all instances of MGLMapView in the current application.
+ used by all instances of `MGLMapView` in the current application.
Mapbox-hosted vector tiles and styles require an API access token, which you
can obtain from the
@@ -25,26 +25,18 @@ MGL_EXPORT
your Mapbox account. They also deter other developers from using your styles
without your permission.
- @param accessToken A Mapbox access token. Calling this method with a value of
- `nil` has no effect.
+ Setting this property to a value of `nil` has no effect.
@note You must set the access token before attempting to load any Mapbox-hosted
style. Therefore, you should generally set it before creating an instance of
- MGLMapView. The recommended way to set an access token is to add an entry to
- your application’s Info.plist file with the key `MGLMapboxAccessToken` and
- the type String. Alternatively, you may call this method from your
+ `MGLMapView`. The recommended way to set an access token is to add an entry
+ to your application’s Info.plist file with the key `MGLMapboxAccessToken`
+ and the type `String`. Alternatively, you may call this method from your
application delegate’s `-applicationDidFinishLaunching:` method.
*/
-+ (void)setAccessToken:(nullable NSString *)accessToken;
+@property (class, nullable) NSString *accessToken;
-/**
- Returns the
- <a href="https://www.mapbox.com/help/define-access-token/">Mapbox access token</a>
- in use by instances of MGLMapView in the current application.
- */
-+ (nullable NSString *)accessToken;
-
-+ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((deprecated("Telemetry settings are now always shown in the ℹ️ menu.")));
++ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((unavailable("Telemetry settings are now always shown in the ℹ️ menu.")));
@end
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index 729caeaa31..73da8ddd63 100644
--- a/platform/darwin/src/MGLAccountManager.m
+++ b/platform/darwin/src/MGLAccountManager.m
@@ -54,11 +54,6 @@
return _sharedManager;
}
-+ (BOOL)mapboxMetricsEnabledSettingShownInApp {
- NSLog(@"mapboxMetricsEnabledSettingShownInApp is no longer necessary; the Mapbox Maps SDK for iOS has changed to always provide a telemetry setting in-app.");
- return YES;
-}
-
+ (void)setAccessToken:(NSString *)accessToken {
accessToken = [accessToken stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
@@ -69,9 +64,7 @@
[MGLAccountManager sharedManager].accessToken = accessToken;
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- // Update MGLMapboxEvents
- // NOTE: This is (likely) the initial setup of MGLMapboxEvents
- [MGLMapboxEvents sharedManager];
+ [MGLMapboxEvents setupWithAccessToken:accessToken];
#endif
}
diff --git a/platform/darwin/src/MGLAccountManager_Private.h b/platform/darwin/src/MGLAccountManager_Private.h
index 655af08f20..b68016b1a7 100644
--- a/platform/darwin/src/MGLAccountManager_Private.h
+++ b/platform/darwin/src/MGLAccountManager_Private.h
@@ -3,7 +3,7 @@
@interface MGLAccountManager (Private)
/// Returns the shared instance of the `MGLAccountManager` class.
-+ (instancetype)sharedManager;
+@property (class, nonatomic, readonly) MGLAccountManager *sharedManager;
/// The current global access token.
@property (atomic) NSString *accessToken;
diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm
index 73147270c1..07d10e852b 100644
--- a/platform/darwin/src/MGLAttributionInfo.mm
+++ b/platform/darwin/src/MGLAttributionInfo.mm
@@ -49,7 +49,12 @@
CGFloat blue;
CGFloat alpha;
#if !TARGET_OS_IPHONE
- linkColor = [linkColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ // CSS uses the sRGB color space.
+ if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) {
+ linkColor = [linkColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ } else {
+ linkColor = [linkColor colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
+ }
#endif
[linkColor getRed:&red green:&green blue:&blue alpha:&alpha];
[css appendFormat:
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h
index 9d2673c859..d5c3ed5403 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.h
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.h
@@ -37,6 +37,7 @@ which it is added.
#pragma mark - Accessing the Paint Attributes
+#if TARGET_OS_IPHONE
/**
The color with which the background will be drawn.
@@ -59,6 +60,30 @@ which it is added.
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *backgroundColor;
+#else
+/**
+ The color with which the background will be drawn.
+
+ The default value of this property is an expression that evaluates to
+ `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 expression containing any of the following:
+
+ * Constant `NSColor` 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) NSExpression *backgroundColor;
+#endif
/**
The transition affecting any changes to this layer’s `backgroundColor` property.
@@ -71,11 +96,11 @@ which it is added.
The opacity at which the background will be drawn.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm
index 7c4cf79709..993645d3a8 100644
--- a/platform/darwin/src/MGLBackgroundStyleLayer.mm
+++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm
@@ -116,7 +116,8 @@
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultBackgroundPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setBackgroundPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h
index caa6d2f6cb..06b4de32f0 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.h
+++ b/platform/darwin/src/MGLCircleStyleLayer.h
@@ -62,9 +62,10 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) {
circles on the map.
Use a circle style layer to configure the visual appearance of point or point
- collection features in vector tiles loaded by an `MGLVectorSource` object or
- `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or
- `MGLPointCollectionFeature` instances in an `MGLShapeSource` object.
+ collection features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`,
+ `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature`
+ instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.
A circle style layer renders circles whose radii are measured in screen units.
To display circles on the map whose radii correspond to real-world distances,
@@ -83,7 +84,7 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) {
let layer = MGLCircleStyleLayer(identifier: "circles", source: population)
layer.sourceLayerIdentifier = "population"
layer.circleColor = NSExpression(forConstantValue: UIColor.green)
- layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)",
+ layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
[12: 2,
22: 180])
layer.circleOpacity = NSExpression(forConstantValue: 0.7)
@@ -116,7 +117,7 @@ MGL_EXPORT
full opacity.
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.
+ 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:
@@ -136,6 +137,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition circleBlurTransition;
+#if TARGET_OS_IPHONE
/**
The fill color of the circle.
@@ -153,6 +155,25 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *circleColor;
+#else
+/**
+ The fill color of the circle.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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 *circleColor;
+#endif
/**
The transition affecting any changes to this layer’s `circleColor` property.
@@ -165,11 +186,11 @@ MGL_EXPORT
The opacity at which the circle will be drawn.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -214,11 +235,11 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -264,6 +285,7 @@ MGL_EXPORT
@property (nonatomic, null_resettable) NSExpression *circlePitchScale __attribute__((unavailable("Use circleScaleAlignment instead.")));
+#if TARGET_OS_IPHONE
/**
The stroke color of the circle.
@@ -281,6 +303,25 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *circleStrokeColor;
+#else
+/**
+ The stroke color of the circle.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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 *circleStrokeColor;
+#endif
/**
The transition affecting any changes to this layer’s `circleStrokeColor` property.
@@ -293,11 +334,11 @@ MGL_EXPORT
The opacity of the circle's stroke.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -320,11 +361,11 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h
index f90f2c94b1..068c49245c 100644
--- a/platform/darwin/src/MGLComputedShapeSource.h
+++ b/platform/darwin/src/MGLComputedShapeSource.h
@@ -1,15 +1,38 @@
-#import "MGLAbstractShapeSource.h"
-
#import "MGLFoundation.h"
#import "MGLGeometry.h"
#import "MGLTypes.h"
-#import "MGLShape.h"
+#import "MGLShapeSource.h"
NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
/**
+ An `NSNumber` object containing a Boolean value; specifies whether the shape of
+ an `MGLComputedShapeSource` should be wrapped to accomodate coordinates with
+ longitudes beyond −180 and 180. The default value is `NO`.
+
+ Setting this option to `YES` affects rendering performance.
+
+ This option is used with the `MGLComputedShapeSource` class; it is ignored when
+ creating an `MGLShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates;
+
+/**
+ An `NSNumber` object containing a Boolean value; specifies whether the shape of
+ an `MGLComputedShapeSource` should be clipped at the edge of each tile. The
+ default value is `NO`.
+
+ Setting this option to `YES` affects rendering performance. Use this option to
+ clip `MGLPolyline`s and `MGLPolygon`s at tile boundaries without artifacts.
+
+ This option is used with the `MGLComputedShapeSource` class; it is ignored when
+ creating an `MGLShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates;
+
+/**
Data source for `MGLComputedShapeSource`. This protocol defines two optional methods for fetching
data, one based on tile coordinates, and one based on a bounding box. Classes that implement this
protocol must implement one, and only one of the methods. Methods on this protocol will not be
@@ -38,21 +61,42 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
- A source for vector data that is fetched one tile at a time. Useful for sources that are
- too large to fit in memory, or are already divided into tiles, but not in Mapbox Vector Tile format.
+ `MGLComputedShapeSource` is a map content source that supplies vector shapes,
+ one tile at a time, to be shown on the map on demand. You implement a class
+ conforming to the `MGLComputedShapeSourceDataSource` protocol that returns
+ instances of `MGLShape` or `MGLFeature`, then add a computed shape source to an
+ `MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
+ layer defines the appearance of any content supplied by the computed shape
+ source.
- Supported options are `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
- `MGLShapeSourceOptionBuffer`, and `MGLShapeSourceOptionSimplificationTolerance.` This source does
- not support clustering.
+ `MGLComputedShapeSource` is similar to `MGLShapeSource` but is optimized for
+ data sets that change dynamically or are too large to fit completely in memory.
+ It is also useful for data that is divided into tiles in a format other than
+ <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tiles</a>. For
+ <a href="http://geojson.org/">GeoJSON</a> data, use the `MGLShapeSource` class.
+ For static tiles or Mapbox Vector Tiles, use the `MGLVectorTileSource` class.
+
+ You can add and remove sources dynamically using methods such as
+ `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`. This class
+ cannot be represented in a style JSON file; you must add it ot the style at
+ runtime.
*/
MGL_EXPORT
-@interface MGLComputedShapeSource : MGLAbstractShapeSource
+@interface MGLComputedShapeSource : MGLSource
/**
Returns a custom shape data source initialized with an identifier, and a
dictionary of options for the source according to the
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
specification</a>.
+
+ This class supports the following options:
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`,
+ `MGLShapeSourceOptionSimplificationTolerance`,
+ `MGLShapeSourceOptionWrapsCoordinates`, and
+ `MGLShapeSourceOptionClipsCoordinates`. Shapes provided by a computed
+ shape source cannot be clustered.
@param identifier A string that uniquely identifies the source.
@param options An `NSDictionary` of options for this source.
@@ -64,6 +108,14 @@ MGL_EXPORT
dictionary of options for the source according to the
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
specification</a>.
+
+ This class supports the following options:
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`,
+ `MGLShapeSourceOptionSimplificationTolerance`,
+ `MGLShapeSourceOptionWrapsCoordinates`, and
+ `MGLShapeSourceOptionClipsCoordinates`. Shapes provided by a computed shape
+ source cannot be clustered.
@param identifier A string that uniquely identifies the source.
@param options An `NSDictionary` of options for this source.
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
index 0a3c92bb97..fb25eb8eb4 100644
--- a/platform/darwin/src/MGLComputedShapeSource.mm
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -1,9 +1,8 @@
-#import "MGLComputedShapeSource.h"
+#import "MGLComputedShapeSource_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
#import "MGLShape_Private.h"
-#import "MGLAbstractShapeSource_Private.h"
#import "MGLGeometry_Private.h"
#include <mbgl/map/map.hpp>
@@ -11,6 +10,63 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/geojson.hpp>
+const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates";
+const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates";
+
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ mbgl::style::CustomGeometrySource::Options sourceOptions;
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.min = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.max = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.tolerance = value.doubleValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionWrapsCoordinates]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionWrapsCoordinates must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.wrap = value.boolValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClipsCoordinates]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClipsCoordinates must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.clip = value.boolValue;
+ }
+
+ return sourceOptions;
+}
+
@interface MGLComputedShapeSource () {
std::unique_ptr<mbgl::style::CustomGeometrySource> _pendingSource;
}
diff --git a/platform/darwin/src/MGLAbstractShapeSource_Private.h b/platform/darwin/src/MGLComputedShapeSource_Private.h
index ddde55b149..e1887caf8d 100644
--- a/platform/darwin/src/MGLAbstractShapeSource_Private.h
+++ b/platform/darwin/src/MGLComputedShapeSource_Private.h
@@ -1,22 +1,12 @@
-#import "MGLAbstractShapeSource.h"
-
#import "MGLFoundation.h"
#import "MGLTypes.h"
-#import "MGLShape.h"
+#import "MGLComputedShapeSource.h"
-#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/style/sources/custom_geometry_source.hpp>
NS_ASSUME_NONNULL_BEGIN
-@interface MGLAbstractShapeSource (Private)
-
-MGL_EXPORT
-
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
-
MGL_EXPORT
mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
-@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index a13821cf96..d0c9e26062 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The `MGLFeature` protocol is used to provide details about geographic features
- contained in an `MGLShapeSource` or `MGLVectorSource` object. Each concrete
+ contained in an `MGLShapeSource` or `MGLVectorTileSource` object. Each concrete
subclass of `MGLShape` in turn has a subclass that conforms to this protocol. A
feature object associates a shape with an optional identifier and attributes.
@@ -23,8 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
In addition to adding data to the map, you can also extract data from the map:
`-[MGLMapView visibleFeaturesAtPoint:]` and related methods return feature
objects that correspond to features in the source. This enables you to inspect
- the properties of features in vector tiles loaded by `MGLVectorSource` objects.
- You also reuse these feature objects as overlay annotations.
+ the properties of features in vector tiles loaded by `MGLVectorTileSource`
+ objects. You also reuse these feature objects as overlay annotations.
While it is possible to add `MGLFeature`-conforming objects to the map as
annotations using `-[MGLMapView addAnnotations:]` and related methods, doing so
@@ -43,12 +43,12 @@ NS_ASSUME_NONNULL_BEGIN
source.
You can configure an `MGLVectorStyleLayer` object to include or exclude a
- specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ specific feature in an `MGLShapeSource` or `MGLVectorTileSource`. In the
`MGLVectorStyleLayer.predicate` property, compare the special `$id` attribute
to the feature’s identifier.
- In vector tiles loaded by `MGLVectorSource` objects, the identifier corresponds
- to the
+ In vector tiles loaded by `MGLVectorTileSource` objects, the identifier
+ corresponds to the
<a href="https://github.com/mapbox/vector-tile-spec/tree/master/2.1#42-features">feature identifier</a>
(`id`). If the source does not specify the feature’s identifier, the value of
this property is `nil`. If specified, the identifier may be an integer,
@@ -83,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN
A dictionary of attributes for this feature.
You can configure an `MGLVectorStyleLayer` object to include or exclude a
- specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ specific feature in an `MGLShapeSource` or `MGLVectorTileSource`. In the
`MGLVectorStyleLayer.predicate` property, compare a key of the attribute
dictionary to the value you want to include. For example, if you want an
`MGLLineStyleLayer` object to display only important features, you might assign
@@ -107,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
`MGLSymbolStyleLayer.textField` to an `MGLStyleValue` object containing the
raw string value `{name}`.
- In vector tiles loaded by `MGLVectorSource` objects, the keys and values of
+ In vector tiles loaded by `MGLVectorTileSource` objects, the keys and values of
each feature’s attribute dictionary are determined by the source. Each
attribute name is a string, while each attribute value may be a null value,
Boolean value, integer, floating-point number, or string. These data types are
@@ -238,11 +238,12 @@ MGL_EXPORT
An `MGLShapeCollectionFeature` object associates a shape collection with an
optional identifier and attributes.
- `MGLShapeCollectionFeature` is most commonly used to add multiple shapes to a single
- `MGLShapeSource`. Configure the appearance of an `MGLSource`’s shape collection
- collectively using an `MGLSymbolStyleLayer` object, or use multiple instances of
- `MGLCircleStyleLayer`, `MGLFillStyleLayer`, and `MGLLineStyleLayer` to
- configure the appearance of each kind of shape inside the collection.
+ `MGLShapeCollectionFeature` is most commonly used to add multiple shapes to a
+ single `MGLShapeSource`. Configure the appearance of an `MGLSource`’s shape
+ collection collectively using an `MGLSymbolStyleLayer` object, or use multiple
+ instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`, and
+ `MGLLineStyleLayer` to configure the appearance of each kind of shape inside
+ the collection.
A shape collection feature is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.3">feature collection</a>
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
index d1d5af6ba2..7c3a0773e4 100644
--- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
@@ -29,9 +29,10 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) {
extruded polygons on the map.
Use a fill-extrusion style layer to configure the visual appearance of polygon
- or multipolygon features in vector tiles loaded by an `MGLVectorSource` object
- or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
- `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+ or multipolygon features. These features can come from vector tiles loaded by
+ an `MGLVectorTileSource` object, or they can be `MGLPolygon`,
+ `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances
+ in an `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing fill-extrusion style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -77,14 +78,14 @@ MGL_EXPORT
This property is measured in meters.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -100,6 +101,7 @@ 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
@@ -123,6 +125,31 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *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 expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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 *fillExtrusionColor;
+#endif
/**
The transition affecting any changes to this layer’s `fillExtrusionColor` property.
@@ -137,11 +164,11 @@ MGL_EXPORT
This property is measured in meters.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -162,11 +189,11 @@ MGL_EXPORT
per-layer, not per-feature, basis, and data-driven styling is not available.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
index 1baa264689..688ce4c1ac 100644
--- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
@@ -231,7 +231,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultFillExtrusionPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h
index 5caab91b45..a159a924e6 100644
--- a/platform/darwin/src/MGLFillStyleLayer.h
+++ b/platform/darwin/src/MGLFillStyleLayer.h
@@ -28,9 +28,10 @@ typedef NS_ENUM(NSUInteger, MGLFillTranslationAnchor) {
optionally stroked) polygons on the map.
Use a fill style layer to configure the visual appearance of polygon or
- multipolygon features in vector tiles loaded by an `MGLVectorSource` object or
- `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
- `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+ multipolygon features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`,
+ `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource`
+ or `MGLComputedShapeSource` object.
You can access an existing fill style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -94,6 +95,7 @@ MGL_EXPORT
@property (nonatomic, null_resettable) NSExpression *fillAntialias __attribute__((unavailable("Use fillAntialiased instead.")));
+#if TARGET_OS_IPHONE
/**
The color of the filled part of this layer.
@@ -114,6 +116,28 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *fillColor;
+#else
+/**
+ The color of the filled part of this layer.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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 *fillColor;
+#endif
/**
The transition affecting any changes to this layer’s `fillColor` property.
@@ -127,11 +151,11 @@ MGL_EXPORT
value will also affect the 1pt stroke around the fill, if the stroke is used.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -147,6 +171,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition fillOpacityTransition;
+#if TARGET_OS_IPHONE
/**
The outline color of the fill. Matches the value of `fillColor` if unspecified.
@@ -164,6 +189,25 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *fillOutlineColor;
+#else
+/**
+ 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 expression that evaluates to `YES`.
+ Otherwise, it is ignored.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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 *fillOutlineColor;
+#endif
/**
The transition affecting any changes to this layer’s `fillOutlineColor` property.
diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm
index ab28b414b5..c975e28d6b 100644
--- a/platform/darwin/src/MGLFillStyleLayer.mm
+++ b/platform/darwin/src/MGLFillStyleLayer.mm
@@ -220,7 +220,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultFillPattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h
index 7c68033abf..a8d3759106 100644
--- a/platform/darwin/src/MGLGeometry.h
+++ b/platform/darwin/src/MGLGeometry.h
@@ -14,6 +14,24 @@ typedef struct __attribute__((objc_boxable)) MGLCoordinateSpan {
CLLocationDegrees longitudeDelta;
} MGLCoordinateSpan;
+/* Defines a point on the map in Mercator projection for a specific zoom level. */
+typedef struct __attribute__((objc_boxable)) MGLMapPoint {
+ /** X coordinate representing a longitude in Mercator projection. */
+ CGFloat x;
+ /** Y coordinate representing a latitide in Mercator projection. */
+ CGFloat y;
+ /** Zoom level at which the X and Y coordinates are valid. */
+ CGFloat zoomLevel;
+} MGLMapPoint;
+
+/* Defines a 4x4 matrix. */
+typedef struct MGLMatrix4 {
+ double m00, m01, m02, m03;
+ double m10, m11, m12, m13;
+ double m20, m21, m22, m23;
+ double m30, m31, m32, m33;
+} MGLMatrix4;
+
/**
Creates a new `MGLCoordinateSpan` from the given latitudinal and longitudinal
deltas.
@@ -26,6 +44,17 @@ NS_INLINE MGLCoordinateSpan MGLCoordinateSpanMake(CLLocationDegrees latitudeDelt
}
/**
+ Creates a new `MGLMapPoint` from the given X and Y coordinates, and zoom level.
+ */
+NS_INLINE MGLMapPoint MGLMapPointMake(CGFloat x, CGFloat y, CGFloat zoomLevel) {
+ MGLMapPoint point;
+ point.x = x;
+ point.y = y;
+ point.zoomLevel = zoomLevel;
+ return point;
+}
+
+/**
Returns `YES` if the two coordinate spans represent the same latitudinal change
and the same longitudinal change.
*/
@@ -181,4 +210,7 @@ NS_INLINE CLLocationDegrees MGLDegreesFromRadians(CGFloat radians) {
return radians * 180 / M_PI;
}
+/** Returns Mercator projection of a WGS84 coordinate at the specified zoom level. */
+extern MGL_EXPORT MGLMapPoint MGLMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel);
+
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLGeometry.mm b/platform/darwin/src/MGLGeometry.mm
index 715a70f0b8..6785779570 100644
--- a/platform/darwin/src/MGLGeometry.mm
+++ b/platform/darwin/src/MGLGeometry.mm
@@ -106,3 +106,19 @@ CGPoint MGLPointRounded(CGPoint point) {
#endif
return CGPointMake(round(point.x * scaleFactor) / scaleFactor, round(point.y * scaleFactor) / scaleFactor);
}
+
+MGLMapPoint MGLMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel) {
+ mbgl::Point<double> projectedCoordinate = mbgl::Projection::project(MGLLatLngFromLocationCoordinate2D(coordinate), std::pow(2.0, zoomLevel));
+ return MGLMapPointMake(projectedCoordinate.x, projectedCoordinate.y, zoomLevel);
+}
+
+MGLMatrix4 MGLMatrix4Make(std::array<double, 16> array) {
+ MGLMatrix4 mat4 = {
+ .m00 = array[0], .m01 = array[1], .m02 = array[2], .m03 = array[3],
+ .m10 = array[4], .m11 = array[5], .m12 = array[6], .m13 = array[7],
+ .m20 = array[8], .m21 = array[9], .m22 = array[10], .m23 = array[11],
+ .m30 = array[12], .m31 = array[13], .m32 = array[14], .m33 = array[15]
+ };
+ return mat4;
+}
+
diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h
index 8b9c6c2327..0bff9b09f5 100644
--- a/platform/darwin/src/MGLGeometry_Private.h
+++ b/platform/darwin/src/MGLGeometry_Private.h
@@ -128,3 +128,5 @@ MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoor
CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate);
CGPoint MGLPointRounded(CGPoint point);
+
+MGLMatrix4 MGLMatrix4Make(std::array<double, 16> mat);
diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h
index 35095fd52e..ad7ba5de01 100644
--- a/platform/darwin/src/MGLHeatmapStyleLayer.h
+++ b/platform/darwin/src/MGLHeatmapStyleLayer.h
@@ -13,10 +13,11 @@ NS_ASSUME_NONNULL_BEGIN
A heatmap visualizes the spatial distribution of a large, dense set of point
data, using color to avoid cluttering the map with individual points at low
zoom levels. The points are weighted by an attribute you specify. Use a heatmap
- style layer in conjunction with point or point collection features in vector
- tiles loaded by an `MGLVectorSource` object or `MGLPointAnnotation`,
- `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature`
- instances in an `MGLShapeSource` object.
+ style layer in conjunction with point or point collection features. These
+ features can come from vector tiles loaded by an `MGLVectorTileSource` object,
+ or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`,
+ or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or
+ `MGLComputedShapeSource` object.
Consider accompanying a heatmap style layer with an `MGLCircleStyleLayer` or
`MGLSymbolStyleLayer` at high zoom levels. If you are unsure whether the point
@@ -35,10 +36,10 @@ NS_ASSUME_NONNULL_BEGIN
```swift
let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
- layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.heatmapWeight = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(magnitude, 'linear', nil, %@)",
[0: 0,
6: 1])
- layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.heatmapIntensity = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
[0: 1,
9: 3])
mapView.style?.addLayer(layer)
@@ -64,9 +65,11 @@ MGL_EXPORT
#pragma mark - Accessing the Paint Attributes
+#if TARGET_OS_IPHONE
/**
- Defines the color of each point based on its density value in a heatmap. Should
- be an expression that uses `$heatmapDensity` as input.
+ The color of each screen point based on its density value in a heatmap. This
+ property is normally set to an interpolation or step expression with the
+ `$heatmapDensity` value as its input.
The default value of this property is an expression that evaluates to a rainbow
color scale from blue to red. Set this property to `nil` to reset it to the
@@ -84,17 +87,40 @@ MGL_EXPORT
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *heatmapColor;
+#else
+/**
+ The color of each screen point based on its density value in a heatmap. This
+ property is normally set to an interpolation or step expression with the
+ `$heatmapDensity` value as its input.
+
+ The default value of this property is an expression that evaluates to a rainbow
+ color scale from blue to red. 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 `NSColor` values
+ * Predefined functions, including mathematical and string operators
+ * Conditional expressions
+ * Variable assignments and references to assigned variables
+ * Interpolation and step functions applied to the `$heatmapDensity` variable
+
+ This property does not support applying interpolation or step functions to
+ feature attributes.
+ */
+@property (nonatomic, null_resettable) NSExpression *heatmapColor;
+#endif
/**
Similar to `heatmapWeight` but controls the intensity of the heatmap globally.
Primarily used for adjusting the heatmap based on zoom level.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -116,11 +142,11 @@ MGL_EXPORT
The global opacity at which the heatmap layer will be drawn.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -145,11 +171,11 @@ MGL_EXPORT
This property is measured in points.
The default value of this property is an expression that evaluates to the float
- `30`. Set this property to `nil` to reset it to the default value.
+ 30. 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
+ * Constant numeric values no less than 1
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -171,11 +197,11 @@ MGL_EXPORT
Especially useful when combined with clustering.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h
index b2eeb59aba..224680160a 100644
--- a/platform/darwin/src/MGLHillshadeStyleLayer.h
+++ b/platform/darwin/src/MGLHillshadeStyleLayer.h
@@ -36,7 +36,7 @@ typedef NS_ENUM(NSUInteger, MGLHillshadeIlluminationAnchor) {
To display posterized hillshading based on vector shapes, as with the <a
href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>
- source, use an `MGLVectorSource` object in conjunction with several
+ source, use an `MGLVectorTileSource` object in conjunction with several
`MGLFillStyleLayer` objects.
You can access an existing hillshade style layer using the
@@ -75,6 +75,7 @@ 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.
@@ -95,6 +96,28 @@ MGL_EXPORT
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *hillshadeAccentColor;
+#else
+/**
+ The shading color used to accentuate rugged terrain like sharp cliffs and
+ gorges.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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) NSExpression *hillshadeAccentColor;
+#endif
/**
The transition affecting any changes to this layer’s `hillshadeAccentColor` property.
@@ -107,11 +130,11 @@ MGL_EXPORT
Intensity of the hillshade
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.
+ 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:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -129,6 +152,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition hillshadeExaggerationTransition;
+#if TARGET_OS_IPHONE
/**
The shading color of areas that faces towards the light source.
@@ -148,6 +172,27 @@ MGL_EXPORT
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *hillshadeHighlightColor;
+#else
+/**
+ The shading color of areas that faces towards the light source.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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) NSExpression *hillshadeHighlightColor;
+#endif
/**
The transition affecting any changes to this layer’s `hillshadeHighlightColor` property.
@@ -187,11 +232,11 @@ MGL_EXPORT
`hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`.
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.
+ 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:
- * Constant numeric values
+ * Constant numeric values between 0 and 359 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -202,6 +247,7 @@ MGL_EXPORT
*/
@property (nonatomic, null_resettable) NSExpression *hillshadeIlluminationDirection;
+#if TARGET_OS_IPHONE
/**
The shading color of areas that face away from the light source.
@@ -221,6 +267,27 @@ MGL_EXPORT
feature attributes.
*/
@property (nonatomic, null_resettable) NSExpression *hillshadeShadowColor;
+#else
+/**
+ The shading color of areas that face away from the light source.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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) NSExpression *hillshadeShadowColor;
+#endif
/**
The transition affecting any changes to this layer’s `hillshadeShadowColor` property.
diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h
index d7e64cbfad..cf4aa50112 100644
--- a/platform/darwin/src/MGLLight.h
+++ b/platform/darwin/src/MGLLight.h
@@ -128,6 +128,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition positionTransition;
+#if TARGET_OS_IPHONE
/**
Color tint for lighting extruded geometries.
@@ -150,6 +151,30 @@ MGL_EXPORT
light property in the Mapbox Style Specification.
*/
@property (nonatomic) NSExpression *color;
+#else
+/**
+ Color tint for lighting extruded geometries.
+
+ The default value of this property is an expression that evaluates to
+ `NSColor.whiteColor`.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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.
+
+ 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) NSExpression *color;
+#endif
/**
The transition affecting any changes to this layer’s `color` property.
@@ -163,11 +188,11 @@ MGL_EXPORT
more extreme contrast.
The default value of this property is an expression that evaluates to the float
- `0.5`.
+ 0.5.
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h
index 003f48ae43..a7510142fc 100644
--- a/platform/darwin/src/MGLLineStyleLayer.h
+++ b/platform/darwin/src/MGLLineStyleLayer.h
@@ -78,9 +78,10 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) {
polylines on the map.
Use a line style layer to configure the visual appearance of polyline or
- multipolyline features in vector tiles loaded by an `MGLVectorSource` object or
- `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or
- `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object.
+ multipolyline features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPolyline`,
+ `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature`
+ instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing line style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -93,7 +94,7 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) {
```swift
let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
layer.sourceLayerIdentifier = "trails"
- layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ layer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
[14: 2,
18: 20])
layer.lineColor = NSExpression(forConstantValue: UIColor.brown)
@@ -179,7 +180,7 @@ MGL_EXPORT
Used to automatically convert miter joins to bevel joins for sharp angles.
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.
+ 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
expression that evaluates to `miter`. Otherwise, it is ignored.
@@ -201,7 +202,7 @@ MGL_EXPORT
Used to automatically convert round joins to miter joins for shallow angles.
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.
+ 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
expression that evaluates to `round`. Otherwise, it is ignored.
@@ -227,11 +228,11 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -247,6 +248,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition lineBlurTransition;
+#if TARGET_OS_IPHONE
/**
The color with which the line will be drawn.
@@ -267,6 +269,28 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *lineColor;
+#else
+/**
+ The color with which the line will be drawn.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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 *lineColor;
+#endif
/**
The transition affecting any changes to this layer’s `lineColor` property.
@@ -291,7 +315,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant array values
+ * Constant array values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -319,11 +343,11 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -348,7 +372,7 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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:
@@ -372,11 +396,11 @@ MGL_EXPORT
The opacity at which the line will be drawn.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -518,11 +542,11 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 4e6ff27b39..619cc70afe 100644
--- a/platform/darwin/src/MGLLineStyleLayer.mm
+++ b/platform/darwin/src/MGLLineStyleLayer.mm
@@ -390,7 +390,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultLinePattern();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setLinePatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
index 426ab1bb00..976213c8ba 100644
--- a/platform/darwin/src/MGLMapSnapshotter.h
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -140,7 +140,7 @@ typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapsh
```swift
let camera = MGLMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365), fromDistance: 100, pitch: 20, heading: 0)
- let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL(), camera: camera, size: CGSize(width: 320, height: 480))
+ let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL, camera: camera, size: CGSize(width: 320, height: 480))
options.zoomLevel = 10
let snapshotter = MGLMapSnapshotter(options: options)
diff --git a/platform/darwin/src/MGLNetworkConfiguration.h b/platform/darwin/src/MGLNetworkConfiguration.h
index 644291ee13..2db46d78c5 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.h
+++ b/platform/darwin/src/MGLNetworkConfiguration.h
@@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLNetworkConfiguration : NSObject
/// Returns the shared instance of the `MGLNetworkConfiguration` class.
-+ (instancetype)sharedManager;
+@property (class, nonatomic, readonly) MGLNetworkConfiguration *sharedManager;
/// The current API base URL. If `nil`, the Mapbox default base API URL is in use.
@property (atomic, nullable) NSURL *apiBaseURL;
diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index b009f893b3..a1f3e686c2 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -70,7 +70,7 @@ typedef NSString *MGLOfflinePackUserInfoKey NS_EXTENSIBLE_STRING_ENUM;
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState;
-extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyState")));
+extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyState")));
/**
The key for an `NSValue` object that indicates an offline pack’s current
@@ -81,7 +81,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__(
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress;
-extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyProgress")));
+extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyProgress")));
/**
The key for an `NSError` object that is encountered in the course of
@@ -91,7 +91,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError;
-extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyError")));
+extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyError")));
/**
The key for an `NSNumber` object that indicates the maximum number of
@@ -103,7 +103,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__(
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount;
-extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyMaximumCount")));
+extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyMaximumCount")));
/**
A block to be called once an offline pack has been completely created and
@@ -170,7 +170,7 @@ MGL_EXPORT
/**
Returns the shared offline storage object.
*/
-+ (instancetype)sharedOfflineStorage;
+@property (class, nonatomic, readonly) MGLOfflineStorage *sharedOfflineStorage;
#pragma mark - Accessing the Delegate
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 7085aa58e5..4d999144e8 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -26,13 +26,9 @@ const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError
const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState = @"State";
-NSString * const MGLOfflinePackStateUserInfoKey = MGLOfflinePackUserInfoKeyState;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress";
-NSString * const MGLOfflinePackProgressUserInfoKey = MGLOfflinePackUserInfoKeyProgress;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error";
-NSString * const MGLOfflinePackErrorUserInfoKey = MGLOfflinePackUserInfoKeyError;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount";
-NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoKeyMaximumCount;
@interface MGLOfflineStorage ()
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.h b/platform/darwin/src/MGLOpenGLStyleLayer.h
index 0b494e8062..df8d2c5365 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.h
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.h
@@ -1,9 +1,11 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
+#import <QuartzCore/QuartzCore.h>
#import "MGLFoundation.h"
#import "MGLStyleValue.h"
#import "MGLStyleLayer.h"
+#import "MGLGeometry.h"
NS_ASSUME_NONNULL_BEGIN
@@ -17,6 +19,7 @@ typedef struct MGLStyleLayerDrawingContext {
CLLocationDirection direction;
CGFloat pitch;
CGFloat fieldOfView;
+ MGLMatrix4 projectionMatrix;
} MGLStyleLayerDrawingContext;
MGL_EXPORT
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm
index 8933a77382..d89fbc80c3 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.mm
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm
@@ -3,53 +3,53 @@
#import "MGLMapView_Private.h"
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
+#import "MGLGeometry_Private.h"
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/math/wrap.hpp>
-/**
- Runs the preparation handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+class MGLOpenGLLayerHost : public mbgl::style::CustomLayerHost {
+public:
+ MGLOpenGLLayerHost(MGLOpenGLStyleLayer *styleLayer) {
+ layerRef = styleLayer;
+ layer = nil;
+ }
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLPrepareCustomStyleLayer(void *context) {
- MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- [layer didMoveToMapView:layer.style.mapView];
-}
+ void initialize() {
+ if (layerRef == nil) return;
+ else if (layer == nil) layer = layerRef;
-/**
- Runs the drawing handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+ [layer didMoveToMapView:layer.style.mapView];
+ }
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRenderParameters &params) {
- MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- MGLStyleLayerDrawingContext drawingContext = {
- .size = CGSizeMake(params.width, params.height),
- .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
- .zoomLevel = params.zoom,
- .direction = mbgl::util::wrap(params.bearing, 0., 360.),
- .pitch = static_cast<CGFloat>(params.pitch),
- .fieldOfView = static_cast<CGFloat>(params.fieldOfView),
- };
- [layer drawInMapView:layer.style.mapView withContext:drawingContext];
-}
+ void render(const mbgl::style::CustomLayerRenderParameters &params) {
+ if(!layer) return;
+
+ MGLStyleLayerDrawingContext drawingContext = {
+ .size = CGSizeMake(params.width, params.height),
+ .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
+ .zoomLevel = params.zoom,
+ .direction = mbgl::util::wrap(params.bearing, 0., 360.),
+ .pitch = static_cast<CGFloat>(params.pitch),
+ .fieldOfView = static_cast<CGFloat>(params.fieldOfView),
+ .projectionMatrix = MGLMatrix4Make(params.projectionMatrix)
+ };
+ [layer drawInMapView:layer.style.mapView withContext:drawingContext];
+ }
-/**
- Runs the completion handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+ void contextLost() {}
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLFinishCustomStyleLayer(void *context) {
- MGLOpenGLStyleLayer *layer = (__bridge_transfer MGLOpenGLStyleLayer *)context;
- [layer willMoveFromMapView:layer.style.mapView];
-}
+ void deinitialize() {
+ if (layer == nil) return;
+
+ [layer willMoveFromMapView:layer.style.mapView];
+ layerRef = layer;
+ layer = nil;
+ }
+private:
+ __weak MGLOpenGLStyleLayer * layerRef;
+ MGLOpenGLStyleLayer * layer = nil;
+};
/**
An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code that
@@ -98,10 +98,7 @@ void MGLFinishCustomStyleLayer(void *context) {
*/
- (instancetype)initWithIdentifier:(NSString *)identifier {
auto layer = std::make_unique<mbgl::style::CustomLayer>(identifier.UTF8String,
- MGLPrepareCustomStyleLayer,
- MGLDrawCustomStyleLayer,
- MGLFinishCustomStyleLayer,
- (__bridge_retained void *)self);
+ std::make_unique<MGLOpenGLLayerHost>(self));
return self = [super initWithPendingLayer:std::move(layer)];
}
@@ -110,22 +107,15 @@ void MGLFinishCustomStyleLayer(void *context) {
}
#pragma mark - Adding to and removing from a map view
-
-- (void)setStyle:(MGLStyle *)style {
- if (_style && style) {
- [NSException raise:@"MGLLayerReuseException"
- format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
- }
- _style = style;
-}
-
- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer {
self.style = style;
+ self.style.openGLLayers[self.identifier] = self;
[super addToStyle:style belowLayer:otherLayer];
}
- (void)removeFromStyle:(MGLStyle *)style {
[super removeFromStyle:style];
+ self.style.openGLLayers[self.identifier] = nil;
self.style = nil;
}
diff --git a/platform/darwin/src/MGLPointAnnotation.h b/platform/darwin/src/MGLPointAnnotation.h
index 1ef0962f99..3dac7a969c 100644
--- a/platform/darwin/src/MGLPointAnnotation.h
+++ b/platform/darwin/src/MGLPointAnnotation.h
@@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
You can add point shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s point shapes collectively using an `MGLCircleStyleLayer` or
+ `MGLVectorTileSource`’s point shapes collectively using an `MGLCircleStyleLayer` or
`MGLSymbolStyleLayer` object.
For more interactivity, add a selectable point annotation to a map view using
@@ -28,7 +28,8 @@ NS_ASSUME_NONNULL_BEGIN
default content of the annotation’s callout (on iOS) or popover (on macOS).
To group multiple related points together in one shape, use an
- `MGLPointCollection` or `MGLShapeCollection` object.
+ `MGLPointCollection` or `MGLShapeCollection` object. To access
+ a point’s attributes, use an `MGLPointFeature` object.
A point shape is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.2">Point</a> geometry
diff --git a/platform/darwin/src/MGLPointCollection.h b/platform/darwin/src/MGLPointCollection.h
index 74b30385a0..65ce95cb0f 100644
--- a/platform/darwin/src/MGLPointCollection.h
+++ b/platform/darwin/src/MGLPointCollection.h
@@ -14,8 +14,9 @@
You can add point collections to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s point collections collectively using an
- `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s point collections collectively using an
+ `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object. To access a point
+ collection’s attributes, use an `MGLPointCollectionFeature` object.
You cannot add an `MGLPointCollection` object directly to a map view as an
annotation. However, you can create individual `MGLPointAnnotation` objects
diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h
index 3fcc1be76d..190b6df9c5 100644
--- a/platform/darwin/src/MGLPolygon.h
+++ b/platform/darwin/src/MGLPolygon.h
@@ -17,8 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
You can add polygon shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s polygons collectively using an `MGLFillStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s polygons collectively using an `MGLFillStyleLayer` or
+ `MGLSymbolStyleLayer` object. To access a polygon’s attributes, use an
+ `MGLPolygonFeature` object.
Alternatively, you can add a polygon overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
@@ -95,8 +96,8 @@ MGL_EXPORT
You can add multipolygon shapes to the map by adding them to an
`MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s multipolygons collectively using an `MGLFillStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s multipolygons collectively using an `MGLFillStyleLayer`
+ or `MGLSymbolStyleLayer` object.
You cannot add an `MGLMultiPolygon` object directly to a map view using
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index e46baa91cc..b1fca5bf28 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -17,8 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
You can add polyline shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s polylines collectively using an `MGLLineStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s polylines collectively using an `MGLLineStyleLayer` or
+ `MGLSymbolStyleLayer` object. To access a polyline’s attributes, use an
+ `MGLPolylineFeature` object.
Alternatively, you can add a polyline overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
@@ -74,8 +75,8 @@ MGL_EXPORT
You can add multipolyline shapes to the map by adding them to an
`MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s multipolylines collectively using an `MGLLineStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s multipolylines collectively using an
+ `MGLLineStyleLayer` or `MGLSymbolStyleLayer` object.
You cannot add an `MGLMultiPolyline` object directly to a map view using
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index e011d09215..26e3518cd8 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -72,9 +72,12 @@
if (count > 1 || middle > traveled) {
for (NSUInteger i = 0; i < count; i++) {
-
+
+ // Avoid a heap buffer overflow when there are only two coordinates.
+ NSUInteger nextIndex = (i + 1 == count) ? 0 : 1;
+
MGLRadianCoordinate2D from = MGLRadianCoordinateFromLocationCoordinate(coordinates[i]);
- MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1]);
+ MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + nextIndex]);
if (traveled >= middle) {
double overshoot = middle - traveled;
@@ -91,7 +94,6 @@
}
traveled += (MGLDistanceBetweenRadianCoordinates(from, to) * mbgl::util::EARTH_RADIUS_M);
-
}
}
diff --git a/platform/darwin/src/MGLRasterDEMSource.h b/platform/darwin/src/MGLRasterDEMSource.h
index d00912ca79..d439fe1645 100644
--- a/platform/darwin/src/MGLRasterDEMSource.h
+++ b/platform/darwin/src/MGLRasterDEMSource.h
@@ -1,6 +1,19 @@
#import "MGLFoundation.h"
-#import "MGLRasterSource.h"
+#import "MGLRasterTileSource.h"
+
+/**
+ An `NSNumber` object containing an unsigned integer that specifies the encoding
+ formula for raster-dem tilesets. The integer corresponds to one of
+ the constants described in `MGLDEMEncoding`.
+
+ The default value for this option is `MGLDEMEncodingMapbox`.
+
+ This option cannot be represented in a TileJSON or style JSON file. It is used
+ with the `MGLRasterDEMSource` class and is ignored when creating an
+ `MGLRasterTileSource` or `MGLVectorTileSource` object.
+ */
+extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding;
/**
`MGLRasterDEMSource` is a map content source that supplies rasterized
@@ -32,6 +45,6 @@
```
*/
MGL_EXPORT
-@interface MGLRasterDEMSource : MGLRasterSource
+@interface MGLRasterDEMSource : MGLRasterTileSource
@end
diff --git a/platform/darwin/src/MGLRasterDEMSource.mm b/platform/darwin/src/MGLRasterDEMSource.mm
index d8639b70e3..27614b9ef4 100644
--- a/platform/darwin/src/MGLRasterDEMSource.mm
+++ b/platform/darwin/src/MGLRasterDEMSource.mm
@@ -1,6 +1,6 @@
#import "MGLRasterDEMSource.h"
-#import "MGLRasterSource_Private.h"
+#import "MGLRasterTileSource_Private.h"
#import "NSURL+MGLAdditions.h"
#import <mbgl/style/sources/raster_dem_source.hpp>
diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h
index 7773246cad..bca9649e5d 100644
--- a/platform/darwin/src/MGLRasterStyleLayer.h
+++ b/platform/darwin/src/MGLRasterStyleLayer.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
imagery on the map, especially raster tiles.
Use a raster style layer to configure the color parameters of raster tiles
- loaded by an `MGLRasterSource` object or raster images loaded by an
+ loaded by an `MGLRasterTileSource` object or raster images loaded by an
`MGLImageSource` object. For example, you could use a raster style layer to
render <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a>
imagery, a <a
@@ -64,7 +64,7 @@ MGL_EXPORT
brightness.
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.
+ 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>
@@ -72,7 +72,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -97,7 +97,7 @@ MGL_EXPORT
brightness.
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.
+ 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>
@@ -105,7 +105,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -129,11 +129,11 @@ MGL_EXPORT
Increase or reduce the contrast of the image.
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.
+ 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
+ * Constant numeric values between −1 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -157,11 +157,11 @@ MGL_EXPORT
This property is measured in milliseconds.
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.
+ 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:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -178,7 +178,7 @@ MGL_EXPORT
This property is measured in degrees.
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.
+ 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>
@@ -210,11 +210,11 @@ MGL_EXPORT
The opacity at which the image will be drawn.
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.
+ 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
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -236,11 +236,11 @@ MGL_EXPORT
Increase or reduce the saturation of the image.
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.
+ 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
+ * Constant numeric values between −1 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLRasterSource.h b/platform/darwin/src/MGLRasterTileSource.h
index 4f4b7c96c3..59b256d5e5 100644
--- a/platform/darwin/src/MGLRasterSource.h
+++ b/platform/darwin/src/MGLRasterTileSource.h
@@ -8,56 +8,59 @@ NS_ASSUME_NONNULL_BEGIN
/**
An `NSNumber` object containing a floating-point number that specifies the
width and height (measured in points) at which the map displays each raster
- image tile when the map’s zoom level is an integer. The raster source scales
- its images up or down when the map’s zoom level falls between two integers.
+ image tile when the map’s zoom level is an integer. The raster tile source
+ scales its images up or down when the map’s zoom level falls between two
+ integers.
The default value for this option is 512. Version 4 of the
<a href="https://www.mapbox.com/api-documentation/#maps">Mapbox Maps API</a>
requires a value of 256, as do many third-party tile servers, so consult your
provider’s documentation for the correct value.
- This option is only applicable to `MGLRasterSource` objects; it is ignored when
- initializing `MGLVectorSource` objects.
+ This option is only applicable to `MGLRasterTileSource` objects; it is ignored
+ when initializing `MGLVectorTileSource` objects.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileSize;
/**
- `MGLRasterSource` is a map content source that supplies raster image tiles to
- be shown on the map. The location of and metadata about the tiles are defined
- either by an option dictionary or by an external file that conforms to the
+ `MGLRasterTileSource` is a map content source that supplies raster image tiles
+ to be shown on the map. The location of and metadata about the tiles are
+ defined either by an option dictionary or by an external file that conforms to
+ the
<a href="https://github.com/mapbox/tilejson-spec/">TileJSON specification</a>.
- A raster source is added to an `MGLStyle` object along with one or more
+ A raster tile source is added to an `MGLStyle` object along with one or more
`MGLRasterStyleLayer` objects. Use a raster style layer to control the
- appearance of content supplied by the raster source.
+ appearance of content supplied by the raster tile source.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-raster"><code>raster</code></a>
source defined by the style JSON file is represented at runtime by an
- `MGLRasterSource` object that you can use to initialize new style layers. You
+ `MGLRasterTileSource` object that you can use to initialize new style layers. You
can also add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
### Example
```swift
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
```
*/
MGL_EXPORT
-@interface MGLRasterSource : MGLTileSource
+@interface MGLRasterTileSource : MGLTileSource
#pragma mark Initializing a Source
/**
- Returns a raster source initialized with an identifier and configuration URL.
+ Returns a raster tile source initialized with an identifier and configuration
+ URL.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -77,13 +80,13 @@ MGL_EXPORT
which it is added.
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
- @return An initialized raster source.
+ @return An initialized raster tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL;
/**
- Returns a raster source initialized with an identifier, configuration URL, and
- tile size.
+ Returns a raster tile source initialized with an identifier, configuration URL,
+ and tile size.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -98,14 +101,14 @@ MGL_EXPORT
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
@param tileSize The width and height (measured in points) of each tiled image
- in the raster source. See the `MGLTileSourceOptionTileSize` documentation
- for details.
- @return An initialized raster source.
+ in the raster tile source. See the `MGLTileSourceOptionTileSize`
+ documentation for details.
+ @return An initialized raster tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize NS_DESIGNATED_INITIALIZER;
/**
- Returns a raster source initialized an identifier, tile URL templates, and
+ Returns a raster tile source initialized an identifier, tile URL templates, and
options.
Tile URL templates are strings that specify the URLs of the raster tile images
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterTileSource.mm
index c47cc199eb..02cfef4ae8 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterTileSource.mm
@@ -1,4 +1,4 @@
-#import "MGLRasterSource_Private.h"
+#import "MGLRasterTileSource_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
@@ -10,16 +10,16 @@
const MGLTileSourceOption MGLTileSourceOptionTileSize = @"MGLTileSourceOptionTileSize";
-static const CGFloat MGLRasterSourceClassicTileSize = 256;
-static const CGFloat MGLRasterSourceRetinaTileSize = 512;
+static const CGFloat MGLRasterTileSourceClassicTileSize = 256;
+static const CGFloat MGLRasterTileSourceRetinaTileSize = 512;
-@interface MGLRasterSource ()
+@interface MGLRasterTileSource ()
@property (nonatomic, readonly) mbgl::style::RasterSource *rawSource;
@end
-@implementation MGLRasterSource
+@implementation MGLRasterTileSource
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
// The style specification default is 512, but 256 is the expected value for
@@ -28,7 +28,7 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
BOOL isMapboxURL = ([configurationURL.scheme isEqualToString:@"mapbox"]
&& [configurationURL.host containsString:@"."]
&& (!configurationURL.path.length || [configurationURL.path isEqualToString:@"/"]));
- CGFloat tileSize = isMapboxURL ? MGLRasterSourceClassicTileSize : MGLRasterSourceRetinaTileSize;
+ CGFloat tileSize = isMapboxURL ? MGLRasterTileSourceClassicTileSize : MGLRasterTileSourceRetinaTileSize;
return [self initWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize];
}
@@ -47,7 +47,7 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options {
mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
- uint16_t tileSize = MGLRasterSourceRetinaTileSize;
+ uint16_t tileSize = MGLRasterTileSourceRetinaTileSize;
if (NSNumber *tileSizeNumber = options[MGLTileSourceOptionTileSize]) {
if (![tileSizeNumber isKindOfClass:[NSNumber class]]) {
[NSException raise:NSInvalidArgumentException
diff --git a/platform/darwin/src/MGLRasterSource_Private.h b/platform/darwin/src/MGLRasterTileSource_Private.h
index 6f40fbc5a9..128dcb447d 100644
--- a/platform/darwin/src/MGLRasterSource_Private.h
+++ b/platform/darwin/src/MGLRasterTileSource_Private.h
@@ -1,4 +1,4 @@
-#import "MGLRasterSource.h"
+#import "MGLRasterTileSource.h"
#include <memory>
@@ -10,7 +10,7 @@ namespace mbgl {
NS_ASSUME_NONNULL_BEGIN
-@interface MGLRasterSource (Private)
+@interface MGLRasterTileSource (Private)
@property (nonatomic, readonly) mbgl::style::RasterSource *rawSource;
diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h
index 31aad0a742..34c8e9628b 100644
--- a/platform/darwin/src/MGLRendererConfiguration.h
+++ b/platform/darwin/src/MGLRendererConfiguration.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLRendererConfiguration : NSObject
/** Returns an instance of the current renderer configuration. */
-+ (instancetype)currentConfiguration;
+@property (class, nonatomic, readonly) MGLRendererConfiguration *currentConfiguration;
/** The file source to use. Defaults to `mbgl::DefaultFileSource` */
@property (nonatomic, readonly) mbgl::DefaultFileSource *fileSource;
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
index e965710552..e0ef0999db 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -12,21 +12,23 @@ NS_ASSUME_NONNULL_BEGIN
Create instances of `MGLPointAnnotation`, `MGLPointCollection`, `MGLPolyline`,
`MGLMultiPolyline`, `MGLPolygon`, `MGLMultiPolygon`, or `MGLShapeCollection` in
- order to use `MGLShape`'s methods. Do not create instances of `MGLShape` directly,
- and do not create your own subclasses of this class. The shape classes correspond
- to the <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
+ order to use `MGLShape`'s methods. Do not create instances of `MGLShape`
+ directly, and do not create your own subclasses of this class. The shape
+ classes correspond to the
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
types in the GeoJSON standard, but some have nonstandard names for backwards
compatibility.
Although you do not create instances of this class directly, you can use its
`+[MGLShape shapeWithData:encoding:error:]` factory method to create one of the
- concrete subclasses of `MGLShape` noted above from GeoJSON data.
+ concrete subclasses of `MGLShape` noted above from GeoJSON data. To access a
+ shape’s attributes, use the corresponding `MGLFeature` class instead.
You can add shapes to the map by adding them to an `MGLShapeSource` object.
- Configure the appearance of an `MGLShapeSource`’s or `MGLVectorSource`’s shapes
- collectively using a concrete instance of `MGLVectorStyleLayer`. Alternatively,
- you can add some kinds of shapes directly to a map view as annotations or
- overlays.
+ Configure the appearance of an `MGLShapeSource`’s or `MGLVectorTileSource`’s
+ shapes collectively using a concrete instance of `MGLVectorStyleLayer`.
+ Alternatively, you can add some kinds of shapes directly to a map view as
+ annotations or overlays.
*/
MGL_EXPORT
@interface MGLShape : NSObject <MGLAnnotation, NSSecureCoding>
diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h
index bb107ee7f0..bec482ca61 100644
--- a/platform/darwin/src/MGLShapeCollection.h
+++ b/platform/darwin/src/MGLShapeCollection.h
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
`MGLShapeCollection` is most commonly used to add multiple shapes to a single
`MGLShapeSource`. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s shape collection collectively using an
+ `MGLVectorTileSource`’s shape collection collectively using an
`MGLSymbolStyleLayer` object, or use multiple instances of
`MGLCircleStyleLayer`, `MGLFillStyleLayer`, and `MGLLineStyleLayer` to
configure the appearance of each kind of shape inside the collection.
@@ -27,7 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
To represent a collection of point, polyline, or polygon shapes, it may be more
convenient to use an `MGLPointCollection`, `MGLMultiPolyline`, or
- `MGLMultiPolygon` object, respectively.
+ `MGLMultiPolygon` object, respectively. To access a shape collection’s
+ attributes, use the corresponding `MGLFeature` object.
A shape collection is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.8">GeometryCollection</a>
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index 929609e91f..6fa93476be 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -1,12 +1,99 @@
-#import "MGLAbstractShapeSource.h"
-
#import "MGLFoundation.h"
#import "MGLTypes.h"
-#import "MGLShape.h"
+#import "MGLSource.h"
NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
+@class MGLShape;
+
+/**
+ Options for `MGLShapeSource` objects.
+ */
+typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
+
+/**
+ An `NSNumber` object containing a Boolean enabling or disabling clustering.
+ If the `shape` property contains point shapes, setting this option to
+ `YES` clusters the points by radius into groups. The default value is `NO`.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
+ source property in the Mapbox Style Specification.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
+
+/**
+ An `NSNumber` object containing an integer; specifies the radius of each
+ cluster if clustering is enabled. A value of 512 produces a radius equal to
+ the width of a tile. The default value is 50.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to cluster points if clustering is enabled. Defaults to one zoom level
+ less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
+ maximum zoom level, the shapes are not clustered.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
+ source property in the Mapbox Style Specification.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
+
+/**
+ An `NSNumber` object containing an integer; specifies the minimum zoom level at
+ which to create vector tiles. The default value is 0.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-minzoom"><code>minzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to create vector tiles. A greater value produces greater detail at high
+ zoom levels. The default value is 18.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the size of the tile
+ buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
+ buffer as wide as the tile itself. Larger values produce fewer rendering
+ artifacts near tile edges and slower performance. The default value is 128.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
+
+/**
+ An `NSNumber` object containing a double; specifies the Douglas-Peucker
+ simplification tolerance. A greater value produces simpler geometries and
+ improves performance. The default value is 0.375.
+
+ This option corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
/**
`MGLShapeSource` is a map content source that supplies vector shapes to be
@@ -16,6 +103,10 @@ NS_ASSUME_NONNULL_BEGIN
`MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
layer defines the appearance of any content supplied by the shape source. You
can update a shape source by setting its `shape` or `URL` property.
+
+ `MGLShapeSource` is optimized for data sets that change dynamically and fit
+ completely in memory. For large data sets that do not fit completely in memory,
+ use the `MGLComputedShapeSource` or `MGLVectorTileSource` class.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson"><code>geojson</code></a>
@@ -41,13 +132,21 @@ NS_ASSUME_NONNULL_BEGIN
```
*/
MGL_EXPORT
-@interface MGLShapeSource : MGLAbstractShapeSource
+@interface MGLShapeSource : MGLSource
#pragma mark Initializing a Source
/**
Returns a shape source with an identifier, URL, and dictionary of options for
the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
@param identifier A string that uniquely identifies the source.
@param url An HTTP(S) URL, absolute file URL, or local file URL relative to the
@@ -60,6 +159,14 @@ MGL_EXPORT
/**
Returns a shape source with an identifier, a shape, and dictionary of options
for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
To specify attributes about the shape, use an instance of an `MGLShape`
subclass that conforms to the `MGLFeature` protocol, such as `MGLPointFeature`.
@@ -81,6 +188,14 @@ MGL_EXPORT
/**
Returns a shape source with an identifier, an array of features, and a dictionary
of options for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
Unlike `-initWithIdentifier:shapes:options:`, this method accepts `MGLFeature`
instances, such as `MGLPointFeature` objects, whose attributes you can use when
@@ -100,6 +215,14 @@ MGL_EXPORT
/**
Returns a shape source with an identifier, an array of shapes, and a dictionary of
options for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
Any `MGLFeature` instance passed into this initializer is treated as an ordinary
shape, causing any attributes to be inaccessible to an `MGLVectorStyleLayer` when
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 8fbb9a18ef..dcc3fd97f5 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -1,5 +1,4 @@
#import "MGLShapeSource_Private.h"
-#import "MGLAbstractShapeSource_Private.h"
#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
@@ -14,7 +13,75 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/renderer/renderer.hpp>
+const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
+const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
+const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
+const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
+
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ auto geoJSONOptions = mbgl::style::GeoJSONOptions();
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.minzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.maxzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ geoJSONOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ geoJSONOptions.tolerance = value.doubleValue;
+ }
+ if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
+ }
+ geoJSONOptions.clusterRadius = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ geoJSONOptions.clusterMaxZoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
+ }
+ geoJSONOptions.cluster = value.boolValue;
+ }
+
+ return geoJSONOptions;
+}
@interface MGLShapeSource ()
diff --git a/platform/darwin/src/MGLShapeSource_Private.h b/platform/darwin/src/MGLShapeSource_Private.h
index 1ea2c39b8e..0720074d1d 100644
--- a/platform/darwin/src/MGLShapeSource_Private.h
+++ b/platform/darwin/src/MGLShapeSource_Private.h
@@ -9,7 +9,7 @@ namespace mbgl {
}
}
-@interface MGLShapeSource (Private)
-@end
+MGL_EXPORT
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index 8d8c936833..7bbf02fa7c 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -16,10 +16,11 @@ NS_ASSUME_NONNULL_BEGIN
add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
- Create instances of `MGLShapeSource`, `MGLImageSource` and the concrete subclasses of
- `MGLTileSource` (`MGLVectorSource` and `MGLRasterSource`) in order to use
- `MGLSource`'s properties and methods. Do not create instances of `MGLSource`
- directly, and do not create your own subclasses of this class.
+ Create instances of `MGLShapeSource`, `MGLComputedShapeSource`,
+ `MGLImageSource`, and the concrete subclasses of `MGLTileSource`
+ (`MGLVectorTileSource` and `MGLRasterTileSource`) in order to use `MGLSource`’s
+ properties and methods. Do not create instances of `MGLSource` directly, and do
+ not create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLSource : NSObject
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 0b360de8fc..6df627d7af 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -68,7 +68,7 @@ MGL_EXPORT
`-streetsStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)streetsStyleURL;
+@property (class, nonatomic, readonly) NSURL *streetsStyleURL;
/**
Returns the URL to the given version of the
@@ -83,13 +83,7 @@ MGL_EXPORT
*/
+ (NSURL *)streetsStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 8 of the
- <a href="https://www.mapbox.com/blog/emerald-gl/">Mapbox Emerald</a> style.
-
- Emerald is a tactile style with subtle textures and dramatic hillshading.
- */
-+ (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
++ (NSURL *)emeraldStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
/**
Returns the URL to the current version of the
@@ -104,7 +98,7 @@ MGL_EXPORT
`-outdoorsStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)outdoorsStyleURL;
+@property (class, nonatomic, readonly) NSURL *outdoorsStyleURL;
/**
Returns the URL to the given version of the
@@ -128,7 +122,7 @@ MGL_EXPORT
`-lightStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)lightStyleURL;
+@property (class, nonatomic, readonly) NSURL *lightStyleURL;
/**
Returns the URL to the given version of the
@@ -153,7 +147,7 @@ MGL_EXPORT
`-darkStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)darkStyleURL;
+@property (class, nonatomic, readonly) NSURL *darkStyleURL;
/**
Returns the URL to the given version of the
@@ -178,7 +172,7 @@ MGL_EXPORT
`-satelliteStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)satelliteStyleURL;
+@property (class, nonatomic, readonly) NSURL *satelliteStyleURL;
/**
Returns the URL to the given version of the
@@ -191,16 +185,8 @@ MGL_EXPORT
*/
+ (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 8 of the
- <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a>
- style.
- Satellite Streets combines the high-resolution satellite and aerial imagery of
- Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox
- Streets.
- */
-+ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURL.")));
++ (NSURL *)hybridStyleURL __attribute__((unavailable("Use -satelliteStreetsStyleURL.")));
/**
Returns the URL to the current version of the
@@ -217,7 +203,7 @@ MGL_EXPORT
`-satelliteStreetsStyleURLWithVersion:` method instead. Such details may
change significantly from version to version.
*/
-+ (NSURL *)satelliteStreetsStyleURL;
+@property (class, nonatomic, readonly) NSURL *satelliteStreetsStyleURL;
/**
Returns the URL to the given version of the
@@ -232,39 +218,14 @@ MGL_EXPORT
*/
+ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
- style.
- */
-+ (NSURL *)trafficDayStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));
-
-/**
- Returns the URL to the given version of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
- style as of publication.
-
- @param version A specific version of the style.
- */
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
++ (NSURL *)trafficDayStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));
-/**
- Returns the URL to the version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
- style.
++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
- */
-+ (NSURL *)trafficNightStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
++ (NSURL *)trafficNightStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
-/**
- Returns the URL to to the version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
- style as of publication.
-
- @param version A specific version of the style.
- */
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
#pragma mark Accessing Metadata About the Style
@@ -455,25 +416,14 @@ MGL_EXPORT
#pragma mark Managing Style Classes
-/**
- Support for style classes has been removed. This property always returns an empty array.
- */
-@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional.")));
-/**
- Support for style classes has been removed. This method always returns NO.
- */
-- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("Support for style classes has been removed.")));
-/**
- Support for style classes has been removed. This method is a no-op.
- */
-- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
-/**
- Support for style classes has been removed. This method is a no-op.
- */
-- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (void)addStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
+
+- (void)removeStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
#pragma mark Managing a Style’s Images
@@ -532,17 +482,22 @@ MGL_EXPORT
#pragma mark Localizing Map Content
/**
- A Boolean value that determines whether the style attempts to localize labels in
- the style into the system’s preferred language.
+ Attempts to localize labels in the style into the given locale.
+
+ This method automatically modifies the text property of any symbol style layer
+ in the style whose source is the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ On iOS, the user can set the system’s preferred language in Settings, General
+ Settings, Language & Region. On macOS, the user can set the system’s preferred
+ language in the Language & Region pane of System Preferences.
- When this property is enabled, the style automatically modifies the text property
- of any symbol style layer whose source is the
- <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox
- Streets source</a>. On iOS, the user can set the system’s preferred language in
- Settings, General Settings, Language & Region. On macOS, the user can set the
- system’s preferred language in the Language & Region pane of System Preferences.
- */
-@property (nonatomic) BOOL localizesLabels;
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale;
+
+@property (nonatomic) BOOL localizesLabels __attribute__((unavailable("Use -localizeLabelsIntoLocale: instead.")));
@end
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index f6fc5533be..867ac6c451 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -18,9 +18,8 @@
#import "MGLSource_Private.h"
#import "MGLLight_Private.h"
#import "MGLTileSource_Private.h"
-#import "MGLVectorSource.h"
-#import "MGLVectorSource_Private.h"
-#import "MGLRasterSource.h"
+#import "MGLVectorTileSource_Private.h"
+#import "MGLRasterTileSource.h"
#import "MGLRasterDEMSource.h"
#import "MGLShapeSource.h"
#import "MGLImageSource.h"
@@ -83,6 +82,7 @@
@property (nonatomic, readonly, weak) MGLMapView *mapView;
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
@property (readonly, copy, nullable) NSURL *URL;
+@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
@end
@@ -116,64 +116,16 @@ MGL_DEFINE_STYLE(satelliteStreets, satellite-streets)
// Make sure all the styles listed in mbgl::util::default_styles::orderedStyles
// are defined above and also declared in MGLStyle.h.
-static_assert(8 == mbgl::util::default_styles::numOrderedStyles,
+static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
"mbgl::util::default_styles::orderedStyles and MGLStyle have different numbers of styles.");
-// Hybrid has been renamed Satellite Streets, so the last Hybrid version is hard-coded here.
-static NSURL *MGLStyleURL_hybrid;
-+ (NSURL *)hybridStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_hybrid = [NSURL URLWithString:@"mapbox://styles/mapbox/satellite-hybrid-v8"];
- });
- return MGLStyleURL_hybrid;
-}
-
-// Emerald is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_emerald;
-+ (NSURL *)emeraldStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_emerald = [NSURL URLWithString:@"mapbox://styles/mapbox/emerald-v8"];
- });
- return MGLStyleURL_emerald;
-}
-
-// Traffic Day is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_trafficDay;
-+ (NSURL *)trafficDayStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_trafficDay = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
- });
- return MGLStyleURL_trafficDay;
-}
-
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version {
- return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-day-v" stringByAppendingFormat:@"%li", (long)version]];
-}
-
-// Traffic Night is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_trafficNight;
-+ (NSURL *)trafficNightStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_trafficNight = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
- });
- return MGLStyleURL_trafficNight;
-}
-
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version {
- return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-night-v" stringByAppendingFormat:@"%li", (long)version]];
-}
-
-
#pragma mark -
- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView {
if (self = [super init]) {
_mapView = mapView;
_rawStyle = rawStyle;
+ _openGLLayers = [NSMutableDictionary dictionary];
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
return self;
@@ -232,11 +184,11 @@ static NSURL *MGLStyleURL_trafficNight;
// TODO: Fill in options specific to the respective source classes
// https://github.com/mapbox/mapbox-gl-native/issues/6584
if (auto vectorSource = rawSource->as<mbgl::style::VectorSource>()) {
- return [[MGLVectorSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
+ return [[MGLVectorTileSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
} else if (auto geoJSONSource = rawSource->as<mbgl::style::GeoJSONSource>()) {
return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView];
} else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) {
- return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
+ return [[MGLRasterTileSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
} else if (auto rasterDEMSource = rawSource->as<mbgl::style::RasterDEMSource>()) {
return [[MGLRasterDEMSource alloc] initWithRawSource:rasterDEMSource mapView:self.mapView];
} else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) {
@@ -534,38 +486,6 @@ static NSURL *MGLStyleURL_trafficNight;
[self didChangeValueForKey:@"layers"];
}
-#pragma mark Style classes
-
-- (NS_ARRAY_OF(NSString *) *)styleClasses
-{
- return @[];
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses
-{
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
-{
-}
-
-- (NSUInteger)countOfStyleClasses {
- return 0;
-}
-
-- (BOOL)hasStyleClass:(NSString *)styleClass
-{
- return NO;
-}
-
-- (void)addStyleClass:(NSString *)styleClass
-{
-}
-
-- (void)removeStyleClass:(NSString *)styleClass
-{
-}
-
#pragma mark Style images
- (void)setImage:(MGLImage *)image forName:(NSString *)name
@@ -650,79 +570,33 @@ static NSURL *MGLStyleURL_trafficNight;
#pragma mark Mapbox Streets source introspection
-- (void)setLocalizesLabels:(BOOL)localizesLabels
-{
- if (_localizesLabels != localizesLabels) {
- _localizesLabels = localizesLabels;
- } else {
- return;
- }
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale {
+ NSSet<MGLVectorTileSource *> *streetsSources =
+ [self.sources filteredSetUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(MGLVectorTileSource * _Nullable source, NSDictionary<NSString *, id> * _Nullable bindings) {
+ return [source isKindOfClass:[MGLVectorTileSource class]] && [source isMapboxStreets];
+ }]];
+ NSSet<NSString *> *streetsSourceIdentifiers = [streetsSources valueForKey:@"identifier"];
- if (_localizesLabels) {
- NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage];
- NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
- for (MGLSymbolStyleLayer *layer in self.layers) {
- if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- continue;
- }
-
- MGLVectorSource *source = (MGLVectorSource *)[self sourceWithIdentifier:layer.sourceIdentifier];
- if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) {
- continue;
- }
-
- NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier];
- if (!localizedKeysByKey) {
- localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage];
- }
-
- NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) {
- NSMutableString *localizedString = string.mutableCopy;
- [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) {
- NSAssert([key isKindOfClass:[NSString class]], @"key is not a string");
- NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string");
- [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key]
- withString:[NSString stringWithFormat:@"{%@}", localizedKey]
- options:0
- range:NSMakeRange(0, localizedString.length)];
- }];
- return localizedString;
- };
-
- 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 = [NSExpression expressionForConstantValue:localizingString];
- }
- }
+ for (MGLSymbolStyleLayer *layer in self.layers) {
+ if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
+ continue;
+ }
+ if (![streetsSourceIdentifiers containsObject:layer.sourceIdentifier]) {
+ continue;
}
- } else {
- [self.localizedLayersByIdentifier enumerateKeysAndObjectsUsingBlock:^(NSString *identifier, NSDictionary<NSObject *, MGLTextLanguage *> *textFields, BOOL *done) {
- MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:identifier];
-
- 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 = [NSExpression expressionForConstantValue:textLanguage.originalTextField];
- }
- }];
-
- }
- }];
-
- self.localizedLayersByIdentifier = [NSMutableDictionary dictionary];
+ NSExpression *text = layer.text;
+ NSExpression *localizedText = [text mgl_expressionLocalizedIntoLocale:locale];
+ if (![localizedText isEqual:text]) {
+ layer.text = localizedText;
+ }
}
}
-- (NS_SET_OF(MGLVectorSource *) *)mapboxStreetsSources {
- return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorSource * _Nonnull source, BOOL * _Nonnull stop) {
- return [source isKindOfClass:[MGLVectorSource class]] && source.mapboxStreets;
+- (NS_SET_OF(MGLVectorTileSource *) *)mapboxStreetsSources {
+ return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorTileSource * _Nonnull source, BOOL * _Nonnull stop) {
+ return [source isKindOfClass:[MGLVectorTileSource class]] && source.mapboxStreets;
}];
}
diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs
index ac7676a1cc..42940083b5 100644
--- a/platform/darwin/src/MGLStyleLayer.mm.ejs
+++ b/platform/darwin/src/MGLStyleLayer.mm.ejs
@@ -136,7 +136,12 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
+<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+<% } -%>
}
<% if (property.original) { -%>
@@ -174,7 +179,12 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
+<% } else { -%>
return MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+<% } -%>
}
<% if (property["transition"]) { -%>
diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h
index f6e9c51729..65f89c5909 100644
--- a/platform/darwin/src/MGLStyleValue.h
+++ b/platform/darwin/src/MGLStyleValue.h
@@ -8,13 +8,13 @@ NS_ASSUME_NONNULL_BEGIN
typedef NSString *MGLStyleFunctionOption NS_STRING_ENUM NS_UNAVAILABLE;
-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.")));
+extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolate:withCurveType:parameters:stops: function with a curve type of “exponential” and a non-nil parameter.")));
extern MGL_EXPORT const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue __attribute__((unavailable("Use +[NSExpression expressionForConditional:trueExpression:falseExpression:] instead.")));
typedef NS_ENUM(NSUInteger, MGLInterpolationMode) {
- 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."))),
+ MGLInterpolationModeExponential __attribute__((unavailable("Use NSExpression instead, applying the mgl_interpolate:withCurveType:parameters:stops: function with a curve type of “exponential”."))) = 0,
+ MGLInterpolationModeInterval __attribute__((unavailable("Use NSExpression instead, calling the mgl_step:from:stops: function."))),
MGLInterpolationModeCategorical __attribute__((unavailable("Use NSExpression instead."))),
MGLInterpolationModeIdentity __attribute__((unavailable("Use +[NSExpression expressionForKeyPath:] instead.")))
} __attribute__((unavailable("Use NSExpression instead.")));
@@ -29,19 +29,19 @@ MGL_EXPORT __attribute__((unavailable("Use +[NSExpression expressionForConstantV
@compatibility_alias MGLStyleConstantValue MGLConstantStyleValue;
-MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, calling the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function.")))
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, calling the mgl_step:from:stops: or mgl_interpolate:withCurveType:parameters:stops: function.")))
@interface MGLStyleFunction<T> : MGLStyleValue<T>
@end
-MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to the $zoomLevel variable.")))
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_step:from:stops: or mgl_interpolate:withCurveType:parameters:stops: function to the $zoomLevel variable.")))
@interface MGLCameraStyleFunction<T> : MGLStyleFunction<T>
@end
-MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function to a key path expression.")))
+MGL_EXPORT __attribute__((unavailable("Use NSExpression instead, applying the mgl_step:from:stops: or mgl_interpolate:withCurveType:parameters:stops: function to a key path expression.")))
@interface MGLSourceStyleFunction<T> : MGLStyleFunction<T>
@end
-MGL_EXPORT __attribute__((unavailable("Use a NSExpression instead with nested mgl_stepWithMinimum:stops: or mgl_interpolateWithCurveType:parameters:stops: function calls.")))
+MGL_EXPORT __attribute__((unavailable("Use a NSExpression instead with nested mgl_step:from:stops: or mgl_interpolate:withCurveType:parameters:stops: function calls.")))
@interface MGLCompositeStyleFunction<T> : MGLStyleFunction<T>
@end
diff --git a/platform/darwin/src/MGLStyleValue.mm b/platform/darwin/src/MGLStyleValue.mm
index 74e1926f79..5103b5f5cf 100644
--- a/platform/darwin/src/MGLStyleValue.mm
+++ b/platform/darwin/src/MGLStyleValue.mm
@@ -5,7 +5,7 @@
const MGLStyleFunctionOption MGLStyleFunctionOptionInterpolationBase = @"MGLStyleFunctionOptionInterpolationBase";
const MGLStyleFunctionOption MGLStyleFunctionOptionDefaultValue = @"MGLStyleFunctionOptionDefaultValue";
-id MGLJSONObjectFromMBGLValue(const mbgl::style::expression::Value &value) {
+id MGLJSONObjectFromMBGLValue(const mbgl::Value &value) {
return value.match([](const mbgl::NullValue) -> id {
return [NSNull null];
}, [](const bool value) {
@@ -24,98 +24,23 @@ id MGLJSONObjectFromMBGLValue(const mbgl::style::expression::Value &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) {
+ }, [&](const std::vector<mbgl::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) {
+ return array;
+ }, [&](const std::unordered_map<std::string, mbgl::Value> &map) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
for (auto &item : map) {
dictionary[@(item.first.c_str())] = MGLJSONObjectFromMBGLValue(item.second);
}
- return @[@"literal", dictionary];
+ return dictionary;
}, [](const auto &) -> id {
return nil;
});
}
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;
- }
- 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];
- }
- if (auto assertExpression = dynamic_cast<const Assertion *>(&mbglExpression)) {
- NSMutableArray *inputs = [NSMutableArray array];
- assertExpression->eachChild([&](const Expression &child) {
- [inputs addObject:MGLJSONObjectFromMBGLExpression(child)];
- });
- return inputs.firstObject;
- }
- 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 (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;
- }
- 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;
- }
- if (auto caseExpression = dynamic_cast<const Case *>(&mbglExpression)) {
- NSMutableArray *expressionObject = [NSMutableArray arrayWithObject:@"case"];
- caseExpression->eachChild([&](const Expression &child) {
- [expressionObject addObject:MGLJSONObjectFromMBGLExpression(child)];
- });
- return expressionObject;
- }
- NSCAssert(NO, @"Unrecognized expression type.");
- return nil;
+ return MGLJSONObjectFromMBGLValue(mbglExpression.serialize());
}
-
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 5124c29a90..cc63793d0d 100644
--- a/platform/darwin/src/MGLStyleValue_Private.h
+++ b/platform/darwin/src/MGLStyleValue_Private.h
@@ -58,7 +58,7 @@ public:
if (mbglValue.isUndefined()) {
return nil;
}
- return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
+ return [NSExpression expressionWithMGLJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
/**
@@ -333,15 +333,15 @@ private: // Private utilities for converting from mbgl to mgl values
}
NSExpression *operator()(const mbgl::style::CameraFunction<MBGLType> &mbglValue) const {
- return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
+ return [NSExpression expressionWithMGLJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
NSExpression *operator()(const mbgl::style::SourceFunction<MBGLType> &mbglValue) const {
- return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
+ return [NSExpression expressionWithMGLJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
NSExpression *operator()(const mbgl::style::CompositeFunction<MBGLType> &mbglValue) const {
- return [NSExpression mgl_expressionWithJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
+ return [NSExpression expressionWithMGLJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
};
};
diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h
index 4cbe953a44..24466b8018 100644
--- a/platform/darwin/src/MGLStyle_Private.h
+++ b/platform/darwin/src/MGLStyle_Private.h
@@ -14,7 +14,7 @@ namespace mbgl {
@class MGLAttributionInfo;
@class MGLMapView;
@class MGLOpenGLStyleLayer;
-@class MGLVectorSource;
+@class MGLVectorTileSource;
@class MGLVectorStyleLayer;
@interface MGLStyle (Private)
@@ -25,7 +25,7 @@ namespace mbgl {
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
-
+@property (nonatomic, readonly, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration;
@end
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 1017db5442..e27f039b75 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -319,9 +319,10 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslationAnchor) {
An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at
points or along lines on the map.
- Use a symbol style layer to configure the visual appearance of labels for
- features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or
- `MGLFeature` instances in an `MGLShapeSource` object.
+ Use a symbol style layer to configure the visual appearance of feature labels.
+ These features can come from vector tiles loaded by an `MGLVectorTileSource`
+ object, or they can be `MGLShape` or `MGLFeature` instances in an
+ `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing symbol style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -460,11 +461,15 @@ MGL_EXPORT
@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
- values, attribute names enclosed in curly brackets (e.g. `{token}`) are
- replaced with the value of the named attribute. Expressions do not support this
- syntax; for equivalent functionality in expressions, use
- `stringByAppendingString:` and key path expressions.
+ Name of a style image to use for drawing an image background.
+
+ Use the `+[MGLStyle setImage:forName:]` method to associate an image with a
+ name that you can set this property to.
+
+ Within a constant string value, a feature attribute name enclosed in curly
+ braces (e.g., `{token}`) is replaced with the value of the named attribute.
+ Tokens inside non-constant expressions are ignored; instead, use `mgl_join:`
+ and key path expressions.
This attribute corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-style-spec/#layout-symbol-icon-image"><code>icon-image</code></a>
@@ -559,14 +564,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -610,7 +615,7 @@ MGL_EXPORT
This property is measured in degrees.
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.
+ 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.
@@ -673,7 +678,7 @@ MGL_EXPORT
This property is measured in factor of the original icon sizes.
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.
+ 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.
@@ -684,7 +689,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -853,7 +858,7 @@ MGL_EXPORT
This property is measured in degrees.
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.
+ 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 expression that evaluates to `line`. Otherwise,
@@ -885,7 +890,7 @@ MGL_EXPORT
This property is measured in ems.
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.
+ 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.
@@ -896,7 +901,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -968,14 +973,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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
expression that evaluates to `line`. Otherwise, it is ignored.
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 1
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -987,10 +992,12 @@ MGL_EXPORT
@property (nonatomic, null_resettable) NSExpression *symbolSpacing;
/**
- Value to use for a text label. Within literal values, attribute names enclosed
- in curly brackets (e.g. `{token}`) are replaced with the value of the named
- attribute. Expressions do not support this syntax; for equivalent functionality
- in expressions, use `stringByAppendingString:` and key path expressions.
+ Value to use for a text label.
+
+ Within a constant string value, a feature attribute name enclosed in curly
+ braces (e.g., `{token}`) is replaced with the value of the named attribute.
+ Tokens inside non-constant expressions are ignored; instead, use `mgl_join:`
+ and key path expressions.
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.
@@ -1122,7 +1129,7 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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.
@@ -1133,7 +1140,7 @@ MGL_EXPORT
You can set this property to an expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1212,7 +1219,7 @@ MGL_EXPORT
This property is measured in ems.
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.
+ 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.
@@ -1234,7 +1241,7 @@ MGL_EXPORT
This property is measured in ems.
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.
+ 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.
@@ -1331,14 +1338,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1382,7 +1389,7 @@ MGL_EXPORT
This property is measured in degrees.
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.
+ 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.
@@ -1463,6 +1470,7 @@ MGL_EXPORT
#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.
@@ -1484,6 +1492,29 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *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 expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
/**
The transition affecting any changes to this layer’s `iconColor` property.
@@ -1498,14 +1529,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1521,6 +1552,7 @@ 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.
@@ -1542,6 +1574,29 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *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 expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
/**
The transition affecting any changes to this layer’s `iconHaloColor` property.
@@ -1556,14 +1611,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1583,14 +1638,14 @@ MGL_EXPORT
The opacity at which the icon will be drawn.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1707,6 +1762,7 @@ MGL_EXPORT
@property (nonatomic, null_resettable) NSExpression *iconTranslateAnchor __attribute__((unavailable("Use iconTranslationAnchor instead.")));
+#if TARGET_OS_IPHONE
/**
The color with which the text will be drawn.
@@ -1727,6 +1783,28 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *textColor;
+#else
+/**
+ The color with which the text will be drawn.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
/**
The transition affecting any changes to this layer’s `textColor` property.
@@ -1741,14 +1819,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1764,6 +1842,7 @@ MGL_EXPORT
*/
@property (nonatomic) MGLTransition textHaloBlurTransition;
+#if TARGET_OS_IPHONE
/**
The color of the text's halo, which helps it stand out from backgrounds.
@@ -1784,6 +1863,28 @@ MGL_EXPORT
feature attributes
*/
@property (nonatomic, null_resettable) NSExpression *textHaloColor;
+#else
+/**
+ The color of the text's halo, which helps it stand out from backgrounds.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
/**
The transition affecting any changes to this layer’s `textHaloColor` property.
@@ -1799,14 +1900,14 @@ MGL_EXPORT
This property is measured in points.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values no less than 0
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
@@ -1826,14 +1927,14 @@ MGL_EXPORT
The opacity at which the text will be drawn.
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.
+ 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 expression containing any of the following:
- * Constant numeric values
+ * Constant numeric values between 0 and 1 inclusive
* Predefined functions, including mathematical and string operators
* Conditional expressions
* Variable assignments and references to assigned variables
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 24d6643e6c..0d9fac4808 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -233,7 +233,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultIconImage();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setIconImage:(NSExpression *)iconImage {
@@ -578,7 +579,8 @@ namespace mbgl {
if (propertyValue.isUndefined()) {
propertyValue = self.rawLayer->getDefaultTextField();
}
- return MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setTextField:(NSExpression *)textField {
diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h
index 2d75fa14d8..4bf09026f2 100644
--- a/platform/darwin/src/MGLTileSource.h
+++ b/platform/darwin/src/MGLTileSource.h
@@ -184,9 +184,9 @@ typedef NS_ENUM(NSUInteger, MGLDEMEncoding) {
Mapbox-hosted tile set, view it in
<a href="https://www.mapbox.com/studio/tilesets/">Mapbox Studio’s Tilesets editor</a>.
- Create instances of `MGLRasterSource` and `MGLVectorSource` in order to use
- `MGLTileSource`'s properties and methods. Do not create instances of `MGLTileSource`
- directly, and do not create your own subclasses of this class.
+ Create instances of `MGLRasterTileSource` and `MGLVectorTileSource` in order
+ to use `MGLTileSource`'s properties and methods. Do not create instances of
+ `MGLTileSource` directly, and do not create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLTileSource : MGLSource
diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm
index c37812ab8e..87ac5be9c2 100644
--- a/platform/darwin/src/MGLTileSource.mm
+++ b/platform/darwin/src/MGLTileSource.mm
@@ -2,6 +2,7 @@
#import "MGLAttributionInfo_Private.h"
#import "MGLGeometry_Private.h"
+#import "MGLRasterDEMSource.h"
#import "NSString+MGLAdditions.h"
#import "NSValue+MGLAdditions.h"
diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h
index 177b1b70f0..d9431215a1 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.h
+++ b/platform/darwin/src/MGLVectorStyleLayer.h
@@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
`MGLVectorStyleLayer` is an abstract superclass for style layers whose content
- is defined by an `MGLShapeSource` or `MGLVectorSource` object.
+ is defined by an `MGLShapeSource` or `MGLVectorTileSource` object.
Create instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`,
`MGLFillExtrusionStyleLayer`, `MGLHeatmapStyleLayer`, `MGLLineStyleLayer`, and
@@ -47,7 +47,7 @@ MGL_EXPORT
```swift
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
layer.sourceLayerIdentifier = "contours"
- layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
mapView.style?.addLayer(layer)
```
*/
diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorTileSource.h
index 968be3c0e0..790c9d4d42 100644
--- a/platform/darwin/src/MGLVectorSource.h
+++ b/platform/darwin/src/MGLVectorTileSource.h
@@ -5,29 +5,37 @@
NS_ASSUME_NONNULL_BEGIN
/**
- `MGLVectorSource` is a map content source that supplies tiled vector data in
- <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tile</a> format to
- be shown on the map. The location of and metadata about the tiles are defined
- either by an option dictionary or by an external file that conforms to the
+ `MGLVectorTileSource` is a map content source that supplies tiled vector data
+ in <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tile</a> format
+ to be shown on the map. The location of and metadata about the tiles are
+ defined either by an option dictionary or by an external file that conforms to
+ the
<a href="https://github.com/mapbox/tilejson-spec/">TileJSON specification</a>.
- A vector source is added to an `MGLStyle` object along with one or more
+ A vector tile source is added to an `MGLStyle` object along with one or more
`MGLVectorStyleLayer` objects. A vector style layer defines the appearance of
- any content supplied by the vector source.
+ any content supplied by the vector tile source.
+
+ `MGLVectorTileSource` is optimized for data sets that are too large to fit
+ completely in memory, such as vector tile sets or data sets managed in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>. For
+ <a href="http://geojson.org/">GeoJSON</a> data, use the `MGLShapeSource`
+ class. For tiled data that changes dynamically, the `MGLComputedShapeSource`
+ class may be a suitable alternative.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-vector"><code>vector</code></a>
source defined by the style JSON file is represented at runtime by an
- `MGLVectorSource` object that you can use to initialize new style layers. You
- can also add and remove sources dynamically using methods such as
+ `MGLVectorTileSource` object that you can use to initialize new style layers.
+ You can also add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
Within each vector tile, each geometric coordinate must lie between
−1&nbsp;×&nbsp;<var>extent</var> and
(<var>extent</var>&nbsp;×&nbsp;2)&nbsp;−&nbsp;1, inclusive. Any vector style
- layer initialized with a vector source must have a non-`nil` value in its
+ layer initialized with a vector tile source must have a non-`nil` value in its
`sourceLayerIdentifier` property.
- Commonly used vector sources include
+ Commonly used vector tile sources include
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>,
<a href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>,
and
@@ -36,23 +44,24 @@ NS_ASSUME_NONNULL_BEGIN
### Example
```swift
- let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ let source = MGLVectorTileSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
```
*/
MGL_EXPORT
-@interface MGLVectorSource : MGLTileSource
+@interface MGLVectorTileSource : MGLTileSource
#pragma mark Initializing a Source
/**
- Returns a vector source initialized with an identifier and configuration URL.
+ Returns a vector tile source initialized with an identifier and configuration
+ URL.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -66,12 +75,12 @@ MGL_EXPORT
which it is added.
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
- @return An initialized vector source.
+ @return An initialized vector tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL NS_DESIGNATED_INITIALIZER;
/**
- Returns a vector source initialized an identifier, tile URL templates, and
+ Returns a vector tile source initialized an identifier, tile URL templates, and
options.
Tile URL templates are strings that specify the URLs of the vector tiles to
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorTileSource.mm
index 90ffe6f98b..c1f7267e4a 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorTileSource.mm
@@ -1,4 +1,4 @@
-#import "MGLVectorSource_Private.h"
+#import "MGLVectorTileSource_Private.h"
#import "MGLFeature_Private.h"
#import "MGLSource_Private.h"
@@ -13,13 +13,13 @@
#include <mbgl/style/sources/vector_source.hpp>
#include <mbgl/renderer/renderer.hpp>
-@interface MGLVectorSource ()
+@interface MGLVectorTileSource ()
@property (nonatomic, readonly) mbgl::style::VectorSource *rawSource;
@end
-@implementation MGLVectorSource
+@implementation MGLVectorTileSource
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String,
@@ -73,30 +73,57 @@
@end
-@implementation MGLVectorSource (Private)
+@implementation MGLVectorTileSource (Private)
+
+/**
+ An array of locale codes with dedicated name fields in the Mapbox Streets
+ source.
+
+ https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ */
+static NSArray * const MGLMapboxStreetsLanguages = @[
+ @"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+
+/**
+ Like `MGLMapboxStreetsLanguages`, but deanglicized for use with
+ `+[NSBundle preferredLocalizationsFromArray:forPreferences:]`.
+ */
+static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[
+ @"mul", @"ar", @"de", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
- // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
static dispatch_once_t onceToken;
static NS_SET_OF(NSString *) *mapboxStreetsLanguages;
dispatch_once(&onceToken, ^{
- // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
- mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil];
+ mapboxStreetsLanguages = [NSSet setWithArray:MGLMapboxStreetsLanguages];
});
return mapboxStreetsLanguages;
}
+ (NSString *)preferredMapboxStreetsLanguage {
- NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects;
- NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages
- forPreferences:[NSLocale preferredLanguages]];
+ return [self preferredMapboxStreetsLanguageForPreferences:[NSLocale preferredLanguages]];
+}
+
++ (NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray {
+ BOOL acceptsEnglish = [preferencesArray filteredArrayUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(NSString * _Nullable language, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [[NSLocale localeWithLocaleIdentifier:language].languageCode isEqualToString:@"en"];
+ }]].count;
+
+ NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:MGLMapboxStreetsAlternativeLanguages
+ forPreferences:preferencesArray];
NSString *mostSpecificLanguage;
for (NSString *language in preferredLanguages) {
if (language.length > mostSpecificLanguage.length) {
mostSpecificLanguage = language;
}
}
- return mostSpecificLanguage ?: @"en";
+ if ([mostSpecificLanguage isEqualToString:@"mul"]) {
+ return acceptsEnglish ? @"en" : nil;
+ }
+ return mostSpecificLanguage;
}
- (BOOL)isMapboxStreets {
@@ -116,7 +143,7 @@
// Replace {name} and {name_*} with the matching localized name tag.
NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name";
NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"];
- for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) {
+ for (NSString *languageCode in [MGLVectorTileSource mapboxStreetsLanguages]) {
NSString *key = [NSString stringWithFormat:@"name_%@", languageCode];
localizedKeysByKey[key] = localizedKey;
}
diff --git a/platform/darwin/src/MGLVectorSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h
index 7d19e03a99..77521869f1 100644
--- a/platform/darwin/src/MGLVectorSource_Private.h
+++ b/platform/darwin/src/MGLVectorTileSource_Private.h
@@ -1,14 +1,15 @@
-#import "MGLVectorSource.h"
+#import "MGLVectorTileSource.h"
NS_ASSUME_NONNULL_BEGIN
-@interface MGLVectorSource (Private)
+@interface MGLVectorTileSource (Private)
@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages;
-+ (NSString *)preferredMapboxStreetsLanguage;
++ (nullable NSString *)preferredMapboxStreetsLanguage;
++ (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray;
- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index 5b54d66aeb..380215ff32 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -1,231 +1,12 @@
#import "NSComparisonPredicate+MGLAdditions.h"
+#import "MGLStyleValue_Private.h"
+
#import "NSPredicate+MGLAdditions.h"
#import "NSExpression+MGLPrivateAdditions.h"
@implementation NSComparisonPredicate (MGLAdditions)
-- (mbgl::style::Filter)mgl_filter {
- NSExpression *leftExpression = self.leftExpression;
- NSExpression *rightExpression = self.rightExpression;
- NSExpressionType leftType = leftExpression.expressionType;
- NSExpressionType rightType = rightExpression.expressionType;
- BOOL isReversed = ((leftType == NSConstantValueExpressionType || leftType == NSAggregateExpressionType)
- && rightType == NSKeyPathExpressionType);
- switch (self.predicateOperatorType) {
- case NSEqualToPredicateOperatorType: {
- mbgl::style::EqualsFilter eqFilter;
- eqFilter.key = self.mgl_keyPath.UTF8String;
- eqFilter.value = self.mgl_constantValue;
-
- // Convert $type == to TypeEqualsFilter.
- if (eqFilter.key == "$type") {
- mbgl::style::TypeEqualsFilter typeEqFilter;
- typeEqFilter.value = self.mgl_featureType;
- return typeEqFilter;
- }
-
- // Convert $id == to IdentifierEqualsFilter.
- if (eqFilter.key == "$id") {
- // Convert $id == nil to NotHasIdentifierFilter.
- if (eqFilter.value.is<mbgl::NullValue>()) {
- return mbgl::style::NotHasIdentifierFilter();
- }
-
- mbgl::style::IdentifierEqualsFilter idEqFilter;
- idEqFilter.value = self.mgl_featureIdentifier;
- return idEqFilter;
- }
-
- // Convert == nil to NotHasFilter.
- if (eqFilter.value.is<mbgl::NullValue>()) {
- mbgl::style::NotHasFilter notHasFilter;
- notHasFilter.key = eqFilter.key;
- return notHasFilter;
- }
-
- return eqFilter;
- }
- case NSNotEqualToPredicateOperatorType: {
- mbgl::style::NotEqualsFilter neFilter;
- neFilter.key = self.mgl_keyPath.UTF8String;
- neFilter.value = self.mgl_constantValue;
-
- // Convert $type != to TypeNotEqualsFilter.
- if (neFilter.key == "$type") {
- mbgl::style::TypeNotEqualsFilter typeNeFilter;
- typeNeFilter.value = self.mgl_featureType;
- return typeNeFilter;
- }
-
- // Convert $id != to IdentifierNotEqualsFilter.
- if (neFilter.key == "$id") {
- // Convert $id != nil to HasIdentifierFilter.
- if (neFilter.value.is<mbgl::NullValue>()) {
- return mbgl::style::HasIdentifierFilter();
- }
-
- mbgl::style::IdentifierNotEqualsFilter idNeFilter;
- idNeFilter.value = self.mgl_featureIdentifier;
- return idNeFilter;
- }
-
- // Convert != nil to HasFilter.
- if (neFilter.value.is<mbgl::NullValue>()) {
- mbgl::style::HasFilter hasFilter;
- hasFilter.key = neFilter.key;
- return hasFilter;
- }
-
- return neFilter;
- }
- case NSGreaterThanPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::LessThanFilter ltFilter;
- ltFilter.key = self.mgl_keyPath.UTF8String;
- ltFilter.value = self.mgl_constantValue;
- return ltFilter;
- } else {
- mbgl::style::GreaterThanFilter gtFilter;
- gtFilter.key = self.mgl_keyPath.UTF8String;
- gtFilter.value = self.mgl_constantValue;
- return gtFilter;
- }
- }
- case NSGreaterThanOrEqualToPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = self.mgl_keyPath.UTF8String;
- lteFilter.value = self.mgl_constantValue;
- return lteFilter;
- } else {
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = self.mgl_keyPath.UTF8String;
- gteFilter.value = self.mgl_constantValue;
- return gteFilter;
- }
- }
- case NSLessThanPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::GreaterThanFilter gtFilter;
- gtFilter.key = self.mgl_keyPath.UTF8String;
- gtFilter.value = self.mgl_constantValue;
- return gtFilter;
- } else {
- mbgl::style::LessThanFilter ltFilter;
- ltFilter.key = self.mgl_keyPath.UTF8String;
- ltFilter.value = self.mgl_constantValue;
- return ltFilter;
- }
- }
- case NSLessThanOrEqualToPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = self.mgl_keyPath.UTF8String;
- gteFilter.value = self.mgl_constantValue;
- return gteFilter;
- } else {
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = self.mgl_keyPath.UTF8String;
- lteFilter.value = self.mgl_constantValue;
- return lteFilter;
- }
- }
- case NSInPredicateOperatorType: {
- if (isReversed) {
- if (leftType == NSConstantValueExpressionType && [leftExpression.constantValue isKindOfClass:[NSString class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"CONTAINS not supported for string comparison."];
- }
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare values IN attribute."];
- }
-
- // Convert $type IN to TypeInFilter.
- if ([leftExpression.keyPath isEqualToString:@"$type"]) {
- mbgl::style::TypeInFilter typeInFilter;
- typeInFilter.values = rightExpression.mgl_aggregateFeatureType;
- return typeInFilter;
- }
-
- // Convert $id IN to IdentifierInFilter.
- if ([leftExpression.keyPath isEqualToString:@"$id"]) {
- mbgl::style::IdentifierInFilter idInFilter;
- idInFilter.values = rightExpression.mgl_aggregateFeatureIdentifier;
- return idInFilter;
- }
-
- mbgl::style::InFilter inFilter;
- inFilter.key = leftExpression.keyPath.UTF8String;
- inFilter.values = rightExpression.mgl_aggregateMBGLValue;
- return inFilter;
- }
- case NSContainsPredicateOperatorType: {
- if (!isReversed) {
- if (rightType == NSConstantValueExpressionType && [rightExpression.constantValue isKindOfClass:[NSString class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"IN not supported for string comparison."];
- }
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare attribute CONTAINS values."];
- }
-
- // Convert CONTAINS $type to TypeInFilter.
- if ([rightExpression.keyPath isEqualToString:@"$type"]) {
- mbgl::style::TypeInFilter typeInFilter;
- typeInFilter.values = leftExpression.mgl_aggregateFeatureType;
- return typeInFilter;
- }
-
- // Convert CONTAINS $id to IdentifierInFilter.
- if ([rightExpression.keyPath isEqualToString:@"$id"]) {
- mbgl::style::IdentifierInFilter idInFilter;
- idInFilter.values = leftExpression.mgl_aggregateFeatureIdentifier;
- return idInFilter;
- }
-
- mbgl::style::InFilter inFilter;
- inFilter.key = rightExpression.keyPath.UTF8String;
- inFilter.values = leftExpression.mgl_aggregateMBGLValue;
- return inFilter;
- }
- case NSBetweenPredicateOperatorType: {
- if (isReversed) {
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare bounds BETWEEN attribute."];
- }
- if (![rightExpression.constantValue isKindOfClass:[NSArray class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"Right side of BETWEEN predicate must be an array."]; // not NSSet
- }
- auto values = rightExpression.mgl_aggregateMBGLValue;
- if (values.size() != 2) {
- [NSException raise:NSInvalidArgumentException
- format:@"Right side of BETWEEN predicate must have two items."];
- }
- mbgl::style::AllFilter allFilter;
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = leftExpression.keyPath.UTF8String;
- gteFilter.value = values[0];
- allFilter.filters.push_back(gteFilter);
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = leftExpression.keyPath.UTF8String;
- lteFilter.value = values[1];
- allFilter.filters.push_back(lteFilter);
- return allFilter;
- }
- case NSMatchesPredicateOperatorType:
- case NSLikePredicateOperatorType:
- case NSBeginsWithPredicateOperatorType:
- case NSEndsWithPredicateOperatorType:
- case NSCustomSelectorPredicateOperatorType:
- [NSException raise:NSInvalidArgumentException
- format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
- }
-
- return {};
-}
-
- (NSString *)mgl_keyPath {
NSExpression *leftExpression = self.leftExpression;
NSExpression *rightExpression = self.rightExpression;
@@ -318,14 +99,45 @@
case NSNotEqualToPredicateOperatorType:
op = @"!=";
break;
+ case NSBetweenPredicateOperatorType: {
+ op = @"all";
+ NSArray *limits = self.rightExpression.constantValue;
+ NSPredicate *leftHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:limits[0]
+ rightExpression:self.leftExpression
+ modifier:NSDirectPredicateModifier
+ type:NSLessThanOrEqualToPredicateOperatorType
+ options:0];
+ NSPredicate *rightHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
+ rightExpression:limits[1]
+ modifier:NSDirectPredicateModifier
+ type:NSLessThanOrEqualToPredicateOperatorType
+ options:0];
+ return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject];
+ }
+ case NSInPredicateOperatorType: {
+ NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil];
+ NSArray *optionsExpressions = self.rightExpression.constantValue;
+ for (id object in optionsExpressions) {
+ id option = ((NSExpression *)object).mgl_jsonExpressionObject;
+ [elements addObject:option];
+ [elements addObject:@YES];
+ }
+ [elements addObject:@NO];
+ return elements;
+ }
+ case NSContainsPredicateOperatorType: {
+ NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
+ rightExpression:self.leftExpression
+ modifier:self.comparisonPredicateModifier
+ type:NSInPredicateOperatorType
+ options:self.options];
+ return inPredicate.mgl_jsonExpressionObject;
+ }
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];
}
diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
index 19568b8033..5a98b763ea 100644
--- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
@@ -1,8 +1,12 @@
#import "NSCompoundPredicate+MGLAdditions.h"
+#import "MGLStyleValue_Private.h"
+
#import "NSPredicate+MGLAdditions.h"
#import "NSExpression+MGLPrivateAdditions.h"
+#include <mbgl/style/conversion/property_value.hpp>
+
@implementation NSCompoundPredicate (MGLAdditions)
- (std::vector<mbgl::style::Filter>)mgl_subfilters
@@ -14,62 +18,6 @@
return filters;
}
-- (mbgl::style::Filter)mgl_filter
-{
- switch (self.compoundPredicateType) {
- case NSNotPredicateType: {
- NSAssert(self.subpredicates.count <= 1, @"NOT predicate cannot have multiple subpredicates.");
- NSPredicate *subpredicate = self.subpredicates.firstObject;
- mbgl::style::Filter subfilter = subpredicate.mgl_filter;
-
- // Convert NOT(!= nil) to NotHasFilter.
- if (subfilter.is<mbgl::style::HasFilter>()) {
- auto hasFilter = subfilter.get<mbgl::style::HasFilter>();
- return mbgl::style::NotHasFilter { .key = hasFilter.key };
- }
-
- // Convert NOT(== nil) to HasFilter.
- if (subfilter.is<mbgl::style::NotHasFilter>()) {
- auto hasFilter = subfilter.get<mbgl::style::NotHasFilter>();
- return mbgl::style::HasFilter { .key = hasFilter.key };
- }
-
- // Convert NOT(IN) or NOT(CONTAINS) to NotInFilter.
- if (subfilter.is<mbgl::style::InFilter>()) {
- auto inFilter = subfilter.get<mbgl::style::InFilter>();
- mbgl::style::NotInFilter notInFilter;
- notInFilter.key = inFilter.key;
- notInFilter.values = inFilter.values;
- return notInFilter;
- }
-
- // Convert NOT(), NOT(AND), NOT(NOT), NOT(==), etc. into NoneFilter.
- mbgl::style::NoneFilter noneFilter;
- if (subfilter.is<mbgl::style::AnyFilter>()) {
- // Flatten NOT(OR).
- noneFilter.filters = subfilter.get<mbgl::style::AnyFilter>().filters;
- } else if (subpredicate) {
- noneFilter.filters = { subfilter };
- }
- return noneFilter;
- }
- case NSAndPredicateType: {
- mbgl::style::AllFilter filter;
- filter.filters = self.mgl_subfilters;
- return filter;
- }
- case NSOrPredicateType: {
- mbgl::style::AnyFilter filter;
- filter.filters = self.mgl_subfilters;
- return filter;
- }
- }
-
- [NSException raise:@"Compound predicate type not handled"
- format:@""];
- return {};
-}
-
@end
@implementation NSCompoundPredicate (MGLExpressionAdditions)
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index b4d36bc1b1..cfdf27aade 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -9,15 +9,150 @@
NS_ASSUME_NONNULL_BEGIN
+typedef NSString *MGLExpressionInterpolationMode NS_TYPED_ENUM;
+
+/**
+ An `NSString` identifying the `linear` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `linear` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeLinear;
+
+/**
+ An `NSString` identifying the `expotential` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `exponential` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeExponential;
+
+/**
+ An `NSString` identifying the `cubic-bezier` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `cubic-bezier` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier;
+
+/**
+ Methods for creating expressions that use Mapbox-specific functionality and for
+ converting to and from the JSON format defined in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions">Mapbox Style Specification</a>.
+ */
@interface NSExpression (MGLAdditions)
+#pragma mark Creating Variable Expressions
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-zoom"><code>zoom</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *zoomLevelVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-heatmap-density"><code>heatmap-density</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *heatmapDensityVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#eexpressions-geometry-type"><code>geometry-type</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *geometryTypeVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-id"><code>id</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *featureIdentifierVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-properties"><code>properties</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *featureAttributesVariableExpression;
+
+@property (class, nonatomic, readonly) NSExpression *featurePropertiesVariableExpression __attribute__((deprecated("Use -featureAttributesVariableExpression.")));
+
+#pragma mark Creating Conditional Expressions
+
+/**
+ Returns a conditional function expression specifying the string predicate, and
+ expressions for each condition.
+
+ @param conditionPredicate The predicate to get evaluated.
+ @param trueExpression The expression for conditions equal to true.
+ @param falseExpression The expression for conditions equal to false.
+ */
++ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression NS_SWIFT_NAME(init(forMGLConditional:trueExpression:falseExpression:));
+
+#pragma mark Creating Ramp, Scale, and Curve Expressions
+
+/**
+ Returns a step function expression specifying the stepping, from expression
+ and stops.
+
+ @param steppingExpression The stepping expression.
+ @param minimumExpression The expression which could be a constant or function expression.
+ @param stops The stops must be an `NSDictionary` constant `NSExpression`.
+ */
++ (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression*)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLStepping:from:stops:));
+
+/**
+ Returns an interpolated function expression specifying the function operator, curve type,
+ parameters and steps.
+
+ @param inputExpression The interpolating expression input.
+ @param curveType The curve type could be `MGLExpressionInterpolationModeLinear`,
+ `MGLExpressionInterpolationModeExponential` and
+ `MGLExpressionInterpolationModeCubicBezier`.
+ @param parameters The parameters expression.
+ @param stops The stops expression.
+ */
++ (instancetype)mgl_expressionForInterpolatingExpression:(nonnull NSExpression*)inputExpression withCurveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLInterpolating:curveType:parameters:stops:));
+
+/**
+ Returns a match function expression specifying the input, matching values,
+ and default value.
+
+ @param inputExpression The matching expression.
+ @param matchedExpressions The matched values expression dictionary must be condition : value.
+ @param defaultExpression The defaultValue expression to be used in case there is no match.
+ */
++ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)inputExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression NS_SWIFT_NAME(init(forMGLMatchingKey:in:default:));
+
+#pragma mark Concatenating String Expressions
+
+/**
+ Returns a constant expression appending the passed expression.
+
+ @note Both the receiver and the given expression must be an `NSString` constant
+ expression type; otherwise, an exception is rised.
+
+ @param expression The expression to append to the receiver.
+ */
+- (instancetype)mgl_expressionByAppendingExpression:(nonnull NSExpression *)expression NS_SWIFT_NAME(mgl_appending(_:));
+
+#pragma mark Converting JSON Expressions
+
/**
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)”
+ See the
+ “[Information for Style Authors](../for-style-authors.html#setting-attribute-values)”
guide for a correspondence of operators and types between the style
specification and the `NSExpression` representation used by this SDK.
@@ -26,14 +161,15 @@ NS_ASSUME_NONNULL_BEGIN
@return An initialized expression equivalent to `object`, suitable for use as
the value of a style layer attribute.
*/
-+ (instancetype)mgl_expressionWithJSONObject:(id)object;
++ (instancetype)expressionWithMGLJSONObject:(id)object NS_SWIFT_NAME(init(mglJSONObject:));
/**
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)”
+ See the
+ “[Information for Style Authors](../for-style-authors.html#setting-attribute-values)”
guide for a correspondence of operators and types between the style
specification and the `NSExpression` representation used by this SDK.
@@ -42,6 +178,24 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+#pragma mark Localizing the Expression
+
+/**
+ Returns a copy of the receiver localized into the given locale.
+
+ This method assumes the receiver refers to the feature attributes that are
+ available in vector tiles supplied by the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ On iOS, the user can set the system’s preferred language in Settings, General
+ Settings, Language & Region. On macOS, the user can set the system’s preferred
+ language in the Language & Region pane of System Preferences.
+
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index 5ad565c398..c82a7dc008 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -10,9 +10,239 @@
#endif
#import "NSPredicate+MGLAdditions.h"
#import "NSValue+MGLStyleAttributeAdditions.h"
+#import "MGLVectorTileSource_Private.h"
+
+#import <objc/runtime.h>
#import <mbgl/style/expression/expression.hpp>
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeLinear = @"linear";
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeExponential = @"exponential";
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = @"cubic-bezier";
+
+@interface MGLAftermarketExpressionInstaller: NSObject
+@end
+
+@implementation MGLAftermarketExpressionInstaller
+
++ (void)load {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ [self installFunctions];
+ });
+}
+
+/**
+ Adds to NSExpression’s built-in repertoire of functions.
+ */
++ (void)installFunctions {
+ Class MGLAftermarketExpressionInstaller = [self class];
+
+ // NSExpression’s built-in functions are backed by class methods on a
+ // private class, so use a function expression to get at the class.
+ // http://funwithobjc.tumblr.com/post/2922267976/using-custom-functions-with-nsexpression
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"sum({})"];
+ NSString *className = NSStringFromClass([functionExpression.operand.constantValue class]);
+
+ // Effectively categorize the class with some extra class methods.
+ Class NSPredicateUtilities = objc_getMetaClass(className.UTF8String);
+#pragma clang push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+ #define INSTALL_METHOD(sel) \
+ { \
+ Method method = class_getInstanceMethod(MGLAftermarketExpressionInstaller, @selector(sel)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel), method_getImplementation(method), method_getTypeEncoding(method)); \
+ }
+ #define INSTALL_CONTROL_STRUCTURE(sel) \
+ { \
+ Method method = class_getInstanceMethod(MGLAftermarketExpressionInstaller, @selector(sel:)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel), method_getImplementation(method), method_getTypeEncoding(method)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel:), method_getImplementation(method), method_getTypeEncoding(method)); \
+ }
+
+ // Install method-like functions, taking the number of arguments implied by
+ // the selector name.
+ INSTALL_METHOD(mgl_join:);
+ INSTALL_METHOD(mgl_round:);
+ INSTALL_METHOD(mgl_interpolate:withCurveType:parameters:stops:);
+ INSTALL_METHOD(mgl_step:from:stops:);
+ INSTALL_METHOD(mgl_coalesce:);
+ INSTALL_METHOD(mgl_does:have:);
+ INSTALL_METHOD(mgl_acos:);
+ INSTALL_METHOD(mgl_cos:);
+ INSTALL_METHOD(mgl_asin:);
+ INSTALL_METHOD(mgl_sin:);
+ INSTALL_METHOD(mgl_atan:);
+ INSTALL_METHOD(mgl_tan:);
+ INSTALL_METHOD(mgl_log2:);
+
+ // Install functions that resemble control structures, taking arbitrary
+ // numbers of arguments. Vararg aftermarket functions need to be declared
+ // with an explicit and implicit first argument.
+ INSTALL_CONTROL_STRUCTURE(MGL_LET);
+ INSTALL_CONTROL_STRUCTURE(MGL_MATCH);
+ INSTALL_CONTROL_STRUCTURE(MGL_IF);
+ INSTALL_CONTROL_STRUCTURE(MGL_FUNCTION);
+
+ #undef INSTALL_AFTERMARKET_FN
+#pragma clang pop
+}
+
+/**
+ Joins the given components into a single string by concatenating each component
+ in order.
+ */
+- (NSString *)mgl_join:(NSArray<NSString *> *)components {
+ return [components componentsJoinedByString:@""];
+}
+
+/**
+ Rounds the given number to the nearest integer. If the number is halfway
+ between two integers, this method rounds it away from zero.
+ */
+- (NSNumber *)mgl_round:(NSNumber *)number {
+ return @(round(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse cosine.
+ */
+- (NSNumber *)mgl_acos:(NSNumber *)number {
+ return @(acos(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the cosine.
+ */
+- (NSNumber *)mgl_cos:(NSNumber *)number {
+ return @(cos(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse sine.
+ */
+- (NSNumber *)mgl_asin:(NSNumber *)number {
+ return @(asin(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the sine.
+ */
+- (NSNumber *)mgl_sin:(NSNumber *)number {
+ return @(sin(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse tangent.
+ */
+- (NSNumber *)mgl_atan:(NSNumber *)number {
+ return @(atan(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the tangent.
+ */
+- (NSNumber *)mgl_tan:(NSNumber *)number {
+ return @(tan(number.doubleValue));
+}
+
+/**
+ Computes the logarithm base two of the value.
+ */
+- (NSNumber *)mgl_log2:(NSNumber *)number {
+ return @(log2(number.doubleValue));
+}
+
+/**
+ A placeholder for a method that evaluates an interpolation expression.
+ */
+- (id)mgl_interpolate:(id)inputExpression withCurveType:(NSString *)curveType parameters:(NSDictionary *)params stops:(NSDictionary *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Interpolation expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates a step expression.
+ */
+- (id)mgl_step:(id)inputExpression from:(id)minimumExpression stops:(NSDictionary *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Step expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates a coalesce expression.
+ */
+- (id)mgl_coalesce:(NSArray<NSExpression *> *)elements {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Coalesce expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ Returns a Boolean value indicating whether the object has a value for the given
+ key.
+ */
+- (BOOL)mgl_does:(id)object have:(NSString *)key {
+ return [object valueForKey:key] != nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression based on an arbitrary
+ number of variable names and assigned expressions.
+ */
+- (id)MGL_LET:(NSString *)firstVariableName, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression and returns the matching element.
+ */
+- (id)MGL_MATCH:(id)firstCondition, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression and returns the matching element.
+ */
+- (id)MGL_IF:(id)firstCondition, ... {
+ va_list argumentList;
+ va_start(argumentList, firstCondition);
+
+ for (id eachExpression = firstCondition; eachExpression; eachExpression = va_arg(argumentList, id)) {
+ if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) {
+ id valueExpression = va_arg(argumentList, id);
+ if ([eachExpression evaluateWithObject:nil]) {
+ return valueExpression;
+ }
+ } else {
+ return eachExpression;
+ }
+ }
+ va_end(argumentList);
+
+ return nil;
+}
+
+
+/**
+ A placeholder for a catch-all method that evaluates an arbitrary number of
+ arguments as an expression according to the Mapbox Style Specification’s
+ expression language.
+ */
+- (id)MGL_FUNCTION:(id)firstArgument, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Mapbox GL function expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+@end
+
@implementation NSExpression (MGLPrivateAdditions)
- (std::vector<mbgl::Value>)mgl_aggregateMBGLValue {
@@ -159,6 +389,112 @@
return {};
}
+// Selectors of functions that can contain tokens in arguments.
+static NSArray * const MGLTokenizedFunctions = @[
+ @"mgl_interpolateWithCurveType:parameters:stops:",
+ @"mgl_interpolate:withCurveType:parameters:stops:",
+ @"mgl_stepWithMinimum:stops:",
+ @"mgl_step:from:stops:",
+];
+
+/**
+ Returns a copy of the given collection with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original collection.
+ */
+NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY_OF(NSExpression *) *collection) {
+ __block NSMutableArray *upgradedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedItem != item) {
+ if (!upgradedCollection) {
+ upgradedCollection = [collection mutableCopy];
+ }
+ upgradedCollection[idx] = upgradedItem;
+ }
+ }];
+ return upgradedCollection ?: collection;
+};
+
+/**
+ Returns a copy of the given stop dictionary with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original stop
+ dictionary.
+ */
+NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokensWithKeyPaths(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops) {
+ __block NSMutableDictionary *upgradedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *upgradedValue = value.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedValue != value) {
+ if (!upgradedStops) {
+ upgradedStops = [stops mutableCopy];
+ }
+ upgradedStops[zoomLevel] = upgradedValue;
+ }
+ }];
+ return upgradedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSString *constantValue = self.constantValue;
+ if ([constantValue isKindOfClass:[NSString class]] &&
+ [constantValue containsString:@"{"] && [constantValue containsString:@"}"]) {
+ NSMutableArray *components = [NSMutableArray array];
+ NSScanner *scanner = [NSScanner scannerWithString:constantValue];
+ scanner.charactersToBeSkipped = nil;
+ while (!scanner.isAtEnd) {
+ NSString *string;
+ if ([scanner scanUpToString:@"{" intoString:&string]) {
+ [components addObject:[NSExpression expressionForConstantValue:string]];
+ }
+
+ NSString *token;
+ if ([scanner scanString:@"{" intoString:NULL]
+ && [scanner scanUpToString:@"}" intoString:&token]
+ && [scanner scanString:@"}" intoString:NULL]) {
+ [components addObject:[NSExpression expressionForKeyPath:token]];
+ }
+ }
+ if (components.count == 1) {
+ return components.firstObject;
+ }
+ return [NSExpression expressionForFunction:@"mgl_join:"
+ arguments:@[[NSExpression expressionForAggregate:components]]];
+ }
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLStopDictionaryByReplacingTokensWithKeyPaths(stops);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ if ([MGLTokenizedFunctions containsObject:self.function]) {
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLCollectionByReplacingTokensWithKeyPaths(arguments);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:self.operand selectorName:self.function arguments:localizedArguments];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
@end
@implementation NSObject (MGLExpressionAdditions)
@@ -258,6 +594,12 @@
return [self valueForKeyPath:@"mgl_jsonExpressionObject"];
}
+- (id)mgl_coalesce {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Coalesce expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
@end
@implementation NSDictionary (MGLExpressionAdditions)
@@ -271,6 +613,13 @@
return expressionObject;
}
+- (id)mgl_has:(id)element {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Has expressions lack underlying Objective-C implementations."];
+ return nil;
+
+}
+
@end
@implementation NSExpression (MGLExpressionAdditions)
@@ -281,23 +630,91 @@
return self;
}
+- (id)mgl_has:(id)element {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Has expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
@end
@implementation NSExpression (MGLAdditions)
++ (NSExpression *)zoomLevelVariableExpression {
+ return [NSExpression expressionForVariable:@"zoomLevel"];
+}
+
++ (NSExpression *)heatmapDensityVariableExpression {
+ return [NSExpression expressionForVariable:@"heatmapDensity"];
+}
+
++ (NSExpression *)geometryTypeVariableExpression {
+ return [NSExpression expressionForVariable:@"geometryType"];
+}
+
++ (NSExpression *)featureIdentifierVariableExpression {
+ return [NSExpression expressionForVariable:@"featureIdentifier"];
+}
+
++ (NSExpression *)featureAttributesVariableExpression {
+ return [NSExpression expressionForVariable:@"featureAttributes"];
+}
+
++ (NSExpression *)featurePropertiesVariableExpression {
+ return [self featureAttributesVariableExpression];
+}
+
++ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression {
+ if (@available(iOS 9.0, *)) {
+ return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression];
+ } else {
+ return [NSExpression expressionForFunction:@"MGL_IF" arguments:@[[NSExpression expressionWithFormat:@"%@", conditionPredicate], trueExpression, falseExpression]];
+ }
+}
+
++ (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression *)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression *)stops {
+ return [NSExpression expressionForFunction:@"mgl_step:from:stops:"
+ arguments:@[steppingExpression, minimumExpression, stops]];
+}
+
++ (instancetype)mgl_expressionForInterpolatingExpression:(nonnull NSExpression *)inputExpression withCurveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters stops:(nonnull NSExpression *)stops {
+ NSExpression *sanitizeParams = parameters ? parameters : [NSExpression expressionForConstantValue:nil];
+ return [NSExpression expressionForFunction:@"mgl_interpolate:withCurveType:parameters:stops:"
+ arguments:@[inputExpression, [NSExpression expressionForConstantValue:curveType], sanitizeParams, stops]];
+}
+
++ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)inputExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression {
+ NSMutableArray *optionsArray = [NSMutableArray arrayWithObjects:inputExpression, nil];
+
+ NSEnumerator *matchEnumerator = matchedExpressions.keyEnumerator;
+ while (NSExpression *key = matchEnumerator.nextObject) {
+ [optionsArray addObject:key];
+ [optionsArray addObject:[matchedExpressions objectForKey:key]];
+ }
+
+ [optionsArray addObject:defaultExpression];
+ return [NSExpression expressionForFunction:@"MGL_MATCH"
+ arguments:optionsArray];
+}
+
+- (instancetype)mgl_expressionByAppendingExpression:(nonnull NSExpression *)expression {
+ NSExpression *subexpression = [NSExpression expressionForAggregate:@[self, expression]];
+ return [NSExpression expressionForFunction:@"mgl_join:" arguments:@[subexpression]];
+}
+
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];
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:object];
[subexpressions addObject:expression];
}
return subexpressions;
}
-+ (instancetype)mgl_expressionWithJSONObject:(id)object {
++ (instancetype)expressionWithMGLJSONObject:(id)object {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
MGLFunctionNamesByExpressionOperator = @{
@@ -309,9 +726,21 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
@"sqrt": @"sqrt:",
@"log10": @"log:",
@"ln": @"ln:",
+ @"abs": @"abs:",
+ @"round": @"mgl_round:",
+ @"acos" : @"mgl_acos:",
+ @"cos" : @"mgl_cos:",
+ @"asin" : @"mgl_asin:",
+ @"sin" : @"mgl_sin:",
+ @"atan" : @"mgl_atan:",
+ @"tan" : @"mgl_tan:",
+ @"log2" : @"mgl_log2:",
+ @"floor": @"floor:",
+ @"ceil": @"ceiling:",
@"^": @"raise:toPower:",
@"upcase": @"uppercase:",
@"downcase": @"lowercase:",
+ @"let": @"MGL_LET",
};
});
@@ -329,7 +758,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
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];
+ dictionary[key] = [NSExpression expressionWithMGLJSONObject:obj];
}];
return [NSExpression expressionForConstantValue:dictionary];
}
@@ -358,31 +787,45 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
if ([argumentObjects.firstObject isKindOfClass:[NSArray class]]) {
return [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects.firstObject)];
}
- return [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
} else if ([op isEqualToString:@"to-boolean"]) {
- NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
return [NSExpression expressionForFunction:operand selectorName:@"boolValue" arguments:@[]];
- } else if ([op isEqualToString:@"to-number"]) {
- NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"to-number"] || [op isEqualToString:@"number"]) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ if (argumentObjects.count == 1) {
+ return [NSExpression expressionWithFormat:@"CAST(%@, 'NSNumber')", operand];
+ }
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:@"to-string"] || [op isEqualToString:@"string"]) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionWithFormat:@"CAST(%@, 'NSString')", operand];
} else if ([op isEqualToString:@"get"]) {
if (argumentObjects.count == 2) {
- NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.lastObject];
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.lastObject];
if ([argumentObjects.firstObject isKindOfClass:[NSString class]]) {
return [NSExpression expressionWithFormat:@"%@.%K", operand, argumentObjects.firstObject];
}
- NSExpression *key = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ NSExpression *key = [NSExpression expressionWithMGLJSONObject: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]];
+ NSString *function = @"count:";
+ if ([subexpressions.firstObject expressionType] == NSConstantValueExpressionType
+ && [[subexpressions.firstObject constantValue] isKindOfClass:[NSString class]]) {
+ function = @"length:";
+ }
+ return [NSExpression expressionForFunction:function arguments:@[subexpressions.firstObject]];
+ } else if ([op isEqualToString:@"rgb"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression mgl_expressionForRGBComponents:subexpressions];
+ } else if ([op isEqualToString:@"rgba"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression mgl_expressionForRGBAComponents:subexpressions];
} else if ([op isEqualToString:@"min"]) {
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions];
@@ -397,39 +840,47 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
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];
+ NSExpression *subexpression = [NSExpression expressionForAggregate:subexpressions];
+ return [NSExpression expressionForFunction:@"mgl_join:" arguments:@[subexpression]];
+ } else if ([op isEqualToString:@"at"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *index = subexpressions.firstObject;
+ NSExpression *operand = subexpressions[1];
+ return [NSExpression expressionForFunction:@"objectFrom:withIndex:" arguments:@[operand, index]];
+ } else if ([op isEqualToString:@"has"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *operand = argumentObjects.count > 1 ? subexpressions[1] : [NSExpression expressionForEvaluatedObject];
+ NSExpression *key = subexpressions.firstObject;
+ return [NSExpression expressionForFunction:@"mgl_does:have:" arguments:@[operand, key]];
} else if ([op isEqualToString:@"interpolate"]) {
NSArray *interpolationOptions = argumentObjects.firstObject;
NSString *curveType = interpolationOptions.firstObject;
- NSExpression *curveTypeExpression = [NSExpression mgl_expressionWithJSONObject:curveType];
+ NSExpression *curveTypeExpression = [NSExpression expressionWithMGLJSONObject: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];
+ NSExpression *curveParameterExpression = [NSExpression expressionWithMGLJSONObject:curveParameters];
argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
- NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects.firstObject];
+ NSExpression *inputExpression = [NSExpression expressionWithMGLJSONObject: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];
+ stops[key] = [NSExpression expressionWithMGLJSONObject:valueExpression];
}
NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
- return [NSExpression expressionForFunction:operand
- selectorName:@"mgl_interpolateWithCurveType:parameters:stops:"
- arguments:@[curveTypeExpression, curveParameterExpression, stopExpression]];
+ return [NSExpression expressionForFunction:@"mgl_interpolate:withCurveType:parameters:stops:"
+ arguments:@[inputExpression, curveTypeExpression, curveParameterExpression, stopExpression]];
} else if ([op isEqualToString:@"step"]) {
- NSExpression *operand = [NSExpression mgl_expressionWithJSONObject:argumentObjects[0]];
+ NSExpression *inputExpression = [NSExpression expressionWithMGLJSONObject:argumentObjects[0]];
NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
NSExpression *minimum;
if (stopExpressions.count % 2) {
- minimum = [NSExpression mgl_expressionWithJSONObject:stopExpressions.firstObject];
+ minimum = [NSExpression expressionWithMGLJSONObject:stopExpressions.firstObject];
stopExpressions = [stopExpressions subarrayWithRange:NSMakeRange(1, stopExpressions.count - 1)];
}
NSMutableDictionary *stops = [NSMutableDictionary dictionaryWithCapacity:stopExpressions.count / 2];
@@ -437,47 +888,66 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
while (NSNumber *key = stopEnumerator.nextObject) {
NSExpression *valueExpression = stopEnumerator.nextObject;
if (minimum) {
- stops[key] = [NSExpression mgl_expressionWithJSONObject:valueExpression];
+ stops[key] = [NSExpression expressionWithMGLJSONObject:valueExpression];
} else {
- minimum = [NSExpression mgl_expressionWithJSONObject:valueExpression];
+ minimum = [NSExpression expressionWithMGLJSONObject:valueExpression];
}
}
NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
- return [NSExpression expressionForFunction:operand
- selectorName:@"mgl_stepWithMinimum:stops:"
- arguments:@[minimum, stopExpression]];
+ return [NSExpression expressionForFunction:@"mgl_step:from:stops:"
+ arguments:@[inputExpression, minimum, stopExpression]];
} else if ([op isEqualToString:@"zoom"]) {
- return [NSExpression expressionForVariable:@"zoomLevel"];
+ return NSExpression.zoomLevelVariableExpression;
} 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]]];
+ return NSExpression.heatmapDensityVariableExpression;
+ } else if ([op isEqualToString:@"geometry-type"]) {
+ return NSExpression.geometryTypeVariableExpression;
+ } else if ([op isEqualToString:@"id"]) {
+ return NSExpression.featureIdentifierVariableExpression;
+ } else if ([op isEqualToString:@"properties"]) {
+ return NSExpression.featureAttributesVariableExpression;
} 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]];
+ NSMutableArray *arguments = [NSMutableArray array];
+
+ for (NSUInteger index = 0; index < argumentObjects.count; index++) {
+ if (index % 2 == 0 && index != argumentObjects.count - 1) {
+ NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:argumentObjects[index]];
+ NSExpression *argument = [NSExpression expressionForConstantValue:predicate];
+ [arguments addObject:argument];
+ } else {
+ [arguments addObject:[NSExpression expressionWithMGLJSONObject:argumentObjects[index]]];
+ }
}
- return [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression];
+
+ if (@available(iOS 9.0, *)) {
+ if (arguments.count == 3) {
+ NSPredicate *conditional = [arguments.firstObject constantValue];
+ return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]];
+ }
+ }
+ return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments];
+ } else if ([op isEqualToString:@"match"]) {
+ NSMutableArray *optionsArray = [NSMutableArray array];
+ NSEnumerator *optionsEnumerator = argumentObjects.objectEnumerator;
+ while (id object = optionsEnumerator.nextObject) {
+ NSExpression *option = [NSExpression expressionWithMGLJSONObject:object];
+ [optionsArray addObject:option];
+ }
+
+ return [NSExpression expressionForFunction:@"MGL_MATCH"
+ arguments:optionsArray];
+ } else if ([op isEqualToString:@"coalesce"]) {
+ NSMutableArray *expressions = [NSMutableArray array];
+ for (id operand in argumentObjects) {
+ [expressions addObject:[NSExpression expressionWithMGLJSONObject:operand]];
+ }
+
+ return [NSExpression expressionWithFormat:@"mgl_coalesce(%@)", expressions];
} else {
- [NSException raise:NSInvalidArgumentException
- format:@"Expression operator %@ not yet implemented.", op];
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array);
+ return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions];
}
}
@@ -500,8 +970,23 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
@"log:": @"log10",
@"ln:": @"ln",
@"raise:toPower:": @"^",
+ @"ceiling:": @"ceil",
+ @"abs:": @"abs",
+ @"floor:": @"floor",
@"uppercase:": @"upcase",
@"lowercase:": @"downcase",
+ @"length:": @"length",
+ @"mgl_round:": @"round",
+ @"mgl_acos:" : @"acos",
+ @"mgl_cos:" : @"cos",
+ @"mgl_asin:" : @"asin",
+ @"mgl_sin:" : @"sin",
+ @"mgl_atan:" : @"atan",
+ @"mgl_tan:" : @"tan",
+ @"mgl_log2:" : @"log2",
+ // Vararg aftermarket expressions need to be declared with an explicit and implicit first argument.
+ @"MGL_LET": @"let",
+ @"MGL_LET:": @"let",
};
});
@@ -513,6 +998,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
if ([self.variable isEqualToString:@"zoomLevel"]) {
return @[@"zoom"];
}
+ if ([self.variable isEqualToString:@"geometryType"]) {
+ return @[@"geometry-type"];
+ }
+ if ([self.variable isEqualToString:@"featureIdentifier"]) {
+ return @[@"id"];
+ }
+ if ([self.variable isEqualToString:@"featureAttributes"]) {
+ return @[@"properties"];
+ }
return @[@"var", self.variable];
}
@@ -573,7 +1067,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
if (op) {
NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
return [@[op] arrayByAddingObjectsFromArray:arguments];
- } else if ([function isEqualToString:@"valueForKeyPath:"]) {
+ } else if ([function isEqualToString:@"valueForKey:"] || [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];
@@ -593,24 +1087,21 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
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:@"mgl_join:"]) {
+ NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"concat"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"stringByAppendingString:"]) {
NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"objectFrom:withIndex:"]) {
+ return @[@"at", self.arguments[1].mgl_jsonExpressionObject, self.arguments[0].mgl_jsonExpressionObject];
} else if ([function isEqualToString:@"boolValue"]) {
return @[@"to-boolean", self.operand.mgl_jsonExpressionObject];
- } else if ([function isEqualToString:@"mgl_numberWithFallbackValues:"] ||
+ } else if ([function isEqualToString:@"mgl_number"] ||
+ [function isEqualToString:@"mgl_numberWithFallbackValues:"] ||
[function isEqualToString:@"decimalValue"] ||
[function isEqualToString:@"floatValue"] ||
[function isEqualToString:@"doubleValue"]) {
@@ -620,40 +1111,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
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_does:have:"] ||
+ [function isEqualToString:@"mgl_has:"]) {
+ return self.mgl_jsonHasExpressionObject;
+ } else if ([function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"]
+ || [function isEqualToString:@"mgl_interpolateWithCurveType:parameters:stops:"]) {
+ return self.mgl_jsonInterpolationExpressionObject;
+ } else if ([function isEqualToString:@"mgl_step:from:stops:"]
+ || [function isEqualToString:@"mgl_stepWithMinimum:stops:"]) {
+ return self.mgl_jsonStepExpressionObject;
} else if ([function isEqualToString:@"mgl_expressionWithContext:"]) {
id context = self.arguments.firstObject;
if ([context isKindOfClass:[NSExpression class]]) {
@@ -666,6 +1132,31 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
}];
[expressionObject addObject:self.operand.mgl_jsonExpressionObject];
return expressionObject;
+ } else if ([function isEqualToString:@"MGL_IF"] ||
+ [function isEqualToString:@"mgl_if:"]) {
+ return self.mgl_jsonIfExpressionObject;
+ } else if ([function isEqualToString:@"MGL_MATCH"] ||
+ [function isEqualToString:@"mgl_match:"]) {
+ return self.mgl_jsonMatchExpressionObject;
+ } else if ([function isEqualToString:@"mgl_coalesce:"] ||
+ [function isEqualToString:@"mgl_coalesce"]) {
+
+ return self.mgl_jsonCoalesceExpressionObject;
+ } else if ([function isEqualToString:@"castObject:toType:"]) {
+ id object = self.arguments.firstObject.mgl_jsonExpressionObject;
+ NSString *type = self.arguments[1].mgl_jsonExpressionObject;
+ if ([type isEqualToString:@"NSString"]) {
+ return @[@"to-string", object];
+ } else if ([type isEqualToString:@"NSNumber"]) {
+ return @[@"to-number", object];
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Casting expression to %@ not yet implemented.", type];
+ } else if ([function isEqualToString:@"MGL_FUNCTION"]) {
+ return self.arguments.mgl_jsonExpressionObject;
+ } else if (op == [MGLColor class] && [function isEqualToString:@"colorWithRed:green:blue:alpha:"]) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[@"rgba"] arrayByAddingObjectsFromArray:arguments];
} else if ([function isEqualToString:@"median:"] ||
[function isEqualToString:@"mode:"] ||
[function isEqualToString:@"stddev:"] ||
@@ -690,7 +1181,17 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
}
case NSConditionalExpressionType: {
- NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, self.trueExpression.mgl_jsonExpressionObject, nil];
+ NSMutableArray *arguments = [NSMutableArray arrayWithObjects:self.predicate.mgl_jsonExpressionObject, nil];
+
+ if (self.trueExpression.expressionType == NSConditionalExpressionType) {
+ // Fold nested conditionals into a single case expression.
+ NSArray *trueArguments = self.trueExpression.mgl_jsonExpressionObject;
+ trueArguments = [trueArguments subarrayWithRange:NSMakeRange(1, trueArguments.count - 1)];
+ [arguments addObjectsFromArray:trueArguments];
+ } else {
+ [arguments addObject:self.trueExpression.mgl_jsonExpressionObject];
+ }
+
if (self.falseExpression.expressionType == NSConditionalExpressionType) {
// Fold nested conditionals into a single case expression.
NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject;
@@ -734,4 +1235,243 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) {
return nil;
}
+- (id)mgl_jsonInterpolationExpressionObject {
+ NSUInteger expectedArgumentCount = [self.function componentsSeparatedByString:@":"].count - 1;
+ if (self.arguments.count < expectedArgumentCount) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"Too few arguments to ‘%@’ function; expected %lu arguments.",
+ self.function, expectedArgumentCount];
+ } else if (self.arguments.count > expectedArgumentCount) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"%lu unexpected arguments to ‘%@’ function; expected %lu arguments.",
+ self.arguments.count - expectedArgumentCount, self.function, expectedArgumentCount];
+ }
+
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"];
+ NSUInteger curveTypeIndex = isAftermarketFunction ? 1 : 0;
+ NSString *curveType = self.arguments[curveTypeIndex].constantValue;
+ NSMutableArray *interpolationArray = [NSMutableArray arrayWithObject:curveType];
+ if ([curveType isEqualToString:@"exponential"]) {
+ id base = [self.arguments[curveTypeIndex + 1] mgl_jsonExpressionObject];
+ [interpolationArray addObject:base];
+ } else if ([curveType isEqualToString:@"cubic-bezier"]) {
+ NSArray *controlPoints = [self.arguments[curveTypeIndex + 1].collection mgl_jsonExpressionObject];
+ [interpolationArray addObjectsFromArray:controlPoints];
+ }
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolationArray, nil];
+ [expressionObject addObject:(isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[curveTypeIndex + 2].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+}
+
+- (id)mgl_jsonStepExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_step:from:stops:"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+ id minimum = self.arguments[minimumIndex].mgl_jsonExpressionObject;
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, minimum, nil];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[minimumIndex + 1].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+}
+
+- (id)mgl_jsonMatchExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"MGL_MATCH"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"match", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, nil];
+ NSArray<NSExpression *> *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue;
+
+ for (NSUInteger index = minimumIndex; index < arguments.count; index++) {
+ [expressionObject addObject:arguments[index].mgl_jsonExpressionObject];
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonIfExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"MGL_IF"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+ NSExpression *firstCondition;
+ id condition;
+
+ if (isAftermarketFunction) {
+ firstCondition = self.arguments.firstObject;
+ } else {
+ firstCondition = self.operand;
+ }
+
+ if ([firstCondition respondsToSelector:@selector(constantValue)] && [firstCondition.constantValue isKindOfClass:[NSComparisonPredicate class]]) {
+ NSPredicate *predicate = (NSPredicate *)firstCondition.constantValue;
+ condition = predicate.mgl_jsonExpressionObject;
+ } else {
+ condition = firstCondition.mgl_jsonExpressionObject;
+ }
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"case", condition, nil];
+ NSArray<NSExpression *> *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue;
+
+ for (NSUInteger index = minimumIndex; index < arguments.count; index++) {
+ if ([arguments[index] respondsToSelector:@selector(constantValue)] && [arguments[index].constantValue isKindOfClass:[NSComparisonPredicate class]]) {
+ NSPredicate *predicate = (NSPredicate *)arguments[index].constantValue;
+ [expressionObject addObject:predicate.mgl_jsonExpressionObject];
+ } else {
+ [expressionObject addObject:arguments[index].mgl_jsonExpressionObject];
+ }
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonCoalesceExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_coalesce:"];
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"coalesce", nil];
+
+ for (NSExpression *expression in (isAftermarketFunction ? self.arguments.firstObject : self.operand).constantValue) {
+ [expressionObject addObject:[expression mgl_jsonExpressionObject]];
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonHasExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_does:have:"];
+ NSExpression *operand = isAftermarketFunction ? self.arguments[0] : self.operand;
+ NSExpression *key = self.arguments[isAftermarketFunction ? 1 : 0];
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"has", key.mgl_jsonExpressionObject, nil];
+ if (operand.expressionType != NSEvaluatedObjectExpressionType) {
+ [expressionObject addObject:operand.mgl_jsonExpressionObject];
+ }
+ return expressionObject;
+}
+
+#pragma mark Localization
+
+/**
+ Returns a localized copy of the given collection.
+
+ If no localization takes place, this method returns the original collection.
+ */
+NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) *collection, NSLocale * _Nullable locale) {
+ __block NSMutableArray *localizedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *localizedItem = [item mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedItem != item) {
+ if (!localizedCollection) {
+ localizedCollection = [collection mutableCopy];
+ }
+ localizedCollection[idx] = localizedItem;
+ }
+ }];
+ return localizedCollection ?: collection;
+};
+
+/**
+ Returns a localized copy of the given stop dictionary.
+
+ If no localization takes place, this method returns the original stop
+ dictionary.
+ */
+NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops, NSLocale * _Nullable locale) {
+ __block NSMutableDictionary *localizedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *localizedValue = [value mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedValue != value) {
+ if (!localizedStops) {
+ localizedStops = [stops mutableCopy];
+ }
+ localizedStops[zoomLevel] = localizedValue;
+ }
+ }];
+ return localizedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLLocalizedStopDictionary(stops, locale);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSKeyPathExpressionType: {
+ if ([self.keyPath isEqualToString:@"name"] || [self.keyPath hasPrefix:@"name_"]) {
+ NSString *localizedKeyPath = @"name";
+ if (![locale.localeIdentifier isEqualToString:@"mul"]) {
+ NSArray *preferences = locale ? @[locale.localeIdentifier] : [NSLocale preferredLanguages];
+ NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences];
+ if (preferredLanguage) {
+ localizedKeyPath = [NSString stringWithFormat:@"name_%@", preferredLanguage];
+ }
+ }
+ return [NSExpression expressionForKeyPath:localizedKeyPath];
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ NSExpression *operand = self.operand;
+ NSExpression *localizedOperand = [operand mgl_expressionLocalizedIntoLocale:locale];
+
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLLocalizedCollection(arguments, locale);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:localizedArguments];
+ }
+ if (localizedOperand != operand) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:self.arguments];
+ }
+ return self;
+ }
+
+ case NSConditionalExpressionType: {
+ if (@available(iOS 9.0, *)) {
+ NSExpression *trueExpression = self.trueExpression;
+ NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale];
+ NSExpression *falseExpression = self.falseExpression;
+ NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) {
+ return [NSExpression expressionForConditional:self.predicate
+ trueExpression:localizedTrueExpression
+ falseExpression:localizedFalseExpression];
+ }
+ }
+ return self;
+ }
+
+ case NSAggregateExpressionType: {
+ NSArray *collection = self.collection;
+ if ([collection isKindOfClass:[NSArray class]]) {
+ NSArray *localizedCollection = MGLLocalizedCollection(collection, locale);
+ if (localizedCollection != collection) {
+ return [NSExpression expressionForAggregate:localizedCollection];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
@end
diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
index 8d1b4d6af5..a1948f9e45 100644
--- a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
@@ -27,6 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
+/**
+ Returns a copy of the receiver with tokens replaced by key path expressions.
+ */
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths;
+
@end
@interface NSNull (MGLExpressionAdditions)
@@ -60,6 +65,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+- (id)mgl_has:(id)element;
+
@end
@interface MGLColor (MGLExpressionAdditions)
@@ -72,6 +79,9 @@ NS_ASSUME_NONNULL_BEGIN
- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context;
+
+- (id)mgl_has:(id)element;
+
@end
extern NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects);
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h
index 89e9e65c64..a73b1a61ba 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.h
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.h
@@ -16,4 +16,8 @@
@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+- (id)mgl_if:(id)firstValue, ...;
+
+- (id)mgl_match:(NSExpression *)firstCase, ...;
+
@end
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index 63c8307803..bbd324bb63 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -3,6 +3,8 @@
#import "MGLValueEvaluator.h"
#import "MGLStyleValue_Private.h"
+#include <mbgl/style/conversion/filter.hpp>
+
class FilterEvaluator {
public:
@@ -206,25 +208,18 @@ public:
- (mbgl::style::Filter)mgl_filter
{
- if ([self isEqual:[NSPredicate predicateWithValue:YES]])
- {
- return mbgl::style::AllFilter();
- }
-
- if ([self isEqual:[NSPredicate predicateWithValue:NO]])
- {
- return mbgl::style::AnyFilter();
- }
-
- if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("])
- {
+ mbgl::style::conversion::Error valueError;
+ NSArray *jsonObject = self.mgl_jsonExpressionObject;
+ auto value = mbgl::style::conversion::convert<mbgl::style::Filter>(mbgl::style::conversion::makeConvertible(jsonObject), valueError);
+
+ if (!value) {
[NSException raise:NSInvalidArgumentException
- format:@"Block-based predicates are not supported."];
+ format:@"Invalid filter value: %@", @(valueError.message.c_str())];
+ return {};
}
-
- [NSException raise:NSInvalidArgumentException
- format:@"Unrecognized predicate type."];
- return {};
+ mbgl::style::Filter filter = std::move(*value);
+
+ return filter;
}
+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
@@ -280,7 +275,7 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
}
if ([op isEqualToString:@">="]) {
NSArray *subexpressions = MGLSubexpressionsWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
- return [NSPredicate predicateWithFormat:@"%K >= %@" argumentArray:subexpressions];
+ return [NSPredicate predicateWithFormat:@"%@ >= %@" argumentArray:subexpressions];
}
if ([op isEqualToString:@"!"]) {
NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
@@ -294,7 +289,43 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
return [NSPredicate predicateWithValue:YES];
}
if ([op isEqualToString:@"all"]) {
- NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ NSArray<NSPredicate *> *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ if (subpredicates.count == 2) {
+ // Determine if the expression is of BETWEEN type
+ if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] &&
+ [subpredicates[1] isKindOfClass:[NSComparisonPredicate class]]) {
+ NSComparisonPredicate *leftCondition = (NSComparisonPredicate *)subpredicates[0];
+ NSComparisonPredicate *rightCondition = (NSComparisonPredicate *)subpredicates[1];
+
+ NSArray *limits;
+ NSExpression *leftConditionExpression;
+
+ if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.rightExpression, rightCondition.rightExpression];
+ leftConditionExpression = leftCondition.leftExpression;
+
+ } else if (leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.leftExpression, rightCondition.rightExpression];
+ leftConditionExpression = leftCondition.rightExpression;
+
+ } else if(leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.leftExpression, rightCondition.leftExpression];
+ leftConditionExpression = leftCondition.rightExpression;
+
+ } else if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.rightExpression, rightCondition.leftExpression];
+ leftConditionExpression = leftCondition.leftExpression;
+ }
+
+ if (limits && leftConditionExpression) {
+ return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression, [NSExpression expressionForAggregate:limits]];
+ }
+ }
+ }
return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
}
if ([op isEqualToString:@"any"]) {
@@ -302,8 +333,13 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
}
- NSAssert(NO, @"Unrecognized expression conditional operator %@.", op);
- return nil;
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:object];
+ return [NSComparisonPredicate predicateWithLeftExpression:expression
+ rightExpression:[NSExpression expressionForConstantValue:@YES]
+ modifier:NSDirectPredicateModifier
+ type:NSEqualToPredicateOperatorType
+ options:0];
+
}
- (id)mgl_jsonExpressionObject {
@@ -324,4 +360,35 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) {
return nil;
}
+- (id)mgl_if:(id)firstValue, ... {
+
+ if ([self evaluateWithObject:nil]) {
+ return firstValue;
+ }
+
+ id eachExpression;
+ va_list argumentList;
+ va_start(argumentList, firstValue);
+
+ while ((eachExpression = va_arg(argumentList, id))) {
+ if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) {
+ id valueExpression = va_arg(argumentList, id);
+ if ([eachExpression evaluateWithObject:nil]) {
+ return valueExpression;
+ }
+ } else {
+ return eachExpression;
+ }
+ }
+ va_end(argumentList);
+
+ return nil;
+}
+
+- (id)mgl_match:(NSExpression *)firstCase, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Match expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
@end
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index 8c9bbe3e21..a61f229511 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -1,5 +1,9 @@
#import "NSString+MGLAdditions.h"
+#if TARGET_OS_OSX
+ #import <Availability.h>
+#endif
+
@implementation NSString (MGLAdditions)
- (NSRange)mgl_wholeRange {
@@ -13,7 +17,7 @@
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale {
NSMutableString *string = self.mutableCopy;
NSOrthography *orthography;
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h
index f3026a389f..9222f04620 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.h
+++ b/platform/darwin/src/NSValue+MGLAdditions.h
@@ -29,6 +29,19 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) CLLocationCoordinate2D MGLCoordinateValue;
/**
+ Creates a new value object containing the specified Mapbox map point structure.
+
+ @param point The value for the new object.
+ @return A new value object that contains the coordinate and zoom level information.
+ */
++ (instancetype)valueWithMGLMapPoint:(MGLMapPoint)point;
+
+/**
+ The Mapbox map point structure representation of the value.
+ */
+@property (readonly) MGLMapPoint MGLMapPointValue;
+
+/**
Creates a new value object containing the specified Mapbox coordinate span
structure.
diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m
index 1383056944..b32445dab7 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.m
+++ b/platform/darwin/src/NSValue+MGLAdditions.m
@@ -14,6 +14,16 @@
return coordinate;
}
++ (instancetype)valueWithMGLMapPoint:(MGLMapPoint)point {
+ return [self valueWithBytes:&point objCType:@encode(MGLMapPoint)];
+}
+
+-(MGLMapPoint) MGLMapPointValue {
+ MGLMapPoint point;
+ [self getValue:&point];
+ return point;
+}
+
+ (instancetype)valueWithMGLCoordinateSpan:(MGLCoordinateSpan)span {
return [self valueWithBytes:&span objCType:@encode(MGLCoordinateSpan)];
}
diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
index e7c2982413..de8080f425 100644
--- a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
+++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
@@ -42,7 +42,7 @@
@"backgroundColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.backgroundColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -66,8 +66,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -95,7 +95,7 @@
@"backgroundOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.backgroundOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -119,8 +119,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -148,7 +148,7 @@
@"backgroundPattern should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'Background Pattern'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.backgroundPattern = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -172,8 +172,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm
index 7677344580..d7bf2a5afd 100644
--- a/platform/darwin/test/MGLCircleStyleLayerTests.mm
+++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"circleBlur should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleBlur = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -77,7 +77,7 @@
XCTAssertEqualObjects(layer.circleBlur, functionExpression,
@"circleBlur should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -85,10 +85,11 @@
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
@"Setting circleBlur to a data expression should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression,
@"circleBlur should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -98,7 +99,8 @@
XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue,
@"Setting circleBlur to a camera-data expression should update circle-blur.");
- XCTAssertEqualObjects(layer.circleBlur, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression,
@"circleBlur should round-trip camera-data expressions.");
@@ -133,7 +135,7 @@
@"circleColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -147,7 +149,7 @@
XCTAssertEqualObjects(layer.circleColor, functionExpression,
@"circleColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -155,10 +157,11 @@
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
@"Setting circleColor to a data expression should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression,
@"circleColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -168,7 +171,8 @@
XCTAssertEqual(rawLayer->getCircleColor(), propertyValue,
@"Setting circleColor to a camera-data expression should update circle-color.");
- XCTAssertEqualObjects(layer.circleColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression,
@"circleColor should round-trip camera-data expressions.");
@@ -203,7 +207,7 @@
@"circleOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -217,7 +221,7 @@
XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
@"circleOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -225,10 +229,11 @@
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
@"Setting circleOpacity to a data expression should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression,
@"circleOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -238,7 +243,8 @@
XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue,
@"Setting circleOpacity to a camera-data expression should update circle-opacity.");
- XCTAssertEqualObjects(layer.circleOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression,
@"circleOpacity should round-trip camera-data expressions.");
@@ -273,7 +279,7 @@
@"circlePitchAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circlePitchAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
@@ -297,8 +303,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -317,7 +323,7 @@
@"circleRadius should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleRadius = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -331,7 +337,7 @@
XCTAssertEqualObjects(layer.circleRadius, functionExpression,
@"circleRadius should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleRadius = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -339,10 +345,11 @@
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
@"Setting circleRadius to a data expression should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression,
@"circleRadius should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleRadius = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -352,7 +359,8 @@
XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue,
@"Setting circleRadius to a camera-data expression should update circle-radius.");
- XCTAssertEqualObjects(layer.circleRadius, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression,
@"circleRadius should round-trip camera-data expressions.");
@@ -387,7 +395,7 @@
@"circleScaleAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleScaleAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::CirclePitchScaleType> intervalStops = {{
@@ -411,8 +419,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -431,7 +439,7 @@
@"circleStrokeColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleStrokeColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -445,7 +453,7 @@
XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
@"circleStrokeColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleStrokeColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -453,10 +461,11 @@
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
@"Setting circleStrokeColor to a data expression should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression,
@"circleStrokeColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleStrokeColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -466,7 +475,8 @@
XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue,
@"Setting circleStrokeColor to a camera-data expression should update circle-stroke-color.");
- XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression,
@"circleStrokeColor should round-trip camera-data expressions.");
@@ -501,7 +511,7 @@
@"circleStrokeOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleStrokeOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -515,7 +525,7 @@
XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
@"circleStrokeOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleStrokeOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -523,10 +533,11 @@
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
@"Setting circleStrokeOpacity to a data expression should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression,
@"circleStrokeOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleStrokeOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -536,7 +547,8 @@
XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue,
@"Setting circleStrokeOpacity to a camera-data expression should update circle-stroke-opacity.");
- XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression,
@"circleStrokeOpacity should round-trip camera-data expressions.");
@@ -571,7 +583,7 @@
@"circleStrokeWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleStrokeWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -585,7 +597,7 @@
XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
@"circleStrokeWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.circleStrokeWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -593,10 +605,11 @@
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
@"Setting circleStrokeWidth to a data expression should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression,
@"circleStrokeWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.circleStrokeWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -606,7 +619,8 @@
XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue,
@"Setting circleStrokeWidth to a camera-data expression should update circle-stroke-width.");
- XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression,
@"circleStrokeWidth should round-trip camera-data expressions.");
@@ -647,7 +661,7 @@
@"circleTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -671,8 +685,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -691,7 +705,7 @@
@"circleTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.circleTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -715,8 +729,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 a216d9ad1c..9edb33a078 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -73,14 +73,14 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "lines"))
}
- func testMGLRasterSource() {
+ func testMGLRasterTileSource() {
//#-example-code
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
@@ -109,13 +109,13 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "hills"))
}
- func testMGLVectorSource() {
+ func testMGLVectorTileSource() {
//#-example-code
- let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ let source = MGLVectorTileSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
@@ -151,7 +151,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLCircleStyleLayer() {
- let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
+ let population = MGLVectorTileSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(population)
//#-example-code
@@ -162,7 +162,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
#else
layer.circleColor = NSExpression(forConstantValue: UIColor.green)
#endif
- layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.75, %@)",
+ layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
[12: 2,
22: 180])
layer.circleOpacity = NSExpression(forConstantValue: 0.7)
@@ -174,13 +174,13 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLLineStyleLayer() {
- let trails = MGLVectorSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!)
+ let trails = MGLVectorTileSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(trails)
//#-example-code
let layer = MGLLineStyleLayer(identifier: "trails-path", source: trails)
layer.sourceLayerIdentifier = "trails"
- layer.lineWidth = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ layer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
[14: 2,
18: 20])
#if os(macOS)
@@ -197,7 +197,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLFillStyleLayer() {
- let parks = MGLVectorSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!)
+ let parks = MGLVectorTileSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(parks)
//#-example-code
@@ -216,7 +216,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLFillExtrusionStyleLayer() {
- let buildings = MGLVectorSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!)
+ let buildings = MGLVectorTileSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(buildings)
//#-example-code
@@ -237,10 +237,10 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let layer = MGLHeatmapStyleLayer(identifier: "earthquake-heat", source: earthquakes)
- layer.heatmapWeight = NSExpression(format: "FUNCTION(magnitude, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.heatmapWeight = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(magnitude, 'linear', nil, %@)",
[0: 0,
6: 1])
- layer.heatmapIntensity = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.heatmapIntensity = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
[0: 1,
9: 3])
mapView.style?.addLayer(layer)
@@ -250,7 +250,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLSymbolStyleLayer() {
- let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
+ let pois = MGLVectorTileSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(pois)
//#-example-code
@@ -275,12 +275,12 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLRasterStyleLayer() {
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
@@ -300,12 +300,12 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
.maximumZoomLevel: 16,
.tileSize: 256,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
- let canals = MGLVectorSource(identifier: "canals", configurationURL: URL(string: "https://example.com/style.json")!)
+ let canals = MGLVectorTileSource(identifier: "canals", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(canals)
let canalShadowLayer = MGLLineStyleLayer(identifier: "waterway-river-canal-shadow", source: canals)
mapView.style?.addLayer(canalShadowLayer)
@@ -322,13 +322,13 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLVectorStyleLayer$predicate() {
- let terrain = MGLVectorSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!)
+ let terrain = MGLVectorTileSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(terrain)
//#-example-code
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
layer.sourceLayerIdentifier = "contours"
- layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -370,7 +370,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
#endif
class MGLStyle {
- static func satelliteStreetsStyleURL() -> URL {
+ static var satelliteStreetsStyleURL: URL {
return MGLDocumentationExampleTests.styleURL
}
}
@@ -378,7 +378,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let camera = MGLMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365), fromDistance: 100, pitch: 20, heading: 0)
- let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL(), camera: camera, size: CGSize(width: 320, height: 480))
+ let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL, camera: camera, size: CGSize(width: 320, height: 480))
options.zoomLevel = 10
let snapshotter = MGLMapSnapshotter(options: options)
diff --git a/platform/darwin/test/MGLDocumentationGuideTests.swift b/platform/darwin/test/MGLDocumentationGuideTests.swift
index f939695f32..4de1d81aa9 100644
--- a/platform/darwin/test/MGLDocumentationGuideTests.swift
+++ b/platform/darwin/test/MGLDocumentationGuideTests.swift
@@ -50,10 +50,10 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
styleLoadingExpectation.fulfill()
}
- func testUsingStyleFunctionsAtRuntime$Stops() {
+ func testMigratingToExpressions$Stops() {
//#-example-code
#if os(macOS)
- let stops: [Float: NSColor] = [
+ let stops: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -61,7 +61,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
10: .white,
]
#else
- let stops: [Float: UIColor] = [
+ let stops: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -71,11 +71,11 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
#endif
//#-end-example-code
- let _ = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ let _ = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
stops[0]!, stops)
}
- func testUsingStyleFunctionsAtRuntime$Linear() {
+ func testMigratingToExpressions$Linear() {
//#-example-code
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
let symbolSource = MGLSource(identifier: "source")
@@ -85,7 +85,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
mapView.style?.addSource(source)
#if os(macOS)
- let stops: [Float: NSColor] = [
+ let stops: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -93,7 +93,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
10: .white,
]
#else
- let stops: [Float: UIColor] = [
+ let stops: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -104,10 +104,10 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
#if os(macOS)
- layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
#else
- layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)",
+ layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
#endif
layer.circleRadius = NSExpression(forConstantValue: 10)
@@ -115,7 +115,37 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Exponential() {
+ func testMigratingToExpressions$LinearConvenience() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ #if os(macOS)
+ let stops: [NSNumber: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+ ]
+ #else
+ let stops: [NSNumber: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+ ]
+ #endif
+
+ //#-example-code
+ layer.circleColor = NSExpression(forMGLInterpolating: NSExpression(forKeyPath: "mag"), curveType: .linear, parameters: nil, stops: NSExpression(forConstantValue: stops))
+ //#-end-example-code
+
+ layer.circleRadius = NSExpression(forConstantValue: 10)
+ mapView.style?.addLayer(layer)
+
+ }
+ func testMigratingToExpressions$Exponential() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
@@ -126,18 +156,32 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
18: 18,
]
- layer.circleRadius = NSExpression(format: "FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 1.5, %@)",
+ layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
stops)
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Interval() {
+ func testMigratingToExpressions$ExponentialConvenience() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ //#-example-code
+ let stops = [
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
+ ]
+
+ layer.circleRadius = NSExpression(forMGLInterpolating: NSExpression.zoomLevelVariable, curveType: MGLExpressionInterpolationMode.exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: stops))
+ //#-end-example-code
+ }
+ func testMigratingToExpressions$Interval() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-example-code
#if os(macOS)
- let stops: [Float: NSColor] = [
+ let stops: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -145,10 +189,10 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
10: .white,
]
- layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
NSColor.green, stops)
#else
- let stops: [Float: UIColor] = [
+ let stops: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
@@ -156,40 +200,53 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
10: .white,
]
- layer.circleColor = NSExpression(format: "FUNCTION(mag, 'mgl_stepWithMinimum:stops:', %@, %@)",
+ layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
UIColor.green, stops)
#endif
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Categorical() {
+ func testMigratingToExpressions$Categorical() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-example-code
#if os(macOS)
- let colors: [String: NSColor] = [
- "earthquake": .orange,
- "explosion": .red,
- "quarry blast": .yellow,
- ]
let defaultColor = NSColor.blue
+ layer.circleColor = NSExpression(
+ format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ NSColor.orange, NSColor.red, NSColor.yellow, defaultColor)
#else
- let colors: [String: UIColor] = [
- "earthquake": .orange,
- "explosion": .red,
- "quarry blast": .yellow,
- ]
let defaultColor = UIColor.blue
+ layer.circleColor = NSExpression(format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ UIColor.orange, UIColor.red, UIColor.yellow, defaultColor)
#endif
+ //#-end-example-code
+ }
+
+ func testMigratingToExpressions$CategoricalValue() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+ //#-example-code
+ #if os(macOS)
+ let stops : [String : NSColor] = ["earthquake" : NSColor.orange,
+ "explosion" : NSColor.red,
+ "quarry blast" : NSColor.yellow]
+ layer.circleColor = NSExpression(
+ format: "FUNCTION(%@, 'valueForKeyPath:', type)",
+ stops)
+ #else
+ let stops : [String : UIColor] = ["earthquake" : UIColor.orange,
+ "explosion" : UIColor.red,
+ "quarry blast" : UIColor.yellow]
layer.circleColor = NSExpression(
- format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
- colors, colors, defaultColor)
+ format: "FUNCTION(%@, 'valueForKeyPath:', type)",
+ stops)
+ #endif
//#-end-example-code
}
-
- func testUsingStyleFunctionsAtRuntime$Identity() {
+ func testMigratingToExpressions$Identity() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
@@ -197,4 +254,23 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
layer.circleRadius = NSExpression(forKeyPath: "mag")
//#-end-example-code
}
+
+ func testMigratingToExpressions$Multiply() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ //#-example-code
+ layer.circleRadius = NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
+ //#-end-example-code
+ }
+
+ func testMigratingToExpressions$Cast() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+
+ //#-example-code
+ let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
+ magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
+ mapView.style?.addLayer(magnitudeLayer)
+ //#-end-example-code
+ }
}
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index a5ed2f7bf5..d54e961b00 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -158,7 +158,7 @@ using namespace std::string_literals;
NSExpression *expression = [NSExpression expressionForVariable:@"zoomLevel"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"zoom"]);
XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$zoomLevel"].mgl_jsonExpressionObject, @[@"zoom"]);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:@[@"zoom"]], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"zoom"]], expression);
NSMutableDictionary *context = [@{@"zoomLevel": @16} mutableCopy];
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @16);
}
@@ -166,25 +166,45 @@ using namespace std::string_literals;
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"heatmap-density"]], expression);
NSMutableDictionary *context = [@{@"heatmapDensity": @1} mutableCopy];
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @1);
}
{
+ NSExpression *expression = [NSExpression expressionForVariable:@"geometryType"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"geometry-type"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$geometryType"].mgl_jsonExpressionObject, @[@"geometry-type"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"geometry-type"]], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"featureIdentifier"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"id"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$featureIdentifier"].mgl_jsonExpressionObject, @[@"id"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"id"]], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"featureAttributes"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"properties"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$featureAttributes"].mgl_jsonExpressionObject, @[@"properties"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"properties"]], expression);
+ }
+ {
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_LET('loremIpsum', 'Lorem ipsum dolor sit amet', uppercase($loremIpsum))", context];
+ NSExpression *compatibilityExpression = [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);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
@@ -193,28 +213,28 @@ using namespace std::string_literals;
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:[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([NSExpression expressionWithMGLJSONObject:@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([NSExpression expressionWithMGLJSONObject:@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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:[NSNull null]], expression);
XCTAssertNil([expression expressionValueWithObject:nil context:nil]);
}
{
@@ -227,7 +247,7 @@ using namespace std::string_literals;
#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([[NSExpression expressionWithMGLJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject);
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @(vector));
}
{
@@ -242,7 +262,7 @@ using namespace std::string_literals;
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([[NSExpression expressionWithMGLJSONObject:jsonExpression].collection valueForKeyPath:@"constantValue"], jsonExpression.lastObject);
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], value);
}
{
@@ -272,19 +292,19 @@ using namespace std::string_literals;
NSArray *jsonExpression = @[@"get", @"highway"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([NSExpression expressionWithFormat:@"highway"].mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
@@ -300,28 +320,28 @@ using namespace std::string_literals;
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
@@ -332,61 +352,66 @@ using namespace std::string_literals;
NSArray *jsonExpression = @[@"+", @1, @1];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 + 1"].mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1), MGLConstantExpression(@1)];
+ NSExpression *expression = [NSExpression expressionForFunction:@"add:to:" arguments:arguments];
+ NSArray *jsonExpression = @[@"+", @1, @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ jsonExpression = @[@"+", @[@"+", @1, @1], @1];
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 + 1 + 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], [NSExpression expressionWithFormat:@"1 + 1 + 1"]);
}
{
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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]];
+ NSArray *jsonExpression = @[@"ceil", @1.5];
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]];
+ NSArray *jsonExpression = @[@"ceil", @-1.5];
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]];
+ NSArray *jsonExpression = @[@"ceil", @2];
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]];
+ NSArray *jsonExpression = @[@"ceil", @-2];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
}
@@ -404,44 +429,64 @@ using namespace std::string_literals;
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@2)]];
- NSArray *jsonExpression = @[@"*", @2, @[@"case", @[@">", @2, @0], @1, @-1]];
+ NSArray *jsonExpression = @[@"abs", @2];
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]];
+ NSArray *jsonExpression = @[@"abs", @-2];
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]];
+ NSArray *jsonExpression = @[@"floor", @1.5];
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]];
+ NSArray *jsonExpression = @[@"floor", @-1.5];
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]];
+ NSArray *jsonExpression = @[@"floor", @2];
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]];
+ NSArray *jsonExpression = @[@"floor", @-2];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
}
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonExpression = @[@"round", @1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonExpression = @[@"round", @-1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@2.5)]];
+ NSArray *jsonExpression = @[@"round", @2.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @3);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-2.5)]];
+ NSArray *jsonExpression = @[@"round", @-2.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-3);
+ }
}
- (void)testTrigonometricExpressionObject {
@@ -450,41 +495,91 @@ using namespace std::string_literals;
NSExpression *expression = [NSExpression expressionForFunction:@"sqrt:" arguments:arguments];
NSArray *jsonExpression = @[@"sqrt", @1, @1];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_log2:" arguments:@[MGLConstantExpression(@1024)]];
+ NSArray *jsonExpression = @[@"log2", @1024];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @10);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_acos:" arguments:@[MGLConstantExpression(@1)]];
+ NSArray *jsonExpression = @[@"acos", @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_cos:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"cos", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_asin:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"asin", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_sin:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"sin", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_atan:" arguments:@[MGLConstantExpression(@20)]];
+ NSArray *jsonExpression = @[@"atan", @20];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ NSNumber *value = [expression expressionValueWithObject:nil context:nil];
+ XCTAssertEqualWithAccuracy(value.doubleValue, 1.52, 0.001);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_tan:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"tan", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
@@ -492,22 +587,31 @@ using namespace std::string_literals;
NSArray *arguments = @[MGLConstantExpression(@"MacDonald")];
{
NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')"];
+ NSExpression *aftermarketExpression = [NSExpression expressionWithFormat:@"mgl_join({'Old', 'MacDonald'})"];
NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(aftermarketExpression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald");
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([aftermarketExpression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], aftermarketExpression);
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"uppercase:" arguments:arguments];
NSArray *jsonExpression = @[@"upcase", @"MacDonald"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
NSExpression *expression = [NSExpression expressionForFunction:@"lowercase:" arguments:arguments];
NSArray *jsonExpression = @[@"downcase", @"MacDonald"];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"length:" arguments:arguments];
+ NSArray *jsonExpression = @[@"length", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
@@ -518,7 +622,20 @@ using namespace std::string_literals;
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_number')"];
+ NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'doubleValue')"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'floatValue')"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'decimalValue')"].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 expressionWithMGLJSONObject:jsonExpression],
+ [NSExpression expressionWithFormat:@"CAST(postalCode, 'NSNumber')"]);
}
{
NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_numberWithFallbackValues:', zipCode)"];
@@ -530,50 +647,117 @@ using namespace std::string_literals;
// 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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
- NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'stringValue')"];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(postalCode, 'NSNumber')"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_numberWithFallbackValues:')"];
+ NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"postalCode": @"02134"} context:nil], @02134);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(number, 'NSString')"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'stringValue')"];
NSArray *jsonExpression = @[@"to-string", @[@"get", @"number"]];
XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
XCTAssertEqualObjects([expression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5");
- XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([compatibilityExpression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)", stops];
+ NSExpression *compatibilityExpression = [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);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
NSDictionary *stops = @{@1: MGLConstantExpression(@2), @3: MGLConstantExpression(@6)};
- NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_interpolateWithCurveType:parameters:stops:', 'exponential', 2, %@)", stops];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, '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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, '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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
{
NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
- NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(x, 'mgl_stepWithMinimum:stops:', 11, %@)", stops];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(x, 11, %@)", stops];
+ NSExpression *compatibilityExpression = [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);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, 11, %@)", stops];
+ NSArray *jsonExpression = @[@"step", @[@"zoom"], @11, @0, @111, @1, @1111];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
}
}
+- (void)testMatchExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(2 - 1, %@, %@, %@, %@, 'default')", MGLConstantExpression(@1),
+ MGLConstantExpression(@"one"),
+ MGLConstantExpression(@0),
+ MGLConstantExpression(@"zero")];
+ NSExpression *predicate = [NSExpression expressionWithFormat:@"2 - 1"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_match:', %@)", predicate, @[MGLConstantExpression(@1),
+ MGLConstantExpression(@"one"),
+ MGLConstantExpression(@0),
+ MGLConstantExpression(@"zero"),
+ MGLConstantExpression(@"default")]];
+ NSArray *jsonExpression = @[@"match", @[@"-", @2, @1], @1, @"one", @0, @"zero", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(2 * 1, %@, %@, 'default')", MGLConstantExpression(@1), MGLConstantExpression(@"one")];
+ NSArray *jsonExpression = @[@"match", @[@"*", @2, @1], @1, @"one", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testCoalesceExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_coalesce(%@)",
+ @[[NSExpression expressionForKeyPath:@"x"],
+ [NSExpression expressionForKeyPath:@"y"],
+ [NSExpression expressionForKeyPath:@"z"],
+ [NSExpression expressionForConstantValue:@0]]];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_coalesce')", @[[NSExpression expressionForKeyPath:@"x"],
+ [NSExpression expressionForKeyPath:@"y"],
+ [NSExpression expressionForKeyPath:@"z"],
+ [NSExpression expressionForConstantValue:@0]]];
+ NSArray *jsonExpression = @[@"coalesce", @[@"get", @"x"], @[@"get", @"y"], @[@"get", @"z"], @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+
+}
+
- (void)testConditionalExpressionObject {
- // FIXME: This test crashes because iOS 8 doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`.
+ // This test crashes on iOS 8, which doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`.
// https://github.com/mapbox/mapbox-gl-native/issues/11007
if (@available(iOS 9.0, *)) {
{
@@ -585,16 +769,341 @@ using namespace std::string_literals;
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);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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);
+ expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)",
+ MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]),
+ MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])];
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_if:', %@)", [NSPredicate predicateWithFormat:@"1 = 2"], @[MGLConstantExpression(@YES), MGLConstantExpression(@NO)]];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ if (@available(iOS 9.0, *)) {
+ expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"];
+ }
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@, %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 1"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @[@"==", @1, @1], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+ {
+ NSArray *jsonExpression = @[
+ @"case",
+ @[
+ @"<",
+ @[@"get", @"area"],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name_en"]
+ ];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(area < 80000, abbr, name_en)"];
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ }
+}
+
+- (void)testLookupExpressionObject {
+ {
+ NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9),
+ MGLConstantExpression(@8),
+ MGLConstantExpression(@7)]];
+ NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:"
+ arguments:@[array, MGLConstantExpression(@1)]];
+ NSArray *jsonExpression = @[@"at", @1, @[ @"literal", @[@9, @8, @7]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9),
+ MGLConstantExpression(@8),
+ MGLConstantExpression(@7)]];
+ NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:"
+ arguments:@[array, [NSExpression expressionForKeyPath:@"x"]]];
+ NSArray *jsonExpression = @[@"at", @[@"get", @"x"], @[ @"literal", @[@9, @8, @7]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[[NSExpression expressionForEvaluatedObject],
+ [NSExpression expressionForConstantValue:@"x"]]];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(self, 'mgl_has:', 'x')"];
+ NSArray *jsonExpression = @[@"has", @"x"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[MGLConstantExpression(@{@"x": MGLConstantExpression(@0)}),
+ MGLConstantExpression(@"x")]];
+ NSArray *jsonExpression = @[@"has", @"x", @[@"literal", @{@"x": @0}]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[[NSExpression expressionForVariable:@"featureAttributes"],
+ [NSExpression expressionForConstantValue:@"x"]]];
+ NSArray *jsonExpression = @[@"has", @"x", @[@"properties"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression;
+ if (@available(iOS 9.0, *)) {
+ expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"];
+ } else {
+ expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)",
+ MGLConstantExpression([NSPredicate predicateWithFormat:@"key != nil"])];
}
+ NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key"], [NSNull null]], @1, @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{} context:nil], @NO);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"key": @"🗝"} context:nil], @YES);
+ }
+ {
+ NSDictionary *dictionary = @{@"key": @"🔑"};
+ NSExpression *expression;
+ if (@available(iOS 9.0, *)) {
+ expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary];
+ } else {
+ NSPredicate *conditional = [NSPredicate predicateWithFormat:@"%@.key != nil", dictionary];
+ expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)",
+ MGLConstantExpression(conditional)];
+ }
+ NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key", @[@"literal", dictionary]], [NSNull null]], @1, @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // The dictionary isn’t equal enough.
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression].description, expression.description);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+}
+
+- (void)testGenericExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('random', 1, 2, 3, 4, 5)"];
+ NSArray *jsonExpression = @[@"random", @1, @2, @3, @4, @5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('random', 1, 2, 3, 4)"];
+ XCTAssertThrowsSpecificNamed([expression expressionValueWithObject:nil context:nil], NSException, NSInvalidArgumentException);
+ }
+}
+
+#pragma mark - Localization tests
+
+- (void)testTokenReplacement {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token}");
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"token"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token} {token}");
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_join({token, ' ', token})"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, '{short}', %@)", @{
+ @1: MGLConstantExpression(@"{short}"),
+ @2: @"…",
+ @3: @"{long}",
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"short"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"long"],
+ }];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+}
+
+- (void)testLocalization {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"Old MacDonald");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{name_en}");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_fr"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"fr-CA"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_zh-Hans"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"zh-Hans"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"tlh"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_fr"],
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_es"],
+ }];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"es-PR"]], expected);
+ }
+ {
+ NSArray *jsonExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name_en"]
+ ],
+ @5, @[@"get", @"name_en"]
+ ];
+ NSArray *localizedJSONExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name"]
+ ],
+ @5, @[@"get", @"name"]
+ ];
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ NSExpression *localizedExpression = [expression mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]];
+ XCTAssertEqualObjects(localizedExpression.mgl_jsonExpressionObject, localizedJSONExpression);
}
}
+- (void)testConvenienceInitializers {
+ {
+ NSExpression *expression = [NSExpression mgl_expressionForConditional:[NSPredicate predicateWithFormat:@"1 = 2"]
+ trueExpression:MGLConstantExpression(@YES)
+ falseExpresssion:MGLConstantExpression(@NO)];
+
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression mgl_expressionForSteppingExpression:[NSExpression expressionForKeyPath:@"x"]
+ fromExpression:[NSExpression expressionForConstantValue:@11]
+ stops:[NSExpression expressionForConstantValue:stops]];
+ NSArray *jsonExpression = @[@"step", @[@"get", @"x"], @11, @0, @111, @1, @1111];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@100), @10: MGLConstantExpression(@200)};
+ NSExpression *expression = [NSExpression mgl_expressionForInterpolatingExpression:[NSExpression expressionForKeyPath:@"x"]
+ withCurveType:MGLExpressionInterpolationModeLinear
+ parameters:nil
+ stops:[NSExpression expressionForConstantValue:stops]];
+ NSArray *jsonExpression = @[@"interpolate", @[@"linear"], @[@"get", @"x"], @0, @100, @10, @200];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [[NSExpression expressionForConstantValue:@"Old"] mgl_expressionByAppendingExpression:[NSExpression expressionForConstantValue:@"MacDonald"]];
+
+ NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *values = @{ MGLConstantExpression(@1): MGLConstantExpression(@"one") };
+ NSExpression *expression = [NSExpression mgl_expressionForMatchingExpression:[NSExpression expressionWithFormat:@"2 * 1"]
+ inDictionary:values
+ defaultExpression:[NSExpression expressionForConstantValue:@"default"]];
+ NSArray *jsonExpression = @[@"match", @[@"*", @2, @1], @1, @"one", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+
@end
diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
index f5e26381d6..6081d104e1 100644
--- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"fillExtrusionBase should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionBase = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -77,7 +77,7 @@
XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
@"fillExtrusionBase should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillExtrusionBase = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -85,10 +85,11 @@
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
@"Setting fillExtrusionBase to a data expression should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression,
@"fillExtrusionBase should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillExtrusionBase = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -98,7 +99,8 @@
XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
@"Setting fillExtrusionBase to a camera-data expression should update fill-extrusion-base.");
- XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression,
@"fillExtrusionBase should round-trip camera-data expressions.");
@@ -133,7 +135,7 @@
@"fillExtrusionColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -147,7 +149,7 @@
XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
@"fillExtrusionColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillExtrusionColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -155,10 +157,11 @@
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
@"Setting fillExtrusionColor to a data expression should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression,
@"fillExtrusionColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillExtrusionColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -168,7 +171,8 @@
XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
@"Setting fillExtrusionColor to a camera-data expression should update fill-extrusion-color.");
- XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression,
@"fillExtrusionColor should round-trip camera-data expressions.");
@@ -203,7 +207,7 @@
@"fillExtrusionHeight should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionHeight = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -217,7 +221,7 @@
XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
@"fillExtrusionHeight should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillExtrusionHeight = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -225,10 +229,11 @@
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
@"Setting fillExtrusionHeight to a data expression should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression,
@"fillExtrusionHeight should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillExtrusionHeight = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -238,7 +243,8 @@
XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
@"Setting fillExtrusionHeight to a camera-data expression should update fill-extrusion-height.");
- XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression,
@"fillExtrusionHeight should round-trip camera-data expressions.");
@@ -273,7 +279,7 @@
@"fillExtrusionOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -297,8 +303,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -326,7 +332,7 @@
@"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}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionPattern = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -350,8 +356,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -385,7 +391,7 @@
@"fillExtrusionTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -409,8 +415,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -429,7 +435,7 @@
@"fillExtrusionTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillExtrusionTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -453,8 +459,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 25521f3ede..a5019f1032 100644
--- a/platform/darwin/test/MGLFillStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"fillAntialiased should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"false"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillAntialiased = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -87,8 +87,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -107,7 +107,7 @@
@"fillColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -121,7 +121,7 @@
XCTAssertEqualObjects(layer.fillColor, functionExpression,
@"fillColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -129,10 +129,11 @@
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
@"Setting fillColor to a data expression should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression,
@"fillColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -142,7 +143,8 @@
XCTAssertEqual(rawLayer->getFillColor(), propertyValue,
@"Setting fillColor to a camera-data expression should update fill-color.");
- XCTAssertEqualObjects(layer.fillColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression,
@"fillColor should round-trip camera-data expressions.");
@@ -177,7 +179,7 @@
@"fillOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -191,7 +193,7 @@
XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
@"fillOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -199,10 +201,11 @@
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
@"Setting fillOpacity to a data expression should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression,
@"fillOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -212,7 +215,8 @@
XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue,
@"Setting fillOpacity to a camera-data expression should update fill-opacity.");
- XCTAssertEqualObjects(layer.fillOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression,
@"fillOpacity should round-trip camera-data expressions.");
@@ -247,7 +251,7 @@
@"fillOutlineColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillOutlineColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -261,7 +265,7 @@
XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
@"fillOutlineColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.fillOutlineColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -269,10 +273,11 @@
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
@"Setting fillOutlineColor to a data expression should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression,
@"fillOutlineColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.fillOutlineColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -282,7 +287,8 @@
XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue,
@"Setting fillOutlineColor to a camera-data expression should update fill-outline-color.");
- XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression,
@"fillOutlineColor should round-trip camera-data expressions.");
@@ -317,7 +323,7 @@
@"fillPattern should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'Fill Pattern'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillPattern = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -341,8 +347,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -376,7 +382,7 @@
@"fillTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -400,8 +406,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -420,7 +426,7 @@
@"fillTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.fillTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -444,8 +450,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm
index 1c85470188..a0ddecf77e 100644
--- a/platform/darwin/test/MGLGeometryTests.mm
+++ b/platform/darwin/test/MGLGeometryTests.mm
@@ -163,4 +163,13 @@
[NSValue valueWithMGLCoordinate:quad.bottomRight],
@"Quad bottom right should be computed correctly.");
}
+
+- (void)testMGLMapPoint {
+ MGLMapPoint point = MGLMapPointForCoordinate(CLLocationCoordinate2DMake(37.936, -80.425), 0.0);
+
+ MGLMapPoint roundTrippedPoint = [NSValue valueWithMGLMapPoint:point].MGLMapPointValue;
+ XCTAssertEqual(point.x, roundTrippedPoint.x);
+ XCTAssertEqual(point.y, roundTrippedPoint.y);
+ XCTAssertEqual(point.zoomLevel, roundTrippedPoint.zoomLevel);
+}
@end
diff --git a/platform/darwin/test/MGLHeatmapColorTests.mm b/platform/darwin/test/MGLHeatmapColorTests.mm
index 8d44064d94..bed777ae05 100644
--- a/platform/darwin/test/MGLHeatmapColorTests.mm
+++ b/platform/darwin/test/MGLHeatmapColorTests.mm
@@ -33,7 +33,7 @@
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
NSExpression *constantExpression2 = [NSExpression expressionWithFormat:@"%@", [MGLColor blueColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($heatmapDensity, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@12: constantExpression2}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($heatmapDensity, %@, %@)", constantExpression, @{@12: constantExpression2}];
layer.heatmapColor = functionExpression;
XCTAssertEqual(rawLayer->getHeatmapColor().evaluate(11.0), mbgl::Color::red(),
@@ -46,15 +46,16 @@
layer.heatmapColor = nil;
XCTAssertTrue(rawLayer->getHeatmapColor().isUndefined(),
@"Unsetting heatmapColor should return heatmap-color to the default value.");
- XCTAssertEqualObjects(layer.heatmapColor, defaultExpression,
+ // The contained colors aren’t object equal, even though their descriptions are.
+ XCTAssertEqualObjects(layer.heatmapColor.description, defaultExpression.description,
@"heatmapColor should return the default value after being unset.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a camera expression is applied to heatmapColor.");
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer should raise an exception if a data expression is applied to heatmapColor.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(bogus, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapColor = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer 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/MGLHeatmapStyleLayerTests.mm b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
index 74121affd8..e4b1917257 100644
--- a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
+++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"heatmapIntensity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapIntensity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -87,8 +87,8 @@
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer 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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapIntensity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer 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.heatmapIntensityTransition = transitionTest;
@@ -116,7 +116,7 @@
@"heatmapOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -140,8 +140,8 @@
functionExpression = [NSExpression expressionForKeyPath:@"bogus"];
XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer 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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
XCTAssertThrowsSpecificNamed(layer.heatmapOpacity = functionExpression, NSException, NSInvalidArgumentException, @"MGLHeatmapLayer 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.heatmapOpacityTransition = transitionTest;
@@ -169,7 +169,7 @@
@"heatmapRadius should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapRadius = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -183,7 +183,7 @@
XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
@"heatmapRadius should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.heatmapRadius = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -191,10 +191,11 @@
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a data expression should update heatmap-radius.");
- XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression,
@"heatmapRadius should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.heatmapRadius = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -204,7 +205,8 @@
XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue,
@"Setting heatmapRadius to a camera-data expression should update heatmap-radius.");
- XCTAssertEqualObjects(layer.heatmapRadius, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression,
@"heatmapRadius should round-trip camera-data expressions.");
@@ -239,7 +241,7 @@
@"heatmapWeight should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.heatmapWeight = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -253,7 +255,7 @@
XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
@"heatmapWeight should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.heatmapWeight = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -261,10 +263,11 @@
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a data expression should update heatmap-weight.");
- XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression,
@"heatmapWeight should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.heatmapWeight = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -274,7 +277,8 @@
XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue,
@"Setting heatmapWeight to a camera-data expression should update heatmap-weight.");
- XCTAssertEqualObjects(layer.heatmapWeight, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression,
@"heatmapWeight should round-trip camera-data expressions.");
diff --git a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm
index b0b7ddd5ee..34937d1674 100644
--- a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm
+++ b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm
@@ -45,7 +45,7 @@
@"hillshadeAccentColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeAccentColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -69,8 +69,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -98,7 +98,7 @@
@"hillshadeExaggeration should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeExaggeration = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -122,8 +122,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -151,7 +151,7 @@
@"hillshadeHighlightColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeHighlightColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -175,8 +175,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -204,7 +204,7 @@
@"hillshadeIlluminationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeIlluminationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::HillshadeIlluminationAnchorType> intervalStops = {{
@@ -228,8 +228,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -248,7 +248,7 @@
@"hillshadeIlluminationDirection should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeIlluminationDirection = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -272,8 +272,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -292,7 +292,7 @@
@"hillshadeShadowColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.hillshadeShadowColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -316,8 +316,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm
index bf98e98320..5490278e98 100644
--- a/platform/darwin/test/MGLLineStyleLayerTests.mm
+++ b/platform/darwin/test/MGLLineStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"lineCap should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'square'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineCap = functionExpression;
mbgl::style::IntervalStops<mbgl::style::LineCapType> intervalStops = {{
@@ -87,8 +87,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -107,7 +107,7 @@
@"lineJoin should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'miter'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineJoin = functionExpression;
mbgl::style::IntervalStops<mbgl::style::LineJoinType> intervalStops = {{
@@ -145,7 +145,7 @@
@"lineMiterLimit should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineMiterLimit = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -169,8 +169,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -189,7 +189,7 @@
@"lineRoundLimit should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineRoundLimit = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -213,8 +213,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -233,7 +233,7 @@
@"lineBlur should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineBlur = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -247,7 +247,7 @@
XCTAssertEqualObjects(layer.lineBlur, functionExpression,
@"lineBlur should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -255,10 +255,11 @@
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
@"Setting lineBlur to a data expression should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression,
@"lineBlur should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -268,7 +269,8 @@
XCTAssertEqual(rawLayer->getLineBlur(), propertyValue,
@"Setting lineBlur to a camera-data expression should update line-blur.");
- XCTAssertEqualObjects(layer.lineBlur, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression,
@"lineBlur should round-trip camera-data expressions.");
@@ -303,7 +305,7 @@
@"lineColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -317,7 +319,7 @@
XCTAssertEqualObjects(layer.lineColor, functionExpression,
@"lineColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -325,10 +327,11 @@
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
@"Setting lineColor to a data expression should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression,
@"lineColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -338,7 +341,8 @@
XCTAssertEqual(rawLayer->getLineColor(), propertyValue,
@"Setting lineColor to a camera-data expression should update line-color.");
- XCTAssertEqualObjects(layer.lineColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression,
@"lineColor should round-trip camera-data expressions.");
@@ -373,7 +377,7 @@
@"lineDashPattern should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 2}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineDashPattern = functionExpression;
mbgl::style::IntervalStops<std::vector<float>> intervalStops = {{
@@ -397,8 +401,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -417,7 +421,7 @@
@"lineGapWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineGapWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -431,7 +435,7 @@
XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
@"lineGapWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineGapWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -439,10 +443,11 @@
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
@"Setting lineGapWidth to a data expression should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression,
@"lineGapWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineGapWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -452,7 +457,8 @@
XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue,
@"Setting lineGapWidth to a camera-data expression should update line-gap-width.");
- XCTAssertEqualObjects(layer.lineGapWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression,
@"lineGapWidth should round-trip camera-data expressions.");
@@ -487,7 +493,7 @@
@"lineOffset should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineOffset = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -501,7 +507,7 @@
XCTAssertEqualObjects(layer.lineOffset, functionExpression,
@"lineOffset should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineOffset = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -509,10 +515,11 @@
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
@"Setting lineOffset to a data expression should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression,
@"lineOffset should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineOffset = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -522,7 +529,8 @@
XCTAssertEqual(rawLayer->getLineOffset(), propertyValue,
@"Setting lineOffset to a camera-data expression should update line-offset.");
- XCTAssertEqualObjects(layer.lineOffset, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression,
@"lineOffset should round-trip camera-data expressions.");
@@ -557,7 +565,7 @@
@"lineOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -571,7 +579,7 @@
XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
@"lineOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -579,10 +587,11 @@
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
@"Setting lineOpacity to a data expression should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression,
@"lineOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -592,7 +601,8 @@
XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue,
@"Setting lineOpacity to a camera-data expression should update line-opacity.");
- XCTAssertEqualObjects(layer.lineOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression,
@"lineOpacity should round-trip camera-data expressions.");
@@ -627,7 +637,7 @@
@"linePattern should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'Line Pattern'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.linePattern = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -651,8 +661,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -686,7 +696,7 @@
@"lineTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -710,8 +720,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -730,7 +740,7 @@
@"lineTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -754,8 +764,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -774,7 +784,7 @@
@"lineWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.lineWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -788,7 +798,7 @@
XCTAssertEqualObjects(layer.lineWidth, functionExpression,
@"lineWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.lineWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -796,10 +806,11 @@
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
@"Setting lineWidth to a data expression should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression,
@"lineWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.lineWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -809,7 +820,8 @@
XCTAssertEqual(rawLayer->getLineWidth(), propertyValue,
@"Setting lineWidth to a camera-data expression should update line-width.");
- XCTAssertEqualObjects(layer.lineWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression,
@"lineWidth should round-trip camera-data expressions.");
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
index d8cad0b166..ab4a7e2d88 100644
--- a/platform/darwin/test/MGLPredicateTests.mm
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -23,295 +23,9 @@ namespace mbgl {
@implementation MGLPredicateTests
-- (void)testFilterization {
- {
- auto actual = [NSPredicate predicateWithValue:YES].mgl_filter;
- mbgl::style::AllFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithValue:NO].mgl_filter;
- mbgl::style::AnyFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a = 'b'"].mgl_filter;
- mbgl::style::EqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"].mgl_filter;
- mbgl::style::TypeEqualsFilter expected = { .value = mbgl::FeatureType::Point };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"].mgl_filter;
- mbgl::style::IdentifierEqualsFilter expected = { .value = UINT64_C(67086180) };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"].mgl_filter;
- mbgl::style::NotHasIdentifierFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a = nil"].mgl_filter;
- mbgl::style::NotHasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"].mgl_filter;
- mbgl::style::TypeNotEqualsFilter expected = { .value = mbgl::FeatureType::Point };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"].mgl_filter;
- mbgl::style::IdentifierNotEqualsFilter expected = { .value = UINT64_C(67086180) };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"].mgl_filter;
- mbgl::style::HasIdentifierFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a != 'b'"].mgl_filter;
- mbgl::style::NotEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a != nil"].mgl_filter;
- mbgl::style::HasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a < 'b'"].mgl_filter;
- mbgl::style::LessThanFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a <= 'b'"].mgl_filter;
- mbgl::style::LessThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a > 'b'"].mgl_filter;
- mbgl::style::GreaterThanFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a >= 'b'"].mgl_filter;
- mbgl::style::GreaterThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@"b", @"z"]].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a IN %@", @[@"b", @"c"]].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", @[@"LineString", @"Polygon"]].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", @[@67086180, @3709678893, @3352016856, @4189833989]].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"'Mapbox' IN a"].mgl_filter, NSException, NSInvalidArgumentException);
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"{'b', 'c'} CONTAINS a"].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@"LineString", @"Polygon"], @"$type"].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"{67086180, 3709678893, 3352016856, 4189833989} CONTAINS %K", @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@67086180, @3709678893, @3352016856, @4189833989], @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a CONTAINS 'Mapbox'"].mgl_filter, NSException, NSInvalidArgumentException);
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"].mgl_filter;
- mbgl::style::AnyFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"].mgl_filter;
- mbgl::style::NoneFilter expected = {
- .filters = {
- mbgl::style::AllFilter {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"].mgl_filter;
- mbgl::style::NoneFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a == nil"].mgl_filter;
- mbgl::style::HasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a != nil"].mgl_filter;
- mbgl::style::NotHasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a IN %@", @[@"b", @"c"]].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT {'b', 'c'} CONTAINS a"].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT %@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
- NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
- XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
- XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
- return NO;
- }].mgl_filter, NSException, NSInvalidArgumentException);
-}
-
- (void)testPredication {
XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]);
-
+
{
mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
@@ -351,12 +65,12 @@ namespace mbgl {
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown };
XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException);
}
-
+
{
mbgl::style::NotHasFilter filter = { .key = "a" };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]);
}
-
+
{
mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
@@ -379,32 +93,32 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::HasFilter filter = { .key = "a" };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]);
}
-
+
{
mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]);
}
-
+
{
mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]);
}
-
+
{
mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]);
}
-
+
{
mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -414,7 +128,7 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -424,7 +138,7 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
}
-
+
{
mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
@@ -441,7 +155,7 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
@@ -458,12 +172,12 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::AllFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -473,12 +187,12 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]);
}
-
+
{
mbgl::style::AnyFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]);
}
-
+
{
mbgl::style::AnyFilter filter = {
.filters = {
@@ -488,12 +202,12 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]);
}
-
+
{
mbgl::style::NoneFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
}
-
+
{
mbgl::style::NoneFilter filter = {
.filters = {
@@ -505,76 +219,339 @@ namespace mbgl {
}
}
-- (void)testSymmetry {
- [self testSymmetryWithFormat:@"a = 1" reverseFormat:@"1 = a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a != 1" reverseFormat:@"1 != a" mustRoundTrip:YES];
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = b"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 = 1"].mgl_filter, NSException, NSInvalidArgumentException);
-
- // In the predicate format language, $ is a special character denoting a
- // variable. Use %K to escape the special feature attribute $id.
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"$id == 670861802"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = $id"].mgl_filter, NSException, NSInvalidArgumentException);
-
- [self testSymmetryWithFormat:@"a = nil" reverseFormat:@"nil = a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a != nil" reverseFormat:@"nil != a" mustRoundTrip:YES];
-
- [self testSymmetryWithFormat:@"a < 1" reverseFormat:@"1 > a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a <= 1" reverseFormat:@"1 >= a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a > 1" reverseFormat:@"1 < a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a >= 1" reverseFormat:@"1 <= a" mustRoundTrip:YES];
-
- [self testSymmetryWithFormat:@"a BETWEEN {1, 2}" reverseFormat:@"1 <= a && 2 >= a" mustRoundTrip:YES];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"1 <= a && 2 >= a"]
- mustRoundTrip:YES];
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"{1, 2} BETWEEN a"].mgl_filter, NSException, NSInvalidArgumentException);
- NSPredicate *betweenSetPredicate = [NSPredicate predicateWithFormat:@"a BETWEEN %@", [NSSet setWithObjects:@1, @2, nil]];
- XCTAssertThrowsSpecificNamed(betweenSetPredicate.mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1}"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1, 2, 3}"].mgl_filter, NSException, NSInvalidArgumentException);
-
- [self testSymmetryWithFormat:@"a IN {1, 2}" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a IN %@", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- mustRoundTrip:YES];
+- (void)testUnsupportedFilterPredicates {
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
+ NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
+ XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
+ XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
+ return NO;
+ }].mgl_filter, NSException, NSInvalidArgumentException);
+}
- // The reverse formats here are a bit backwards because we canonicalize
- // a reverse CONTAINS to a forward IN.
- [self testSymmetryWithFormat:@"{1, 2} CONTAINS a" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- mustRoundTrip:NO];
+- (void)testComparisonPredicates {
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"];
+ NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"];
+ NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"];
+ NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"];
+ NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"];
+ NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = 'b'"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType = 'Point'"];
+ NSArray *jsonExpression = @[@"==", @[@"geometry-type"], @"Point"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 67086180"];
+ NSArray *jsonExpression = @[@"==", @[@"id"], @67086180];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = nil"];
+ NSArray *jsonExpression = @[@"==", @[@"id"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = nil"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType != 'Point'"];
+ NSArray *jsonExpression = @[@"!=", @[@"geometry-type"], @"Point"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != 67086180"];
+ NSArray *jsonExpression = @[@"!=", @[@"id"], @67086180];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != nil"];
+ NSArray *jsonExpression = @[@"!=", @[@"id"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != 'b'"];
+ NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != nil"];
+ NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"];
+ NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"];
+ NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"];
+ NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"];
+ NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"];
+ NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
}
-- (void)testSymmetryWithFormat:(NSString *)forwardFormat reverseFormat:(NSString *)reverseFormat mustRoundTrip:(BOOL)mustRoundTrip {
- NSPredicate *forwardPredicate = [NSPredicate predicateWithFormat:forwardFormat];
- NSPredicate *reversePredicate = reverseFormat ? [NSPredicate predicateWithFormat:reverseFormat] : nil;
- [self testSymmetryWithPredicate:forwardPredicate reversePredicate:reversePredicate mustRoundTrip:mustRoundTrip];
+- (void)testCompoundPredicates {
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
+ NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"];
+ NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"];
+ NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"];
+ NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a == nil"];
+ NSArray *jsonExpression = @[@"!", @[@"==", @[@"get", @"a"], [NSNull null]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a != nil"];
+ NSArray *jsonExpression = @[@"!", @[@"!=", @[@"get", @"a"], [NSNull null]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
}
-- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate reversePredicate:(NSPredicate *)reversePredicate mustRoundTrip:(BOOL)mustRoundTrip {
+- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate mustRoundTrip:(BOOL)mustRoundTrip {
auto forwardFilter = forwardPredicate.mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
if (mustRoundTrip) {
// A collection of ints may turn into an aggregate of longs, for
// example, so compare formats instead of the predicates themselves.
XCTAssertEqualObjects(forwardPredicate.predicateFormat, forwardPredicateAfter.predicateFormat);
- }
-
- if (reversePredicate) {
- auto reverseFilter = reversePredicate.mgl_filter;
- NSPredicate *reversePredicateAfter = [NSPredicate mgl_predicateWithFilter:reverseFilter];
- XCTAssertNotEqualObjects(reversePredicate, reversePredicateAfter);
-
- XCTAssertEqualObjects(forwardPredicateAfter, reversePredicateAfter);
- }
-}
-
-- (void)testComparisonExpressionArray {
- {
- NSArray *expected = @[@"==", @1, @2];
- XCTAssertEqualObjects([NSPredicate predicateWithFormat:@"1 = 2"].mgl_jsonExpressionObject, expected);
+ } else {
+ XCTAssertEqualObjects(forwardPredicate, forwardPredicateAfter);
}
}
diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm
index 5ded23ee3c..c8e454743e 100644
--- a/platform/darwin/test/MGLRasterStyleLayerTests.mm
+++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm
@@ -45,7 +45,7 @@
@"maximumRasterBrightness should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.maximumRasterBrightness = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -69,8 +69,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -89,7 +89,7 @@
@"minimumRasterBrightness should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.minimumRasterBrightness = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -113,8 +113,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -133,7 +133,7 @@
@"rasterContrast should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.rasterContrast = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -157,8 +157,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -186,7 +186,7 @@
@"rasterFadeDuration should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.rasterFadeDuration = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -210,8 +210,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -230,7 +230,7 @@
@"rasterHueRotation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.rasterHueRotation = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -254,8 +254,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -274,7 +274,7 @@
@"rasterOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.rasterOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -298,8 +298,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
@@ -327,7 +327,7 @@
@"rasterSaturation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.rasterSaturation = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -351,8 +351,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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;
diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift
index 576e3da86b..727d8bf0c6 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -23,9 +23,9 @@ extension MGLSDKTestHelpers {
class func protocolMethodDescriptions(_ p: Protocol) -> Set<String> {
var methods = Set<String>()
var methodCount = UInt32()
- let methodDescriptionList: UnsafeMutablePointer<objc_method_description>! = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
+ let methodDescriptionList = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
for i in 0..<Int(methodCount) {
- let description: objc_method_description = methodDescriptionList[i]
+ let description = methodDescriptionList![i]
XCTAssertNotNil(description.name?.description)
methods.insert(description.name!.description)
}
@@ -36,11 +36,16 @@ extension MGLSDKTestHelpers {
class func classMethodDescriptions(_ cls: Swift.AnyClass) -> Set<String> {
var methods = Set<String>()
var methodCount = UInt32()
- let methodList: UnsafeMutablePointer<Method?>! = class_copyMethodList(cls, &methodCount)
+ let methodList = class_copyMethodList(cls, &methodCount)
for i in 0..<Int(methodCount) {
- let method = methodList[i]
- let selector : Selector = method_getName(method)
- methods.insert(selector.description)
+ let method = methodList![i]
+ let selector = method_getName(method)
+ #if os(macOS)
+ methods.insert(selector.description)
+ #else
+ XCTAssertNotNil(selector)
+ methods.insert(selector!.description)
+ #endif
}
free(methodList)
return methods
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index 868dca730a..d3f9a599e2 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -2,7 +2,6 @@
#import <Mapbox/Mapbox.h>
#import "MGLFeature_Private.h"
-#import "MGLAbstractShapeSource_Private.h"
#import "MGLShapeSource_Private.h"
#import "MGLSource_Private.h"
diff --git a/platform/darwin/test/MGLSourceQueryTests.m b/platform/darwin/test/MGLSourceQueryTests.m
index d1ef180a52..b321da1ea4 100644
--- a/platform/darwin/test/MGLSourceQueryTests.m
+++ b/platform/darwin/test/MGLSourceQueryTests.m
@@ -7,8 +7,8 @@
@implementation MGLSourceQueryTests
-- (void) testQueryVectorSource {
- MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"vector" tileURLTemplates:@[@"fake"] options:nil];
+- (void) testQueryVectorTileSource {
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"vector" tileURLTemplates:@[@"fake"] options:nil];
NSSet *sourceLayers = [NSSet setWithObjects:@"buildings", @"water", nil];
NSArray* features = [source featuresInSourceLayersWithIdentifiers:sourceLayers predicate:nil];
// Source not added yet, so features is 0
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
index e17501ed18..f70f0bba23 100644
--- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs
+++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
@@ -36,8 +36,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -80,7 +80,7 @@
@"<%- 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}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.<%- objCName(property) %> = functionExpression;
mbgl::style::IntervalStops<<%- mbglType(property) %>> intervalStops = {{
@@ -95,7 +95,7 @@
@"<%- objCName(property) %> should round-trip camera expressions.");
<% if (property["property-function"] && isInterpolatable(property)) { -%>
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.<%- objCName(property) %> = functionExpression;
mbgl::style::ExponentialStops<<%- mbglType(property) %>> exponentialStops = { {{18, <%- mbglTestValue(property, type) %>}}, 1.0 };
@@ -103,10 +103,11 @@
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
@"Setting <%- objCName(property) %> to a data expression should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression,
@"<%- objCName(property) %> should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.<%- objCName(property) %> = functionExpression;
std::map<float, <%- mbglType(property) %>> innerStops { {18, <%- mbglTestValue(property, type) %>} };
@@ -116,7 +117,8 @@
XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue,
@"Setting <%- objCName(property) %> to a camera-data expression should update <%- originalPropertyName(property) %>.");
- XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression,
@"<%- objCName(property) %> should round-trip camera-data expressions.");
<% } -%>
<% if (!property.required) { -%>
@@ -131,8 +133,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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) { -%>
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 8f610e338c..6048f39ea3 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -1,6 +1,7 @@
#import <Mapbox/Mapbox.h>
#import "NSBundle+MGLAdditions.h"
+#import "MGLVectorTileSource_Private.h"
#import <mbgl/util/default_styles.hpp>
@@ -64,12 +65,6 @@
XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @(mbgl::util::default_styles::dark.url));
XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @(mbgl::util::default_styles::satellite.url));
XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURL].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url));
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8");
- XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8");
-#pragma clang diagnostic pop
}
- (void)testVersionedStyleURLs {
@@ -99,19 +94,8 @@
@(mbgl::util::default_styles::satelliteStreets.url));
XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString,
@"mapbox://styles/mapbox/satellite-streets-v99");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:mbgl::util::default_styles::trafficDay.currentVersion].absoluteString,
- @(mbgl::util::default_styles::trafficDay.url));
- XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:99].absoluteString,
- @"mapbox://styles/mapbox/traffic-day-v99");
- XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:mbgl::util::default_styles::trafficNight.currentVersion].absoluteString,
- @(mbgl::util::default_styles::trafficNight.url));
- XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:99].absoluteString,
- @"mapbox://styles/mapbox/traffic-night-v99");
-#pragma clang diagnostic pop
-
- static_assert(8 == mbgl::util::default_styles::numOrderedStyles,
+
+ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
"MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
}
@@ -143,7 +127,7 @@
NSString *styleHeader = self.stringWithContentsOfStyleHeader;
NSError *versionedMethodError;
- NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE");
+ NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*(?!traffic)\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE");
NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError];
XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods.");
NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
@@ -174,89 +158,89 @@
[self.style addSource:shapeSource];
XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException, @"MGLRedundantSourceException");
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"rasterSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42];
- [self.style addSource:rasterSource];
- XCTAssertThrowsSpecificNamed([self.style addSource:rasterSource], NSException, @"MGLRedundantSourceException");
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"rasterTileSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42];
+ [self.style addSource:rasterTileSource];
+ XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException, @"MGLRedundantSourceException");
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"vectorSource" configurationURL:[NSURL URLWithString:@".json"]];
- [self.style addSource:vectorSource];
- XCTAssertThrowsSpecificNamed([self.style addSource:vectorSource], NSException, @"MGLRedundantSourceException");
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"vectorTileSource" configurationURL:[NSURL URLWithString:@".json"]];
+ [self.style addSource:vectorTileSource];
+ XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException, @"MGLRedundantSourceException");
}
- (void)testAddingSourcesWithDuplicateIdentifiers {
- MGLVectorSource *source1 = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
- MGLVectorSource *source2 = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source1 = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source2 = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
[self.style addSource: source1];
XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, @"MGLRedundantSourceIdentifierException");
}
- (void)testRemovingSourcesBeforeAddingThem {
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"raster-source" tileURLTemplates:@[] options:nil];
- [self.style removeSource:rasterSource];
- [self.style addSource:rasterSource];
- XCTAssertNotNil([self.style sourceWithIdentifier:rasterSource.identifier]);
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"raster-tile-source" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:rasterTileSource];
+ [self.style addSource:rasterTileSource];
+ XCTAssertNotNil([self.style sourceWithIdentifier:rasterTileSource.identifier]);
MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shape-source" shape:nil options:nil];
[self.style removeSource:shapeSource];
[self.style addSource:shapeSource];
XCTAssertNotNil([self.style sourceWithIdentifier:shapeSource.identifier]);
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"vector-source" tileURLTemplates:@[] options:nil];
- [self.style removeSource:vectorSource];
- [self.style addSource:vectorSource];
- XCTAssertNotNil([self.style sourceWithIdentifier:vectorSource.identifier]);
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"vector-tile-source" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:vectorTileSource];
+ [self.style addSource:vectorTileSource];
+ XCTAssertNotNil([self.style sourceWithIdentifier:vectorTileSource.identifier]);
}
- (void)testAddingSourceOfTypeABeforeSourceOfTypeBWithSameIdentifier {
- // Add a raster source
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style addSource:rasterSource];
+ // Add a raster tile source
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style addSource:rasterTileSource];
- // Attempt to remove an image source with the same identifier as the raster source
+ // Attempt to remove an image source with the same identifier as the raster tile source
MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"some-identifier" coordinateQuad: { } URL:[NSURL URLWithString:@"http://host/image.png"]];
[self.style removeSource:imageSource];
- // The raster source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]);
+ // The raster tile source should still be added
+ XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MGLRasterTileSource class]]);
- // Remove the raster source
- [self.style removeSource:rasterSource];
+ // Remove the raster tile source
+ [self.style removeSource:rasterTileSource];
// Add the shape source
[self.style addSource:imageSource];
- // Attempt to remove a vector source with the same identifer as the shape source
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style removeSource:vectorSource];
+ // Attempt to remove a vector tile source with the same identifer as the shape source
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:vectorTileSource];
// The image source should still be added
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLImageSource class]]);
// Remove the image source
[self.style removeSource:imageSource];
- // Add the vector source
- [self.style addSource:vectorSource];
+ // Add the vector tile source
+ [self.style addSource:vectorTileSource];
- // Attempt to remove the previously created raster source that has the same identifer as the shape source
- [self.style removeSource:rasterSource];
- // The vector source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorSource class]]);
+ // Attempt to remove the previously created raster tile source that has the same identifer as the shape source
+ [self.style removeSource:rasterTileSource];
+ // The vector tile source should still be added
+ XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorTileSource class]]);
}
- (void)testRemovingSourceInUse {
- // Add a raster source
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style addSource:rasterSource];
+ // Add a raster tile source
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style addSource:rasterTileSource];
// Add a layer using it
- MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:rasterSource];
+ MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:rasterTileSource];
[self.style addLayer:fillLayer];
- // Attempt to remove the raster source
- [self.style removeSource:rasterSource];
+ // Attempt to remove the raster tile source
+ [self.style removeSource:rasterTileSource];
// Ensure it is still there
- XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]);
+ XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MGLRasterTileSource class]]);
}
- (void)testLayers {
@@ -306,7 +290,7 @@
- (void)testAddingLayersWithDuplicateIdentifiers {
// Just some source
- MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
[self.style addSource: source];
// Add initial layer
@@ -383,13 +367,6 @@
return styleHeader;
}
-- (void)testClasses {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqual(self.style.styleClasses.count, 0);
-#pragma clang diagnostic pop
-}
-
- (void)testImages {
NSString *imageName = @"TrackingLocationMask";
#if TARGET_OS_IPHONE
@@ -443,4 +420,45 @@
XCTAssertEqualObjects(layers[startIndex++].identifier, layer4.identifier);
}
+#pragma mark Localization tests
+
+- (void)testLanguageMatching {
+ {
+ NSArray *preferences = @[@"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"en-US"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"fr"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hant"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"mul"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+}
+
@end
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
index 05e091b5c8..cf2f80125a 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -63,7 +63,7 @@
@"iconAllowsOverlap should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconAllowsOverlap = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -87,8 +87,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -107,7 +107,7 @@
@"iconAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{
@@ -145,7 +145,7 @@
@"iconIgnoresPlacement should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconIgnoresPlacement = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -169,8 +169,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -189,7 +189,7 @@
@"iconImageName should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'Icon Image'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconImageName = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -233,7 +233,7 @@
@"iconOffset should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconOffset = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -247,7 +247,7 @@
XCTAssertEqualObjects(layer.iconOffset, functionExpression,
@"iconOffset should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconOffset = functionExpression;
mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 };
@@ -255,10 +255,11 @@
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
@"Setting iconOffset to a data expression should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression,
@"iconOffset should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconOffset = functionExpression;
std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} };
@@ -268,7 +269,8 @@
XCTAssertEqual(rawLayer->getIconOffset(), propertyValue,
@"Setting iconOffset to a camera-data expression should update icon-offset.");
- XCTAssertEqualObjects(layer.iconOffset, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression,
@"iconOffset should round-trip camera-data expressions.");
@@ -294,7 +296,7 @@
@"iconOptional should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconOptional = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -318,8 +320,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -338,7 +340,7 @@
@"iconPadding should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconPadding = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -362,8 +364,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -382,7 +384,7 @@
@"iconPitchAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconPitchAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
@@ -406,8 +408,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -426,7 +428,7 @@
@"iconRotation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconRotation = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -440,7 +442,7 @@
XCTAssertEqualObjects(layer.iconRotation, functionExpression,
@"iconRotation should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconRotation = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -448,10 +450,11 @@
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
@"Setting iconRotation to a data expression should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression,
@"iconRotation should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconRotation = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -461,7 +464,8 @@
XCTAssertEqual(rawLayer->getIconRotate(), propertyValue,
@"Setting iconRotation to a camera-data expression should update icon-rotate.");
- XCTAssertEqualObjects(layer.iconRotation, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression,
@"iconRotation should round-trip camera-data expressions.");
@@ -487,7 +491,7 @@
@"iconRotationAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconRotationAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
@@ -511,8 +515,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -531,7 +535,7 @@
@"iconScale should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconScale = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -545,7 +549,7 @@
XCTAssertEqualObjects(layer.iconScale, functionExpression,
@"iconScale should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconScale = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -553,10 +557,11 @@
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
@"Setting iconScale to a data expression should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression,
@"iconScale should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconScale = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -566,7 +571,8 @@
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
@"Setting iconScale to a camera-data expression should update icon-size.");
- XCTAssertEqualObjects(layer.iconScale, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression,
@"iconScale should round-trip camera-data expressions.");
@@ -592,7 +598,7 @@
@"iconTextFit should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'both'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconTextFit = functionExpression;
mbgl::style::IntervalStops<mbgl::style::IconTextFitType> intervalStops = {{
@@ -616,8 +622,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -642,7 +648,7 @@
@"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}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconTextFitPadding = functionExpression;
mbgl::style::IntervalStops<std::array<float, 4>> intervalStops = {{
@@ -666,8 +672,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -686,7 +692,7 @@
@"keepsIconUpright should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.keepsIconUpright = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -710,8 +716,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -730,7 +736,7 @@
@"keepsTextUpright should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"false"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.keepsTextUpright = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -754,8 +760,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -774,7 +780,7 @@
@"maximumTextAngle should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.maximumTextAngle = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -798,8 +804,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -818,7 +824,7 @@
@"maximumTextWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.maximumTextWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -832,7 +838,7 @@
XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
@"maximumTextWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.maximumTextWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -840,10 +846,11 @@
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
@"Setting maximumTextWidth to a data expression should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression,
@"maximumTextWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.maximumTextWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -853,7 +860,8 @@
XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue,
@"Setting maximumTextWidth to a camera-data expression should update text-max-width.");
- XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression,
@"maximumTextWidth should round-trip camera-data expressions.");
@@ -879,7 +887,7 @@
@"symbolAvoidsEdges should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.symbolAvoidsEdges = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -903,8 +911,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -923,7 +931,7 @@
@"symbolPlacement should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'line'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.symbolPlacement = functionExpression;
mbgl::style::IntervalStops<mbgl::style::SymbolPlacementType> intervalStops = {{
@@ -947,8 +955,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -967,7 +975,7 @@
@"symbolSpacing should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.symbolSpacing = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -991,8 +999,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1011,7 +1019,7 @@
@"text should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'Text Field'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.text = functionExpression;
mbgl::style::IntervalStops<std::string> intervalStops = {{
@@ -1049,7 +1057,7 @@
@"textAllowsOverlap should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textAllowsOverlap = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -1073,8 +1081,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1093,7 +1101,7 @@
@"textAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'bottom-right'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = {{
@@ -1131,7 +1139,7 @@
@"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}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textFontNames = functionExpression;
mbgl::style::IntervalStops<std::vector<std::string>> intervalStops = {{
@@ -1169,7 +1177,7 @@
@"textFontSize should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textFontSize = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1183,7 +1191,7 @@
XCTAssertEqualObjects(layer.textFontSize, functionExpression,
@"textFontSize should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textFontSize = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -1191,10 +1199,11 @@
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
@"Setting textFontSize to a data expression should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression,
@"textFontSize should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textFontSize = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -1204,7 +1213,8 @@
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
@"Setting textFontSize to a camera-data expression should update text-size.");
- XCTAssertEqualObjects(layer.textFontSize, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression,
@"textFontSize should round-trip camera-data expressions.");
@@ -1230,7 +1240,7 @@
@"textIgnoresPlacement should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textIgnoresPlacement = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -1254,8 +1264,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1274,7 +1284,7 @@
@"textJustification should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'right'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textJustification = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TextJustifyType> intervalStops = {{
@@ -1312,7 +1322,7 @@
@"textLetterSpacing should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textLetterSpacing = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1326,7 +1336,7 @@
XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
@"textLetterSpacing should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textLetterSpacing = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -1334,10 +1344,11 @@
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
@"Setting textLetterSpacing to a data expression should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression,
@"textLetterSpacing should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textLetterSpacing = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -1347,7 +1358,8 @@
XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue,
@"Setting textLetterSpacing to a camera-data expression should update text-letter-spacing.");
- XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression,
@"textLetterSpacing should round-trip camera-data expressions.");
@@ -1373,7 +1385,7 @@
@"textLineHeight should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textLineHeight = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1397,8 +1409,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1423,7 +1435,7 @@
@"textOffset should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textOffset = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -1437,7 +1449,7 @@
XCTAssertEqualObjects(layer.textOffset, functionExpression,
@"textOffset should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textOffset = functionExpression;
mbgl::style::ExponentialStops<std::array<float, 2>> exponentialStops = { {{18, { 1, 1 }}}, 1.0 };
@@ -1445,10 +1457,11 @@
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
@"Setting textOffset to a data expression should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression,
@"textOffset should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textOffset = functionExpression;
std::map<float, std::array<float, 2>> innerStops { {18, { 1, 1 }} };
@@ -1458,7 +1471,8 @@
XCTAssertEqual(rawLayer->getTextOffset(), propertyValue,
@"Setting textOffset to a camera-data expression should update text-offset.");
- XCTAssertEqualObjects(layer.textOffset, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression,
@"textOffset should round-trip camera-data expressions.");
@@ -1484,7 +1498,7 @@
@"textOptional should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"true"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textOptional = functionExpression;
mbgl::style::IntervalStops<bool> intervalStops = {{
@@ -1508,8 +1522,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1528,7 +1542,7 @@
@"textPadding should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textPadding = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1552,8 +1566,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1572,7 +1586,7 @@
@"textPitchAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textPitchAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
@@ -1596,8 +1610,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1616,7 +1630,7 @@
@"textRotation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textRotation = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1630,7 +1644,7 @@
XCTAssertEqualObjects(layer.textRotation, functionExpression,
@"textRotation should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textRotation = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -1638,10 +1652,11 @@
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
@"Setting textRotation to a data expression should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression,
@"textRotation should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textRotation = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -1651,7 +1666,8 @@
XCTAssertEqual(rawLayer->getTextRotate(), propertyValue,
@"Setting textRotation to a camera-data expression should update text-rotate.");
- XCTAssertEqualObjects(layer.textRotation, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression,
@"textRotation should round-trip camera-data expressions.");
@@ -1677,7 +1693,7 @@
@"textRotationAlignment should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'auto'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textRotationAlignment = functionExpression;
mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = {{
@@ -1701,8 +1717,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -1721,7 +1737,7 @@
@"textTransform should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'lowercase'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textTransform = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TextTransformType> intervalStops = {{
@@ -1759,7 +1775,7 @@
@"iconColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -1773,7 +1789,7 @@
XCTAssertEqualObjects(layer.iconColor, functionExpression,
@"iconColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -1781,10 +1797,11 @@
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
@"Setting iconColor to a data expression should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression,
@"iconColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -1794,7 +1811,8 @@
XCTAssertEqual(rawLayer->getIconColor(), propertyValue,
@"Setting iconColor to a camera-data expression should update icon-color.");
- XCTAssertEqualObjects(layer.iconColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression,
@"iconColor should round-trip camera-data expressions.");
@@ -1829,7 +1847,7 @@
@"iconHaloBlur should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconHaloBlur = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1843,7 +1861,7 @@
XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
@"iconHaloBlur should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconHaloBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -1851,10 +1869,11 @@
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
@"Setting iconHaloBlur to a data expression should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression,
@"iconHaloBlur should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconHaloBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -1864,7 +1883,8 @@
XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue,
@"Setting iconHaloBlur to a camera-data expression should update icon-halo-blur.");
- XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression,
@"iconHaloBlur should round-trip camera-data expressions.");
@@ -1899,7 +1919,7 @@
@"iconHaloColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconHaloColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -1913,7 +1933,7 @@
XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
@"iconHaloColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconHaloColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -1921,10 +1941,11 @@
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
@"Setting iconHaloColor to a data expression should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression,
@"iconHaloColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconHaloColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -1934,7 +1955,8 @@
XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue,
@"Setting iconHaloColor to a camera-data expression should update icon-halo-color.");
- XCTAssertEqualObjects(layer.iconHaloColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression,
@"iconHaloColor should round-trip camera-data expressions.");
@@ -1969,7 +1991,7 @@
@"iconHaloWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconHaloWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -1983,7 +2005,7 @@
XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
@"iconHaloWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconHaloWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -1991,10 +2013,11 @@
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
@"Setting iconHaloWidth to a data expression should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression,
@"iconHaloWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconHaloWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -2004,7 +2027,8 @@
XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue,
@"Setting iconHaloWidth to a camera-data expression should update icon-halo-width.");
- XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression,
@"iconHaloWidth should round-trip camera-data expressions.");
@@ -2039,7 +2063,7 @@
@"iconOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -2053,7 +2077,7 @@
XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
@"iconOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.iconOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -2061,10 +2085,11 @@
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
@"Setting iconOpacity to a data expression should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression,
@"iconOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.iconOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -2074,7 +2099,8 @@
XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue,
@"Setting iconOpacity to a camera-data expression should update icon-opacity.");
- XCTAssertEqualObjects(layer.iconOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression,
@"iconOpacity should round-trip camera-data expressions.");
@@ -2115,7 +2141,7 @@
@"iconTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -2139,8 +2165,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -2159,7 +2185,7 @@
@"iconTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.iconTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -2183,8 +2209,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -2203,7 +2229,7 @@
@"textColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -2217,7 +2243,7 @@
XCTAssertEqualObjects(layer.textColor, functionExpression,
@"textColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -2225,10 +2251,11 @@
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
@"Setting textColor to a data expression should update text-color.");
- XCTAssertEqualObjects(layer.textColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression,
@"textColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -2238,7 +2265,8 @@
XCTAssertEqual(rawLayer->getTextColor(), propertyValue,
@"Setting textColor to a camera-data expression should update text-color.");
- XCTAssertEqualObjects(layer.textColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression,
@"textColor should round-trip camera-data expressions.");
@@ -2273,7 +2301,7 @@
@"textHaloBlur should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textHaloBlur = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -2287,7 +2315,7 @@
XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
@"textHaloBlur should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textHaloBlur = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -2295,10 +2323,11 @@
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
@"Setting textHaloBlur to a data expression should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression,
@"textHaloBlur should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textHaloBlur = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -2308,7 +2337,8 @@
XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue,
@"Setting textHaloBlur to a camera-data expression should update text-halo-blur.");
- XCTAssertEqualObjects(layer.textHaloBlur, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression,
@"textHaloBlur should round-trip camera-data expressions.");
@@ -2343,7 +2373,7 @@
@"textHaloColor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"%@", [MGLColor redColor]];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textHaloColor = functionExpression;
mbgl::style::IntervalStops<mbgl::Color> intervalStops = {{
@@ -2357,7 +2387,7 @@
XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
@"textHaloColor should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textHaloColor = functionExpression;
mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
@@ -2365,10 +2395,11 @@
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
@"Setting textHaloColor to a data expression should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression,
@"textHaloColor should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textHaloColor = functionExpression;
std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
@@ -2378,7 +2409,8 @@
XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue,
@"Setting textHaloColor to a camera-data expression should update text-halo-color.");
- XCTAssertEqualObjects(layer.textHaloColor, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression,
@"textHaloColor should round-trip camera-data expressions.");
@@ -2413,7 +2445,7 @@
@"textHaloWidth should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textHaloWidth = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -2427,7 +2459,7 @@
XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
@"textHaloWidth should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textHaloWidth = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -2435,10 +2467,11 @@
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
@"Setting textHaloWidth to a data expression should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression,
@"textHaloWidth should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textHaloWidth = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -2448,7 +2481,8 @@
XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue,
@"Setting textHaloWidth to a camera-data expression should update text-halo-width.");
- XCTAssertEqualObjects(layer.textHaloWidth, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression,
@"textHaloWidth should round-trip camera-data expressions.");
@@ -2483,7 +2517,7 @@
@"textOpacity should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"0xff"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textOpacity = functionExpression;
mbgl::style::IntervalStops<float> intervalStops = {{
@@ -2497,7 +2531,7 @@
XCTAssertEqualObjects(layer.textOpacity, functionExpression,
@"textOpacity should round-trip camera expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION(keyName, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}];
layer.textOpacity = functionExpression;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
@@ -2505,10 +2539,11 @@
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
@"Setting textOpacity to a data expression should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, functionExpression,
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression,
@"textOpacity should round-trip data expressions.");
- functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', nil, %@)", @{@10: functionExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: functionExpression}];
layer.textOpacity = functionExpression;
std::map<float, float> innerStops { {18, 0xff} };
@@ -2518,7 +2553,8 @@
XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue,
@"Setting textOpacity to a camera-data expression should update text-opacity.");
- XCTAssertEqualObjects(layer.textOpacity, functionExpression,
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression,
@"textOpacity should round-trip camera-data expressions.");
@@ -2559,7 +2595,7 @@
@"textTranslation should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"{1, 1}"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textTranslation = functionExpression;
mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = {{
@@ -2583,8 +2619,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
}
@@ -2603,7 +2639,7 @@
@"textTranslationAnchor should round-trip constant value expressions.");
constantExpression = [NSExpression expressionWithFormat:@"'viewport'"];
- NSExpression *functionExpression = [NSExpression expressionWithFormat:@"FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', %@, %@)", constantExpression, @{@18: constantExpression}];
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", constantExpression, @{@18: constantExpression}];
layer.textTranslationAnchor = functionExpression;
mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = {{
@@ -2627,8 +2663,8 @@
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}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm
index 74c84184e1..2319f66447 100644
--- a/platform/darwin/test/MGLTileSetTests.mm
+++ b/platform/darwin/test/MGLTileSetTests.mm
@@ -66,8 +66,16 @@
// when the tile set has attribution infos
MGLAttributionInfo *mapboxInfo = [[MGLAttributionInfo alloc] initWithTitle:[[NSAttributedString alloc] initWithString:@"Mapbox"]
URL:[NSURL URLWithString:@"https://www.mapbox.com/"]];
+#if TARGET_OS_IPHONE
+ UIColor *redColor = [UIColor redColor];
+#else
+ // CSS uses the sRGB color space. In macOS 10.12 Sierra and below,
+ // -[NSColor redColor] is in the calibrated RGB space and has a slightly
+ // different sRGB value than on iOS and macOS 10.13 High Sierra.
+ NSColor *redColor = [NSColor colorWithSRGBRed:1 green:0 blue:0 alpha:1];
+#endif
NSAttributedString *gl = [[NSAttributedString alloc] initWithString:@"GL" attributes:@{
- NSBackgroundColorAttributeName: [MGLColor redColor],
+ NSBackgroundColorAttributeName: redColor,
}];
MGLAttributionInfo *glInfo = [[MGLAttributionInfo alloc] initWithTitle:gl URL:nil];
tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
@@ -82,7 +90,7 @@
#else
NSString *html = (@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica\">"
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
- @"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; background-color: #ff2600\">GL</font>\n");
+ @"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; background-color: #ff0000\">GL</font>\n");
#endif
XCTAssertEqualObjects(@(tileSet.attribution.c_str()), html);