28 #include <Qt3DRender/QGeometryRenderer> 32 static void _heightMapMinMax(
const QByteArray &heightMap,
float &zMin,
float &zMax )
34 const float *zBits = (
const float * ) heightMap.constData();
35 int zCount = heightMap.count() /
sizeof( float );
38 zMin = zMax = std::numeric_limits<float>::quiet_NaN();
39 for (
int i = 0; i < zCount; ++i )
42 if ( std::isnan( z ) )
49 zMin = std::min( zMin, z );
50 zMax = std::max( zMax, z );
55 QgsDemTerrainTileLoader::QgsDemTerrainTileLoader( QgsTerrainEntity *terrain, QgsChunkNode *node )
56 : QgsTerrainTileLoader( terrain, node )
79 connect( heightMapGenerator, &QgsDemHeightMapGenerator::heightMapReady,
this, &QgsDemTerrainTileLoader::onHeightMapReady );
80 mHeightMapJobId = heightMapGenerator->render( node->tileX(), node->tileY(), node->tileZ() );
81 mResolution = heightMapGenerator->resolution();
84 Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *parent )
87 _heightMapMinMax( mHeightMap, zMin, zMax );
89 if ( std::isnan( zMin ) || std::isnan( zMax ) )
99 double side = extent.
width();
100 double half = side / 2;
103 QgsTerrainTileEntity *entity =
new QgsTerrainTileEntity( mNode->tileId() );
107 Qt3DRender::QGeometryRenderer *mesh =
new Qt3DRender::QGeometryRenderer;
108 mesh->setGeometry(
new DemTerrainTileGeometry( mResolution, side, map.
terrainVerticalScale(), mSkirtHeight, mHeightMap, mesh ) );
109 entity->addComponent( mesh );
117 Qt3DCore::QTransform *transform =
nullptr;
118 transform =
new Qt3DCore::QTransform();
119 entity->addComponent( transform );
121 transform->setScale( side );
122 transform->setTranslation( QVector3D( x0 + half, 0, - ( y0 + half ) ) );
126 entity->setEnabled(
false );
127 entity->setParent( parent );
131 void QgsDemTerrainTileLoader::onHeightMapReady(
int jobId,
const QByteArray &heightMap )
133 if ( mHeightMapJobId == jobId )
135 this->mHeightMap = heightMap;
136 mHeightMapJobId = -1;
148 #include <QtConcurrent/QtConcurrentRun> 149 #include <QFutureWatcher> 155 , mTilingScheme( tilingScheme )
156 , mResolution( resolution )
162 QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
164 delete mClonedProvider;
170 QgsEventTracing::ScopedEvent e( QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ) );
174 std::unique_ptr<QgsRasterProjector> projector;
175 if ( provider->
crs() != destCrs )
180 input = projector.get();
182 std::unique_ptr< QgsRasterBlock > block( input->
block( 1, extent, res, res ) );
188 data = block->data();
191 if ( block->hasNoData() )
194 float *floatData =
reinterpret_cast<float *
>( data.data() );
195 Q_ASSERT( data.count() %
sizeof( float ) == 0 );
196 int count = data.count() /
sizeof( float );
197 for (
int i = 0; i < count; ++i )
199 if ( block->isNoData( i ) )
200 floatData[i] = std::numeric_limits<float>::quiet_NaN();
210 return downloader->
getHeightMap( extent, res, destCrs );
213 int QgsDemHeightMapGenerator::render(
int x,
int y,
int z )
215 QgsChunkNodeId tileId( x, y, z );
217 QgsEventTracing::addEvent( QgsEventTracing::AsyncBegin, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), tileId.text() );
220 QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
221 float mapUnitsPerPixel = extent.
width() / mResolution;
222 extent.grow( mapUnitsPerPixel / 2 );
224 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
228 jd.jobId = ++mLastJobId;
234 jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
236 jd.future = QtConcurrent::run( _readOnlineDtm, mDownloader.get(),
extent, mResolution, mTilingScheme.crs() );
238 QFutureWatcher<QByteArray> *fw =
new QFutureWatcher<QByteArray>( nullptr );
239 fw->setFuture( jd.future );
240 connect( fw, &QFutureWatcher<QByteArray>::finished,
this, &QgsDemHeightMapGenerator::onFutureFinished );
241 connect( fw, &QFutureWatcher<QByteArray>::finished, fw, &QObject::deleteLater );
243 mJobs.insert( fw, jd );
248 QByteArray QgsDemHeightMapGenerator::renderSynchronously(
int x,
int y,
int z )
251 QgsRectangle extent = mTilingScheme.tileToExtent( x, y, z );
252 float mapUnitsPerPixel = extent.
width() / mResolution;
253 extent.
grow( mapUnitsPerPixel / 2 );
255 QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
258 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, extent, mResolution, mResolution ) );
264 data = block->data();
271 float QgsDemHeightMapGenerator::heightAt(
double x,
double y )
279 if ( mDtmCoarseData.isEmpty() )
281 std::unique_ptr< QgsRasterBlock > block( mDtm->dataProvider()->block( 1, rect, res, res ) );
283 mDtmCoarseData = block->data();
284 mDtmCoarseData.detach();
287 int cellX = ( int )( ( x - rect.
xMinimum() ) / rect.
width() * res + .5f );
288 int cellY = ( int )( ( rect.
yMaximum() - y ) / rect.
height() * res + .5f );
289 cellX = qBound( 0, cellX, res - 1 );
290 cellY = qBound( 0, cellY, res - 1 );
292 const float *data = (
const float * ) mDtmCoarseData.constData();
293 return data[cellX + cellY * res];
296 void QgsDemHeightMapGenerator::onFutureFinished()
298 QFutureWatcher<QByteArray> *fw =
static_cast<QFutureWatcher<QByteArray>*
>( sender() );
300 Q_ASSERT( mJobs.contains( fw ) );
301 JobData jobData = mJobs.value( fw );
306 QgsEventTracing::addEvent( QgsEventTracing::AsyncEnd, QStringLiteral(
"3D" ), QStringLiteral(
"DEM" ), jobData.tileId.text() );
308 QByteArray data = jobData.future.result();
309 emit heightMapReady( jobData.jobId, data );
3 Axis-aligned bounding box - in world coords.
A rectangle specified with double values.
bool setInput(QgsRasterInterface *input) override
Set input.
QgsTerrainGenerator * clone() const override
Makes a copy of the current instance.
bool isTerrainShadingEnabled() const
Returns whether terrain shading is enabled.
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer) ...
QgsPhongMaterialSettings terrainShadingMaterial() const
Returns terrain shading material.
Represents a raster layer.
3 Implementation of terrain generator that uses online resources to download heightmaps.
virtual Type type() const =0
What texture generator implementation is this.
int resolution() const
Returns resolution of the generator (how many elevation samples on one side of a terrain tile) ...
Thirty two bit floating point (float)
float skirtHeight() const
Returns skirt height (in world units). Skirts at the edges of terrain tiles help hide cracks between ...
double y() const
Returns Y coordinate.
3 Definition of the world
QByteArray getHeightMap(const QgsRectangle &extentOrig, int res, const QgsCoordinateReferenceSystem &destCrs, const QgsCoordinateTransformContext &context=QgsCoordinateTransformContext(), QString tmpFilenameImg=QString(), QString tmpFilenameTif=QString())
For given extent and resolution (number of pixels for width/height) in specified CRS, download necessary tile images (if not cached already) and produce height map out of them (byte array of res*res float values)
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
Terrain is built from raster layer with digital elevation model.
void grow(double delta)
Grows the rectangle in place by the specified amount.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
double width() const
Returns the width of the rectangle.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
float skirtHeight() const
Returns skirt height (in world units). Skirts at the edges of terrain tiles help hide cracks between ...
QgsRectangle extent() const override
extent of the terrain in terrain's CRS
const QgsTilingScheme & tilingScheme() const
Returns tiling scheme of the terrain.
Contains information about the context in which a coordinate transform is executed.
Base class for processing filters like renderers, reprojector, resampler etc.
3 Takes care of downloading terrain data from a publicly available data source.
double terrainVerticalScale() const
Returns vertical scale (exaggeration) of terrain.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
QgsDemHeightMapGenerator * heightMapGenerator()
Returns height map generator object - takes care of extraction of elevations from the layer) ...
3 Implementation of terrain generator that uses a raster layer with DEM to build terrain.
This class represents a coordinate reference system (CRS).
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Terrain is built from downloaded tiles with digital elevation model.
3 The class encapsulates tiling scheme (just like with WMTS / TMS / XYZ layers).
QgsRectangle tileToExtent(int x, int y, int z) const
Returns map coordinates of the extent of a tile.
double x() const
Returns X coordinate.
double height() const
Returns the height of the rectangle.
Base class for raster data providers.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...