QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
31
36
38{
39 element.setAttribute( u"offset"_s, qgsDoubleToString( mOffset ) );
40 element.setAttribute( u"scale"_s, qgsDoubleToString( mScale ) );
41}
42
44{
45 mOffset = element.attribute( u"offset"_s ).toDouble();
46 mScale = element.attribute( u"scale"_s ).toDouble();
47}
48
49//
50// QgsFlatTerrainProvider
51//
52
54{
55 return u"flat"_s;
56}
57
58bool QgsFlatTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
59{
60 const QDomElement terrainElement = element.firstChildElement( u"TerrainProvider"_s );
61 if ( terrainElement.isNull() )
62 return false;
63
64 readCommonProperties( terrainElement, context );
65 return true;
66}
67
68QDomElement QgsFlatTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
69{
70 QDomElement element = document.createElement( u"TerrainProvider"_s );
71 writeCommonProperties( element, context );
72 return element;
73}
74
79
80double QgsFlatTerrainProvider::heightAt( double, double ) const
81{
82 return mOffset;
83}
84
89
91{
92 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsFlatTerrainProvider::prepare", "prepare() must be called from the main thread" );
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 u"raster"_s;
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( u"TerrainProvider"_s );
126 if ( terrainElement.isNull() )
127 return false;
128
129 QString layerId = terrainElement.attribute( u"layer"_s );
130 QString layerName = terrainElement.attribute( u"layerName"_s );
131 QString layerSource = terrainElement.attribute( u"layerSource"_s );
132 QString layerProvider = terrainElement.attribute( u"layerProvider"_s );
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( u"TerrainProvider"_s );
142 if ( mRasterLayer )
143 {
144 element.setAttribute( u"layer"_s, mRasterLayer.layerId );
145 element.setAttribute( u"layerName"_s, mRasterLayer.name );
146 element.setAttribute( u"layerSource"_s, mRasterLayer.source );
147 element.setAttribute( u"layerProvider"_s, mRasterLayer.provider );
148 }
149
150 writeCommonProperties( element, context );
151 return element;
152}
153
155{
156 return mRasterProvider ? mRasterProvider->crs() : ( 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
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 ) || !qgsDoubleNear( otherTerrain->scale(), mScale ) || mRasterLayer.get() != otherTerrain->layer() )
192 return false;
193
194 return true;
195}
196
198{
199 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsRasterDemTerrainProvider::prepare", "prepare() must be called from the main thread" );
200
201 if ( mRasterLayer && mRasterLayer->isValid() )
202 mRasterProvider.reset( mRasterLayer->dataProvider()->clone() );
203}
204
206{
207 mRasterLayer.setLayer( layer );
208}
209
211{
212 return mRasterLayer.get();
213}
214
217 , mRasterLayer( other.mRasterLayer )
218 , mRasterProvider( nullptr )
219{}
220
221
222//
223// QgsMeshTerrainProvider
224//
225
227{
228 return u"mesh"_s;
229}
230
232{
233 if ( mMeshLayer )
234 return; // already assigned
235
236 mMeshLayer.resolve( project );
237}
238
239bool QgsMeshTerrainProvider::readXml( const QDomElement &element, const QgsReadWriteContext &context )
240{
241 const QDomElement terrainElement = element.firstChildElement( u"TerrainProvider"_s );
242 if ( terrainElement.isNull() )
243 return false;
244
245 QString layerId = terrainElement.attribute( u"layer"_s );
246 QString layerName = terrainElement.attribute( u"layerName"_s );
247 QString layerSource = terrainElement.attribute( u"layerSource"_s );
248 QString layerProvider = terrainElement.attribute( u"layerProvider"_s );
249 mMeshLayer = _LayerRef<QgsMeshLayer>( layerId, layerName, layerSource, layerProvider );
250
251 readCommonProperties( terrainElement, context );
252 return true;
253}
254
255QDomElement QgsMeshTerrainProvider::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
256{
257 QDomElement element = document.createElement( u"TerrainProvider"_s );
258 if ( mMeshLayer )
259 {
260 element.setAttribute( u"layer"_s, mMeshLayer.layerId );
261 element.setAttribute( u"layerName"_s, mMeshLayer.name );
262 element.setAttribute( u"layerSource"_s, mMeshLayer.source );
263 element.setAttribute( u"layerProvider"_s, mMeshLayer.provider );
264 }
265
266 writeCommonProperties( element, context );
267 return element;
268}
269
271{
272 return mMeshLayer ? mMeshLayer->crs() : QgsCoordinateReferenceSystem();
273}
274
275double QgsMeshTerrainProvider::heightAt( double x, double y ) const
276{
277 if ( mTriangularMesh.vertices().empty() && mMeshLayer && QThread::currentThread() == QCoreApplication::instance()->thread() )
278 const_cast< QgsMeshTerrainProvider * >( this )->prepare(); // auto prepare if we are on main thread and haven't already!
279
280 return QgsMeshLayerUtils::interpolateZForPoint( mTriangularMesh, x, y ) * mScale + mOffset;
281}
282
287
289{
290 if ( other->type() != type() )
291 return false;
292
293 const QgsMeshTerrainProvider *otherTerrain = qgis::down_cast< const QgsMeshTerrainProvider * >( other );
294 if ( !qgsDoubleNear( otherTerrain->offset(), mOffset ) || !qgsDoubleNear( otherTerrain->scale(), mScale ) || mMeshLayer.get() != otherTerrain->layer() )
295 return false;
296
297 return true;
298}
299
301{
302 Q_ASSERT_X( QThread::currentThread() == QCoreApplication::instance()->thread(), "QgsMeshTerrainProvider::prepare", "prepare() must be called from the main thread" );
303 if ( mMeshLayer )
304 {
305 mMeshLayer->updateTriangularMesh();
306 mTriangularMesh = *mMeshLayer->triangularMesh();
307 }
308}
309
311{
312 mMeshLayer.setLayer( layer );
313}
314
316{
317 return mMeshLayer.get();
318}
319
322 , mMeshLayer( other.mMeshLayer )
323 , mTriangularMesh( QgsTriangularMesh() )
324{}
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:113
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:6893
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
Internal structure to keep weak pointer to QgsMapLayer or layerId if the layer is not available yet.