QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
19#include "qgsmeshlayerutils.h"
20#include "qgsrasterbandstats.h"
21
22#include <QString>
23#include <QThread>
24
25using namespace Qt::StringLiterals;
26
28
33
40
42{
43 element.setAttribute( u"offset"_s, qgsDoubleToString( mOffset ) );
44 element.setAttribute( u"scale"_s, qgsDoubleToString( mScale ) );
45}
46
48{
49 mOffset = element.attribute( u"offset"_s ).toDouble();
50 mScale = element.attribute( u"scale"_s ).toDouble();
51}
52
53//
54// QgsFlatTerrainProvider
55//
56
58{
59 return u"flat"_s;
60}
61
62bool QgsFlatTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
63{
64 const QDomElement terrainElement = element.firstChildElement( u"TerrainProvider"_s );
65 if ( terrainElement.isNull() )
66 return false;
67
68 readCommonProperties( terrainElement, context );
69 return true;
70}
71
72QDomElement QgsFlatTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
73{
74 QDomElement element = document.createElement( u"TerrainProvider"_s );
75 writeCommonProperties( element, context );
76 return element;
77}
78
83
84double QgsFlatTerrainProvider::heightAt( double, double ) const
85{
86 return mOffset;
87}
88
93
95{
96 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsFlatTerrainProvider::prepare", "prepare() must be called from the main thread" );
97
98}
99
101{
102 if ( other->type() != type() )
103 return false;
104
105 const QgsFlatTerrainProvider *otherTerrain = qgis::down_cast< const QgsFlatTerrainProvider * >( other );
106
107 return qgsDoubleNear( otherTerrain->offset(), mOffset );
108}
109
110
111//
112// QgsRasterDemTerrainProvider
113//
114
116{
117 return u"raster"_s;
118}
119
121{
122 if ( mRasterLayer )
123 return; // already assigned
124
125 mRasterLayer.resolve( project );
126}
127
128bool QgsRasterDemTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
129{
130 const QDomElement terrainElement = element.firstChildElement( u"TerrainProvider"_s );
131 if ( terrainElement.isNull() )
132 return false;
133
134 QString layerId = terrainElement.attribute( u"layer"_s );
135 QString layerName = terrainElement.attribute( u"layerName"_s );
136 QString layerSource = terrainElement.attribute( u"layerSource"_s );
137 QString layerProvider = terrainElement.attribute( u"layerProvider"_s );
138 mRasterLayer = _LayerRef<QgsRasterLayer>( layerId, layerName, layerSource, layerProvider );
139
140 readCommonProperties( terrainElement, context );
141 return true;
142}
143
144QDomElement QgsRasterDemTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
145{
146 QDomElement element = document.createElement( u"TerrainProvider"_s );
147 if ( mRasterLayer )
148 {
149 element.setAttribute( u"layer"_s, mRasterLayer.layerId );
150 element.setAttribute( u"layerName"_s, mRasterLayer.name );
151 element.setAttribute( u"layerSource"_s, mRasterLayer.source );
152 element.setAttribute( u"layerProvider"_s, mRasterLayer.provider );
153 }
154
155 writeCommonProperties( element, context );
156 return element;
157}
158
160{
161 return mRasterProvider ? mRasterProvider->crs()
162 : ( mRasterLayer ? mRasterLayer->crs() : QgsCoordinateReferenceSystem() );
163}
164
165double QgsRasterDemTerrainProvider::heightAt( double x, double y ) const
166{
167 // TODO -- may want to use a more efficient approach here, i.e. requesting whole
168 // blocks upfront instead of multiple sample calls
169 bool ok = false;
170 double res = std::numeric_limits<double>::quiet_NaN();
171 if ( mRasterProvider )
172 {
173 res = mRasterProvider->sample( QgsPointXY( x, y ), 1, &ok );
174 }
175 else if ( QThread::currentThread() == QCoreApplication::instance()->thread() && mRasterLayer && mRasterLayer->isValid() )
176 {
177 res = mRasterLayer->dataProvider()->sample( QgsPointXY( x, y ), 1, &ok );
178 }
179
180 if ( ok )
181 return res * mScale + mOffset;
182
183 return std::numeric_limits<double>::quiet_NaN();
184}
185
190
192{
193 if ( other->type() != type() )
194 return false;
195
196 const QgsRasterDemTerrainProvider *otherTerrain = qgis::down_cast< const QgsRasterDemTerrainProvider * >( other );
197 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
198 || !qgsDoubleNear( otherTerrain->scale(), mScale )
199 || mRasterLayer.get() != otherTerrain->layer() )
200 return false;
201
202 return true;
203}
204
206{
207 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsRasterDemTerrainProvider::prepare", "prepare() must be called from the main thread" );
208
209 if ( mRasterLayer && mRasterLayer->isValid() )
210 mRasterProvider.reset( mRasterLayer->dataProvider()->clone() );
211}
212
214{
215 mRasterLayer.setLayer( layer );
216}
217
219{
220 return mRasterLayer.get();
221}
222
225 , mRasterLayer( other.mRasterLayer )
226 , mRasterProvider( nullptr )
227{
228}
229
230
231//
232// QgsMeshTerrainProvider
233//
234
236{
237 return u"mesh"_s;
238}
239
241{
242 if ( mMeshLayer )
243 return; // already assigned
244
245 mMeshLayer.resolve( project );
246}
247
248bool QgsMeshTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
249{
250 const QDomElement terrainElement = element.firstChildElement( u"TerrainProvider"_s );
251 if ( terrainElement.isNull() )
252 return false;
253
254 QString layerId = terrainElement.attribute( u"layer"_s );
255 QString layerName = terrainElement.attribute( u"layerName"_s );
256 QString layerSource = terrainElement.attribute( u"layerSource"_s );
257 QString layerProvider = terrainElement.attribute( u"layerProvider"_s );
258 mMeshLayer = _LayerRef<QgsMeshLayer>( layerId, layerName, layerSource, layerProvider );
259
260 readCommonProperties( terrainElement, context );
261 return true;
262}
263
264QDomElement QgsMeshTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
265{
266 QDomElement element = document.createElement( u"TerrainProvider"_s );
267 if ( mMeshLayer )
268 {
269 element.setAttribute( u"layer"_s, mMeshLayer.layerId );
270 element.setAttribute( u"layerName"_s, mMeshLayer.name );
271 element.setAttribute( u"layerSource"_s, mMeshLayer.source );
272 element.setAttribute( u"layerProvider"_s, mMeshLayer.provider );
273 }
274
275 writeCommonProperties( element, context );
276 return element;
277}
278
280{
281 return mMeshLayer ? mMeshLayer->crs() : QgsCoordinateReferenceSystem();
282}
283
284double QgsMeshTerrainProvider::heightAt( double x, double y ) const
285{
286 if ( mTriangularMesh.vertices().empty() && mMeshLayer && QThread::currentThread() == QCoreApplication::instance()->thread() )
287 const_cast< QgsMeshTerrainProvider * >( this )->prepare(); // auto prepare if we are on main thread and haven't already!
288
289 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
290}
291
296
298{
299 if ( other->type() != type() )
300 return false;
301
302 const QgsMeshTerrainProvider *otherTerrain = qgis::down_cast< const QgsMeshTerrainProvider * >( other );
303 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
304 || !qgsDoubleNear( otherTerrain->scale(), mScale )
305 || mMeshLayer.get() != otherTerrain->layer() )
306 return false;
307
308 return true;
309}
310
312{
313 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsMeshTerrainProvider::prepare", "prepare() must be called from the main thread" );
314 if ( mMeshLayer )
315 {
316 mMeshLayer->updateTriangularMesh();
317 mTriangularMesh = *mMeshLayer->triangularMesh();
318 }
319}
320
322{
323 mMeshLayer.setLayer( layer );
324}
325
327{
328 return mMeshLayer.get();
329}
330
333 , mMeshLayer( other.mMeshLayer )
334 , mTriangularMesh( QgsTriangularMesh() )
335{
336
337}
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
Represents a coordinate reference system (CRS).
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
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.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
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
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.
Represents a 2D point.
Definition qgspointxy.h:62
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:112
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.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context) override
Reads the terrain provider state from a DOM element.
Represents a raster layer.
A container for the context for various read/write operations on objects.
A triangular/derived mesh with vertices in map coordinates.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6817
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
Internal structure to keep weak pointer to QgsMapLayer or layerId if the layer is not available yet.