-
Notifications
You must be signed in to change notification settings - Fork 122
Allow predicates to test whether a feature lies within a given shape #184
Conversation
NSData *shapeData = [NSJSONSerialization dataWithJSONObject:object options:0 error:&error]; | ||
MGLShape *shape; | ||
if (shapeData && !error) { | ||
shape = [MGLShape shapeWithData:shapeData encoding:NSUTF8StringEncoding error:&error]; |
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.
This works, but there may be a more efficient way to convert this JSON-like NSDictionary structure into an MGLShape. Perhaps we could first convert the NSDictionary structure into a C++ representation of the GeoJSON object, then convert to an MGLShape.
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.
Is there any performance considerations for bridging into an C++ object then back to MGLShape
? Is this something that is going to be called frequently?
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.
In the current implementation, MGLStyleValueTransformer::PropertyExpressionEvaluator
converts the C++ mapbox::geojson
struct to a GeoJSON-formatted NSDictionary, then this method converts it to a string, then back to a C++ mapbox::geojson
struct, then finally to an MGLShape object. It is more roundabout than it should be.
This conversion process happens once when setting a layer’s predicate or paint/layout property (which can happen automatically e.g. when localizing the style). mbgl will evaluate the already-converted expression on each feature, but no SDK-side code will run as part of the rendering loop.
Ideally, mbgl would preserve the mapbox::geojson
struct so that MGLJSONObjectFromMBGLValue()
could convert it directly to an MGLShape. Absent such a change in mbgl, there are two alternative, purely SDK-side approaches:
- Convert the NSDictionary directly to C++
mapbox::geojson
structs then to MGLShape, skipping NSJSONSerialization - Parse the NSDictionary into an MGLShape, skipping C++
but I’m inclined to treat either optimization as tail work for when we know more about how this expression operator will be used. The performance characteristics could differ depending on the complexity of the passed-in GeoJSON feature.
@1ec5 will there be a developer-facing API that references |
The [NSPredicate predicateWithFormat:@"SELF IN %@", [MGLPolygon polygonWithCoodinates:coords count:4]]; But if you’re referring to something more reminiscent of the JSON expression format, it’s also possible to use
The latter syntax is already possible without this PR, now that #183 has landed. I suppose we could install an aftermarket function like |
@@ -354,6 +354,7 @@ In style specification | Method, function, or predicate type | Format string syn | |||
`case` | `+[NSExpression expressionForConditional:trueExpression:falseExpression:]` or `MGL_IF` or `+[NSExpression mgl_expressionForConditional:trueExpression:falseExpresssion:]` | `TERNARY(1 = 2, YES, NO)` or `MGL_IF(1 = 2, YES, 2 = 2, YES, NO)` | |||
`coalesce` | `mgl_coalesce:` | `mgl_coalesce({x, y, z})` | |||
`match` | `MGL_MATCH` or `+[NSExpression mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')` | |||
`within` | `NSInPredicateOperatorType` | `SELF IN %@` or `%@ CONTAINS SELF` where `%@` is an `MGLShape` |
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.
As always, if a developer is already familiar with the style specification’s JSON expression format or a similar syntax on Android, they can consult the “Information for Style Authors” guide to learn the corresponding NSExpression syntax.
Per #175 (comment), MGLConversion needs to convert from mapbox-gl-native-ios/platform/darwin/src/MGLConversion.h Lines 127 to 130 in 534d64c
This code is called as part of |
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.
Thanks! Just the question from @fabian-guerra
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 agree that we could treat optimizations as tail work. Thank you for explaining. LGTM in that case.
534d64c
to
aa95fdb
Compare
As of 11424ded73745e734c2d1f58b9fad8d7bd8ae635, the Debug ‣ Manipulate Style command adds a circle layer that should mark each POI around here. However, nothing shows up because the
As far as I can tell, the SDK code is behaving correctly and the shape is valid, but the expression never evaluates to true. I’ve also tried reversing the polygon and removing the closing point to no avail. /cc @zmiao |
@@ -55,6 +55,18 @@ The following aggregate operators are supported: | |||
`NSInPredicateOperatorType` | `key IN { 'iOS', 'macOS', 'tvOS', 'watchOS' }` | |||
`NSContainsPredicateOperatorType` | `{ 'iOS', 'macOS', 'tvOS', 'watchOS' } CONTAINS key` | |||
|
|||
You can use the `IN` and `CONTAINS` operators to test whether a value appears in a collection, whether a string is a substring of a larger string, or whether the evaluated feature (`SELF`) lies within a given `MGLShape` or `MGLFeature`. For example, to show one delicious local chain of sandwich shops, but not similarly named steakhouses and pizzerias: |
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.
MGLFeature support was added in mapbox/mapbox-gl-native#16232, which we haven’t pulled into this repository yet.
I’ve confirmed that this will also be fixed by mapbox/mapbox-gl-native#16232 once we pull it in. |
#210 will pull in the fix. No additional changes are needed for |
11424de
to
d4041d2
Compare
Fixed an issue where GeoJSON geometries were converted to MGLFeature instances instead of MGLShape objects.
d4041d2
to
9e28d66
Compare
Added support for the expression syntax “SELF IN shape” or “shape CONTAINS SELF”, which is interpreted as a “within” expression in JSON format.
9e28d66
to
e27b889
Compare
Added support for the NSPredicate syntax
SELF IN shape
orshape CONTAINS SELF
, whereshape
is an MGLShape object or MGLFeature instance. These syntaxes are interpreted aswithin
expressions in JSON format.Fixed an issue where GeoJSON geometries were converted to MGLFeature instances instead of MGLShape objects. This issue probably hasn’t come up in the wild because the conversion code was previously only part of a feature querying code path, and feature querying methods return features anyways. But since it’s possible for the
shape
inSELF IN shape
to be an MGLShape, that shape should get round-tripped as an MGLShape rather than an MGLFeature.Fixes #175. Depends on #183, which in turn depends on #181. Retarget and rebase this PR onto master before merging.
/cc @mapbox/maps-ios