tangrams / tangram

WebGL map rendering engine for creative cartography
https://tangram.city
MIT License
2.22k stars 290 forks source link

add queryLayers() #671

Closed meetar closed 6 years ago

meetar commented 6 years ago

Similar to queryFeatures() but just a list of available layer names from the datasource, rather than from the scene file as in scene.config.layers.

Allows simple querying in cases when the data isn't human-readable or already known. Also allows run-time style building in these cases, as well as dynamic "layers" UI construction.

nvkelso commented 6 years ago

/cc @burritojustice

meetar commented 6 years ago

@nvkelso 🤫

bcamper commented 6 years ago

Can you say a bit more about the intended use cases? Since it only covers the layers in currently visible tiles, it is a very specific feature.

I just want to understand more because while it's a small code scope, I don't think a core purpose of Tangram should be to explore unknown data (vs. styling known data). If you don't know what the layers are, you won't be able to see them anyway.

It also may make sense to do this as a sub-case of queryFeatures() (e.g. group by layer) instead, without expanding the public API surface more.

Again it's a fairly small addition but I do think we should be focusing library scope (obviously we have not in the past! :) with an eye towards managing and improving library size etc.

meetar commented 6 years ago

@bcamper Sure, it could definitely be something included as an option in queryFeatures() – that would probably make more sense.

The primary use case would be for adding a "layers" interface in style-creation apps such as Tangram Play. It would also be useful in situations where you have an un- or poorly-documented datasource in a non-human-readable format, and you'd like to know which layers are available in the current view. This information is already decoded by the tiles-processing mechanism but not exposed anywhere.

bcamper commented 6 years ago

Right, I think there are some interesting things that could be gleaned from it, I guess I just think it's of questionable value for something like Play, where you essentially have to say "here are the layers we've seen so far, but there might be more, who knows!"

TileJSON is a more comprehensive/intentional way of solving that problem, and Play could support that.

bcamper commented 6 years ago

Separately, it also just occurred to me that however this kind of thing is implemented, it should specify which source name the layer belongs to, since scenes can have multiple sources and there could even be layer name collisions between them.

bcamper commented 6 years ago

I took a stab at adding support for this (and more!) in queryFeatures(), which results in a smaller code change footprint and API change:

https://github.com/tangrams/tangram/compare/query-layers

This adds properties for $source, $layer, and $geometry to the features returned by queryFeatures(). You could already filter these in queryFeatures() calls (using the same filter syntax we use in the scene file), but this allows you to also use the group_by feature with them. In addition, a $visible property was added. While this property (by definition!) doesn't exist for a scene file filter, it's a logical extension here.

You can do some neat things with these options, such as:

Features grouped by layer (with feature count for each): scene.queryFeatures({ group_by: '$layer' }) screen shot 2018-09-19 at 3 59 52 pm

Features grouped by layer and kind: scene.queryFeatures({ group_by: ['$layer', 'kind'] }) screen shot 2018-09-19 at 4 05 31 pm

Features grouped by layer and geometry type: scene.queryFeatures({ group_by: ['$layer', '$geometry'] }) screen shot 2018-09-19 at 4 01 36 pm

Features grouped by layer and visibility: scene.queryFeatures({ group_by: ['$layer', '$visible'] }) screen shot 2018-09-19 at 4 02 53 pm

Or even a more granular breakdown: scene.queryFeatures({ group_by: ['$layer', '$geometry', '$visible'] }) screen shot 2018-09-19 at 4 04 23 pm

nvkelso commented 6 years ago

💯 to including the layer in the query results! I've wanted to include that in the debug UX for a long while :)

bcamper commented 6 years ago

The only potential oddity with the above branch is that these "special" properties are added directly to the properties in the returned GeoJSON objects. When filtering source data in the scene file, these properties are implied, but not actually in the same namespace as the feature properties themselves. If you had source data that contained any of these property names, currently those properties would be overridden when accessed via queryFeatures(). Another option would be to put them outside the properties on the GeoJSON, but then they wouldn't be accessible to any standard GeoJSON-reading software. It was also faster/smaller to code it this way.

bcamper commented 6 years ago

Hmm @nvkelso that's a different call entirely, scene.getFeatureAt() for "what is the feature at this specific point" vs. scene.queryFeatures() which is a broader syntax to query all the features in the current visible tile set. It's relevant to my point above though because getFeatureAt() does have data source name and layer included, though as separate properties, like this:

screen shot 2018-09-19 at 4 13 27 pm

The difference is that getFeatureAt() doesn't return a real GeoJSON object, just a plain JSON. Anyway maybe trying to normalize these makes sense... or maybe it's not worth it. Overall I think it's more useful to have actual GeoJSON-styles results, but it would be a breaking change to getFeatureAt().

nvkelso commented 6 years ago

Cool! That works for me. I access them with $source_layer and $source_name?

bcamper commented 6 years ago

Yep but no need for the leading $ :)

bcamper commented 6 years ago

Closing this and replacing with #676. I brought come of the comments over as well.