[ios, macos] Update Style Functions guide (#11724)

This commit is contained in:
Jordan Kiley
2018-04-19 18:43:04 -07:00
committed by GitHub
parent de8fd3f97b
commit f81f770741
20 changed files with 853 additions and 439 deletions

View File

@@ -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)

View File

@@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

View File

@@ -626,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});
@@ -757,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({

View File

@@ -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,
@@ -75,7 +75,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
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,
@@ -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)
@@ -131,13 +161,27 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-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,
@@ -148,7 +192,7 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
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,
@@ -162,34 +206,47 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
//#-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
layer.circleColor = NSExpression(
format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
colors, colors, defaultColor)
//#-end-example-code
}
func testUsingStyleFunctionsAtRuntime$Identity() {
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: "FUNCTION(%@, 'valueForKeyPath:', type)",
stops)
#endif
//#-end-example-code
}
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
}
}

View File

@@ -0,0 +1,266 @@
<!--
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 v4.0.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 iOS 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:
```swift
let stops: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
```
## 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:
```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: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
### 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:
```swift
let stops = [
12: 0.5,
14: 2,
18: 18,
]
layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
stops)
```
### 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:
```swift
let stops: [NSNumber: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
UIColor.green, stops)
```
### 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:
```swift
let defaultColor = UIColor.blue
layer.circleColor = NSExpression(format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
UIColor.orange, UIColor.red, UIColor.yellow, defaultColor)
```
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:
```swift
layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![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.
```swift
layer.circleRadius = NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
```
![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.
```swift
let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
mapView.style?.addLayer(magnitudeLayer)
```
![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)

View File

@@ -1,154 +0,0 @@
<!--
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.
```swift
let stops: [Float: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
```
## 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.
```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: [Float: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
![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`.
```swift
let stops = [
12: 0.5,
14: 2,
18: 18,
]
layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
stops)
```
### 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.
```swift
let stops: [Float: UIColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
UIColor.green, stops)
```
![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.
```swift
let colors: [String: UIColor] = [
"earthquake": .orange,
"explosion": .red,
"quarry blast": .yellow,
]
let defaultColor = UIColor.blue
layer.circleColor = NSExpression(
format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
colors, colors, defaultColor)
```
![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
### 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`.
```swift
layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![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)

View File

@@ -19,7 +19,7 @@ custom_categories:
children:
- Adding Markers to a Map
- Runtime Styling
- Using Style Functions at Runtime
- Migrating to Expressions
- Working with Mapbox Studio
- Working with GeoJSON Data
- Predicates and Expressions

View File

@@ -2,7 +2,7 @@
## 0.7.0
The 0.7._x_ series of releases will be the last to support macOS 10.10. The minimum iOS deployment version will increase to macOS 10.11.0 in a future release.
The 0.7._x_ series of releases will be the last to support macOS 10.10. The minimum macOS deployment version will increase to macOS 10.11.0 in a future release.
### Packaging

View File

@@ -0,0 +1,267 @@
<!--
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 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 macOS 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:
```swift
let stops: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
```
## 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:
```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: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
### 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:
```swift
let stops = [
12: 0.5,
14: 2,
18: 18,
]
layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
stops)
```
### 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:
```swift
let stops: [NSNumber: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
NSColor.green, stops)
```
### 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:
```swift
let defaultColor = NSColor.blue
layer.circleColor = NSExpression(
format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
NSColor.orange, NSColor.red, NSColor.yellow, defaultColor)
```
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:
```swift
layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![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.
```swift
layer.circleRadius = NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
```
![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.
```swift
let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
mapView.style?.addLayer(magnitudeLayer)
```
![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)

View File

@@ -1,154 +0,0 @@
<!--
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.
```swift
let stops: [Float: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
```
## 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.
```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: [Float: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
stops)
layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
```
![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`.
```swift
let stops = [
12: 0.5,
14: 2,
18: 18,
]
layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
stops)
```
### 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.
```swift
let stops: [Float: NSColor] = [
0: .yellow,
2.5: .orange,
5: .red,
7.5: .blue,
10: .white,
]
layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
NSColor.green, stops)
```
![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.
```swift
let colors: [String: NSColor] = [
"earthquake": .orange,
"explosion": .red,
"quarry blast": .yellow,
]
let defaultColor = NSColor.blue
layer.circleColor = NSExpression(
format: "TERNARY(FUNCTION(%@, 'valueForKeyPath:', type) != nil, FUNCTION(%@, 'valueForKeyPath:', type), %@)",
colors, colors, defaultColor)
```
![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
### 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`.
```swift
layer.circleRadius = NSExpression(forKeyPath: "mag")
```
![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)

View File

@@ -20,7 +20,7 @@ custom_categories:
- Working with GeoJSON Data
- Predicates and Expressions
- For Style Authors
- Using Style Functions at Runtime
- Style Layers Using Expressions
- Tile URL Templates
- Info.plist Keys
- name: Maps