Skip to content
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 for On-The-Fly GeoJson Layers and a new Mutator Transform #2095

Merged
merged 17 commits into from
Aug 3, 2022

Conversation

kevinkreiser
Copy link
Contributor

@kevinkreiser kevinkreiser commented Jul 13, 2022

The basic gist here is I want to add 2 features.

  1. i want to be able to drop a new layer into a tile build and be able to reference that layer from queries. if the layer is not there on the filesystem i want the transforms that reference it to do nothing (same thing that happens when a layer isnt found). the bulk of this was accomplished over in: add support for preprocessed inline geojson layers tilequeue#414
  2. i want a new transform that allows me, with injected python, to be able to mutate what the properties or geometry of a given feature becomes. it seemed to me we already had some of these things especially for properties but geometry was lacking.

Once I had those two features implemented I added a landmarks layer to the queries which simply allows you to drop a geojson file into your tile build and mark buildings with it and become a point feature layer.

# we expect it to already be in mercator projection and we only fill out the minimal structure of the feature tuple
# there's no ID, most of the datum stuff is missing, we are mostly worried about the shape and properties
# we return None if we couldnt load the layer for any reason
def _load_external_layer(params, name):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is moving over to tilequeue where the rest of the layers are loaded and ill add a new section to queries.yaml instead of having it be part of the transform

queries.yaml Outdated
area-inclusion-threshold: 1
simplify_before_intersect: false
simplify_start: 0,
layer_path: ./metatile-layers/landmarks.geojson
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this property signifies an "inline" layer. when the features are loaded from file we expect them to be already processed. that is we will not try to load a landmarks.yaml and do processing on the data that is in the geojson file. we expect the tags and values to be there and the coordinates to already be in mercator projection

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make the name more explicit about its behavior, like pre_processed_layer_path might be a better name?

@@ -542,6 +552,23 @@ post_process:
source_layer: roads
properties: [layer]

# supersede existing osm buildings with data from landmark geojson layer
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

landmarks mark buildings with a superseded property so that when the two overlap the downstream consumer of the tiles will be able to tell

target_attribute: superseded
linear: false

# turn the landmark geojson polygons into point features
Copy link
Contributor Author

@kevinkreiser kevinkreiser Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the final output of the landmarks layer is a point layer, basically landmark pois with a bunch of metadata about the landmark. the mutate transform lets us both modify the geom and the properties with python code that gets eval'd directly. pretty powerful stuff. you can reference existing properties of a feature using the {properties} tag and you can access the geometry using the {shape} tag. in the future we might want to allow more stuff to be referenced.

queries.yaml Outdated
@@ -1865,6 +1892,7 @@ post_process:
- addr_housenumber
- addr_street
- osm_relation
exclude: ['superseded']
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a building gets marked by a landmark, we exclude it from merging with other buildings, keeping it a single entity

Comment on lines +10 to +18
kdtree
lxml==4.6.2
mapbox-vector-tile==1.2.0
ModestMaps==1.4.7
protobuf==3.4.0
psycopg2==2.7.3.2
pyclipper==1.0.6
pycountry==17.9.23
pyshp==2.3.0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently my system thought i needed these, i didnt check to see if that was the case. i can revert

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah its because setup.py has them

Comment on lines +9764 to +9769
"""
We take a layer and we modify the geometry using the python expression in the query. The expressions have access to
both the existing shape and existing properties via python string format replacements {shape} and {properties}
respectively. Each expression is then eval'd to replace the existing feature in the layer with the result of the
expressions. By default the expressions are a no-op
"""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty good explanation here. basically we loop over the features and eval the python in the queries.yaml to compute new properties or geometry. if you omitted one or the other (they are both optional) it just leaves the existing values unchanged.

queries.yaml Outdated
area-inclusion-threshold: 1
simplify_before_intersect: false
simplify_start: 0,
layer_path: ./metatile-layers/landmarks.geojson
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make the name more explicit about its behavior, like pre_processed_layer_path might be a better name?

integration-test/2095_inline_layers.py Show resolved Hide resolved
vectordatasource/transform.py Outdated Show resolved Hide resolved
'crs': {'type': 'name', 'properties': {'name': 'urn:ogc:def:crs:EPSG::3857'}},
'features': [
{'type': 'Feature',
'properties': {'name': 'null island hut', 'superseded': True, 'height': math.pi, 'origin': [1, 1], 'id': 42},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙇

@@ -9,6 +9,7 @@ all:
- boundaries
- transit
- admin_areas
- landmarks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add docs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the traffic layers for inspiration for optional layer descriptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha! i was looking around for docs, it seemed like even the changelog isnt up to date. ill grep around to see if i can find them!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG is out of date. Historically that's been updated as releases are tagged – and it's past time to tag v1.9. Thanks for the nudge.

queries.yaml Outdated Show resolved Hide resolved
@kevinkreiser
Copy link
Contributor Author

kevinkreiser commented Aug 1, 2022

the additional integration test failures in ci are just because this branch depends on the changes in this pr: tilezen/tilequeue#414

if end_zoom is not None and zoom >= end_zoom:
return None

# for the max zoom a transform needs to be re-entrant so we take a copy here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is max zoom here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah sorry the last zoom in the tile pyramids that we create (z16). there is code over in tilequeue here: https://github.com/tilezen/tilequeue/blob/master/tilequeue/process.py#L613 where it basically calls into the transforms a second time (hence re-entrancy)

@tgrigsby-sc
Copy link
Contributor

I'd remove the strikethrough in the description before merging

@kevinkreiser kevinkreiser merged commit 4caaedc into master Aug 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants