Skip to content

Commit

Permalink
Expression docs edits and amendments (mapbox#5511)
Browse files Browse the repository at this point in the history
Refs https://github.com/mapbox/mapbox-gl-js/issues/5500

 - Add short introductory explainer for basic expression syntax
 - Add explanation after "Property expression" and "Zoom-only"
 expression examples
 - Add brief explanation of 'input', 'labels', 'stops' components of
 match and curve expressions
 - Add section intro for `Decision` and `Types` sections
 - Use more explicit type placeholders, `OutputType` rather than `T`
  • Loading branch information
anandthakker authored Oct 24, 2017
1 parent 351cd76 commit 9af0c49
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 57 deletions.
40 changes: 20 additions & 20 deletions docs/style-spec/_generate/expression-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,38 +56,38 @@ const types = {
parameters: ['value', { repeat: [ 'fallback: value' ] }]
}],
at: [{
type: 'T',
type: 'ItemType',
parameters: ['number', 'array']
}],
case: [{
type: 'T',
parameters: [{ repeat: ['condition: boolean', 'output: T'] }, 'default: T']
type: 'OutputType',
parameters: [{ repeat: ['condition: boolean', 'output: OutputType'] }, 'default: OutputType']
}],
coalesce: [{
type: 'T',
parameters: [{repeat: 'T'}]
type: 'OutputType',
parameters: [{repeat: ['OutputType']}]
}],
curve: [{
type: 'T',
type: 'OutputType',
parameters: [
'input: value',
'input: number',
'["step"]',
'stop_output_0: T',
'stop_input_1: number, stop_output_1: T',
'stop_input_n: number, stop_output_n: T, ...'
'stop_output_0: OutputType',
'stop_input_1: number, stop_output_1: OutputType',
'stop_input_n: number, stop_output_n: OutputType, ...'
]
}, {
type: 'T (number, array<number>, or Color)',
type: 'OutputType (number, array<number>, or Color)',
parameters: [
'input: number',
'interpolation: ["linear"] | ["exponential", base] | ["cubic-bezier", x1, y1, x2, y2 ]',
'stop_input_1: number, stop_output_1: T',
'stop_input_n: number, stop_output_n: T, ...'
'stop_input_1: number, stop_output_1: OutputType',
'stop_input_n: number, stop_output_n: OutputType, ...'
]
}],
let: [{
type: 'T',
parameters: [{ repeat: ['string (alphanumeric literal)', 'any']}, 'T']
type: 'OutputType',
parameters: [{ repeat: ['string (alphanumeric literal)', 'any']}, 'OutputType']
}],
literal: [{
type: 'array<T, N>',
Expand All @@ -97,12 +97,12 @@ const types = {
parameters: ['{...} (JSON object literal)']
}],
match: [{
type: 'U',
type: 'OutputType',
parameters: [
'input: T (number or string)',
'label_1: T | [T, T, ...], output_1: U',
'label_n: T | [T, T, ...], output_n: U, ...',
'default: U'
'input: InputType (number or string)',
'label_1: InputType | [InputType, InputType, ...], output_1: OutputType',
'label_n: InputType | [InputType, InputType, ...], output_n: OutputType, ...',
'default: OutputType'
]
}],
var: [{
Expand Down
2 changes: 1 addition & 1 deletion docs/style-spec/_generate/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function renderParams (params, maxLength) {
result.push(t);
} else if (t.repeat) {
const repeated = renderParams(t.repeat, Infinity);
result.push(`${repeated.slice(2)}, (${repeated.slice(2)}${repeated}, ...)`);
result.push(`${repeated.slice(2)}${repeated}, ...`);
}
}

Expand Down
88 changes: 65 additions & 23 deletions docs/style-spec/_generate/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,8 @@ <h2><a href='#layers' title='link to layers'>Layers</a></h2>
<code>"paint"</code> object. Changes to a paint property are cheap and happen synchronously.
</p>
<p class='space-bottom4 quiet small'>Key:
<a class='icon smooth-ramp quiet micro space-right inline' href='#types-expression-curve' title='Supports all interpolation types'>supports all interpolation types</a>
<a class='icon step-ramp quiet micro space-right inline' href='#types-expression-curve' title='Supports step interpolation only'>supports "step" interpolation only</a>
<a class='icon smooth-ramp quiet micro space-right inline' href='#expressions-curve' title='Supports all interpolation types'>supports all interpolation types</a>
<a class='icon step-ramp quiet micro space-right inline' href='#expressions-curve' title='Supports step interpolation only'>supports "step" interpolation only</a>
<span class='icon opacity quiet micro space-right inline' title='Transitionable'>transitionable</span>
</p>

Expand Down Expand Up @@ -876,6 +876,16 @@ <h2><a href='#expressions' title='link to expressions'>Expressions</a></h2>
</tbody>
</table>

<h3>Syntax</h3>
<p>Mapbox GL JS expressions uses a Lisp-like syntax, represented using JSON arrays. Expressions follow this format:</p>
<div class='col12 space-bottom'>
{% highlight js %}
[expression_name, argument_0, argurment_1, ...]
{% endhighlight %}
</div>
<p>The <code>expression_name</code> is the name of a specific expression, e.g. <a href="#expressions-*"><code>'*'</code></a> or <a href="#expressions-case"><code>'case'</code></a>. Each <code>argument_i</code> is either a literal value (a string, number, boolean, or <code>null</code>), or else another expression in the array form above.</p>

<h3>Property expressions</h3>
<p>A <a id="property-expression" class="anchor"></a><strong>property expression</strong> is any expression defined using an expression that includes a reference to feature property data. Property expressions allow the appearance of a feature to change with its properties. They can be used to visually differentate types of features within the same layer or create data visualizations.</p>

<h4>Example: a property expression</h4>
Expand All @@ -894,9 +904,29 @@ <h4>Example: a property expression</h4>
{% endhighlight %}
</div>

<p>In this example <code>["get", "temperature"]</code> uses <a href="#expressions-get"><code>'get'</code></a> to look up the <code>"temperature"</code> property of each feature. That value is then used in the <a href="#expressions-rgb"><code>'rgb'</code></a> expression to define a color in terms of its red, green, and blue components.

<h3>Zoom expressions</h3>
<p>A <a id="zoom-expression" class="anchor"></a><strong>zoom expression</strong> is any expression defined using an expression that includes <code>["zoom"]</code>. Such expressions allow the the appearance of a layer to change with the map’s zoom level. Zoom expressions can be used to create the illusion of depth and control data density.</p>

<p>Any zoom expression used in a layout or paint property must be of the following form (see <a href="#types-expression-curve">curves</a> for more details):</p>
<h4>Example: a zoom-only expression</h4>
<div class='col12 space-bottom'>
{% highlight js %}
{
"circle-radius": [
"curve", ["linear"], ["zoom"],
// zoom is 5 (or less) -> circle radius will be 1px
5, 1,
// zoom is 10 (or greater) -> circle radius will be 2px
10, 2
]
}
{% endhighlight %}
</div>

<p>This example uses a <a href="#expressions-curve"><code>'curve'</code></a> expression to define a linear relationship between zoom level and circle size using a set of input-output pairs. In this case, the expression indicates that the circle radius should be 1 pixel when the zoom level is 5, and 2 pixels when the zoom is 10. (See <a href="#expressions-curve"><code>the 'curve' documentation</code></a> for more details.)

<p>Note that any zoom expression used in a layout or paint property must be of the following form:</p>

<div class='col12 space-bottom'>
{% highlight js %}
Expand All @@ -916,22 +946,7 @@ <h4>Example: a property expression</h4>
{% endhighlight %}
</div>

<p>That is, in layout or paint properties, <code>["zoom"]</code> may only appear as the input to an outer <a href="#types-expression-curve">curve</a>, and may not appear anywhere else in the expression.</p>

<h4>Example: a zoom-only expression</h4>
<div class='col12 space-bottom'>
{% highlight js %}
{
"circle-radius": [
"curve", ["linear"], ["zoom"],
// zoom is 5 (or less) -> circle radius will be 1px
5, 1,
// zoom is 10 (or greater) -> circle radius will be 2px
10, 2
]
}
{% endhighlight %}
</div>
<p>That is, in layout or paint properties, <code>["zoom"]</code> may only appear as the input to an outer <a href="#expressions-curve">curve</a>, and may not appear anywhere else in the expression.</p>

<h4>Example: a zoom-and-property expression</h4>
<p>Combining zoom and property expressions allows a layer's or appearance to change with both the zoom level <em>and</em> each feature's properties.</p>
Expand All @@ -941,11 +956,9 @@ <h4>Example: a zoom-and-property expression</h4>
{
"circle-radius": [
"curve", ["linear"], ["zoom"],
// zoom is 0 and "rating" is 0 -> circle radius will be 0px
// zoom is 0 and "rating" is 5 -> circle radius will be 5px
// when zoom is 0, set each feature's circle radius to the value of its "rating" property
0, ["get", "rating"],
// zoom is 10 and "rating" is 0 -> circle radius will be 4 * 0 = 0px
// zoom is 10 and "rating" is 5 -> circle radius will be 4 * 5 = 20px
// when zoom is 0, set each feature's circle radius to four times the value of its "rating" property
10, ["*", 4, ["get", "rating"]]
]
}
Expand All @@ -954,10 +967,39 @@ <h4>Example: a zoom-and-property expression</h4>

<p>There is an important difference between <em>layout</em> and <em>paint</em> properties in the way that expressions are evaluated. Paint properties are re-evaluated whenever the zoom level changes, even fractionally. The rendered value of a paint property will change, for example, as the map moves between zoom levels <code>4.1</code> and <code>4.6</code>. Layout properties, on the other hand, are evaluated only once for each integer zoom level. To continue the prior example: the rendering of a layout property will <em>not</em> change between zoom levels <code>4.1</code> and <code>4.6</code>, no matter what stops are specified; but at zoom level <code>5</code>, the expression will be re-evaluated, and the property's rendered value will change. (You can include fractional zoom levels in a layout property zoom expression, and it will affect the generated values; but, still, the rendering will only change at integer zoom levels.)</p>

<h3>Expression reference</h3>

<div class='keyline-all fill-white'>
<% for (var group of groupedExpressions) { %>
<div class='pad2 keyline-bottom'>
<h4 style='font-size: 100%' class="pad2x"><a id='expressions-<%= group.slug %>' href='#expressions-<%= group.slug %>'><%= group.name %></a></h4>

<% if (group.slug === 'types') { %>
<p>
The expressions in this section are provided for the purpose of
testing for and converting between different data types like strings,
numbers, and boolean values.</p>
<p>Often, such tests and conversions are
unnecessary, but they may be necessary in some expressions where the
type of a certain sub-expression is ambiguous. They can also be
useful in cases where your feature data has inconsistent types; for
example, you could use <code>to-number</code> to make sure that
values like <code>"1.5"</code> (instead of <code>1.5</code>) are
treated as numeric values.
</p>
<% } else if (group.slug === 'decision') { %>
<p>
The expressions in this section can be used to add conditional
logic to your styles. For example, the <a
href="#expressions-case"><code>'case'</code></a> expression
provides basic "if/then/else" logic, and <a
href="#expressions-match"><code>'match'</code></a> allows you to
map specific values of an input expression to different output
expressions.
</p>

<% } %>

<% for (var expression of group.expressions) { %>
<%= renderExpression(expression) %>
<% } %>
Expand Down
26 changes: 13 additions & 13 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -1863,27 +1863,27 @@
"group": "Types"
},
"array": {
"doc": "Asserts that the input is an array (optinally with a specific item type and length).",
"doc": "Asserts that the input is an array (optionally with a specific item type and length). If, when the input expression is evaluated, it is not of the asserted type, then this assertion will cause the whole expression to be aborted.",
"group": "Types"
},
"at": {
"doc": "Retrieves an item from an array.",
"group": "Lookup"
},
"case": {
"doc": "Yields the value of the first output expression whose corresponding test evaluates to true.",
"doc": "Selects the first output whose corresponding test condition evaluates to true.",
"group": "Decision"
},
"match": {
"doc": "Yields the output value whose label value matches the input, or the fallback value if no match is found.",
"doc": "Selects the output whose label value matches the input value, or the fallback value if no match is found. The `input` can be any string or number expression (e.g. `[\"get\", \"building_type\"]`). Each label can either be a single literal value or an array of values.",
"group": "Decision"
},
"coalesce": {
"doc": "Evaluates each expression in turn until the first non-null value is obtained, and returns that value.",
"group": "Decision"
},
"curve": {
"doc": "Interpolates an output value based on a set of input/output pairs using the specified interpolation strategy. A set of one input and one output value is known as a \"stop.\" Stop inputs must be numeric literals in strictly ascending order.\n\nInterpolation types:\n- `[\"step\"]`: returns the output value of the stop just less than the input , or the first input if the input is less than the first stop.\n- `[\"linear\"]`: interpolates linearly between the pair of stops just less than and just greater than the input .\n- `[\"exponential\", base]`: interpolates exponentially between the stops just less than and just greater than the input . `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y2, x2, y2]`: interpolates using the cubic bezier curve defined by the given control points\n\nNote that interpolation types other that \"step\" are only supported when the output type is `number`, `array<number>`, or `Color`.",
"doc": "Interpolates an output value based on a set of input/output pairs using the specified interpolation strategy. A set of one input and one output value is known as a \"stop.\" \n\nThe `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`). Stop inputs must be numeric literals in strictly ascending order.\n\nInterpolation types:\n- `[\"step\"]`: returns the output value of the stop just less than the input , or the first input if the input is less than the first stop.\n- `[\"linear\"]`: interpolates linearly between the pair of stops just less than and just greater than the input .\n- `[\"exponential\", base]`: interpolates exponentially between the stops just less than and just greater than the input . `base` controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.\n- `[\"cubic-bezier\", x1, y2, x2, y2]`: interpolates using the cubic bezier curve defined by the given control points\n\nNote that interpolation types other that \"step\" are only supported when the output type is `number`, `array<number>`, or `Color`.",
"group": "Ramps, scales, curves"
},
"ln2": {
Expand All @@ -1903,39 +1903,39 @@
"group": "Types"
},
"string": {
"doc": "Asserts that the input value is a String. If multiple values are provided, each one is evaluated in order until a String value is obtained.",
"doc": "Asserts that the input value is a string. If multiple values are provided, each one is evaluated in order until a string value is obtained. If, when the last provided input is evaluated, it is not of the asserted type, then this assertion will cause the whole expression to be aborted.",
"group": "Types"
},
"number": {
"doc": "Asserts that the input value is a Number. If multiple values are provided, each one is evaluated in order until a Number value is obtained.",
"doc": "Asserts that the input value is a number. If multiple values are provided, each one is evaluated in order until a number value is obtained. If, when the last provided input is evaluated, it is not a number, then this assertion will cause the whole expression to be aborted.",
"group": "Types"
},
"boolean": {
"doc": "Asserts that the input value is a Boolean. If multiple values are provided, each one is evaluated in order until a Boolean value is obtained.",
"doc": "Asserts that the input value is a boolean. If multiple values are provided, each one is evaluated in order until a boolean value is obtained. If, when the last provided input is evaluated, it is not of the asserted type, then this assertion will cause the whole expression to be aborted.",
"group": "Types"
},
"object": {
"doc": "Asserts that the input value is an Objects.",
"group": "Types"
},
"to-string": {
"doc": "Coerces the input value to a String.",
"doc": "Coerces the input value to a string.",
"group": "Types"
},
"to-number": {
"doc": "Coerces the input value to a Number, if possible. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.",
"doc": "Coerces the input value to a number, if possible. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.",
"group": "Types"
},
"to-boolean": {
"doc": "Coerces the input value to a Boolean.",
"doc": "Coerces the input value to a boolean.",
"group": "Types"
},
"to-rgba": {
"doc": "Returns the an array of the given color's r, g, b, a components.",
"group": "Color"
},
"to-color": {
"doc": "Coerces the input value to a Color. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.",
"doc": "Coerces the input value to a color. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.",
"group": "Types"
},
"rgb": {
Expand All @@ -1947,11 +1947,11 @@
"group": "Color"
},
"get": {
"doc": "Retrieves an object property value. If it's not provided, the object argument defaults to [\"properties\"]. Returns null if the requested property is missing from the object.",
"doc": "Retrieves a property value from the current feature's properties (or from another object if one is provided). Returns null if the requested property is missing.",
"group": "Lookup"
},
"has": {
"doc": "Tests for the presence of an object property value. If it's not provided, the object argument defaults to [\"properties\"].",
"doc": "Tests for the presence of an property value in the current featur's properties (or from another object if one is provided).",
"group": "Lookup"
},
"length": {
Expand Down

0 comments on commit 9af0c49

Please sign in to comment.