-
-
Notifications
You must be signed in to change notification settings - Fork 93
Description
Design Proposal: Allow creating arrays with expressions
Motivation
A number of layer properties take arrays as their values. For example, see text-offset and text-variable-anchor-offset. The only way to produce an array in the existing expression system is to either get it from a feature with get, or create it with literal. Neither of these allow calculating the elements of the array with math expressions, though you can select between different literal arrays or properties with conditional match / case expressions.
For arrays of just numbers (let's call them vectors), one can work around this for certain use cases by interpolating between multiple vectors, and doing your math on the interpolation input. MapLibre Compose does this workaround to simulate multiplying the magnitude of a text-offset (to convert between SP and EM units).
For arrays containing other types, not even that workaround is available (example: text-variable-anchor-offset). MapLibre Compose was unable to work around this.
Some use cases that could benefit from expressions in array elements:
- x and y components of an offset based on different feature properties (see this old "bug" report: Bug: literal expression cannot parse ["get", ..] #545)
- calculating an offset with
sinandcos, such as to offset in the direction of some angle (see this StackOverflow question: https://stackoverflow.com/questions/52715411/mapbox-gl-data-driven-styling-for-text-offset) - scaling the magnitude of an offset, for example sargunv/maplibre-compose scales properties provided in EM units in order to support Compose's TextUnit type which can be in either SP or EM units. The scalar could be an expression itself (example: dividing SP
text-max-widthbytext-sizeto produce EMtext-max-width), so the multiplication must be done in expression evaluation, not pre processing. This works great for single number properties but runs into problems with array properties (text-offsetandtext-variable-anchor-offset).
Proposed Change
I propose a new function in the style spec, [], that takes any number of expression arguments and returns an array of all those arguments. The syntax would look like:
{
"text-offset": ["[]", ["get", "label_x"], ["get", "label_y"]]
}This is in contrast to literal, which does not evaluate its arguments as expressions.
API Modifications
Just the new function []. Also perhaps a similar function {} for objects.
Migration Plan and Compatibility
No migration needed for the main proposal; this is new functionality.
Some alternatives in the "Rejected Alternatives" section below are backwards incompatible and would need a migration if they're selected.
Rejected Alternatives
- Update
literalto evaluate its elements as expressions: this would not be backwards compatible in cases of nested arrays. The only place in the style spec that uses nested arrays istext-variable-anchor-offset, whose value looks like["top", [1, 2], "bottom", [3, 4]]. If it weren't for that backwards incompatibility, I'd prefer this alternative. - Add an optional boolean parameter to
literalto evaluate its elements as expressions: I think this is confusing to the reader, as it's not immediately apparent whattrueorfalsemean when reading a style spec.Also, my impression (haven't read the code so perhaps wrong) is thatliteralis part of the expression parser, and not part of expression evaluation like other functions. - Just evaluate elements of any array that doesn't start with a string: If you want the first element of such an array to be a literal string, you'd have to escape it with something like
concat. Feels inconsistent. - Double brackets syntax like
[[ ["get", "x"], ["get", "y"] ]]: Syntax is ambiguous in case of single element arrays where the first element is an array of strings (looks like a function call). Would need some sort of escape like the above alternative. - New function as proposed, but alternative names: I considered various alternative names before landing on
[]:- Looking for existing functions in the spec that take some input and return a new object, we have
collatorthat creates a Collator,formatthat creates a Formatted , andimagethat creates a ResolvedImage. So, the natural convention might bearrayto create an Array, but that function name is already taken for a function to assert that the input is an array (similar tostring,number,boolean, andobject) - We have
to-color,to-string, etc, which all take a single value and convert it to the target type (some with fallbacks). The nameto-arrayis available, but the convention doesn’t quite match what we want. rgbandrgbaare closest in concept to what we want (evaluate some arguments as expressions, construct a result object from it) but of course that naming convention doesn't have an obvious analogue for arbitrary arrays.- So, I think a new naming convention is necessary. Alternative:
new-arrayis pretty clear in intent, but could get visually noisy in case of nested arrays. I don't really dislike this, just like[]a bit better as it would take less of the reader's focus away from the content of the array. - So, I landed on
[].
- Looking for existing functions in the spec that take some input and return a new object, we have
EDIT: additional alternatives as considered in the issue thread
- New functions
pointandpaddingspecifically for those types: Solves the issue for some properties (text-offset) but not others (text-variable-anchor-offset,line-dasharray, etc). I think it's worthwhile to have those but we'd still need[]in addition to that.