Skip to content

Commit

Permalink
xpose single vector tile encoding method
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarteau authored and wonder-sk committed Sep 7, 2021
1 parent e850195 commit f7cc194
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 0 deletions.
14 changes: 14 additions & 0 deletions python/core/auto_generated/vectortile/qgsvectortilewriter.sip.in
Expand Up @@ -174,6 +174,20 @@ an empty string if writing was successful.
QgsRectangle fullExtent() const;
%Docstring
Returns calculated extent that combines extent of all input layers
%End

QByteArray writeSingleTile( QgsTileXYZ tileID, QgsFeedback *feedback = 0, int buffer = 256, int resolution = 4096 ) const;
%Docstring
Encodes single MVT tile

:param tileID: Tile identifier
:param feedback: optional, provide cancellation functionality
:param resolution: the resolution of coordinates of geometries within the tile. The default is 4096
:param buffer: size of the buffer zone around tile edges in integer tile coordinates. The default is 256 (~6%)

Returns a QByteArray with the encoded data

.. versionadded:: 3.22
%End

};
Expand Down
23 changes: 23 additions & 0 deletions src/core/vectortile/qgsvectortilewriter.cpp
Expand Up @@ -296,3 +296,26 @@ QString QgsVectorTileWriter::mbtilesJsonSchema()
rootObj["vector_layers"] = arrayLayers;
return QString::fromStdString( QgsJsonUtils::jsonFromVariant( rootObj ).dump() );
}


QByteArray QgsVectorTileWriter::writeSingleTile( QgsTileXYZ tileID, QgsFeedback *feedback, int buffer, int resolution ) const
{
int zoomLevel = tileID.zoomLevel();

QgsVectorTileMVTEncoder encoder( tileID );
encoder.setTileBuffer( buffer );
encoder.setResolution( resolution );
encoder.setTransformContext( mTransformContext );

for ( const QgsVectorTileWriter::Layer &layer : std::as_const( mLayers ) )
{
if ( ( layer.minZoom() >= 0 && zoomLevel < layer.minZoom() ) ||
( layer.maxZoom() >= 0 && zoomLevel > layer.maxZoom() ) )
continue;

encoder.addLayer( layer.layer(), feedback, layer.filterExpression(), layer.layerName() );
}

return encoder.encode();
}

14 changes: 14 additions & 0 deletions src/core/vectortile/qgsvectortilewriter.h
Expand Up @@ -165,6 +165,20 @@ class CORE_EXPORT QgsVectorTileWriter
//! Returns calculated extent that combines extent of all input layers
QgsRectangle fullExtent() const;

/**
* Encodes single MVT tile
*
* \param tileID Tile identifier
* \param feedback optional, provide cancellation functionality
* \param resolution the resolution of coordinates of geometries within the tile. The default is 4096
* \param buffer size of the buffer zone around tile edges in integer tile coordinates. The default is 256 (~6%)
*
* Returns a QByteArray with the encoded data
*
* \since QGIS 3.22
*/
QByteArray writeSingleTile( QgsTileXYZ tileID, QgsFeedback *feedback = nullptr, int buffer = 256, int resolution = 4096 ) const;

private:
bool writeTileFileXYZ( const QString &sourcePath, QgsTileXYZ tileID, const QgsTileMatrix &tileMatrix, const QByteArray &tileData );
QString mbtilesJsonSchema();
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -372,6 +372,7 @@ ADD_PYTHON_TEST(PyQgsSelectiveMasking test_selective_masking.py)
ADD_PYTHON_TEST(PyQgsRelationEditorWidgetRegistry test_qgsrelationeditorwidgetregistry.py)
ADD_PYTHON_TEST(PyQgsActionWidgetWrapper test_qgsactionwidgetwrapper.py)
ADD_PYTHON_TEST(PyQgsAttributeEditorAction test_qgsattributeeditoraction.py)
ADD_PYTHON_TEST(PyQgsVectorTile test_qgsvectortile.py)

if (NOT WIN32)
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)
Expand Down
92 changes: 92 additions & 0 deletions tests/src/python/test_qgsvectortile.py
@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
'''
test_vectortile.py
--------------------------------------
Date : September 2021
Copyright : (C) 2021 David Marteau
email : david dot marteau at 3liz dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
'''

import qgis # NOQA
import tempfile
import shutil

from qgis.testing import unittest, start_app
from utilities import unitTestDataPath

from qgis.PyQt.QtCore import QUrl
from qgis.core import (QgsVectorLayer,
QgsVectorTileWriter,
QgsDataSourceUri,
QgsTileXYZ)

from pathlib import Path

TEST_DATA_PATH = Path(unitTestDataPath())


start_app()


class TestVectorTile(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.tempdir = Path(tempfile.mkdtemp())

@classmethod
def tearDownClass(cls):
shutil.rmtree(cls.tempdir, True)

def setUp(self):
"""Run before each test."""
pass

def tearDown(self):
"""Run after each test."""
pass

def testSingleTileEncode(self):
""" Test vector tile encoding from python
"""
vlPoints = QgsVectorLayer(str(TEST_DATA_PATH / "points.shp"), "points", "ogr")
vlLines = QgsVectorLayer(str(TEST_DATA_PATH / "lines.shp"), "lines", "ogr")
vlPolys = QgsVectorLayer(str(TEST_DATA_PATH / "polys.shp"), "polys", "ogr")

layers = [QgsVectorTileWriter.Layer(vl) for vl in (vlPoints, vlLines, vlPolys)]

writer = QgsVectorTileWriter()
writer.setMaxZoom(3)
writer.setLayers(layers)

data = writer.writeSingleTile(QgsTileXYZ(0, 0, 0))

ds = QgsDataSourceUri()
ds.setParam("type", "xyz")
ds.setParam("url", (self.tempdir / "{z}-{x}-{y}.pbf").as_uri())

# Create pbf files
writer.setDestinationUri(ds.encodedUri().data().decode())
res = writer.writeTiles()
self.assertEqual(writer.errorMessage(), "")
self.assertTrue(res)

# Compare encoded data to written file
# Read data from file
with (self.tempdir / "0-0-0.pbf").open("rb") as fp:
output = fp.read()

# Compare binary data
self.assertEqual(ascii(data.data()), ascii(output))


if __name__ == '__main__':
unittest.main()

0 comments on commit f7cc194

Please sign in to comment.