QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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 <QThread>
23
25
30
37
39{
40 element.setAttribute( QStringLiteral( "offset" ), qgsDoubleToString( mOffset ) );
41 element.setAttribute( QStringLiteral( "scale" ), qgsDoubleToString( mScale ) );
42}
43
45{
46 mOffset = element.attribute( QStringLiteral( "offset" ) ).toDouble();
47 mScale = element.attribute( QStringLiteral( "scale" ) ).toDouble();
48}
49
50//
51// QgsFlatTerrainProvider
52//
53
55{
56 return QStringLiteral( "flat" );
57}
58
59bool QgsFlatTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
60{
61 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
62 if ( terrainElement.isNull() )
63 return false;
64
65 readCommonProperties( terrainElement, context );
66 return true;
67}
68
69QDomElement QgsFlatTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
70{
71 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
72 writeCommonProperties( element, context );
73 return element;
74}
75
80
81double QgsFlatTerrainProvider::heightAt( double, double ) const
82{
83 return mOffset;
84}
85
90
92{
93 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsFlatTerrainProvider::prepare", "prepare() must be called from the main thread" );
94
95}
96
98{
99 if ( other->type() != type() )
100 return false;
101
102 const QgsFlatTerrainProvider *otherTerrain = qgis::down_cast< const QgsFlatTerrainProvider * >( other );
103
104 return qgsDoubleNear( otherTerrain->offset(), mOffset );
105}
106
107
108//
109// QgsRasterDemTerrainProvider
110//
111
113{
114 return QStringLiteral( "raster" );
115}
116
118{
119 if ( mRasterLayer )
120 return; // already assigned
121
122 mRasterLayer.resolve( project );
123}
124
125bool QgsRasterDemTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
126{
127 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
128 if ( terrainElement.isNull() )
129 return false;
130
131 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
132 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
133 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
134 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
135 mRasterLayer = _LayerRef<QgsRasterLayer>( layerId, layerName, layerSource, layerProvider );
136
137 readCommonProperties( terrainElement, context );
138 return true;
139}
140
141QDomElement QgsRasterDemTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
142{
143 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
144 if ( mRasterLayer )
145 {
146 element.setAttribute( QStringLiteral( "layer" ), mRasterLayer.layerId );
147 element.setAttribute( QStringLiteral( "layerName" ), mRasterLayer.name );
148 element.setAttribute( QStringLiteral( "layerSource" ), mRasterLayer.source );
149 element.setAttribute( QStringLiteral( "layerProvider" ), mRasterLayer.provider );
150 }
151
152 writeCommonProperties( element, context );
153 return element;
154}
155
157{
158 return mRasterProvider ? mRasterProvider->crs()
159 : ( mRasterLayer ? mRasterLayer->crs() : QgsCoordinateReferenceSystem() );
160}
161
162double QgsRasterDemTerrainProvider::heightAt( double x, double y ) const
163{
164 // TODO -- may want to use a more efficient approach here, i.e. requesting whole
165 // blocks upfront instead of multiple sample calls
166 bool ok = false;
167 double res = std::numeric_limits<double>::quiet_NaN();
168 if ( mRasterProvider )
169 {
170 res = mRasterProvider->sample( QgsPointXY( x, y ), 1, &ok );
171 }
172 else if ( QThread::currentThread() == QCoreApplication::instance()->thread() && mRasterLayer && mRasterLayer->isValid() )
173 {
174 res = mRasterLayer->dataProvider()->sample( QgsPointXY( x, y ), 1, &ok );
175 }
176
177 if ( ok )
178 return res * mScale + mOffset;
179
180 return std::numeric_limits<double>::quiet_NaN();
181}
182
187
189{
190 if ( other->type() != type() )
191 return false;
192
193 const QgsRasterDemTerrainProvider *otherTerrain = qgis::down_cast< const QgsRasterDemTerrainProvider * >( other );
194 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
195 || !qgsDoubleNear( otherTerrain->scale(), mScale )
196 || mRasterLayer.get() != otherTerrain->layer() )
197 return false;
198
199 return true;
200}
201
203{
204 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsRasterDemTerrainProvider::prepare", "prepare() must be called from the main thread" );
205
206 if ( mRasterLayer && mRasterLayer->isValid() )
207 mRasterProvider.reset( mRasterLayer->dataProvider()->clone() );
208}
209
211{
212 mRasterLayer.setLayer( layer );
213}
214
216{
217 return mRasterLayer.get();
218}
219
222 , mRasterLayer( other.mRasterLayer )
223 , mRasterProvider( nullptr )
224{
225}
226
227
228//
229// QgsMeshTerrainProvider
230//
231
233{
234 return QStringLiteral( "mesh" );
235}
236
238{
239 if ( mMeshLayer )
240 return; // already assigned
241
242 mMeshLayer.resolve( project );
243}
244
245bool QgsMeshTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
246{
247 const QDomElement terrainElement = element.firstChildElement( QStringLiteral( "TerrainProvider" ) );
248 if ( terrainElement.isNull() )
249 return false;
250
251 QString layerId = terrainElement.attribute( QStringLiteral( "layer" ) );
252 QString layerName = terrainElement.attribute( QStringLiteral( "layerName" ) );
253 QString layerSource = terrainElement.attribute( QStringLiteral( "layerSource" ) );
254 QString layerProvider = terrainElement.attribute( QStringLiteral( "layerProvider" ) );
255 mMeshLayer = _LayerRef<QgsMeshLayer>( layerId, layerName, layerSource, layerProvider );
256
257 readCommonProperties( terrainElement, context );
258 return true;
259}
260
261QDomElement QgsMeshTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
262{
263 QDomElement element = document.createElement( QStringLiteral( "TerrainProvider" ) );
264 if ( mMeshLayer )
265 {
266 element.setAttribute( QStringLiteral( "layer" ), mMeshLayer.layerId );
267 element.setAttribute( QStringLiteral( "layerName" ), mMeshLayer.name );
268 element.setAttribute( QStringLiteral( "layerSource" ), mMeshLayer.source );
269 element.setAttribute( QStringLiteral( "layerProvider" ), mMeshLayer.provider );
270 }
271
272 writeCommonProperties( element, context );
273 return element;
274}
275
277{
278 return mMeshLayer ? mMeshLayer->crs() : QgsCoordinateReferenceSystem();
279}
280
281double QgsMeshTerrainProvider::heightAt( double x, double y ) const
282{
283 if ( mTriangularMesh.vertices().empty() && mMeshLayer && QThread::currentThread() == QCoreApplication::instance()->thread() )
284 const_cast< QgsMeshTerrainProvider * >( this )->prepare(); // auto prepare if we are on main thread and haven't already!
285
286 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
287}
288
293
295{
296 if ( other->type() != type() )
297 return false;
298
299 const QgsMeshTerrainProvider *otherTerrain = qgis::down_cast< const QgsMeshTerrainProvider * >( other );
300 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset )
301 || !qgsDoubleNear( otherTerrain->scale(), mScale )
302 || mMeshLayer.get() != otherTerrain->layer() )
303 return false;
304
305 return true;
306}
307
309{
310 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsMeshTerrainProvider::prepare", "prepare() must be called from the main thread" );
311 if ( mMeshLayer )
312 {
313 mMeshLayer->updateTriangularMesh();
314 mTriangularMesh = *mMeshLayer->triangularMesh();
315 }
316}
317
319{
320 mMeshLayer.setLayer( layer );
321}
322
324{
325 return mMeshLayer.get();
326}
327
330 , mMeshLayer( other.mMeshLayer )
331 , mTriangularMesh( QgsTriangularMesh() )
332{
333
334}
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:60
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
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:6524
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
Internal structure to keep weak pointer to QgsMapLayer or layerId if the layer is not available yet.