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
Support nested properties with the 'get' expression #15813
Conversation
📦 Preview the website for this branch here: https://deploy-preview-15813--ol-site.netlify.app/. |
Nice! Thanks for taking this on. I realize this may sound stubborn, but I’d like to see a real example where type hints are needed before introducing another operator that we might just remove later. By “a real example” I mean an expression used in a style object. |
@tschaub Actually it's the ambiguity of size and scale properties, which use |
See #15818. I'll rebase and modify this pull request accordingly once that's in. And we could also make it so this no longer is a breaking change, by ignoring object literal keys at the end of the chain when the previous value is not an |
Tests will pass once #15818 is merged and this pull request is rebased. |
003b6ba
to
d0f36b2
Compare
Rebased again, without a merge commit. Thanks for any review. |
src/ol/expr/cpu.js
Outdated
const args = expression.args; | ||
let value = context.properties[name]; | ||
for (let i = 1, ii = args.length; i < ii; ++i) { | ||
if (typeof value !== 'object') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we continue on null
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I‘ll add a null check tomorrow.
src/ol/expr/cpu.js
Outdated
if (value === null || typeof value !== 'object') { | ||
break; | ||
} | ||
const keyExpression = /** @type {LiteralExpression} */ (args[i]); | ||
const key = /** @type {string|number} */ (keyExpression.value); | ||
if (typeof key !== 'number' && Array.isArray(value)) { | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like with properties {"a": 1}
and ["get", "a", "b"]
it would currently return 1
?
Same for properties {"a": [1]}
and ["get", "a", "b"]
would return [1]
?
Should this return undefined
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning undefined
in such cases was my initial approach, but then I thought it might be useful to allow data structures with variable nesting depth - and be backwards compatible with the removed type hint as last argument.
If people think there is no practical use of this, and it rather adds confusion, I'll be happy to go back to the initial approach and make this a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should move on from the type hint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, updated. Now it's a breaking change.
'Expected the attribute name of a get operation to be a string', | ||
); | ||
} | ||
context.properties.add(String(arg.value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We aren't leveraging the properties
of the parsing context right now in Canvas, but here is the idea. As we parse style expressions, we build up a lookup of all the data that is needed from a feature (feature id, geometry, properties). Then during the evaluation phase, we could supply a minimal set of data in the evaluation context.
Currently, we don't do this in Canvas. Instead we add all the feature properties to the evaluation context. If we were passing the data around to/from workers, we might instead want to encode a subset of the data for that purpose.
In the WebGL renderer, the properties
from the parsing context is passed to the evaluation context and then used when building shaders and uploading attributes to the gpu (based on a limited set of feature properties instead of all feature properties).
To support working with a subset of feature properties in the Canvas renderer in the future, we want to keep track of these "property paths" that are used in style expressions. Now that we support deeply nested paths, instead of the parsing context properties being Set<string>
it could be Array<Array<string>>
(where a single entry might be ['path', 'to', 'property']
. Since deeply nested paths aren't supported in WebGL yet, this would break things on that side. For now, since we aren't using the parsing context properties
in Canvas, we could just store a dot delimited path to all the properties (e.g. 'path.to.property'
). This isn't the best long-term solution if people do crazy things like put dots in their property names, but it could be fine for now.
The tl;dr is that we don't want to add 'path'
, 'to'
, and 'property'
to this context.properties
set. Instead we could add 'path.to.property'
(and then later change the type when we want to fully support nested properties in both Canvas and WebGL).
Thanks for your work on this, @ahocevar. It feels like a simpler and more functional solution now (together with #15818). Hope my comment above makes sense. Since we aren't currently using the parsing context |
Thanks for the thorough review, @tschaub. Your suggested change sounds interesting. I think it makes sense as a follow-up change at a later time, and maybe it also makes sense to solve the problems a dot delimited properties path would impose before doing that. |
Great feature for flat styles, thanks for this! |
I'm working on support for accessing nested properties in WebGL here: main...tschaub:openlayers:nested (this reworks the property handling as described above). |
Fixes #15505.
This pull request implements what was discussed in #15505.
Examples for the new features:
Nested properties:
Nested properties with array:
This change is backwards incompatible, the second argument to the
get
expression is no longer interpreted as type hint.