-
-
Notifications
You must be signed in to change notification settings - Fork 739
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
Expression engine extension points #1295
Comments
Can you share the style that you used for the example pictures above? |
Assuming this is really needed (judging by the upstream issue mapbox/mapbox-gl-js#9462), there are several paths to implement this:
|
vector source style
geojson source style
|
I would like to be able to register an expression extension so instead of "layout": {
"text-field": [
// text-field value is computed: coalesce(get("bytes"), "")
"coalesce", [ "get", "bytes" ], ""
],
"text-size": 14
} I could do something like the below, where "layout": {
"text-field": [
// text-field value is computed: my_custom(coalesce(get("bytes"), ""))
"my_custom": [
"coalesce", [ "get", "bytes" ], ""
],
],
"text-size": 14
} |
@nreese thx for the example. I agree custom expression functions would be the most flexible solution. It might also put too much maintenance burden compared to a dedicated text transformation function. Compare the API requirement for an expression extension that can do anything (i.e. has execution context and supports all data types as inputs and outputs), vs a text-only function with no context. I will let @HarelM speak about the API complexities/burderns, as I might be totally wrong here :) My usage example with a custom text function: "layout": {
"text-field": [
// text_custom_transform("my_custom", coalesce(get("bytes"), ""))
// note that the second value could be "{name_en}" instead of an array
"text-custom-transform": [
"my-custom",
["coalesce", [ "get", "bytes" ], ""],
]
],
"text-size": 14
} |
There is this concept of runtime styling versus the style.json approach. I don't quite understand it but maybe we should be careful to not mix the two. Can someone comment on the declarative thing? Sorry if I am a bit confused... |
The point is that if we introduce this feature, the style file alone will not tell you how the map looks like. You need style plus callback code. This is not necessarily bad, but we have to have a plan how this would look like on the native project... |
@nreese can you maybe share some more use case examples? I tend to say that we should rather expand the expressions than introduce a more general callback registration functionality. The cool thing about the current way of things is that you can copy-paste style sections. This works because they are sort of self-describing. For your example above. Turning |
I did not know that. Always thought that you can do something like |
Per @1ec5 there was also a relevant discussion on the native side in mapbox/mapbox-gl-native#7860 |
For my specific use case in Kibana, there are many formatters so it would not be trivial to solve them all with expression code. There are durations, percentages, static look-up tables, and more. For the specific example of displaying a value as bytes, |
@wipfli This was addressed as a "Design Alternatives" "Custom expression logic could be avoided by performing the logic at vector tile generation. However, this is not always possible. In the use case above, the vector tiles are generated from the data store and logic for formatting labels is not available at the data store abstraction level." The short answer being, it is not always possible to add formatted data to the tile generation process. |
In yesterday's technical steering committee meeting, the following came up: Americana's creative usage of the missing style image was mentioned by @mojodna as a way to get run-time callbacks from the style. |
The style image callback runs well after style evaluation: osm-americana/openstreetmap-americana#243 (comment). By contrast, anything done as part of evaluating an expression has to run in a Web worker (multiple Web workers), which complicates things a bit. Some languages like Swift make it possible to prevent unintended capturing of variables outside of scope, but I don’t know if that’s the case for TypeScript. |
Seems like this got a bit stalled. Another use case where I think we do not have a good answer is a mix of static and dynamic data. For example, let's say there are millions of data points with metadata. Each point can be styled based to that metadata, and have some popup which shows that metadata on a click/mouseover. But now let's say there is some dynamic "current state" -- something that rapidly changes each second. I could subscribe a stream of Ideally, I would love to just add a styling rule like |
Doesn't setFeatureState solves this? |
@HarelM sorry for not replying earlier - yes, the feature state concept fully solves it, thanks! |
Hi. Joining the conversation as I'm facing a use-case related to this feature. Documented here #2077 Basically, I'm trying to color some symbol icons based on the distance relative to a GeoJSON point. The As stated in #2077; "significant bundle size increase" + "already implemented" sounds for a perfect "plugin" fit. I think this is the perfect use-case because its both :
In other words, with an expression "extension" mecanism; this would be roughly implementable in 1 hour — and would even be style-spec compliant; but in the current state of art this is very tough to implement. |
Moved from: #2077 (comment) |
To understand what's needed for a "distance"-plugin, I have patched a rudimentary implementation of As you can see, the implementation of It would be quite easy to naively add a hook Considerations for a clean implementation1. Encapsulation of context accessThere already is a We can easily add a expression 2. Providing a hook for adding native expression typesWith "native" I mean the function will receive evaluated values of JS base type as arguments and has no access to the enviroment or other parts of the library at all.
This way we can implement the distance computation as a native expression type:
and use it:
3. Providing a hook for adding "macro expression" typesUnfortunately this way we cannot provide the To fix this, we could add "macro expressions", which would not be added to the list of available expressions, but used while compiling an expression; may be this way (I'm not sure about the syntax):
Resulting in the expression
being replaced by and compiled as
Heureka. Addendum: the literal object {type: ...} has to be written as ['literal', {type: ...}] |
Can you better clarify the difference between the two APIs? From my point of view there should be a single API registerCustomExpression or something similar. |
access to the evaluation context
Because you have to use the MapLibre library source while creating the plugin and use version numbers to verify the compatibility between a plugin and the library. Which is cumbersome and overkill if the only thing you want is to define a ['replace-all']. Otherwise, you would have to freeze the EvaluationContext class, which is something I certainly do not want. register native expressions
The calls have nothing in common and have completely different signatures. register macro expressionsI made up the second one only to allow implementing the Currently, I see no other usage. In fact, if it were only for me, I would permanently add the not easyUnfortunately, I have to take back the “easily”. The expressions are evaluated both in the UI process and in the Worker processes; for this purpose, the buildin expressions are marshalled and included in the Worker blob-uri. If after creating the Worker you want to add an expression, you have to both store it in the UI process and send a serialization of it as a message to the workers, where a message handler would add it to the buildin expressions. For the same reason, custom expression also have no access to static variables, i.e. lookup tables; the possibilities are therefore very limited. Maybe they are not worth the effort at all. crude implementationNevertheless, to have a basis for further discussions, I've patched the current maplibre-gl.js to allow custom expressions as maplibre-gl+distance+custom-expressions-v1.js. It does not use the Worker message system, but I have set the library initialization on hold, and you have to explicitly initialize the library after you have defined your custom expressions. You can find a usage example as jsfiddle. This hack is for obvious reasons not intended as a base for a real implementation. |
I agree a plugin system such as one that is discussed here should have a good interface that would change rarely, like the Cordova/Ionic plugin interface/system, which holds nicely for a long time now. |
The current implementation of I suggest a small (obvious) patch, which makes the method callable multiple time. With this change, you get the internal hook to design an interface around.
|
Motivation
I work on a mapping application that allows users to configure formatters to customize how values are displayed. These formatters are javascript functions that take a value and return a string. I would like to be able to use these formatters in a maplibre expression to format values displayed as labels.
Below are 2 images. The first uses 'vector' source and displays the raw value. The second uses 'geojson' source and adds a new property to each feature that contains the value transformed into a human readable byte string. It is not possible to display formatted values with 'vector' source. Allowing expression extension points would close this feature gap and allow custom label formatting with 'vector' source.
Here are some other example of formatting functions
Beyond these, there are limitless possibilities for formatting values. Custom expressions provides an escape hatch to allow users easily fill any functionality gaps.
There are many additional use cases and further discussion can be found at mapbox/mapbox-gl-js#9462
Design Alternatives
Custom expression logic could be avoided by performing the logic at vector tile generation. However, this is not always possible. In the use case above, the vector tiles are generated from the data store and logic for formatting labels is not available at the data store abstraction level.
Users could also fall back to 'geojson' sources and add new properties to features as a work around. However, falling back to 'geojson' sources provides a much slower alternative when large data volumes are involved.
Design
Mock-Up
The below mock-up is copied from mapbox/mapbox-gl-js#9462
This is modelled after how expression functions are defined in definitions/index.js.
Note that its using the type names defined in compound_expression.js
The text was updated successfully, but these errors were encountered: