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

[FEATURE] Vector tile layer - part 1 #35341

Merged
merged 27 commits into from
Apr 2, 2020
Merged

Conversation

wonder-sk
Copy link
Member

@wonder-sk wonder-sk commented Mar 25, 2020

This is the initial work on vector tile layer support 🎉

The implementation is based on what has been previously discussed in Vector Tiles QEP:
qgis/QGIS-Enhancement-Proposals#162

image
(The red lines are not rendering artifacts, they are drawn intentionally to show tile borders.)

Summary

So far this only includes changes to qgis_core library.

Main additions to the public API:

  • QgsVectorTileLayer - the main map layer class
  • QgsVectorTileRenderer - base class for renderer class implementations for vector tiles
  • QgsVectorTileBasicRenderer - default renderer implementation

New private classes (may be added to public API if needed):

  • QgsVectorTileLoader - handles fetching of tiles from network / mbtiles
  • QgsVectorTileMVTDecoder - handles decoding of raw tile data to features
  • QgsVectorTileLayerRenderer - takes care of overall rendering (fetch + decode + draw)
  • QgsVectorTileUtils - various useful functions

Some new classes that may be shared by vector and raster tiles:

  • QgsTileXYZ - position of a tile in tile matrix set (zoom level, column, row)
  • QgsTileRange - rectangular selection in a tile matrix (start/end column and row)
  • QgsTileMatrix - description of a tile matrix (map extent, zoom level, scale, number of rows/columns)

(also QgsTileMatrixSet to be added later when we support other tile matrix sets than just GoogleCRS84Quad)

Testing

You can load a vector tile layer from Python console, e.g.:

ds = QgsDataSourceUri()
ds.setParam("type","xyz")
ds.setParam("url", "https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=YOUR_FREE_API_KEY")
uri = bytes(ds.encodedUri()).decode('utf-8')
vtl = QgsVectorTileLayer(uri, "Vector Tiles Test")
QgsProject.instance().addMapLayer(vtl)

It is also possible to load vector tiles from a local MBTiles file - for "type" use "mbtiles" and for "url" use a local path (e.g. "/home/martin/x.mbtiles").

Rendering / Styling

We will probably need to have a discussion how the default renderer should work (I call it "basic"). It is currently based on rules ("styles") that refer to sub-layer name, geometry type and filter expression, and they have a QgsSymbol assigned. This is similar to what other rendering engines support. In theory in QGIS we could do more and support any vector renderer (categorized/graduated/rule-based/...) but for now I have kept things simple.

The other consideration is how to deal with existing styles - at this point most existing styles are done using Mapbox GL JSON style format. It is a question whether we should have a custom tile renderer which would try to emulate the native rendering as much as possible -- or to just have an importer that would convert those styles to our "basic" renderer (likely having somewhat lower fidelity). I have first started with a custom renderer, but over time I am thinking that using our "basic" renderer everywhere would be better also for the future.

Next Steps

Once this PR is merged, there will be more PRs coming to integrate vector tile layers into QGIS application: browser integration, styling GUI, identify tool support, attribute table.

Also there is currently no support for labeling, which should be added as well.

Thanks

Huge thanks to all funders who have contributed to the crowdfunding and made this possible 👏 👏 👏
https://www.lutraconsulting.co.uk/blog/2020/04/02/vectortiles-donors/

@nirvn
Copy link
Contributor

nirvn commented Mar 25, 2020

@wonder-sk , this is wonderful! I have a fair amount of thoughts on the styling front, I'll write more tomorrow. But the general line being, IMHO we need to be trying to match tile styling as much as we can with that of non tiled vector layers. And we need to make sure some of the basic QGIS environment expectations, such as feature based data defined properties, are met.

Great work! And thanks to the funders

python/CMakeLists.txt Show resolved Hide resolved
src/core/vectortile/qgsvectortilebasicrenderer.cpp Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortilebasicrenderer.cpp Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortilebasicrenderer.cpp Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortilebasicrenderer.h Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortileloader.cpp Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortilemvtdecoder.cpp Outdated Show resolved Hide resolved
{
if ( tmpPoints.size() > 0 )
{
outputLinestrings.append( new QgsLineString( tmpPoints ) );
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note that if you're after maximum speed, the fastest like string constructor is the one which accepts seperate double vectors for X and y

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point... but from some timing test I did the decoding phase is quite fast - it is actually evaluation of filter expressions that's the slowest bit. If you don't mind I would leave this unchanged. (+ I find that separation of vectors for X and Y in QgsLineString is very strange - it should be more cache friendly if they were together, and just one array instead of two.)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Mmm possibly? But it'd need to be an array of doubles and not QgsPoint (don't want to store all those unused z/m values). And then you'd need to upgrade all the existing performance sensitive code which uses the two vector constructor... 😜

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah an array of doubles is what I had in mind too... and yes, it would be quite some work to get all that converted... maybe one day :-)

src/core/vectortile/qgsvectortilemvtdecoder.cpp Outdated Show resolved Hide resolved
@nyalldawson
Copy link
Collaborator

Will there be data providers for vector tiles?

@nyalldawson
Copy link
Collaborator

The red lines are not rendering artifacts, they are drawn intentionally to show tile borders.

I'm curious to know if there any visible artefacts under these lines - does the antialiasing of the clip rectangles cause visible edge effects?

@nyalldawson
Copy link
Collaborator

I have first started with a custom renderer, but over time I am thinking that using our "basic" renderer everywhere would be better also for the future

I'd also tend towards conversion vs custom renderer

@wonder-sk
Copy link
Member Author

Thanks for the review @nyalldawson

Regarding colors - I was unsure how set up default renderer. In theory we would want to show each sub-layer with a different color, but given that we may not even know all possible sub-layer names, I thought it would be best to use the same color for all layers. For the particular choice of default colors - I will be happy with any better colors people suggest :-)

Will there be data providers for vector tiles?

I did not need the concept of data providers so far, so I have not introduced them... It would make most sense to create data providers when we know what kind of abstraction do we need.

I'm curious to know if there any visible artefacts under these lines - does the antialiasing of the clip rectangles cause visible edge effects?

I have not seen any artifacts so far! I was worried about that initially, but it seems that Qt is doing very good job with clipping (and I tried it also with reprojection when tile boundaries were not aligned with axes).

@nyalldawson
Copy link
Collaborator

Regarding colors - I was unsure how set up default renderer.

Could you call QgsColorSchemeRegistry::fetchRandomStyleColor() ? that should give one not-random-but-pretend-random (nice) color per call.

For the particular choice of default colors - I will be happy with any better colors people suggest :-)

Failing all else, I'd pick the first color from: QgsColorSchemeRegistry::randomStyleColorScheme()

I have not seen any artifacts so far! I was worried about that initially, but it seems that Qt is doing very good job with clipping

Great to hear! I was concerned given the long-standing issue/limitation regarding neighbouring polygon bounds showing through due to antialiasing of the edges. (Maybe clipping paths completely skip the antialiasing on the boundary?)

@wonder-sk wonder-sk changed the title WIP: Vector tile layer - part 1 Vector tile layer - part 1 Mar 27, 2020
@wonder-sk wonder-sk changed the title Vector tile layer - part 1 [FEATURE]Vector tile layer - part 1 Mar 27, 2020
@wonder-sk wonder-sk changed the title [FEATURE]Vector tile layer - part 1 [FEATURE] Vector tile layer - part 1 Mar 27, 2020
@wonder-sk wonder-sk closed this Mar 27, 2020
@wonder-sk wonder-sk reopened this Mar 27, 2020
@wonder-sk
Copy link
Member Author

I think this PR should be ready for merging...

src/core/qgstiles.cpp Outdated Show resolved Hide resolved
@@ -123,6 +123,15 @@ Sets renderer for the map layer.
QgsVectorTileRenderer *renderer() const;
%Docstring
Returns currently assigned renderer
%End

void setTileBorderRenderingEnabled( bool enabled );
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't this be better as a render context flag?

Copy link
Member Author

Choose a reason for hiding this comment

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

So it would be a global flag applied to all layers? And probably to raster tiles as well?
I don't know... I would probably prefer to keep it as is, maybe it can be moved to render context later. There are few more things like having tile XYZ numbers printed or border line/color configuration that would be nice to have too, but not sure if they all belong to render context.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it just struck me that it applies to other layers/providers too so would be nice to have something less ad-hoc to handle it. Could we hide from sip at least so it's not stable API?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm... removing it from sip would kind of remove the whole point of having it in API at all - I wanted to be able to turn this flag on/off from python console. I would be also fine with removal of this flag before the release if you don't like it, but now it is helping me with development... :-)

As a side note - it would be probably reasonable to mark vector tile layer related stuff as experimental API for now and pronounce it stable e.g. for 3.16 ...?

src/gui/qgsbrowserdockwidget_p.cpp Outdated Show resolved Hide resolved
tests/src/core/testqgsvectortilelayer.cpp Outdated Show resolved Hide resolved
src/core/vectortile/qgsvectortilelayerrenderer.cpp Outdated Show resolved Hide resolved
@nyalldawson
Copy link
Collaborator

+1 to merge after above comments are resolved

@nyalldawson
Copy link
Collaborator

Oh, you'll also need to get the azure builds fixed before merging!

@wonder-sk wonder-sk closed this Mar 30, 2020
@wonder-sk wonder-sk reopened this Mar 30, 2020
It turns out different versions of protobuf library may not necessarily
work with different versions of code generated by protoc (for example,
these files worked fine on Ubuntu 18.04 but they do not work on 19.10
which has newer version of protobuf library).

Also, we should be fine with using just "lite" version of the library
which is about an order of magnitude smaller (lite 0.3mb vs full 2.3mb).
@saberraz
Copy link
Contributor

Hmmmm...I now get this error when I try to compile (even from a fresh build dir):
'../src/core/protobuf::protoc', needed by 'src/core/vector_tile.pb.cc', missing and no known rule to make it

@wonder-sk
Copy link
Member Author

This needs protobuf in osgeo4w to be compiled as a dynamic library. Right now it is built as a static library and the build fails on Windows...

- remove protobuf-devel from explicit list - should be included in deps
- only use the extra #define where needed
- disable vector tile test on azure for now (can't debug it)
@wonder-sk wonder-sk merged commit 5307584 into qgis:master Apr 2, 2020
basilrabi added a commit to basilrabi/QGIS that referenced this pull request Apr 4, 2020
@timlinux timlinux added Changelog Items that are queued to appear in the visual changelog - remove after harvesting and removed Changelog Items that are queued to appear in the visual changelog - remove after harvesting labels May 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants