Skip to content
Permalink
Browse files
Merge pull request #44826 from rldhont/vector-tiles-custom-def
[API] Encode and write vector tiles in different CRS than EPSG:3857
  • Loading branch information
rldhont committed Sep 10, 2021
2 parents 9adc3aa + da42404 commit 922b8fffc5eae7223fee72f2d079b63a39d834f5
@@ -108,9 +108,26 @@ Please note that we follow the XYZ convention of X/Y axes, i.e. top-left tile ha
%End
public:

static QgsTileMatrix fromWebMercator( int mZoomLevel );
static QgsTileMatrix fromWebMercator( int zoomLevel );
%Docstring
Returns a tile matrix for the usual web mercator
%End

static QgsTileMatrix fromCustomDef( int zoomLevel, const QgsCoordinateReferenceSystem &crs,
const QgsPointXY &z0TopLeftPoint, double z0Dimension,
int z0MatrixWidth = 1, int z0MatrixHeight = 1 );
%Docstring
Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units
%End

static QgsTileMatrix fromTileMatrix( const int &zoomLevel, const QgsTileMatrix &tileMatrix );
%Docstring
Returns a tile matrix based on another one
%End

QgsCoordinateReferenceSystem crs() const;
%Docstring
Returns the authority identifier for the CRS of the tile matrix
%End

int zoomLevel() const;
@@ -156,6 +173,11 @@ Returns tile range that fully covers the given extent
QPointF mapToTileCoordinates( const QgsPointXY &mapPoint ) const;
%Docstring
Returns row/column coordinates (floating point number) from the given point in map coordinates
%End

bool isRootTileMatrix() const;
%Docstring
Returns the root status of the tile matrix (zoom level == 0)
%End

};
@@ -128,7 +128,7 @@ See the class description about the syntax of destination URIs.

void setExtent( const QgsRectangle &extent );
%Docstring
Sets extent of vector tile output. Currently always in EPSG:3857.
Sets extent of vector tile output.
If unset, it will use the full extent of all input layers combined
%End

@@ -154,6 +154,11 @@ Sets that will be written to the output dataset. See class description for more
void setTransformContext( const QgsCoordinateTransformContext &transformContext );
%Docstring
Sets coordinate transform context for transforms between layers and tile matrix CRS
%End

bool setRootTileMatrix( const QgsTileMatrix &tileMatrix );
%Docstring
Sets zoom level 0 tile matrix
%End

bool writeTiles( QgsFeedback *feedback = 0 );
@@ -16,22 +16,64 @@
#include "qgstiles.h"

#include "qgslogger.h"
#include "qgscoordinatereferencesystem.h"
#include "qgssettings.h"

QgsTileMatrix QgsTileMatrix::fromWebMercator( int zoomLevel )
{
double z0xMin = -20037508.3427892, z0yMax = 20037508.3427892;

return fromCustomDef( zoomLevel, QgsCoordinateReferenceSystem( "EPSG:3857" ), QgsPointXY( z0xMin, z0yMax ), 2 * z0yMax );
}

QgsTileMatrix QgsTileMatrix::fromCustomDef( int zoomLevel, const QgsCoordinateReferenceSystem &crs,
const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth, int z0MatrixHeight )
{
// Square extent calculation
double z0xMin = z0TopLeftPoint.x();
double z0yMax = z0TopLeftPoint.y();
double z0xMax = z0xMin + z0MatrixWidth * z0Dimension;
double z0yMin = z0yMax - z0MatrixHeight * z0Dimension;

// Constant for scale denominator calculation
const double tileSize = 256.0;
const double PIXELS_TO_M = 2.8 / 10000.0; // WMS/WMTS define "standardized rendering pixel size" as 0.28mm
const double UNIT_TO_M = QgsUnitTypes::fromUnitToUnitFactor( crs.mapUnits(), QgsUnitTypes::DistanceMeters );
// Scale denominator calculation
double scaleDenom0 = ( z0Dimension / tileSize ) * ( UNIT_TO_M / PIXELS_TO_M );

int numTiles = static_cast<int>( pow( 2, zoomLevel ) ); // assuming we won't ever go over 30 zoom levels
double z0xMin = -20037508.3427892, z0yMin = -20037508.3427892;
double z0xMax = 20037508.3427892, z0yMax = 20037508.3427892;
double s0 = 559082264.0287178; // scale denominator at zoom level 0 of GoogleCRS84Quad

QgsTileMatrix tm;
tm.mCrs = crs;
tm.mZoomLevel = zoomLevel;
tm.mMatrixWidth = numTiles;
tm.mMatrixHeight = numTiles;
tm.mMatrixWidth = z0MatrixWidth * numTiles;
tm.mMatrixHeight = z0MatrixHeight * numTiles;
tm.mTileXSpan = ( z0xMax - z0xMin ) / tm.mMatrixWidth;
tm.mTileYSpan = ( z0yMax - z0yMin ) / tm.mMatrixHeight;
tm.mExtent = QgsRectangle( z0xMin, z0yMin, z0xMax, z0yMax );
tm.mScaleDenom = s0 / pow( 2, zoomLevel );
tm.mScaleDenom = scaleDenom0 / pow( 2, zoomLevel );
return tm;
}


QgsTileMatrix QgsTileMatrix::fromTileMatrix( const int &zoomLevel, const QgsTileMatrix &tileMatrix )
{
QgsTileMatrix tm;
int numTiles = static_cast<int>( pow( 2, zoomLevel ) ); // assuming we won't ever go over 30 zoom levels
int aZoomLevel = tileMatrix.zoomLevel();
int aNumTiles = static_cast<int>( pow( 2, aZoomLevel ) );
int aMatrixWidth = tileMatrix.matrixWidth();
int aMatrixHeight = tileMatrix.matrixHeight();
QgsRectangle aExtent = tileMatrix.extent();
tm.mCrs = tileMatrix.crs();
tm.mZoomLevel = zoomLevel;
tm.mMatrixWidth = aMatrixWidth * numTiles / aNumTiles;
tm.mMatrixHeight = aMatrixHeight * numTiles / aNumTiles;
tm.mTileXSpan = aExtent.width() / tm.mMatrixWidth;
tm.mTileYSpan = aExtent.height() / tm.mMatrixHeight;
tm.mExtent = aExtent;
tm.mScaleDenom = tileMatrix.scale() * pow( 2, aZoomLevel ) / pow( 2, zoomLevel );
return tm;
}

@@ -20,6 +20,7 @@
#include "qgis_sip.h"

#include "qgsrectangle.h"
#include "qgscoordinatereferencesystem.h"

/**
* \ingroup core
@@ -104,7 +105,18 @@ class CORE_EXPORT QgsTileMatrix
public:

//! Returns a tile matrix for the usual web mercator
static QgsTileMatrix fromWebMercator( int mZoomLevel );
static QgsTileMatrix fromWebMercator( int zoomLevel );

//! Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units
static QgsTileMatrix fromCustomDef( int zoomLevel, const QgsCoordinateReferenceSystem &crs,
const QgsPointXY &z0TopLeftPoint, double z0Dimension,
int z0MatrixWidth = 1, int z0MatrixHeight = 1 );

//! Returns a tile matrix based on another one
static QgsTileMatrix fromTileMatrix( const int &zoomLevel, const QgsTileMatrix &tileMatrix );

//! Returns the authority identifier for the CRS of the tile matrix
QgsCoordinateReferenceSystem crs() const { return mCrs; }

//! Returns zoom level of the tile matrix
int zoomLevel() const { return mZoomLevel; }
@@ -133,9 +145,14 @@ class CORE_EXPORT QgsTileMatrix
//! Returns row/column coordinates (floating point number) from the given point in map coordinates
QPointF mapToTileCoordinates( const QgsPointXY &mapPoint ) const;

//! Returns the root status of the tile matrix (zoom level == 0)
bool isRootTileMatrix() const { return mZoomLevel == 0; }

private:
//! Crs associated with the tile matrix
QgsCoordinateReferenceSystem mCrs;
//! Zoom level index associated with the tile matrix
int mZoomLevel;
int mZoomLevel = -1;
//! Number of columns of the tile matrix
int mMatrixWidth;
//! Number of rows of the tile matrix
@@ -145,14 +145,22 @@ QgsVectorTileMVTEncoder::QgsVectorTileMVTEncoder( QgsTileXYZ tileID )
{
const QgsTileMatrix tm = QgsTileMatrix::fromWebMercator( mTileID.zoomLevel() );
mTileExtent = tm.tileExtent( mTileID );
mCrs = tm.crs();
}

QgsVectorTileMVTEncoder::QgsVectorTileMVTEncoder( QgsTileXYZ tileID, const QgsTileMatrix &tileMatrix )
: mTileID( tileID )
{
mTileExtent = tileMatrix.tileExtent( mTileID );
mCrs = tileMatrix.crs();
}

void QgsVectorTileMVTEncoder::addLayer( QgsVectorLayer *layer, QgsFeedback *feedback, QString filterExpression, QString layerName )
{
if ( feedback && feedback->isCanceled() )
return;

const QgsCoordinateTransform ct( layer->crs(), QgsCoordinateReferenceSystem( "EPSG:3857" ), mTransformContext );
const QgsCoordinateTransform ct( layer->crs(), mCrs, mTransformContext );

QgsRectangle layerTileExtent = mTileExtent;
try
@@ -40,9 +40,12 @@
class CORE_EXPORT QgsVectorTileMVTEncoder
{
public:
//! Creates MVT encoder for the given tile coordinates
//! Creates MVT encoder for the given tile coordinates for Web Mercator
explicit QgsVectorTileMVTEncoder( QgsTileXYZ tileID );

//! Creates MVT encoder for the given tile coordinates and tile matrix
explicit QgsVectorTileMVTEncoder( QgsTileXYZ tileID, const QgsTileMatrix &tileMatrix );

//! Returns resolution of coordinates of geometries within the tile. The default is 4096.
int resolution() const { return mResolution; }
//! Sets the resolution of coordinates of geometries within the tile
@@ -76,6 +79,7 @@ class CORE_EXPORT QgsVectorTileMVTEncoder
QgsCoordinateTransformContext mTransformContext;

QgsRectangle mTileExtent;
QgsCoordinateReferenceSystem mCrs;

QgsVectorTileFeatures mFeatures;

@@ -35,6 +35,18 @@

QgsVectorTileWriter::QgsVectorTileWriter()
{
setRootTileMatrix( QgsTileMatrix::fromWebMercator( 0 ) );
}


bool QgsVectorTileWriter::setRootTileMatrix( const QgsTileMatrix &tileMatrix )
{
if ( tileMatrix.isRootTileMatrix() )
{
mRootTileMatrix = tileMatrix;
return true;
}
return false;
}


@@ -94,7 +106,7 @@ bool QgsVectorTileWriter::writeTiles( QgsFeedback *feedback )
int tilesToCreate = 0;
for ( int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
{
QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( zoomLevel );
QgsTileMatrix tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mRootTileMatrix );

QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( outputExtent );
tilesToCreate += ( tileRange.endRow() - tileRange.startRow() + 1 ) *
@@ -137,7 +149,7 @@ bool QgsVectorTileWriter::writeTiles( QgsFeedback *feedback )
{
try
{
QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( "EPSG:3857" ), QgsCoordinateReferenceSystem( "EPSG:4326" ), mTransformContext );
QgsCoordinateTransform ct( mRootTileMatrix.crs(), QgsCoordinateReferenceSystem( "EPSG:4326" ), mTransformContext );
QgsRectangle wgsExtent = ct.transform( outputExtent );
QString boundsStr = QString( "%1,%2,%3,%4" )
.arg( wgsExtent.xMinimum() ).arg( wgsExtent.yMinimum() )
@@ -149,12 +161,14 @@ bool QgsVectorTileWriter::writeTiles( QgsFeedback *feedback )
// bounds won't be written (not a problem - it is an optional value)
}
}
if ( !mMetadata.contains( "crs" ) )
mbtiles->setMetadataValue( "crs", mRootTileMatrix.crs().authid() );
}

int tilesCreated = 0;
for ( int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
{
QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( zoomLevel );
QgsTileMatrix tileMatrix = QgsTileMatrix::fromTileMatrix( zoomLevel, mRootTileMatrix );

QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( outputExtent );
for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
@@ -216,12 +230,11 @@ bool QgsVectorTileWriter::writeTiles( QgsFeedback *feedback )
QgsRectangle QgsVectorTileWriter::fullExtent() const
{
QgsRectangle extent;
QgsCoordinateReferenceSystem destCrs( "EPSG:3857" );

for ( const Layer &layer : mLayers )
{
QgsVectorLayer *vl = layer.layer();
QgsCoordinateTransform ct( vl->crs(), destCrs, mTransformContext );
QgsCoordinateTransform ct( vl->crs(), mRootTileMatrix.crs(), mTransformContext );
try
{
QgsRectangle r = ct.transformBoundingBox( vl->extent() );
@@ -318,4 +331,3 @@ QByteArray QgsVectorTileWriter::writeSingleTile( QgsTileXYZ tileID, QgsFeedback

return encoder.encode();
}

@@ -17,8 +17,10 @@
#define QGSVECTORTILEWRITER_H

#include <QCoreApplication>
#include "qgstiles.h"
#include "qgsrectangle.h"
#include "qgscoordinatetransformcontext.h"
#include "qgscoordinatereferencesystem.h"

class QgsFeedback;
class QgsTileMatrix;
@@ -128,7 +130,7 @@ class CORE_EXPORT QgsVectorTileWriter
void setDestinationUri( const QString &uri ) { mDestinationUri = uri; }

/**
* Sets extent of vector tile output. Currently always in EPSG:3857.
* Sets extent of vector tile output.
* If unset, it will use the full extent of all input layers combined
*/
void setExtent( const QgsRectangle &extent ) { mExtent = extent; }
@@ -147,6 +149,11 @@ class CORE_EXPORT QgsVectorTileWriter
//! Sets coordinate transform context for transforms between layers and tile matrix CRS
void setTransformContext( const QgsCoordinateTransformContext &transformContext ) { mTransformContext = transformContext; }

/**
* Sets zoom level 0 tile matrix
*/
bool setRootTileMatrix( const QgsTileMatrix &tileMatrix );

/**
* Writes vector tiles according to the configuration.
* Returns TRUE on success (upon failure one can get error cause using errorMessage())
@@ -184,6 +191,7 @@ class CORE_EXPORT QgsVectorTileWriter
QString mbtilesJsonSchema();

private:
QgsTileMatrix mRootTileMatrix;
QgsRectangle mExtent;
int mMinZoom = 0;
int mMaxZoom = 4;

0 comments on commit 922b8ff

Please sign in to comment.