Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Proposal: Global map state #886

Open
hiddewie opened this issue Nov 4, 2024 · 3 comments
Open

Design Proposal: Global map state #886

hiddewie opened this issue Nov 4, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@hiddewie
Copy link

hiddewie commented Nov 4, 2024

Design Proposal: Global map state

Motivation

Split off from #516 (comment), and originally posted / requested in maplibre/maplibre-gl-js#4964.

The goal is to store some global map state on the Map object, which can be used in the style. This makes it easier to let the user configure the map for certain preferences, which can then dynamically be used in the style to modify the appearance of the map.

My concrete use case is supporting a map theme, where the user selects a theme and this impacts the look and feel of the web application, in particular the features on the map.

Proposed Change

Introduce a function map-state which takes a single string argument which is the key.

The return value is the value in the global map state, if present, otherwise null.

Examples:

paint: {
  'text-color': ['map-state', 'color'],
}

Handling unset key in the map state

paint: {
  'text-color': ['coalesce', ['map-state', 'color'], 'red']
}

The map state will function as a key-value dictionary, so the values in the map state contains string keys and string, numeric or boolean values. We could choose to support e.g. arrays as values as well.

The map-state function should have the same semantics as feature-state which uses data set on a feature.

API Modifications

The expression map-state will be supported.

Migration Plan and Compatibility

No migration needed, this is new functionality.

The feature should not be complex to implement in the specification and client libraries because:

  • the function feature-state already exists, and this function is similar although it takes the state from a different place
  • the map state is a new feature, so this will not conflict with existing features.

Probably the biggest complexity is managing the global map state changing its value, and recomputing the style for the impacted features on the map.

Rejected Alternatives

  • Support global map state maplibre-gl-js#4964 (comment): Changing the style dynamically during loading of the map: this makes the map style highly coupled with the client using the style. In particular, the client application has to walk every style expression, and match the expression against the replacements with the configured global values from the user configuration.
  • Support global map state maplibre-gl-js#4964 (comment): using let expressions and dynamically transforming the style. This still couples the client with the map style, and still requires walking the expressions in the map style to dynamically replace the let values with the configured values.
  • Proposal: Support registering external functions to be used in expressions in the style #516: Design proposal to add arbitrary functions to the map. That proposal would replace this proposal, but the scope is much bigger and more complex to implement than this feature.
  • Proposal: Support registering external functions to be used in expressions in the style #516 (comment): Generate a style per value of the user configuration. This is possible for global map state with a finite set of values. Once the values become non-enumerable (like numbers between 0 and 1), or many properties which can all be combined, the number of JSON styles to generate explodes. It is possible to create a server to dynamically create a style on request, taking the user parameters into account, but this requires a server creating the styles on demand, instead of putting static files in a web file server.
@sjg-wdw
Copy link

sjg-wdw commented Nov 4, 2024

This is a really interesting idea and I'd like to weigh in from the implementation side.

Having overseen the MapLibre Native upgrade this year and last, I can say that the flexibility in the style spec causes a lot of extra work at the implementation level.

The difference in perspective between the style sheets and the GPU focused real time rendering is vast. That gulf has to be bridged by the renderer implementations each time they see a style sheet and a vector tile.

The MapLibre toolkits already do this work, so that's good for MapLibre. It bad for anyone else who might want to write a new renderer and it does use a lot of extra power on mobile devices for flexibility that's rarely used.

At the root, the problem is that style sheets are meant to be flexible enough for editing and be simple enough to be parsed by the map renderer. Those two goals pull in opposite directions.

Rather than adding more flexibility into the style spec itself, I wonder if it might be time to break things apart. Add more flexibility in a high level version of the style sheet and remove flexibility from the low level.

In your example, perhaps add meta-tags to the style sheet that denote states and then process that into multiple actual style sheets. To make switching easier, name things the same, provide unique IDs, perhaps. That sort of thing.

Maybe follow the logic all the way into generating style sheets the same way we do static HTML sites these days. It would be kind of nice to just dump a block of Javascript in the middle of a style sheet to do some logic at generation time.

On the compiled side, you could imagine a header block to tell us what's in the style sheet. There's all sorts of stuff we could turn off if we didn't have to expect it and new renderers could have some idea of what they need to support to get most of the way there.

A bit more than you're looking for, I'm sure, so the short version is... maybe go in this other direction?

@hiddewie
Copy link
Author

hiddewie commented Nov 6, 2024

Thanks for the comment, interesting perspective.

We could also describe this same design proposal as a set of style input parameters, which are used to generate a concrete style. I don't have a concrete proposal of how this would look like from the API and implementation perspective.

I do agree that it seems valuable to move as much processing away from parsing the style and low level dynamic style elements, into a simple style specification that can be fed to a GPU for rendering.

@BTolputt
Copy link

Worth noting that I found this discussion whilst looking for a means by which I could dynamically alter the "text-size" & "line-width" properties (be they constant or expression) due to high vs low DPI screens in the Win32 world.

At the moment, I'm having to pre-process the style JSON and replace wildcards with the calculated DPI at style load time (requiring a style reload when the window is moved between monitors). Not great, but OK for styles I code personally, however not a reasonable option for client created styles.

Being able to access global variable representing the DPI scale factor for this in expressions would make creating these styles far more manageable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants