QGIS API Documentation  3.27.0-Master (11ef3e5184)
qgstiles.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstiles.cpp
3  --------------------------------------
4  Date : March 2020
5  Copyright : (C) 2020 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgstiles.h"
17 
18 #include "qgslogger.h"
20 #include "qgsrendercontext.h"
21 
23 {
24  constexpr double z0xMin = -20037508.3427892;
25  constexpr double z0yMax = 20037508.3427892;
26 
27  return fromCustomDef( zoomLevel, QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), QgsPointXY( z0xMin, z0yMax ), 2 * z0yMax );
28 }
29 
31  const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth, int z0MatrixHeight )
32 {
33  // Square extent calculation
34  double z0xMin = z0TopLeftPoint.x();
35  double z0yMax = z0TopLeftPoint.y();
36  double z0xMax = z0xMin + z0MatrixWidth * z0Dimension;
37  double z0yMin = z0yMax - z0MatrixHeight * z0Dimension;
38 
39  // Constant for scale denominator calculation
40  constexpr double TILE_SIZE = 256.0;
41  constexpr double PIXELS_TO_M = 2.8 / 10000.0; // WMS/WMTS define "standardized rendering pixel size" as 0.28mm
43  // Scale denominator calculation
44  const double scaleDenom0 = ( z0Dimension / TILE_SIZE ) * ( unitToMeters / PIXELS_TO_M );
45 
46  int numTiles = static_cast<int>( pow( 2, zoomLevel ) ); // assuming we won't ever go over 30 zoom levels
47 
48  QgsTileMatrix tm;
49  tm.mCrs = crs;
50  tm.mZoomLevel = zoomLevel;
51  tm.mMatrixWidth = z0MatrixWidth * numTiles;
52  tm.mMatrixHeight = z0MatrixHeight * numTiles;
53  tm.mTileXSpan = ( z0xMax - z0xMin ) / tm.mMatrixWidth;
54  tm.mTileYSpan = ( z0yMax - z0yMin ) / tm.mMatrixHeight;
55  tm.mExtent = QgsRectangle( z0xMin, z0yMin, z0xMax, z0yMax );
56  tm.mScaleDenom = scaleDenom0 / pow( 2, zoomLevel );
57  return tm;
58 }
59 
60 QgsTileMatrix QgsTileMatrix::fromTileMatrix( const int zoomLevel, const QgsTileMatrix &tileMatrix )
61 {
62  QgsTileMatrix tm;
63  int numTiles = static_cast<int>( pow( 2, zoomLevel ) ); // assuming we won't ever go over 30 zoom levels
64  int aZoomLevel = tileMatrix.zoomLevel();
65  int aNumTiles = static_cast<int>( pow( 2, aZoomLevel ) );
66  int aMatrixWidth = tileMatrix.matrixWidth();
67  int aMatrixHeight = tileMatrix.matrixHeight();
68  QgsRectangle aExtent = tileMatrix.extent();
69  tm.mCrs = tileMatrix.crs();
70  tm.mZoomLevel = zoomLevel;
71  tm.mMatrixWidth = aMatrixWidth * numTiles / aNumTiles;
72  tm.mMatrixHeight = aMatrixHeight * numTiles / aNumTiles;
73  tm.mTileXSpan = aExtent.width() / tm.mMatrixWidth;
74  tm.mTileYSpan = aExtent.height() / tm.mMatrixHeight;
75  tm.mExtent = aExtent;
76  tm.mScaleDenom = tileMatrix.scale() * pow( 2, aZoomLevel ) / pow( 2, zoomLevel );
77  return tm;
78 }
79 
81 {
82  double xMin = mExtent.xMinimum() + mTileXSpan * id.column();
83  double xMax = xMin + mTileXSpan;
84  double yMax = mExtent.yMaximum() - mTileYSpan * id.row();
85  double yMin = yMax - mTileYSpan;
86  return QgsRectangle( xMin, yMin, xMax, yMax );
87 }
88 
90 {
91  double x = mExtent.xMinimum() + mTileXSpan * id.column() + mTileXSpan / 2;
92  double y = mExtent.yMaximum() - mTileYSpan * id.row() - mTileYSpan / 2;
93  return QgsPointXY( x, y );
94 }
95 
97 {
98  double x0 = std::clamp( r.xMinimum(), mExtent.xMinimum(), mExtent.xMaximum() );
99  double y0 = std::clamp( r.yMinimum(), mExtent.yMinimum(), mExtent.yMaximum() );
100  double x1 = std::clamp( r.xMaximum(), mExtent.xMinimum(), mExtent.xMaximum() );
101  double y1 = std::clamp( r.yMaximum(), mExtent.yMinimum(), mExtent.yMaximum() );
102  if ( x0 >= x1 || y0 >= y1 )
103  return QgsTileRange(); // nothing to display
104 
105  double tileX1 = ( x0 - mExtent.xMinimum() ) / mTileXSpan;
106  double tileX2 = ( x1 - mExtent.xMinimum() ) / mTileXSpan;
107  double tileY1 = ( mExtent.yMaximum() - y1 ) / mTileYSpan;
108  double tileY2 = ( mExtent.yMaximum() - y0 ) / mTileYSpan;
109 
110  QgsDebugMsgLevel( QStringLiteral( "Tile range of edges [%1,%2] - [%3,%4]" ).arg( tileX1 ).arg( tileY1 ).arg( tileX2 ).arg( tileY2 ), 2 );
111 
112  // figure out tile range from zoom
113  int startColumn = std::clamp( static_cast<int>( floor( tileX1 ) ), 0, mMatrixWidth - 1 );
114  int endColumn = std::clamp( static_cast<int>( floor( tileX2 ) ), 0, mMatrixWidth - 1 );
115  int startRow = std::clamp( static_cast<int>( floor( tileY1 ) ), 0, mMatrixHeight - 1 );
116  int endRow = std::clamp( static_cast<int>( floor( tileY2 ) ), 0, mMatrixHeight - 1 );
117  return QgsTileRange( startColumn, endColumn, startRow, endRow );
118 }
119 
120 QPointF QgsTileMatrix::mapToTileCoordinates( const QgsPointXY &mapPoint ) const
121 {
122  double dx = mapPoint.x() - mExtent.xMinimum();
123  double dy = mExtent.yMaximum() - mapPoint.y();
124  return QPointF( dx / mTileXSpan, dy / mTileYSpan );
125 }
126 
127 //
128 // QgsTileMatrixSet
129 //
130 
132 {
133  return mTileMatrices.isEmpty();
134 }
135 
136 void QgsTileMatrixSet::addGoogleCrs84QuadTiles( int minimumZoom, int maximumZoom )
137 {
138  if ( maximumZoom < minimumZoom )
139  std::swap( minimumZoom, maximumZoom );
140 
141  for ( int zoom = minimumZoom; zoom <= maximumZoom; ++zoom )
142  {
144  }
145 
146  mRootMatrix = QgsTileMatrix::fromWebMercator( 0 );
147 }
148 
150 {
151  return mTileMatrices.value( zoom );
152 }
153 
155 {
156  return mRootMatrix;
157 }
158 
160 {
161  mRootMatrix = matrix;
162 }
163 
165 {
166  mTileMatrices.insert( matrix.zoomLevel(), matrix );
167 }
168 
170 {
171  int res = -1;
172  for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
173  {
174  if ( res == -1 || it->zoomLevel() < res )
175  res = it->zoomLevel();
176  }
177  return res;
178 }
179 
181 {
182  int res = -1;
183  for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
184  {
185  if ( res == -1 || it->zoomLevel() > res )
186  res = it->zoomLevel();
187  }
188  return res;
189 }
190 
191 void QgsTileMatrixSet::dropMatricesOutsideZoomRange( int minimumZoom, int maximumZoom )
192 {
193  for ( auto it = mTileMatrices.begin(); it != mTileMatrices.end(); )
194  {
195  if ( it->zoomLevel() < minimumZoom || it->zoomLevel() > maximumZoom )
196  {
197  it = mTileMatrices.erase( it );
198  }
199  else
200  {
201  ++it;
202  }
203  }
204 }
205 
207 {
208  if ( mTileMatrices.empty() )
210 
211  return mTileMatrices.value( minimumZoom() ).crs();
212 }
213 
214 double QgsTileMatrixSet::scaleToZoom( double scale ) const
215 {
216  int zoomUnder = -1;
217  int zoomOver = -1;
218  double scaleUnder = 0;
219  double scaleOver = 0;
220 
221  switch ( mScaleToTileZoomMethod )
222  {
224  {
225  // TODO: it seems that map scale is double (is that because of high-dpi screen?)
226  // (this TODO was taken straight from QgsVectorTileUtils::scaleToZoom!)
227  scale *= 2;
228  break;
229  }
231  break;
232  }
233 
234  for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
235  {
236  if ( it->scale() > scale && ( zoomUnder == -1 || zoomUnder < it->zoomLevel() ) )
237  {
238  zoomUnder = it->zoomLevel();
239  scaleUnder = it->scale();
240  }
241  if ( it->scale() < scale && ( zoomOver == -1 || zoomOver > it->zoomLevel() ) )
242  {
243  zoomOver = it->zoomLevel();
244  scaleOver = it->scale();
245  }
246  }
247 
248  if ( zoomUnder < 0 )
249  return zoomOver;
250  if ( zoomOver < 0 )
251  return zoomUnder;
252  else
253  return ( scaleUnder - scale ) / ( scaleUnder - scaleOver ) * ( zoomOver - zoomUnder ) + zoomUnder;
254 }
255 
256 int QgsTileMatrixSet::scaleToZoomLevel( double scale ) const
257 {
258  int tileZoom = 0;
259  switch ( mScaleToTileZoomMethod )
260  {
262  tileZoom = static_cast<int>( round( scaleToZoom( scale ) ) );
263  break;
265  tileZoom = static_cast<int>( floor( scaleToZoom( scale ) ) );
266  break;
267  }
268 
269  return std::clamp( tileZoom, minimumZoom(), maximumZoom() );
270 }
271 
273 {
274  return calculateTileScaleForMap( context.rendererScale(),
276  context.mapExtent(),
277  context.outputSize(),
278  context.painter()->device()->logicalDpiX() );
279 }
280 
281 double QgsTileMatrixSet::calculateTileScaleForMap( double actualMapScale, const QgsCoordinateReferenceSystem &mapCrs, const QgsRectangle &mapExtent, const QSize mapSize, const double mapDpi ) const
282 {
283  switch ( mScaleToTileZoomMethod )
284  {
286  return actualMapScale;
287 
289  if ( mapCrs.isGeographic() )
290  {
291  // ESRI calculates the scale for geographic CRS ***ALWAYS*** at the equator, regardless of map extent!
292  // see https://support.esri.com/en/technical-article/000007211, https://gis.stackexchange.com/questions/33270/how-does-arcmap-calculate-scalebar-inside-a-wgs84-layout
293  constexpr double METERS_PER_DEGREE = M_PI / 180.0 * 6378137;
294  constexpr double INCHES_PER_METER = 39.370078;
295  const double mapWidthInches = mapExtent.width() * METERS_PER_DEGREE * INCHES_PER_METER;
296 
297  double scale = mapWidthInches * mapDpi / static_cast< double >( mapSize.width() );
298 
299  // Note: I **think** there's also some magic which ESRI applies when rendering tiles ON SCREEN,
300  // which may be something like adjusting the scale based on the ratio between the map DPI and 96 DPI,
301  // e.g. scale *= mapDpi / 96.0;
302  // BUT the same adjustment isn't applied when exporting maps. This needs further investigation!
303 
304  return scale;
305  }
306  else
307  {
308  return actualMapScale;
309  }
310  }
312 }
313 
314 bool QgsTileMatrixSet::readXml( const QDomElement &element, QgsReadWriteContext & )
315 {
316  mTileMatrices.clear();
317 
318  mScaleToTileZoomMethod = qgsEnumKeyToValue( element.attribute( QStringLiteral( "scaleToZoomMethod" ) ), Qgis::ScaleToTileZoomLevelMethod::MapBox );
319 
320  auto readMatrixFromElement = []( const QDomElement & matrixElement )->QgsTileMatrix
321  {
322  QgsTileMatrix matrix;
323  matrix.mZoomLevel = matrixElement.attribute( QStringLiteral( "zoomLevel" ) ).toInt();
324  matrix.mMatrixWidth = matrixElement.attribute( QStringLiteral( "matrixWidth" ) ).toInt();
325  matrix.mMatrixHeight = matrixElement.attribute( QStringLiteral( "matrixHeight" ) ).toInt();
326  matrix.mExtent = QgsRectangle(
327  matrixElement.attribute( QStringLiteral( "xMin" ) ).toDouble(),
328  matrixElement.attribute( QStringLiteral( "yMin" ) ).toDouble(),
329  matrixElement.attribute( QStringLiteral( "xMax" ) ).toDouble(),
330  matrixElement.attribute( QStringLiteral( "yMax" ) ).toDouble()
331  );
332 
333  matrix.mScaleDenom = matrixElement.attribute( QStringLiteral( "scale" ) ).toDouble();
334  matrix.mTileXSpan = matrixElement.attribute( QStringLiteral( "tileXSpan" ) ).toDouble();
335  matrix.mTileYSpan = matrixElement.attribute( QStringLiteral( "tileYSpan" ) ).toDouble();
336  matrix.mCrs.readXml( matrixElement );
337  return matrix;
338  };
339 
340  const QDomNodeList children = element.childNodes();
341  for ( int i = 0; i < children.size(); i++ )
342  {
343  const QDomElement matrixElement = children.at( i ).toElement();
344  if ( matrixElement.tagName() == QLatin1String( "rootMatrix" ) )
345  continue;
346 
347  QgsTileMatrix matrix = readMatrixFromElement( matrixElement );
348  if ( matrix.zoomLevel() == 0 ) // old project compatibility
349  mRootMatrix = matrix;
350 
351  addMatrix( matrix );
352  }
353 
354  const QDomElement rootElement = element.firstChildElement( QStringLiteral( "rootMatrix" ) );
355  if ( !rootElement.isNull() )
356  {
357  mRootMatrix = readMatrixFromElement( rootElement );
358  }
359 
360  return true;
361 }
362 
363 QDomElement QgsTileMatrixSet::writeXml( QDomDocument &document, const QgsReadWriteContext & ) const
364 {
365  QDomElement setElement = document.createElement( QStringLiteral( "matrixSet" ) );
366  setElement.setAttribute( QStringLiteral( "scaleToZoomMethod" ), qgsEnumValueToKey( mScaleToTileZoomMethod ) );
367 
368  auto writeMatrixToElement = [&document]( const QgsTileMatrix & matrix, QDomElement & matrixElement )
369  {
370  matrixElement.setAttribute( QStringLiteral( "zoomLevel" ), matrix.zoomLevel() );
371  matrixElement.setAttribute( QStringLiteral( "matrixWidth" ), matrix.matrixWidth() );
372  matrixElement.setAttribute( QStringLiteral( "matrixHeight" ), matrix.matrixHeight() );
373 
374  matrixElement.setAttribute( QStringLiteral( "xMin" ), qgsDoubleToString( matrix.mExtent.xMinimum() ) );
375  matrixElement.setAttribute( QStringLiteral( "xMax" ), qgsDoubleToString( matrix.mExtent.xMaximum() ) );
376  matrixElement.setAttribute( QStringLiteral( "yMin" ), qgsDoubleToString( matrix.mExtent.yMinimum() ) );
377  matrixElement.setAttribute( QStringLiteral( "yMax" ), qgsDoubleToString( matrix.mExtent.yMaximum() ) );
378 
379  matrixElement.setAttribute( QStringLiteral( "scale" ), qgsDoubleToString( matrix.scale() ) );
380  matrixElement.setAttribute( QStringLiteral( "tileXSpan" ), qgsDoubleToString( matrix.mTileXSpan ) );
381  matrixElement.setAttribute( QStringLiteral( "tileYSpan" ), qgsDoubleToString( matrix.mTileYSpan ) );
382 
383  matrix.crs().writeXml( matrixElement, document );
384  };
385 
386  for ( auto it = mTileMatrices.constBegin(); it != mTileMatrices.constEnd(); ++it )
387  {
388  QDomElement matrixElement = document.createElement( QStringLiteral( "matrix" ) );
389  writeMatrixToElement( *it, matrixElement );
390  setElement.appendChild( matrixElement );
391  }
392 
393  QDomElement rootElement = document.createElement( QStringLiteral( "rootMatrix" ) );
394  writeMatrixToElement( mRootMatrix, rootElement );
395  setElement.appendChild( rootElement );
396 
397  return setElement;
398 }
@ Esri
No scale doubling, always rounds down when matching to available tile levels.
@ MapBox
Uses a scale doubling approach to account for hi-DPI tiles, and rounds to the nearest tile level for ...
This class represents a coordinate reference system (CRS).
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
double rendererScale() const
Returns the renderer map scale.
QSize outputSize() const
Returns the size of the resulting rendered image, in pixels.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
virtual QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Writes the set to an XML element.
Definition: qgstiles.cpp:363
void addGoogleCrs84QuadTiles(int minimumZoom=0, int maximumZoom=14)
Adds tile matrices corresponding to the standard web mercator/GoogleCRS84Quad setup.
Definition: qgstiles.cpp:136
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system associated with the tiles.
Definition: qgstiles.cpp:206
double scaleForRenderContext(const QgsRenderContext &context) const
Calculates the correct scale to use for the tiles when rendered using the specified render context.
Definition: qgstiles.cpp:272
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition: qgstiles.cpp:169
int scaleToZoomLevel(double scale) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition: qgstiles.cpp:256
double scaleToZoom(double scale) const
Calculates a fractional zoom level given a map scale denominator.
Definition: qgstiles.cpp:214
int maximumZoom() const
Returns the maximum zoom level for tiles present in the set.
Definition: qgstiles.cpp:180
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition: qgstiles.cpp:149
QgsTileMatrix rootMatrix() const
Returns the root tile matrix (usually corresponding to zoom level 0).
Definition: qgstiles.cpp:154
virtual bool readXml(const QDomElement &element, QgsReadWriteContext &context)
Reads the set from an XML element.
Definition: qgstiles.cpp:314
void addMatrix(const QgsTileMatrix &matrix)
Adds a matrix to the set.
Definition: qgstiles.cpp:164
double calculateTileScaleForMap(double actualMapScale, const QgsCoordinateReferenceSystem &mapCrs, const QgsRectangle &mapExtent, const QSize mapSize, const double mapDpi) const
Calculates the correct scale to use for the tiles when rendered using the specified map properties.
Definition: qgstiles.cpp:281
void dropMatricesOutsideZoomRange(int minimumZoom, int maximumZoom)
Deletes any existing matrices which fall outside the zoom range specified by minimumZoom to maximumZo...
Definition: qgstiles.cpp:191
bool isEmpty() const
Returns true if the matrix set is empty.
Definition: qgstiles.cpp:131
void setRootMatrix(const QgsTileMatrix &matrix)
Sets the root tile matrix (usually corresponding to zoom level 0).
Definition: qgstiles.cpp:159
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:108
QgsRectangle tileExtent(QgsTileXYZ id) const
Returns extent of the given tile in this matrix.
Definition: qgstiles.cpp:80
QPointF mapToTileCoordinates(const QgsPointXY &mapPoint) const
Returns row/column coordinates (floating point number) from the given point in map coordinates.
Definition: qgstiles.cpp:120
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:96
static QgsTileMatrix fromWebMercator(int zoomLevel)
Returns a tile matrix for the usual web mercator.
Definition: qgstiles.cpp:22
QgsRectangle extent() const
Returns extent of the tile matrix.
Definition: qgstiles.h:163
int matrixWidth() const
Returns number of columns of the tile matrix.
Definition: qgstiles.h:157
QgsCoordinateReferenceSystem crs() const
Returns the crs of the tile matrix.
Definition: qgstiles.h:131
double scale() const
Returns scale denominator of the tile matrix.
Definition: qgstiles.h:170
QgsPointXY tileCenter(QgsTileXYZ id) const
Returns center of the given tile in this matrix.
Definition: qgstiles.cpp:89
int matrixHeight() const
Returns number of rows of the tile matrix.
Definition: qgstiles.h:160
static QgsTileMatrix fromTileMatrix(int zoomLevel, const QgsTileMatrix &tileMatrix)
Returns a tile matrix based on another one.
Definition: qgstiles.cpp:60
int zoomLevel() const
Returns the zoom level of the tile matrix.
Definition: qgstiles.h:146
static QgsTileMatrix fromCustomDef(int zoomLevel, const QgsCoordinateReferenceSystem &crs, const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth=1, int z0MatrixHeight=1)
Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units.
Definition: qgstiles.cpp:30
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:71
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition: qgis.h:2700
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2466
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:2681
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3148
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
const QgsCoordinateReferenceSystem & crs