Skip to content

Design Proposal: Allow creating arrays with expressions #950

@sargunv

Description

@sargunv

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:

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 literal to 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 is text-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 literal to evaluate its elements as expressions: I think this is confusing to the reader, as it's not immediately apparent what true or false mean when reading a style spec. Also, my impression (haven't read the code so perhaps wrong) is that literal is 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 collator that creates a Collator, format that creates a Formatted , and image that creates a ResolvedImage. So, the natural convention might be array to create an Array, but that function name is already taken for a function to assert that the input is an array (similar to string, number, boolean, and object)
    • We have to-color, to-string, etc, which all take a single value and convert it to the target type (some with fallbacks). The name to-array is available, but the convention doesn’t quite match what we want.
    • rgb and rgba are 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-array is 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 [].

EDIT: additional alternatives as considered in the issue thread

  • New functions point and padding specifically 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions