Open MikkoSteerpath opened 9 months ago
I see the need, but I wonder if just catching the data as it's being parsed might be simpler. Extending the style spec to incorporate out of band functions is probably more logical in Javascript than it is on mobile. Where messing with the data after it's been parsed probably meshes better on both major toolkits.
I agree with @sjg-wdw that it would be better to add way for the user to get all attributes of a point/line/polygon after parsing, and then being able to freely modify the attributes with a user-defined function. This means one can update/add/delete properties...
I can see that modifying the attribute data could be quite interesting as well, it has some challenges such as how to define which objects to modify, how to update the previous modification when the users locale changes and how to track when the data with the relevant properties is loaded.
In particular considering clusters, where the data is generated on the fly using the style rules, I don't think this approach will be easy to implement but feel free to prove me wrong.
As for this approach being less logical on natives I would like to give some counter arguments:
OnStyleImageMissingListener
when ever an image is missing. Granted, the listener pattern is not used for providing data, as there can be typically multiple java listeners for events, but in other non-maplibre libraries setting providers is a common pattern in addition to the listener pattern.In summary, the evaluation inside maplibre is already dynamic and takes into account many global and runtime variables, the handler for custom code would just be another one. I would also argue that from a developer/library user point of view, setting a handler/delegate/provider is a common pattern on all platform.
I guess the next question, assuming this is possible, might be the API signature.
You mentioned the javascript API, that can be very "loose", what would be the API for the other platforms, given that they are more strict in terms of types etc?
Would sending Object[]
as the parameters in Android makes sense?
For Native I'd run this by the team and see what they think. Might be something really easy to do, but at least they're familiar with that part of the system.
I agree with @sjg-wdw that it would be better to add way for the user to get all attributes of a point/line/polygon after parsing, and then being able to freely modify the attributes with a user-defined function. This means one can update/add/delete properties...
It would be great if we could modify the GeoJSON shapes too, in addition to properties.
This got me thinking about a potentially related use case that I am going to have to support soon: creating ellipses on the fly. Basically I have a set of points with semi-major and semi-minor axes, but I need to render them as ellipses (or polygonal approximations of them, given GeoJSON constraints). These will be delivered to the front-end as features in vector tiles. I have control over the vector tiles, but the size of the (potentially many) features coming across the wire would dramatically increase if I have to create the polygon approximations on the server. It would be ideal if I could handle this transformation client-side.
This idea has been explored before in https://github.com/maplibre/maplibre-gl-js/issues/1295 and there is a bit of a summary in a comment about the challenges.
@neodescis if we allow geometry-on-the-fly we could also do bezier curves for rounded lines...
I was referenced here to discuss a feature request for MapLibre GL JS, written in https://github.com/maplibre/maplibre-gl-js/issues/4964.
In summary, I would like to have a way to specify (global) map state that can somehow be used in the style. The use case is to define a map theme, and allow the style to dynamically change the rendering of the style based on the chosen user theme.
My feature request would be satisfied if I could define custom functions to be used in the style specification, such that I can make some values in the style dynamic based on the map configuration in the client application.
If it makes it easier to discuss, I could also write a separate proposal just for the MapLibre style specification to allow defining and using map state in a style. There is a proposal for syntax in https://github.com/maplibre/maplibre-gl-js/issues/4964, similar to how feature-state
can be used.
Seeing that this proposal has been open for more than 10 months, what is the best way to move this forward?
I'm pretty sure I've seen something similar related to dark theme and light theme that can benefit from "global" variables, but I can't find it... :-( You can open a separate design proposal for the spec for something that you think can facilitate for this. Other approach that I've seen people do it generate the style.json from parameters instead of using a style.json file, this obviously has all the flexibility of any programming language you decide to use. Generating the json is fairly straightforward.
Thanks, I split off this proposal into https://github.com/maplibre/maplibre-style-spec/issues/886 where it can be discussed in depth.
Design Proposal: External style functions
Motivation
Some things, such as converting unix timestamps to be shown in a human readable way in for the end users locale can be extremely complex to accomplish using style expressions. Another case is implementing for example thousands separator for numbers. These things depend on the app user's locale settings and potentially even timezones.
While it could be theoretically possible to extend the style specification with every helper function app developers would need, or write extremely long and complex expressions, in many cases it would be much more convenient for everyone if there was a standard way for applications to register their own utility functions that could be used from the style.
Another example in the past was that I would have liked to use regular expressions in the string handling in the style. This proposal and js implementation were never accepted, because regular expression engines are different on different platforms, and there was a risk of introducing cross platform incompatibilities in the spec (iirc). Meanwhile, as a developer, I had no reasonable route forward (except forking the entire project, on 3 platforms), even if I was willing to accept minor differences between platforms.
The feature could also be used for prototyping new functionality and showcasing possibilities of functionality that could be included in the specification. For example one could hack together a function which returns the
icon-offset
based on the camera transformation matrix, to correctly place the POI in 3d space, even though this feature is not yet available.If the value of the parameter could be updated based on animation frames, it would even be possible to make icons bounce by returning a different value for
icon-offset
each frame.Proposed Change
Extend the style specification with a new expressions
exec
which takes two parameters, a string type parameter for the name of the external function to execute, and an array type parameter with the the parameters passed to the external function.Examples:
["exec", "formatDate", [ ["get", "timestamp"], ["get", "poi_timezone"]] ]
API Modifications
Define a new type that is a catch all for all
exec
functions.Add a new API function to the
maplibre
programmatic api to set and unset the catch all handler.The new types and API could be:
Internally the engine would keep a map of the exec functions used, the previous value and
Migration Plan and Compatibility
This does not replace existing functionality but allows easy prototyping of new style expressions prior to rolling them into the spec.
Rejected Alternatives
The reason for a catch all function rather than registering individual handlers are:
registerFunction(funcName, funcImpl)
on top of this single handler, but if maplibre implements this friendlier api, it is not possible to implement a catchall api.The reason for passing params in a single array rather than passing params indivdually:
The reason why the catch all cannot just return the value:
until-animattion-frame
and then once the fetch has resolved, the final value can be returned with a cacheHint offorever
.