QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 <QThread>
20
22
24{
25
26}
27
29 : mScale( other.mScale )
30 , mOffset( other.mOffset )
31{
32
33}
34
36{
37 element.setAttribute( QStringLiteral( "offset" ), qgsDoubleToString( mOffset ) );
38 element.setAttribute( QStringLiteral( "scale" ), qgsDoubleToString( mScale ) );
39}
40
42{
43 mOffset = element.attribute( QStringLiteral( "offset" ) ).toDouble();
44 mScale = element.attribute( QStringLiteral( "scale" ) ).toDouble();
45}
46
47//
48// QgsFlatTerrainProvider
49//
50
52{
53 return QStringLiteral( "flat" );
54}
55
56bool QgsFlatTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
57{
58 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
59 if ( terrainElement.isNull() )
60 return false;
61
62 readCommonProperties( terrainElement, context );
63 return true;
64}
65
66QDomElement QgsFlatTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
67{
68 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
69 writeCommonProperties( element, context );
70 return element;
71}
72
74{
76}
77
78double QgsFlatTerrainProvider::heightAt( double, double ) const
79{
80 return mOffset;
81}
82
84{
85 return new QgsFlatTerrainProvider( *this );
86}
87
89{
90 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsFlatTerrainProvider::prepare", "prepare() must be called from the main thread" );
91
92}
93
95{
96 if ( other->type() != type() )
97 return false;
98
99 const QgsFlatTerrainProvider *otherTerrain = qgis::down_cast< const QgsFlatTerrainProvider * >( other );
100
101 return qgsDoubleNear( otherTerrain->offset(), mOffset );
102}
103
104
105//
106// QgsRasterDemTerrainProvider
107//
108
110{
111 return QStringLiteral( "raster" );
112}
113
115{
116 if ( mRasterLayer )
117 return; // already assigned
118
119 mRasterLayer.resolve( project );
120}
121
122bool QgsRasterDemTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
123{
124 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
125 if ( terrainElement.isNull() )
126 return false;
127
128 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
129 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
130 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
131 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
132 mRasterLayer = _LayerRef<QgsRasterLayer>( layerId, layerName, layerSource, layerProvider );
133
134 readCommonProperties( terrainElement, context );
135 return true;
136}
137
138QDomElement QgsRasterDemTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
139{
140 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
141 if ( mRasterLayer )
142 {
143 element.setAttribute( QStringLiteral( "layer" ), mRasterLayer.layerId );
144 element.setAttribute( QStringLiteral( "layerName" ), mRasterLayer.name );
145 element.setAttribute( QStringLiteral( "layerSource" ), mRasterLayer.source );
146 element.setAttribute( QStringLiteral( "layerProvider" ), mRasterLayer.provider );
147 }
148
149 writeCommonProperties( element, context );
150 return element;
151}
152
154{
155 return mRasterProvider ? mRasterProvider->crs()
156 : ( mRasterLayer ? mRasterLayer->crs() : QgsCoordinateReferenceSystem() );
157}
158
159double QgsRasterDemTerrainProvider::heightAt( double x, double y ) const
160{
161 // TODO -- may want to use a more efficient approach here, i.e. requesting whole
162 // blocks upfront instead of multiple sample calls
163 bool ok = false;
164 double res = std::numeric_limits<double>::quiet_NaN();
165 if ( mRasterProvider )
166 {
167 res = mRasterProvider->sample( QgsPointXY( x, y ), 1, &ok );
168 }
169 else if ( QThread::currentThread() == QCoreApplication::instance()->thread() && mRasterLayer && mRasterLayer->isValid() )
170 {
171 res = mRasterLayer->dataProvider()->sample( QgsPointXY( x, y ), 1, &ok );
172 }
173
174 if ( ok )
175 return res * mScale + mOffset;
176
177 return std::numeric_limits<double>::quiet_NaN();
178}
179
181{
182 return new QgsRasterDemTerrainProvider( *this );
183}
184
186{
187 if ( other->type() != type() )
188 return false;
189
190 const QgsRasterDemTerrainProvider *otherTerrain = qgis::down_cast< const QgsRasterDemTerrainProvider * >( other );
191 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
192 || !qgsDoubleNear( otherTerrain->scale(), mScale )
193 || mRasterLayer.get() != otherTerrain->layer() )
194 return false;
195
196 return true;
197}
198
200{
201 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsRasterDemTerrainProvider::prepare", "prepare() must be called from the main thread" );
202
203 if ( mRasterLayer && mRasterLayer->isValid() )
204 mRasterProvider.reset( mRasterLayer->dataProvider()->clone() );
205}
206
208{
209 mRasterLayer.setLayer( layer );
210}
211
213{
214 return mRasterLayer.get();
215}
216
219 , mRasterLayer( other.mRasterLayer )
220{
221
222}
223
224
225//
226// QgsMeshTerrainProvider
227//
228
230{
231 return QStringLiteral( "mesh" );
232}
233
235{
236 if ( mMeshLayer )
237 return; // already assigned
238
239 mMeshLayer.resolve( project );
240}
241
242bool QgsMeshTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
243{
244 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
245 if ( terrainElement.isNull() )
246 return false;
247
248 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
249 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
250 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
251 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
252 mMeshLayer = _LayerRef<QgsMeshLayer>( layerId, layerName, layerSource, layerProvider );
253
254 readCommonProperties( terrainElement, context );
255 return true;
256}
257
258QDomElement QgsMeshTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
259{
260 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
261 if ( mMeshLayer )
262 {
263 element.setAttribute( QStringLiteral( "layer" ), mMeshLayer.layerId );
264 element.setAttribute( QStringLiteral( "layerName" ), mMeshLayer.name );
265 element.setAttribute( QStringLiteral( "layerSource" ), mMeshLayer.source );
266 element.setAttribute( QStringLiteral( "layerProvider" ), mMeshLayer.provider );
267 }
268
269 writeCommonProperties( element, context );
270 return element;
271}
272
274{
275 return mMeshLayer ? mMeshLayer->crs() : QgsCoordinateReferenceSystem();
276}
277
278double QgsMeshTerrainProvider::heightAt( double x, double y ) const
279{
280 if ( mTriangularMesh.vertices().empty() && mMeshLayer && QThread::currentThread() == QCoreApplication::instance()->thread() )
281 const_cast< QgsMeshTerrainProvider * >( this )->prepare(); // auto prepare if we are on main thread and haven't already!
282
283 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
284}
285
287{
288 return new QgsMeshTerrainProvider( *this );
289}
290
292{
293 if ( other->type() != type() )
294 return false;
295
296 const QgsMeshTerrainProvider *otherTerrain = qgis::down_cast< const QgsMeshTerrainProvider * >( other );
297 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
298 || !qgsDoubleNear( otherTerrain->scale(), mScale )
299 || mMeshLayer.get() != otherTerrain->layer() )
300 return false;
301
302 return true;
303}
304
306{
307 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsMeshTerrainProvider::prepare", "prepare() must be called from the main thread" );
308 if ( mMeshLayer )
309 {
310 mMeshLayer->updateTriangularMesh();
311 mTriangularMesh = *mMeshLayer->triangularMesh();
312 }
313}
314
316{
317 mMeshLayer.setLayer( layer );
318}
319
321{
322 return mMeshLayer.get();
323}
324
327 , mMeshLayer( other.mMeshLayer )
328{
329
330}
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:79
bool isValid
Definition: qgsmaplayer.h:81
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
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:59
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
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:2466
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
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.