Don't apply tile scale doubling hack to esri generated vector tiles

We shouldn't apply this hack if we want to match ESRI's zoom to scale
level handling
This commit is contained in:
Nyall Dawson 2022-03-11 09:41:09 +10:00
parent 2faa91a327
commit 222ce1b5ab
7 changed files with 78 additions and 18 deletions

View File

@ -302,6 +302,24 @@ Reads the set from an XML ``element``.
virtual QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
%Docstring
Writes the set to an XML element.
%End
bool applyTileScaleDoublingHack() const;
%Docstring
Returns ``True`` if the scale doubling hack used to match MapBox scale to tile zoom levels should be applied.
The default is that this hack will be applied.
.. seealso:: :py:func:`setApplyTileScaleDoublingHack`
%End
void setApplyTileScaleDoublingHack( bool apply );
%Docstring
Sets whether the scale doubling hack used to match MapBox scale to tile zoom levels should be applied.
The default is that this hack will be applied.
.. seealso:: :py:func:`applyTileScaleDoublingHack`
%End
};

View File

@ -35,6 +35,7 @@ Initializes the tile structure settings from an ESRI REST VectorTileService ``js
This same structure is utilized in ESRI vtpk archives in the root.json file.
%End
};
/************************************************************************

View File

@ -201,9 +201,12 @@ double QgsTileMatrixSet::scaleToZoom( double scale ) const
double scaleUnder = 0;
double scaleOver = 0;
// TODO: it seems that map scale is double (is that because of high-dpi screen?)
// (this TODO was taken straight from QgsVectorTileUtils::scaleToZoom!)
scale *= 2;
if ( mApplyTileScaleDoubleHack )
{
// TODO: it seems that map scale is double (is that because of high-dpi screen?)
// (this TODO was taken straight from QgsVectorTileUtils::scaleToZoom!)
scale *= 2;
}
for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
{
@ -238,6 +241,8 @@ bool QgsTileMatrixSet::readXml( const QDomElement &element, QgsReadWriteContext
{
mTileMatrices.clear();
mApplyTileScaleDoubleHack = element.attribute( QStringLiteral( "applyScaleDoubleHack" ), QStringLiteral( "1" ) ).toInt();
const QDomNodeList children = element.childNodes();
for ( int i = 0; i < children.size(); i++ )
{
@ -266,6 +271,7 @@ bool QgsTileMatrixSet::readXml( const QDomElement &element, QgsReadWriteContext
QDomElement QgsTileMatrixSet::writeXml( QDomDocument &document, const QgsReadWriteContext & ) const
{
QDomElement setElement = document.createElement( QStringLiteral( "matrixSet" ) );
setElement.setAttribute( QStringLiteral( "applyScaleDoubleHack" ), mApplyTileScaleDoubleHack ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
{

View File

@ -294,10 +294,28 @@ class CORE_EXPORT QgsTileMatrixSet
*/
virtual QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
/**
* Returns TRUE if the scale doubling hack used to match MapBox scale to tile zoom levels should be applied.
*
* The default is that this hack will be applied.
*
* \see setApplyTileScaleDoublingHack()
*/
bool applyTileScaleDoublingHack() const { return mApplyTileScaleDoubleHack; }
/**
* Sets whether the scale doubling hack used to match MapBox scale to tile zoom levels should be applied.
*
* The default is that this hack will be applied.
*
* \see applyTileScaleDoublingHack()
*/
void setApplyTileScaleDoublingHack( bool apply ) { mApplyTileScaleDoubleHack = apply; }
private:
QMap< int, QgsTileMatrix > mTileMatrices;
bool mApplyTileScaleDoubleHack = true;
};
#endif // QGSTILES_H

View File

@ -28,6 +28,9 @@ QgsVectorTileMatrixSet QgsVectorTileMatrixSet::fromWebMercator()
bool QgsVectorTileMatrixSet::fromEsriJson( const QVariantMap &json )
{
// doesn't apply to ESRI tile zoom levels
setApplyTileScaleDoublingHack( false );
const QVariantMap tileInfo = json.value( QStringLiteral( "tileInfo" ) ).toMap();
const QVariantMap origin = tileInfo.value( QStringLiteral( "origin" ) ).toMap();

View File

@ -43,6 +43,7 @@ class CORE_EXPORT QgsVectorTileMatrixSet : public QgsTileMatrixSet
* \note This same structure is utilized in ESRI vtpk archives in the root.json file.
*/
bool fromEsriJson( const QVariantMap &json );
};
#endif // QGSVECTORTILEMATRIXSET_H

View File

@ -63,6 +63,10 @@ class TestQgsTiles(unittest.TestCase):
def testQgsTileMatrixSet(self):
matrix_set = QgsTileMatrixSet()
# should be applied by default in order to match MapBox rendering of tiles
self.assertTrue(matrix_set.applyTileScaleDoublingHack())
self.assertEqual(matrix_set.minimumZoom(), -1)
self.assertEqual(matrix_set.maximumZoom(), -1)
self.assertFalse(matrix_set.crs().isValid())
@ -132,6 +136,25 @@ class TestQgsTiles(unittest.TestCase):
self.assertEqual(matrix_set.scaleToZoomLevel(198251572), 2)
self.assertEqual(matrix_set.scaleToZoomLevel(6503144), 3)
# disable scale doubling hack
matrix_set.setApplyTileScaleDoublingHack(False)
self.assertFalse(matrix_set.applyTileScaleDoublingHack())
self.assertAlmostEqual(matrix_set.scaleToZoom(776503144), 1, 5)
self.assertEqual(matrix_set.scaleToZoom(1776503144), 1)
self.assertAlmostEqual(matrix_set.scaleToZoom(388251572), 2, 5)
self.assertAlmostEqual(matrix_set.scaleToZoom(288251572), 2.515, 2)
self.assertAlmostEqual(matrix_set.scaleToZoom(194125786), 3, 5)
self.assertAlmostEqual(matrix_set.scaleToZoom(188251572), 3.0, 3)
self.assertEqual(matrix_set.scaleToZoom(6503144), 3)
self.assertEqual(matrix_set.scaleToZoomLevel(776503144), 1)
self.assertEqual(matrix_set.scaleToZoomLevel(1776503144), 1)
self.assertEqual(matrix_set.scaleToZoomLevel(76503144), 3)
self.assertEqual(matrix_set.scaleToZoomLevel(388251572), 2)
self.assertEqual(matrix_set.scaleToZoomLevel(298251572), 2)
self.assertEqual(matrix_set.scaleToZoomLevel(198251572), 3)
self.assertEqual(matrix_set.scaleToZoomLevel(6503144), 3)
def testTileMatrixSetGoogle(self):
matrix_set = QgsTileMatrixSet()
matrix_set.addGoogleCrs84QuadTiles(1, 13)
@ -176,15 +199,6 @@ class TestQgsTiles(unittest.TestCase):
def testVectorTileMatrixSet(self):
matrix_set = QgsVectorTileMatrixSet()
matrix_set.setZ0xMinimum(-1000)
matrix_set.setZ0xMaximum(1300)
matrix_set.setZ0yMinimum(3000)
matrix_set.setZ0yMaximum(4300)
self.assertEqual(matrix_set.z0xMinimum(), -1000)
self.assertEqual(matrix_set.z0xMaximum(), 1300)
self.assertEqual(matrix_set.z0yMinimum(), 3000)
self.assertEqual(matrix_set.z0yMaximum(), 4300)
matrix_set.addMatrix(
QgsTileMatrix.fromCustomDef(1, QgsCoordinateReferenceSystem('EPSG:4326'), QgsPointXY(1, 2), 1000, 4, 8))
@ -201,11 +215,6 @@ class TestQgsTiles(unittest.TestCase):
self.assertEqual(set2.minimumZoom(), 1)
self.assertEqual(set2.maximumZoom(), 3)
self.assertEqual(set2.z0xMinimum(), -1000)
self.assertEqual(set2.z0xMaximum(), 1300)
self.assertEqual(set2.z0yMinimum(), 3000)
self.assertEqual(set2.z0yMaximum(), 4300)
self.assertEqual(set2.tileMatrix(1).crs().authid(), 'EPSG:4326')
self.assertEqual(set2.tileMatrix(2).crs().authid(), 'EPSG:4326')
self.assertEqual(set2.tileMatrix(3).crs().authid(), 'EPSG:3857')
@ -347,6 +356,10 @@ class TestQgsTiles(unittest.TestCase):
self.assertFalse(vector_tile_set.fromEsriJson({}))
self.assertTrue(vector_tile_set.fromEsriJson(esri_metadata))
# we should NOT apply the tile scale doubling hack to ESRI tiles, otherwise our scales
# are double what ESRI use for the same tile sets
self.assertFalse(vector_tile_set.applyTileScaleDoublingHack())
self.assertEqual(vector_tile_set.minimumZoom(), 0)
self.assertEqual(vector_tile_set.maximumZoom(), 14)