QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsterrainprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsterrainprovider.cpp
3 ---------------
4 begin : February 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include "qgsterrainprovider.h"
18#include "qgsmeshlayerutils.h"
19#include "qgsrasterbandstats.h"
20#include <QThread>
21
23
25{
26
27}
28
30 : mScale( other.mScale )
31 , mOffset( other.mOffset )
32{
33
34}
35
37{
38 element.setAttribute( QStringLiteral( "offset" ), qgsDoubleToString( mOffset ) );
39 element.setAttribute( QStringLiteral( "scale" ), qgsDoubleToString( mScale ) );
40}
41
43{
44 mOffset = element.attribute( QStringLiteral( "offset" ) ).toDouble();
45 mScale = element.attribute( QStringLiteral( "scale" ) ).toDouble();
46}
47
48//
49// QgsFlatTerrainProvider
50//
51
53{
54 return QStringLiteral( "flat" );
55}
56
57bool QgsFlatTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
58{
59 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
60 if ( terrainElement.isNull() )
61 return false;
62
63 readCommonProperties( terrainElement, context );
64 return true;
65}
66
67QDomElement QgsFlatTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
68{
69 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
70 writeCommonProperties( element, context );
71 return element;
72}
73
75{
77}
78
79double QgsFlatTerrainProvider::heightAt( double, double ) const
80{
81 return mOffset;
82}
83
85{
86 return new QgsFlatTerrainProvider( *this );
87}
88
90{
91 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsFlatTerrainProvider::prepare", "prepare() must be called from the main thread" );
92
93}
94
96{
97 if ( other->type() != type() )
98 return false;
99
100 const QgsFlatTerrainProvider *otherTerrain = qgis::down_cast< const QgsFlatTerrainProvider * >( other );
101
102 return qgsDoubleNear( otherTerrain->offset(), mOffset );
103}
104
105
106//
107// QgsRasterDemTerrainProvider
108//
109
111{
112 return QStringLiteral( "raster" );
113}
114
116{
117 if ( mRasterLayer )
118 return; // already assigned
119
120 mRasterLayer.resolve( project );
121}
122
123bool QgsRasterDemTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
124{
125 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
126 if ( terrainElement.isNull() )
127 return false;
128
129 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
130 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
131 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
132 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
133 mRasterLayer = _LayerRef<QgsRasterLayer>( layerId, layerName, layerSource, layerProvider );
134
135 readCommonProperties( terrainElement, context );
136 return true;
137}
138
139QDomElement QgsRasterDemTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
140{
141 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
142 if ( mRasterLayer )
143 {
144 element.setAttribute( QStringLiteral( "layer" ), mRasterLayer.layerId );
145 element.setAttribute( QStringLiteral( "layerName" ), mRasterLayer.name );
146 element.setAttribute( QStringLiteral( "layerSource" ), mRasterLayer.source );
147 element.setAttribute( QStringLiteral( "layerProvider" ), mRasterLayer.provider );
148 }
149
150 writeCommonProperties( element, context );
151 return element;
152}
153
155{
156 return mRasterProvider ? mRasterProvider->crs()
157 : ( mRasterLayer ? mRasterLayer->crs() : QgsCoordinateReferenceSystem() );
158}
159
160double QgsRasterDemTerrainProvider::heightAt( double x, double y ) const
161{
162 // TODO -- may want to use a more efficient approach here, i.e. requesting whole
163 // blocks upfront instead of multiple sample calls
164 bool ok = false;
165 double res = std::numeric_limits<double>::quiet_NaN();
166 if ( mRasterProvider )
167 {
168 res = mRasterProvider->sample( QgsPointXY( x, y ), 1, &ok );
169 }
170 else if ( QThread::currentThread() == QCoreApplication::instance()->thread() && mRasterLayer && mRasterLayer->isValid() )
171 {
172 res = mRasterLayer->dataProvider()->sample( QgsPointXY( x, y ), 1, &ok );
173 }
174
175 if ( ok )
176 return res * mScale + mOffset;
177
178 return std::numeric_limits<double>::quiet_NaN();
179}
180
182{
183 return new QgsRasterDemTerrainProvider( *this );
184}
185
187{
188 if ( other->type() != type() )
189 return false;
190
191 const QgsRasterDemTerrainProvider *otherTerrain = qgis::down_cast< const QgsRasterDemTerrainProvider * >( other );
192 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
193 || !qgsDoubleNear( otherTerrain->scale(), mScale )
194 || mRasterLayer.get() != otherTerrain->layer() )
195 return false;
196
197 return true;
198}
199
201{
202 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsRasterDemTerrainProvider::prepare", "prepare() must be called from the main thread" );
203
204 if ( mRasterLayer && mRasterLayer->isValid() )
205 mRasterProvider.reset( mRasterLayer->dataProvider()->clone() );
206}
207
209{
210 mRasterLayer.setLayer( layer );
211}
212
214{
215 return mRasterLayer.get();
216}
217
220 , mRasterLayer( other.mRasterLayer )
221{
222
223}
224
225
226//
227// QgsMeshTerrainProvider
228//
229
231{
232 return QStringLiteral( "mesh" );
233}
234
236{
237 if ( mMeshLayer )
238 return; // already assigned
239
240 mMeshLayer.resolve( project );
241}
242
243bool QgsMeshTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
244{
245 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
246 if ( terrainElement.isNull() )
247 return false;
248
249 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
250 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
251 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
252 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
253 mMeshLayer = _LayerRef<QgsMeshLayer>( layerId, layerName, layerSource, layerProvider );
254
255 readCommonProperties( terrainElement, context );
256 return true;
257}
258
259QDomElement QgsMeshTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
260{
261 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
262 if ( mMeshLayer )
263 {
264 element.setAttribute( QStringLiteral( "layer" ), mMeshLayer.layerId );
265 element.setAttribute( QStringLiteral( "layerName" ), mMeshLayer.name );
266 element.setAttribute( QStringLiteral( "layerSource" ), mMeshLayer.source );
267 element.setAttribute( QStringLiteral( "layerProvider" ), mMeshLayer.provider );
268 }
269
270 writeCommonProperties( element, context );
271 return element;
272}
273
275{
276 return mMeshLayer ? mMeshLayer->crs() : QgsCoordinateReferenceSystem();
277}
278
279double QgsMeshTerrainProvider::heightAt( double x, double y ) const
280{
281 if ( mTriangularMesh.vertices().empty() && mMeshLayer && QThread::currentThread() == QCoreApplication::instance()->thread() )
282 const_cast< QgsMeshTerrainProvider * >( this )->prepare(); // auto prepare if we are on main thread and haven't already!
283
284 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
285}
286
288{
289 return new QgsMeshTerrainProvider( *this );
290}
291
293{
294 if ( other->type() != type() )
295 return false;
296
297 const QgsMeshTerrainProvider *otherTerrain = qgis::down_cast< const QgsMeshTerrainProvider * >( other );
298 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
299 || !qgsDoubleNear( otherTerrain->scale(), mScale )
300 || mMeshLayer.get() != otherTerrain->layer() )
301 return false;
302
303 return true;
304}
305
307{
308 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsMeshTerrainProvider::prepare", "prepare() must be called from the main thread" );
309 if ( mMeshLayer )
310 {
311 mMeshLayer->updateTriangularMesh();
312 mTriangularMesh = *mMeshLayer->triangularMesh();
313 }
314}
315
317{
318 mMeshLayer.setLayer( layer );
319}
320
322{
323 return mMeshLayer.get();
324}
325
328 , mMeshLayer( other.mMeshLayer )
329{
330
331}
Abstract base class for terrain providers.
virtual QString type() const =0
Returns the unique type ID string for the provider.
void readCommonProperties(const QDomElement &element, const QgsReadWriteContext &context)
Reads common properties from a DOM element.
virtual ~QgsAbstractTerrainProvider()
void writeCommonProperties(QDomElement &element, const QgsReadWriteContext &context) const
Writes common properties to a DOM element.
double offset() const
Returns the vertical offset value, used for adjusting the heights from the terrain provider.
double scale() const
Returns the vertical scale factor, which can be used to exaggerate vertical heights.
virtual void resolveReferences(const QgsProject *project)
Resolves reference to layers from stored layer ID (if it has not been resolved already)
QgsAbstractTerrainProvider()=default
Constructor for QgsAbstractTerrainProvider.
double mOffset
Offset amount.
This class represents a coordinate reference system (CRS).
A terrain provider where the terrain is a simple flat surface.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the terrain provider state from a DOM element.
bool equals(const QgsAbstractTerrainProvider *other) const override
Returns true if the provider is equal to other.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const override
Returns a DOM element representing the state of the terrain provider.
QgsFlatTerrainProvider * clone() const override
Creates a clone of the provider and returns the new object.
QgsCoordinateReferenceSystem crs() const override
Returns the native coordinate reference system of the terrain provider.
QgsFlatTerrainProvider()=default
Constructor for QgsFlatTerrainProvider.
void prepare() override
Called on the main thread prior to accessing the provider from a background thread.
double heightAt(double x, double y) const override
Returns the height at the point (x,y) in the terrain provider's native crs().
QString type() const override
Returns the unique type ID string for the provider.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
bool isValid
Definition: qgsmaplayer.h:83
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:101
void updateTriangularMesh(const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Gets native mesh and updates (creates if it doesn't exist) the base triangular mesh.
QgsTriangularMesh * triangularMesh(double minimumTriangleSize=0) const
Returns triangular mesh (nullptr before rendering or calling to updateMesh).
A terrain provider that uses the Z values of a mesh layer to build a terrain surface.
void setLayer(QgsMeshLayer *layer)
Sets the mesh layer to be used as the terrain source.
QgsMeshTerrainProvider()=default
Constructor for QgsMeshTerrainProvider.
QgsMeshTerrainProvider * clone() const override
Creates a clone of the provider and returns the new object.
bool equals(const QgsAbstractTerrainProvider *other) const override
Returns true if the provider is equal to other.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const override
Returns a DOM element representing the state of the terrain provider.
void prepare() override
Called on the main thread prior to accessing the provider from a background thread.
QString type() const override
Returns the unique type ID string for the provider.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the terrain provider state from a DOM element.
void resolveReferences(const QgsProject *project) override
Resolves reference to layers from stored layer ID (if it has not been resolved already)
double heightAt(double x, double y) const override
Returns the height at the point (x,y) in the terrain provider's native crs().
QgsMeshLayer * layer() const
Returns the mesh layer to be used as the terrain source.
QgsCoordinateReferenceSystem crs() const override
Returns the native coordinate reference system of the terrain provider.
A class to represent a 2D point.
Definition: qgspointxy.h:60
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
virtual double sample(const QgsPointXY &point, int band, bool *ok=nullptr, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Samples a raster value from the specified band found at the point position.
A terrain provider where the terrain source is a raster DEM layer.
QgsRasterDemTerrainProvider * clone() const override
Creates a clone of the provider and returns the new object.
void setLayer(QgsRasterLayer *layer)
Sets the raster layer with elevation model to be used as the terrain source.
QgsCoordinateReferenceSystem crs() const override
Returns the native coordinate reference system of the terrain provider.
void prepare() override
Called on the main thread prior to accessing the provider from a background thread.
void resolveReferences(const QgsProject *project) override
Resolves reference to layers from stored layer ID (if it has not been resolved already)
QgsRasterLayer * layer() const
Returns the raster layer with elevation model to be used as the terrain source.
bool equals(const QgsAbstractTerrainProvider *other) const override
Returns true if the provider is equal to other.
double heightAt(double x, double y) const override
Returns the height at the point (x,y) in the terrain provider's native crs().
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const override
Returns a DOM element representing the state of the terrain provider.
QString type() const override
Returns the unique type ID string for the provider.
QgsRasterDemTerrainProvider()=default
Constructor for QgsRasterDemTerrainProvider.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the terrain provider state from a DOM element.
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
The class is used as a container of context for various read/write operations on other objects.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:5124
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
QString source
Weak reference to layer public source.
QString name
Weak reference to layer name.
TYPE * get() const
Returns a pointer to the layer, or nullptr if the reference has not yet been matched to a layer.
QString provider
Weak reference to layer provider.
TYPE * resolve(const QgsProject *project)
Resolves the map layer by attempting to find a layer with matching ID within a project.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.
QString layerId
Original layer ID.