mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
Limit 3d scenes' 2d extent
This commit is contained in:
parent
694ebda27c
commit
f2067aba1e
@ -49,6 +49,24 @@ Writes configuration to a DOM element, to be used later with :py:func:`~Qgs3DMap
|
||||
void resolveReferences( const QgsProject &project );
|
||||
%Docstring
|
||||
Resolves references to other objects (map layers) after the call to :py:func:`~Qgs3DMapSettings.readXml`
|
||||
%End
|
||||
|
||||
QgsRectangle extent() const;
|
||||
%Docstring
|
||||
Returns the 3D scene's 2D extent in project's CRS
|
||||
|
||||
.. versionadded:: 3.30
|
||||
%End
|
||||
|
||||
void setExtent( const QgsRectangle &extent );
|
||||
%Docstring
|
||||
Sets the 3D scene's 2D ``extent`` in project's CRS, while also setting the scene's origin to the extent's center
|
||||
This needs to be called during initialization, as terrain will only be generated
|
||||
within this extent and layer 3D data will only be loaded within this extent too.
|
||||
|
||||
.. seealso:: :py:func:`setOrigin`
|
||||
|
||||
.. versionadded:: 3.30
|
||||
%End
|
||||
|
||||
void setOrigin( const QgsVector3D &origin );
|
||||
|
@ -14,8 +14,9 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgschunkloader_p.h"
|
||||
|
||||
#include "qgschunknode_p.h"
|
||||
#include "qgs3dutils.h"
|
||||
|
||||
#include <QVector>
|
||||
///@cond PRIVATE
|
||||
|
||||
@ -60,7 +61,11 @@ QVector<QgsChunkNode *> QgsQuadtreeChunkLoaderFactory::createChildren( QgsChunkN
|
||||
const float chZMax = dy ? bbox.zMax : zc;
|
||||
const float chYMin = bbox.yMin;
|
||||
const float chYMax = bbox.yMax;
|
||||
children << new QgsChunkNode( childId, QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
|
||||
const QgsAABB childBbox = QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax );
|
||||
const QgsPointXY center = mExtent.center();
|
||||
QgsRectangle childRect = Qgs3DUtils::worldToMapExtent( childBbox, QgsVector3D( center.x(), center.y(), 0 ) );
|
||||
if ( mExtent.intersects( childRect ) )
|
||||
children << new QgsChunkNode( childId, childBbox, childError, node );
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
//
|
||||
|
||||
#include "qgschunkqueuejob_p.h"
|
||||
#include "qgsrectangle.h"
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
@ -109,6 +110,7 @@ class _3D_EXPORT QgsQuadtreeChunkLoaderFactory : public QgsChunkLoaderFactory
|
||||
float mRootError;
|
||||
//! maximum allowed depth of quad tree
|
||||
int mMaxLevel;
|
||||
QgsRectangle mExtent;
|
||||
|
||||
};
|
||||
|
||||
|
@ -94,6 +94,7 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
|
||||
, mRendererUsage( other.mRendererUsage )
|
||||
, m3dAxisSettings( other.m3dAxisSettings )
|
||||
, mIsDebugOverlayEnabled( other.mIsDebugOverlayEnabled )
|
||||
, mExtent( other.mExtent )
|
||||
{
|
||||
for ( QgsAbstract3DRenderer *renderer : std::as_const( other.mRenderers ) )
|
||||
{
|
||||
@ -128,6 +129,13 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
|
||||
elemOrigin.attribute( QStringLiteral( "y" ) ).toDouble(),
|
||||
elemOrigin.attribute( QStringLiteral( "z" ) ).toDouble() );
|
||||
|
||||
QDomElement elemExtent = elem.firstChildElement( QStringLiteral( "extent" ) );
|
||||
mExtent = QgsRectangle(
|
||||
elemExtent.attribute( QStringLiteral( "xMin" ) ).toDouble(),
|
||||
elemExtent.attribute( QStringLiteral( "yMin" ) ).toDouble(),
|
||||
elemExtent.attribute( QStringLiteral( "xMax" ) ).toDouble(),
|
||||
elemExtent.attribute( QStringLiteral( "yMax" ) ).toDouble() );
|
||||
|
||||
QDomElement elemCamera = elem.firstChildElement( QStringLiteral( "camera" ) );
|
||||
if ( !elemCamera.isNull() )
|
||||
{
|
||||
@ -340,6 +348,13 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
|
||||
elemOrigin.setAttribute( QStringLiteral( "z" ), QString::number( mOrigin.z() ) );
|
||||
elem.appendChild( elemOrigin );
|
||||
|
||||
QDomElement elemExtent = doc.createElement( QStringLiteral( "extent" ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "xMin" ), QString::number( mExtent.xMinimum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "yMin" ), QString::number( mExtent.yMinimum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "xMax" ), QString::number( mExtent.xMaximum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "yMax" ), QString::number( mExtent.yMaximum() ) );
|
||||
elem.appendChild( elemExtent );
|
||||
|
||||
QDomElement elemCamera = doc.createElement( QStringLiteral( "camera" ) );
|
||||
elemCamera.setAttribute( QStringLiteral( "field-of-view" ), mFieldOfView );
|
||||
elemCamera.setAttribute( QStringLiteral( "projection-type" ), static_cast< int >( mProjectionType ) );
|
||||
@ -484,6 +499,31 @@ void Qgs3DMapSettings::resolveReferences( const QgsProject &project )
|
||||
}
|
||||
}
|
||||
|
||||
void Qgs3DMapSettings::setExtent( const QgsRectangle &extent )
|
||||
{
|
||||
mExtent = extent;
|
||||
const QgsPointXY center = mExtent.center();
|
||||
setOrigin( QgsVector3D( center.x(), center.y(), 0 ) );
|
||||
if ( mTerrainGenerator )
|
||||
{
|
||||
QgsRectangle terrainExtent = mExtent;
|
||||
if ( mCrs != mTerrainGenerator->crs() )
|
||||
{
|
||||
QgsCoordinateTransform ct = QgsCoordinateTransform( mCrs, mTerrainGenerator->crs(), mTransformContext );
|
||||
ct.setBallparkTransformsAreAppropriate( true );
|
||||
try
|
||||
{
|
||||
terrainExtent = ct.transformBoundingBox( mExtent );
|
||||
}
|
||||
catch ( const QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Transformation of map extent to terrain crs failed." ) );
|
||||
}
|
||||
}
|
||||
mTerrainGenerator->setExtent( terrainExtent );
|
||||
}
|
||||
}
|
||||
|
||||
QgsVector3D Qgs3DMapSettings::mapToWorldCoordinates( const QgsVector3D &mapCoords ) const
|
||||
{
|
||||
return Qgs3DUtils::mapToWorldCoordinates( mapCoords, mOrigin );
|
||||
@ -581,11 +621,11 @@ QList<QgsMapLayer *> Qgs3DMapSettings::layers() const
|
||||
|
||||
void Qgs3DMapSettings::configureTerrainFromProject( QgsProjectElevationProperties *properties, const QgsRectangle &fullExtent )
|
||||
{
|
||||
setExtent( fullExtent );
|
||||
if ( properties->terrainProvider()->type() == QLatin1String( "flat" ) )
|
||||
{
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
setTerrainGenerator( flatTerrain );
|
||||
|
||||
setTerrainElevationOffset( properties->terrainProvider()->offset() );
|
||||
@ -621,7 +661,6 @@ void Qgs3DMapSettings::configureTerrainFromProject( QgsProjectElevationPropertie
|
||||
{
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
setTerrainGenerator( flatTerrain );
|
||||
}
|
||||
}
|
||||
@ -684,6 +723,21 @@ void Qgs3DMapSettings::setTerrainGenerator( QgsTerrainGenerator *gen )
|
||||
disconnect( mTerrainGenerator.get(), &QgsTerrainGenerator::terrainChanged, this, &Qgs3DMapSettings::terrainGeneratorChanged );
|
||||
}
|
||||
|
||||
QgsRectangle terrainExtent = mExtent;
|
||||
if ( mCrs != gen->crs() )
|
||||
{
|
||||
QgsCoordinateTransform ct = QgsCoordinateTransform( mCrs, gen->crs(), mTransformContext );
|
||||
ct.setBallparkTransformsAreAppropriate( true );
|
||||
try
|
||||
{
|
||||
terrainExtent = ct.transformBoundingBox( mExtent );
|
||||
}
|
||||
catch ( const QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Transformation of map extent to terrain crs failed." ) );
|
||||
}
|
||||
}
|
||||
gen->setExtent( terrainExtent );
|
||||
mTerrainGenerator.reset( gen );
|
||||
connect( mTerrainGenerator.get(), &QgsTerrainGenerator::extentChanged, this, &Qgs3DMapSettings::terrainGeneratorChanged );
|
||||
connect( mTerrainGenerator.get(), &QgsTerrainGenerator::terrainChanged, this, &Qgs3DMapSettings::terrainGeneratorChanged );
|
||||
|
@ -74,6 +74,22 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
|
||||
//! Resolves references to other objects (map layers) after the call to readXml()
|
||||
void resolveReferences( const QgsProject &project );
|
||||
|
||||
/**
|
||||
* Returns the 3D scene's 2D extent in project's CRS
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
QgsRectangle extent() const { return mExtent; }
|
||||
|
||||
/**
|
||||
* Sets the 3D scene's 2D \a extent in project's CRS, while also setting the scene's origin to the extent's center
|
||||
* This needs to be called during initialization, as terrain will only be generated
|
||||
* within this extent and layer 3D data will only be loaded within this extent too.
|
||||
*
|
||||
* \see setOrigin()
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
void setExtent( const QgsRectangle &extent );
|
||||
|
||||
/**
|
||||
* Sets coordinates in map CRS at which our 3D world has origin (0,0,0)
|
||||
*
|
||||
@ -253,7 +269,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
|
||||
float terrainElevationOffset() const { return mTerrainElevationOffset; }
|
||||
|
||||
/**
|
||||
* Sets terrain generator.
|
||||
* Sets terrain generator and sets extent() as the generator's extent.
|
||||
*
|
||||
* It takes care of producing terrain tiles from the input data.
|
||||
* Takes ownership of the generator.
|
||||
@ -262,6 +278,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
|
||||
*
|
||||
* \see terrainGenerator()
|
||||
* \see setTerrainRenderingEnabled()
|
||||
* \see setExtent()
|
||||
*/
|
||||
void setTerrainGenerator( QgsTerrainGenerator *gen SIP_TRANSFER ) SIP_SKIP;
|
||||
|
||||
@ -941,6 +958,8 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
|
||||
|
||||
bool mIsDebugOverlayEnabled = false;
|
||||
|
||||
QgsRectangle mExtent; //!< 2d extent used to limit the 3d view
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -35,6 +35,7 @@ QgsPointCloud3DRenderContext::QgsPointCloud3DRenderContext( const Qgs3DMapSettin
|
||||
, mCoordinateTransform( coordinateTransform )
|
||||
, mFeedback( new QgsFeedback )
|
||||
{
|
||||
updateExtent();
|
||||
}
|
||||
|
||||
void QgsPointCloud3DRenderContext::setAttributes( const QgsPointCloudAttributeCollection &attributes )
|
||||
@ -63,6 +64,7 @@ QSet<int> QgsPointCloud3DRenderContext::getFilteredOutValues() const
|
||||
void QgsPointCloud3DRenderContext::setCoordinateTransform( const QgsCoordinateTransform &coordinateTransform )
|
||||
{
|
||||
mCoordinateTransform = coordinateTransform;
|
||||
updateExtent();
|
||||
}
|
||||
|
||||
bool QgsPointCloud3DRenderContext::isCanceled() const
|
||||
@ -74,6 +76,28 @@ void QgsPointCloud3DRenderContext::cancelRendering() const
|
||||
{
|
||||
mFeedback->cancel();
|
||||
}
|
||||
|
||||
void QgsPointCloud3DRenderContext::updateExtent()
|
||||
{
|
||||
if ( map().extent().isEmpty() )
|
||||
{
|
||||
// an empty extent means no filter, so let's pass it without transformation
|
||||
mExtent = QgsRectangle();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mExtent = mCoordinateTransform.transformBoundingBox( map().extent(), Qgis::TransformDirection::Reverse );
|
||||
}
|
||||
catch ( const QgsCsException & )
|
||||
{
|
||||
// bad luck, can't reproject for some reason. Let's use an empty extent to skip filtering.
|
||||
QgsDebugMsg( QStringLiteral( "Transformation of extent failed!" ) );
|
||||
mExtent = QgsRectangle();
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---------
|
||||
|
||||
|
||||
|
@ -188,7 +188,16 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
|
||||
* Returns the feedback object used to cancel rendering and check if rendering was canceled.
|
||||
*/
|
||||
QgsFeedback *feedback() const { return mFeedback.get(); }
|
||||
|
||||
/**
|
||||
* Returns the 3D scene's extent in layer crs.
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
QgsRectangle extent() const { return mExtent; }
|
||||
|
||||
private:
|
||||
//! Recalculates the 3D scene's extent in layer's crs
|
||||
void updateExtent();
|
||||
#ifdef SIP_RUN
|
||||
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh );
|
||||
#endif
|
||||
@ -199,6 +208,7 @@ class _3D_NO_EXPORT QgsPointCloud3DRenderContext : public Qgs3DRenderContext
|
||||
double mZValueFixedOffset = 0;
|
||||
QgsCoordinateTransform mCoordinateTransform;
|
||||
std::unique_ptr<QgsFeedback> mFeedback;
|
||||
QgsRectangle mExtent;
|
||||
};
|
||||
|
||||
|
||||
|
@ -139,6 +139,16 @@ QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( cons
|
||||
, mPointBudget( pointBudget )
|
||||
{
|
||||
mSymbol.reset( symbol );
|
||||
|
||||
try
|
||||
{
|
||||
mExtent = mCoordinateTransform.transformBoundingBox( mMap.extent(), Qgis::TransformDirection::Reverse );
|
||||
}
|
||||
catch ( const QgsCsException & )
|
||||
{
|
||||
// bad luck, can't reproject for some reason
|
||||
QgsDebugMsg( QStringLiteral( "Transformation of extent failed." ) );
|
||||
}
|
||||
}
|
||||
|
||||
QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
|
||||
@ -182,6 +192,9 @@ QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( Qg
|
||||
|
||||
if ( !mPointCloudIndex->hasNode( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
|
||||
continue;
|
||||
if ( !mExtent.isEmpty() &&
|
||||
!mPointCloudIndex->nodeMapExtent( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ).intersects( mExtent ) )
|
||||
continue;
|
||||
|
||||
// the Y and Z coordinates below are intentionally flipped, because
|
||||
// in chunk node IDs the X,Y axes define horizontal plane,
|
||||
|
@ -82,6 +82,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
|
||||
double mZValueOffset = 0;
|
||||
int mPointBudget = 1000000;
|
||||
bool mTriangulate = false;
|
||||
QgsRectangle mExtent; //!< This should hold the map's extent in layer's crs
|
||||
};
|
||||
|
||||
|
||||
|
@ -166,8 +166,9 @@ QgsRuleBasedChunkLoaderFactory::QgsRuleBasedChunkLoaderFactory( const Qgs3DMapSe
|
||||
, mRootRule( rootRule->clone() )
|
||||
, mLeafLevel( leafLevel )
|
||||
{
|
||||
const QgsAABB rootBbox = Qgs3DUtils::layerToWorldExtent( vl->extent(), zMin, zMax, vl->crs(), map.origin(), map.crs(), map.transformContext() );
|
||||
const QgsAABB rootBbox = Qgs3DUtils::mapToWorldExtent( map.extent(), zMin, zMax, map.origin() );
|
||||
setupQuadtree( rootBbox, -1, leafLevel ); // negative root error means that the node does not contain anything
|
||||
mExtent = map.extent();
|
||||
}
|
||||
|
||||
QgsRuleBasedChunkLoaderFactory::~QgsRuleBasedChunkLoaderFactory() = default;
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
QgsTilingScheme::QgsTilingScheme( const QgsRectangle &fullExtent, const QgsCoordinateReferenceSystem &crs )
|
||||
: mCrs( crs )
|
||||
, mFullExtent( fullExtent )
|
||||
{
|
||||
mMapOrigin = QgsPointXY( fullExtent.xMinimum(), fullExtent.yMinimum() );
|
||||
mBaseTileSide = std::max( fullExtent.width(), fullExtent.height() );
|
||||
|
@ -60,10 +60,19 @@ class _3D_EXPORT QgsTilingScheme
|
||||
//! Returns CRS of the tiling scheme
|
||||
QgsCoordinateReferenceSystem crs() const { return mCrs; }
|
||||
|
||||
/**
|
||||
* Returns the full extent used in the constructor, which might not be square.
|
||||
* Level 0 tile is centered at the full extent and the full extent completely fits into the level 0 tile
|
||||
* \since QGIS 3.30
|
||||
*/
|
||||
QgsRectangle fullExtent() const { return mFullExtent; }
|
||||
|
||||
private:
|
||||
QgsPointXY mMapOrigin; //!< Origin point in map coordinates: (0,0) in the tiling scheme
|
||||
double mBaseTileSide = 0; //!< Length of tile side at zoom level 0 in map coordinates
|
||||
QgsCoordinateReferenceSystem mCrs; //!< CRS of the coordinates
|
||||
QgsRectangle mFullExtent; //!< The fullExtent used in constructor
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
@ -158,7 +158,7 @@ QgsVectorLayerChunkLoaderFactory::QgsVectorLayerChunkLoaderFactory( const Qgs3DM
|
||||
, mSymbol( symbol->clone() )
|
||||
, mLeafLevel( leafLevel )
|
||||
{
|
||||
QgsAABB rootBbox = Qgs3DUtils::layerToWorldExtent( vl->extent(), zMin, zMax, vl->crs(), map.origin(), map.crs(), map.transformContext() );
|
||||
QgsAABB rootBbox = Qgs3DUtils::mapToWorldExtent( map.extent(), zMin, zMax, map.origin() );
|
||||
// add small padding to avoid clipping of point features located at the edge of the bounding box
|
||||
rootBbox.xMin -= 1.0;
|
||||
rootBbox.xMax += 1.0;
|
||||
@ -167,6 +167,7 @@ QgsVectorLayerChunkLoaderFactory::QgsVectorLayerChunkLoaderFactory( const Qgs3DM
|
||||
rootBbox.zMin -= 1.0;
|
||||
rootBbox.zMax += 1.0;
|
||||
setupQuadtree( rootBbox, -1, leafLevel ); // negative root error means that the node does not contain anything
|
||||
mExtent = map.extent();
|
||||
}
|
||||
|
||||
QgsChunkLoader *QgsVectorLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
|
||||
|
@ -515,6 +515,7 @@ void QgsSingleColorPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *p
|
||||
|
||||
QgsPointCloudRequest request;
|
||||
request.setAttributes( attributes );
|
||||
request.setFilterRect( context.extent() );
|
||||
std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
|
||||
if ( !block )
|
||||
return;
|
||||
@ -638,6 +639,7 @@ void QgsColorRampPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc,
|
||||
|
||||
QgsPointCloudRequest request;
|
||||
request.setAttributes( attributes );
|
||||
request.setFilterRect( context.extent() );
|
||||
std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
|
||||
if ( !block )
|
||||
return;
|
||||
@ -743,6 +745,7 @@ void QgsRGBPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const
|
||||
|
||||
QgsPointCloudRequest request;
|
||||
request.setAttributes( attributes );
|
||||
request.setFilterRect( context.extent() );
|
||||
std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
|
||||
if ( !block )
|
||||
return;
|
||||
@ -906,6 +909,7 @@ void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex
|
||||
|
||||
QgsPointCloudRequest request;
|
||||
request.setAttributes( attributes );
|
||||
request.setFilterRect( context.extent() );
|
||||
std::unique_ptr<QgsPointCloudBlock> block( pointCloudBlock( pc, n, request, context ) );
|
||||
if ( !block )
|
||||
return;
|
||||
|
@ -51,6 +51,7 @@ QgsTerrainGenerator *QgsDemTerrainGenerator::clone() const
|
||||
cloned->mLayer = mLayer;
|
||||
cloned->mResolution = mResolution;
|
||||
cloned->mSkirtHeight = mSkirtHeight;
|
||||
cloned->mExtent = mExtent;
|
||||
cloned->updateGenerator();
|
||||
return cloned;
|
||||
}
|
||||
@ -95,7 +96,8 @@ void QgsDemTerrainGenerator::readXml( const QDomElement &elem )
|
||||
void QgsDemTerrainGenerator::resolveReferences( const QgsProject &project )
|
||||
{
|
||||
mLayer = QgsMapLayerRef( project.mapLayer( mLayer.layerId ) );
|
||||
updateGenerator();
|
||||
// now that we have the layer, call setExtent() again so we can keep the intersection of mExtent and layer's extent
|
||||
setExtent( mExtent );
|
||||
}
|
||||
|
||||
QgsChunkLoader *QgsDemTerrainGenerator::createChunkLoader( QgsChunkNode *node ) const
|
||||
@ -104,17 +106,42 @@ QgsChunkLoader *QgsDemTerrainGenerator::createChunkLoader( QgsChunkNode *node )
|
||||
return new QgsDemTerrainTileLoader( mTerrain, node, const_cast<QgsDemTerrainGenerator *>( this ) );
|
||||
}
|
||||
|
||||
void QgsDemTerrainGenerator::setExtent( const QgsRectangle &extent )
|
||||
{
|
||||
if ( !mLayer )
|
||||
{
|
||||
// Keep the whole extent for now and setExtent() will be called by again by resolveReferences()
|
||||
mExtent = extent;
|
||||
return;
|
||||
}
|
||||
|
||||
QgsRectangle layerExtent = mLayer->extent();
|
||||
if ( mCrs != mLayer->crs() )
|
||||
{
|
||||
QgsCoordinateTransform ct( mLayer->crs(), mCrs, mTransformContext );
|
||||
ct.setBallparkTransformsAreAppropriate( true );
|
||||
try
|
||||
{
|
||||
layerExtent = ct.transformBoundingBox( layerExtent );
|
||||
}
|
||||
catch ( const QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Transformation of layer extent to terrain crs failed." ) );
|
||||
}
|
||||
}
|
||||
// no need to have an mExtent larger than the actual layer's extent
|
||||
mExtent = extent.intersect( layerExtent );
|
||||
updateGenerator();
|
||||
|
||||
emit extentChanged();
|
||||
}
|
||||
|
||||
void QgsDemTerrainGenerator::updateGenerator()
|
||||
{
|
||||
QgsRasterLayer *dem = layer();
|
||||
if ( dem )
|
||||
{
|
||||
QgsRectangle te = dem->extent();
|
||||
QgsCoordinateTransform terrainToMapTransform( dem->crs(), mCrs, mTransformContext );
|
||||
terrainToMapTransform.setBallparkTransformsAreAppropriate( true );
|
||||
te = terrainToMapTransform.transformBoundingBox( te );
|
||||
|
||||
mTerrainTilingScheme = QgsTilingScheme( te, mCrs );
|
||||
mTerrainTilingScheme = QgsTilingScheme( mExtent, mCrs );
|
||||
delete mHeightMapGenerator;
|
||||
mHeightMapGenerator = new QgsDemHeightMapGenerator( dem, mTerrainTilingScheme, mResolution, mTransformContext );
|
||||
mIsValid = true;
|
||||
|
@ -71,6 +71,7 @@ class _3D_EXPORT QgsDemTerrainGenerator : public QgsTerrainGenerator
|
||||
QgsTerrainGenerator *clone() const override SIP_FACTORY;
|
||||
Type type() const override;
|
||||
QgsRectangle extent() const override;
|
||||
void setExtent( const QgsRectangle &extent ) override;
|
||||
float heightAt( double x, double y, const Qgs3DMapSettings &map ) const override;
|
||||
void writeXml( QDomElement &elem ) const override;
|
||||
void readXml( const QDomElement &elem ) override;
|
||||
|
@ -168,7 +168,7 @@ QgsDemHeightMapGenerator::~QgsDemHeightMapGenerator()
|
||||
}
|
||||
|
||||
|
||||
static QByteArray _readDtmData( QgsRasterDataProvider *provider, const QgsRectangle &extent, int res, const QgsCoordinateReferenceSystem &destCrs )
|
||||
static QByteArray _readDtmData( QgsRasterDataProvider *provider, const QgsRectangle &extent, int res, const QgsCoordinateReferenceSystem &destCrs, const QgsRectangle &clippingExtent )
|
||||
{
|
||||
provider->moveToThread( QThread::currentThread() );
|
||||
|
||||
@ -190,6 +190,17 @@ static QByteArray _readDtmData( QgsRasterDataProvider *provider, const QgsRectan
|
||||
if ( block )
|
||||
{
|
||||
block->convert( Qgis::DataType::Float32 ); // currently we expect just floats
|
||||
|
||||
// set noData outside our clippingExtent
|
||||
QRect subRect = QgsRasterBlock::subRect( extent, block->width(), block->height(), clippingExtent );
|
||||
if ( !block->hasNoDataValue() )
|
||||
{
|
||||
// QgsRasterBlock::setIsNoDataExcept() misbehaves without a defined no data value
|
||||
// see https://github.com/qgis/QGIS/issues/51285
|
||||
block->setNoDataValue( std::numeric_limits<float>::lowest() );
|
||||
}
|
||||
block->setIsNoDataExcept( subRect );
|
||||
|
||||
data = block->data();
|
||||
data.detach(); // this should make a deep copy
|
||||
|
||||
@ -225,9 +236,9 @@ int QgsDemHeightMapGenerator::render( const QgsChunkNodeId &nodeId )
|
||||
QgsRectangle extent = mTilingScheme.tileToExtent( nodeId );
|
||||
float mapUnitsPerPixel = extent.width() / mResolution;
|
||||
extent.grow( mapUnitsPerPixel / 2 );
|
||||
// but make sure not to go beyond the full extent (returns invalid values)
|
||||
QgsRectangle fullExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
|
||||
extent = extent.intersect( fullExtent );
|
||||
// but make sure not to go beyond the root tile's full extent (returns invalid values)
|
||||
QgsRectangle rootTileExtent = mTilingScheme.tileToExtent( 0, 0, 0 );
|
||||
extent = extent.intersect( rootTileExtent );
|
||||
|
||||
JobData jd;
|
||||
jd.jobId = ++mLastJobId;
|
||||
@ -241,7 +252,7 @@ int QgsDemHeightMapGenerator::render( const QgsChunkNodeId &nodeId )
|
||||
if ( mDtm )
|
||||
{
|
||||
mClonedProvider->moveToThread( nullptr );
|
||||
jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs() );
|
||||
jd.future = QtConcurrent::run( _readDtmData, mClonedProvider, extent, mResolution, mTilingScheme.crs(), mTilingScheme.fullExtent() );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qgschunknode_p.h"
|
||||
#include "qgsterrainentity_p.h"
|
||||
#include "qgsterraintileentity_p.h"
|
||||
#include "qgs3dutils.h"
|
||||
/// @cond PRIVATE
|
||||
|
||||
|
||||
@ -53,7 +54,6 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare
|
||||
// create material
|
||||
|
||||
const Qgs3DMapSettings &map = terrain()->map3D();
|
||||
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial(), !map.layers().empty() );
|
||||
|
||||
// create transform
|
||||
|
||||
@ -63,11 +63,23 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare
|
||||
|
||||
// set up transform according to the extent covered by the quad geometry
|
||||
const QgsAABB bbox = mNode->bbox();
|
||||
const double side = bbox.xMax - bbox.xMin;
|
||||
const double half = side / 2;
|
||||
|
||||
transform->setScale( side );
|
||||
transform->setTranslation( QVector3D( bbox.xMin + half, 0, bbox.zMin + half ) );
|
||||
const QgsAABB mapFullExtent = Qgs3DUtils::mapToWorldExtent( map.extent(), bbox.yMin, bbox.yMax, map.origin() );
|
||||
|
||||
const QgsAABB commonExtent = QgsAABB( std::max( bbox.xMin, mapFullExtent.xMin ),
|
||||
bbox.yMin,
|
||||
std::max( bbox.zMin, mapFullExtent.zMin ),
|
||||
std::min( bbox.xMax, mapFullExtent.xMax ),
|
||||
bbox.yMax,
|
||||
std::min( bbox.zMax, mapFullExtent.zMax )
|
||||
);
|
||||
const double xSide = commonExtent.xExtent();
|
||||
const double zSide = commonExtent.zExtent();
|
||||
|
||||
transform->setScale3D( QVector3D( xSide, 1, zSide ) );
|
||||
transform->setTranslation( QVector3D( commonExtent.xMin + xSide / 2, 0, commonExtent.zMin + zSide / 2 ) );
|
||||
|
||||
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial(), !map.layers().empty() );
|
||||
|
||||
entity->setParent( parent );
|
||||
return entity;
|
||||
@ -109,28 +121,12 @@ void QgsFlatTerrainGenerator::rootChunkHeightRange( float &hMin, float &hMax ) c
|
||||
|
||||
void QgsFlatTerrainGenerator::writeXml( QDomElement &elem ) const
|
||||
{
|
||||
const QgsRectangle r = mExtent;
|
||||
QDomElement elemExtent = elem.ownerDocument().createElement( QStringLiteral( "extent" ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "xmin" ), QString::number( r.xMinimum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "xmax" ), QString::number( r.xMaximum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "ymin" ), QString::number( r.yMinimum() ) );
|
||||
elemExtent.setAttribute( QStringLiteral( "ymax" ), QString::number( r.yMaximum() ) );
|
||||
elem.appendChild( elemExtent );
|
||||
|
||||
// crs is not read/written - it should be the same as destination crs of the map
|
||||
Q_UNUSED( elem )
|
||||
}
|
||||
|
||||
void QgsFlatTerrainGenerator::readXml( const QDomElement &elem )
|
||||
{
|
||||
const QDomElement elemExtent = elem.firstChildElement( QStringLiteral( "extent" ) );
|
||||
const double xmin = elemExtent.attribute( QStringLiteral( "xmin" ) ).toDouble();
|
||||
const double xmax = elemExtent.attribute( QStringLiteral( "xmax" ) ).toDouble();
|
||||
const double ymin = elemExtent.attribute( QStringLiteral( "ymin" ) ).toDouble();
|
||||
const double ymax = elemExtent.attribute( QStringLiteral( "ymax" ) ).toDouble();
|
||||
|
||||
setExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
|
||||
|
||||
// crs is not read/written - it should be the same as destination crs of the map
|
||||
Q_UNUSED( elem )
|
||||
}
|
||||
|
||||
void QgsFlatTerrainGenerator::setCrs( const QgsCoordinateReferenceSystem &crs )
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "qgsterraingenerator.h"
|
||||
#include "qgsterraintileloader_p.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include <Qt3DExtras/QPlaneGeometry>
|
||||
|
||||
#define SIP_NO_FILE
|
||||
@ -76,7 +75,6 @@ class _3D_EXPORT QgsFlatTerrainGenerator : public QgsTerrainGenerator
|
||||
|
||||
void updateTilingScheme();
|
||||
|
||||
QgsRectangle mExtent;
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
};
|
||||
|
||||
|
@ -75,7 +75,6 @@ class _3D_EXPORT QgsOnlineTerrainGenerator : public QgsTerrainGenerator
|
||||
|
||||
void updateGenerator();
|
||||
|
||||
QgsRectangle mExtent;
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
QgsCoordinateTransformContext mTransformContext;
|
||||
|
||||
|
@ -38,6 +38,22 @@ int QgsTerrainTextureGenerator::render( const QgsRectangle &extent, QgsChunkNode
|
||||
{
|
||||
QgsMapSettings mapSettings( baseMapSettings() );
|
||||
mapSettings.setExtent( extent );
|
||||
QSize size = QSize( mTextureSize );
|
||||
if ( mMap.terrainGenerator()->type() == QgsTerrainGenerator::Flat )
|
||||
{
|
||||
// The flat terrain generator might have non-square tiles, clipped at the scene's extent.
|
||||
// We need to produce non-square textures for those cases.
|
||||
const QgsRectangle clippedExtent = extent.intersect( mMap.extent() );
|
||||
if ( !qgsDoubleNear( clippedExtent.width(), clippedExtent.height() ) )
|
||||
{
|
||||
if ( clippedExtent.height() > clippedExtent.width() )
|
||||
size.setWidth( std::round( size.width() * clippedExtent.width() / clippedExtent.height() ) );
|
||||
else if ( clippedExtent.height() < clippedExtent.width() )
|
||||
size.setHeight( std::round( size.height() * clippedExtent.height() / clippedExtent.width() ) );
|
||||
}
|
||||
mapSettings.setExtent( clippedExtent );
|
||||
}
|
||||
mapSettings.setOutputSize( size );
|
||||
|
||||
QList<QgsMapLayer *> layers = mMap.layers();
|
||||
QList<QgsMapLayer *> toBeRenderedLayers;
|
||||
|
@ -308,6 +308,7 @@ void Qgs3DMapConfigWidget::apply()
|
||||
{
|
||||
QgsDemTerrainGenerator *demTerrainGen = new QgsDemTerrainGenerator;
|
||||
demTerrainGen->setCrs( mMap->crs(), QgsProject::instance()->transformContext() );
|
||||
demTerrainGen->setExtent( mMap->extent() );
|
||||
demTerrainGen->setLayer( demLayer );
|
||||
demTerrainGen->setResolution( spinTerrainResolution->value() );
|
||||
demTerrainGen->setSkirtHeight( spinTerrainSkirtHeight->value() );
|
||||
|
@ -15920,11 +15920,6 @@ void QgisApp::read3DMapViewSettings( Qgs3DMapCanvasWidget *widget, QDomElement &
|
||||
// these things are not saved in project
|
||||
map->setSelectionColor( mMapCanvas->selectionColor() );
|
||||
map->setBackgroundColor( mMapCanvas->canvasColor() );
|
||||
if ( map->terrainRenderingEnabled() && map->terrainGenerator() && map->terrainGenerator()->type() == QgsTerrainGenerator::Flat )
|
||||
{
|
||||
QgsFlatTerrainGenerator *flatTerrainGen = static_cast<QgsFlatTerrainGenerator *>( map->terrainGenerator() );
|
||||
flatTerrainGen->setExtent( mMapCanvas->projectExtent() );
|
||||
}
|
||||
map->setOutputDpi( QGuiApplication::primaryScreen()->logicalDotsPerInch() );
|
||||
|
||||
widget->setMapSettings( map );
|
||||
|
@ -446,7 +446,7 @@ bool QgsRenderChecker::compareImages( const QString &testName, const QString &re
|
||||
const QString diffImageFileName = QFileInfo( mDiffImageFile ).fileName();
|
||||
const QString myImagesString = QString(
|
||||
"<tr>"
|
||||
"<td colspan=2>Compare actual and expected result</td>"
|
||||
"<td colspan=2>Compare expected and actual result</td>"
|
||||
"<td>Difference (all blue is good, any red is bad)</td>"
|
||||
"</tr>\n<tr>"
|
||||
"<td colspan=2 id=\"td-%1-%7\"></td>\n"
|
||||
@ -500,7 +500,7 @@ bool QgsRenderChecker::compareImages( const QString &testName, const QString &re
|
||||
|
||||
const QString diffSizeImagesString = QString(
|
||||
"<tr>"
|
||||
"<td colspan=3>Compare actual and expected result</td>"
|
||||
"<td colspan=3>Compare expected and actual result</td>"
|
||||
"</tr>\n<tr>"
|
||||
"<td align=center><img src=\"%1\"></td>\n"
|
||||
"<td align=center><img width=%3 height=%4 src=\"%2\"></td>\n"
|
||||
|
@ -90,6 +90,9 @@ class TestQgs3DRendering : public QgsTest
|
||||
void testAnimationExport();
|
||||
void testBillboardRendering();
|
||||
void testInstancedRendering();
|
||||
void testFilteredFlatTerrain();
|
||||
void testFilteredDemTerrain();
|
||||
void testFilteredExtrudedPolygons();
|
||||
|
||||
private:
|
||||
// color tolerance < 2 was failing polygon3d_extrusion test
|
||||
@ -220,12 +223,11 @@ void TestQgs3DRendering::testFlatTerrain()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerRgb );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -269,7 +271,7 @@ void TestQgs3DRendering::testDemTerrain()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerRgb );
|
||||
|
||||
QgsDemTerrainGenerator *demTerrain = new QgsDemTerrainGenerator;
|
||||
@ -302,7 +304,7 @@ void TestQgs3DRendering::testTerrainShading()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
// no terrain layers set!
|
||||
|
||||
QgsDemTerrainGenerator *demTerrain = new QgsDemTerrainGenerator;
|
||||
@ -350,7 +352,7 @@ void TestQgs3DRendering::testMeshTerrain()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
|
||||
QgsMeshTerrainGenerator *meshTerrain = new QgsMeshTerrainGenerator;
|
||||
meshTerrain->setCrs( mProject->crs(), mProject->transformContext() );
|
||||
@ -385,7 +387,7 @@ void TestQgs3DRendering::testExtrudedPolygons()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerBuildings << mLayerRgb );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -394,7 +396,6 @@ void TestQgs3DRendering::testExtrudedPolygons()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -453,7 +454,7 @@ void TestQgs3DRendering::testExtrudedPolygonsDataDefined()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerBuildings << mLayerRgb );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -462,7 +463,6 @@ void TestQgs3DRendering::testExtrudedPolygonsDataDefined()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -492,12 +492,11 @@ void TestQgs3DRendering::testExtrudedPolygonsGoochShading()
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
|
||||
map->setLightSources( {defaultLight.clone() } );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerBuildings << mLayerRgb );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -535,7 +534,7 @@ void TestQgs3DRendering::testPolygonsEdges()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
|
||||
QgsPhongMaterialSettings materialSettings;
|
||||
materialSettings.setAmbient( Qt::lightGray );
|
||||
@ -564,7 +563,6 @@ void TestQgs3DRendering::testPolygonsEdges()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -610,12 +608,11 @@ void TestQgs3DRendering::testLineRendering()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerLines );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -674,12 +671,11 @@ void TestQgs3DRendering::testLineRenderingCurved()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerLines );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -718,7 +714,7 @@ void TestQgs3DRendering::testBufferedLineRendering()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerLines );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -727,7 +723,6 @@ void TestQgs3DRendering::testBufferedLineRendering()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -767,7 +762,7 @@ void TestQgs3DRendering::testBufferedLineRenderingWidth()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerLines );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -776,7 +771,6 @@ void TestQgs3DRendering::testBufferedLineRenderingWidth()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -803,7 +797,7 @@ void TestQgs3DRendering::testMapTheme()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerDtm );
|
||||
|
||||
// set theme - this should override what we set in setLayers()
|
||||
@ -812,7 +806,6 @@ void TestQgs3DRendering::testMapTheme()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -839,7 +832,7 @@ void TestQgs3DRendering::testMesh()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerMeshDataset );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -848,18 +841,17 @@ void TestQgs3DRendering::testMesh()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
|
||||
engine.setRootEntity( scene );
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 3000, 25, 45 );
|
||||
|
||||
Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
// When running the test on Travis, it would initially return empty rendered image.
|
||||
// Capturing the initial image and throwing it away fixes that. Hopefully we will
|
||||
// find a better fix in the future.
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 3000, 25, 45 );
|
||||
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
delete scene;
|
||||
delete map;
|
||||
@ -882,7 +874,7 @@ void TestQgs3DRendering::testMesh_datasetOnFaces()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerMeshDataset );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
@ -891,18 +883,17 @@ void TestQgs3DRendering::testMesh_datasetOnFaces()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
|
||||
engine.setRootEntity( scene );
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 3000, 25, 45 );
|
||||
|
||||
Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
// When running the test on Travis, it would initially return empty rendered image.
|
||||
// Capturing the initial image and throwing it away fixes that. Hopefully we will
|
||||
// find a better fix in the future.
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 3000, 25, 45 );
|
||||
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
delete scene;
|
||||
delete map;
|
||||
@ -937,7 +928,7 @@ void TestQgs3DRendering::testMeshSimplified()
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerMeshSimplified );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
|
||||
QgsMesh3DSymbol *symbolDataset = new QgsMesh3DSymbol;
|
||||
symbolDataset->setVerticalDatasetGroupIndex( 11 );
|
||||
@ -1005,7 +996,7 @@ void TestQgs3DRendering::testRuleBasedRenderer()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerBuildings );
|
||||
|
||||
QgsPointLightSettings defaultLight;
|
||||
@ -1015,7 +1006,6 @@ void TestQgs3DRendering::testRuleBasedRenderer()
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -1059,11 +1049,10 @@ void TestQgs3DRendering::testAnimationExport()
|
||||
|
||||
Qgs3DMapSettings map;
|
||||
map.setCrs( mProject->crs() );
|
||||
map.setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map.setExtent( fullExtent );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map.crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map.setTerrainGenerator( flatTerrain );
|
||||
|
||||
Qgs3DAnimationSettings animSettings;
|
||||
@ -1129,12 +1118,11 @@ void TestQgs3DRendering::testInstancedRendering()
|
||||
|
||||
Qgs3DMapSettings *mapSettings = new Qgs3DMapSettings;
|
||||
mapSettings->setCrs( mProject->crs() );
|
||||
mapSettings->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
mapSettings->setExtent( fullExtent );
|
||||
mapSettings->setLayers( QList<QgsMapLayer *>() << layerPointsZ.get() );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( mapSettings->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
mapSettings->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -1205,12 +1193,11 @@ void TestQgs3DRendering::testBillboardRendering()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerPointsZ.get() );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -1259,12 +1246,11 @@ void TestQgs3DRendering::testEpsg4978LineRendering()
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( p.crs() );
|
||||
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << layerLines );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
flatTerrain->setExtent( fullExtent );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
@ -1293,5 +1279,140 @@ void TestQgs3DRendering::testEpsg4978LineRendering()
|
||||
QVERIFY( renderCheck( "4978_line_rendering_2", img2, 40, 15 ) );
|
||||
}
|
||||
|
||||
void TestQgs3DRendering::testFilteredFlatTerrain()
|
||||
{
|
||||
QgsRectangle fullExtent = mLayerDtm->extent();
|
||||
// Set the extent to have width > height
|
||||
fullExtent.setYMaximum( fullExtent.yMaximum() - fullExtent.height() / 3 );
|
||||
fullExtent.setYMinimum( fullExtent.yMinimum() + fullExtent.height() / 3 );
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerRgb );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
|
||||
engine.setRootEntity( scene );
|
||||
|
||||
// look from the top
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 2000, 0, 0 );
|
||||
|
||||
// When running the test on Travis, it would initially return empty rendered image.
|
||||
// Capturing the initial image and throwing it away fixes that. Hopefully we will
|
||||
// find a better fix in the future.
|
||||
Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
|
||||
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
renderCheck( "flat_terrain_filtered_1", img, 40 );
|
||||
|
||||
// Now set the extent to have height > width and redo
|
||||
fullExtent = mLayerDtm->extent();
|
||||
fullExtent.setXMaximum( fullExtent.xMaximum() - fullExtent.width() / 3 );
|
||||
fullExtent.setXMinimum( fullExtent.xMinimum() + fullExtent.width() / 3 );
|
||||
map->setExtent( fullExtent );
|
||||
|
||||
QImage img2 = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
|
||||
delete scene;
|
||||
delete map;
|
||||
|
||||
QVERIFY( renderCheck( "flat_terrain_filtered_2", img2, 40 ) );
|
||||
}
|
||||
|
||||
void TestQgs3DRendering::testFilteredDemTerrain()
|
||||
{
|
||||
QgsRectangle fullExtent = mLayerDtm->extent();
|
||||
// Set the extent to have width > height
|
||||
fullExtent.setYMaximum( fullExtent.yMaximum() - fullExtent.height() / 3 );
|
||||
fullExtent.setYMinimum( fullExtent.yMinimum() + fullExtent.height() / 3 );
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerRgb );
|
||||
|
||||
QgsDemTerrainGenerator *demTerrain = new QgsDemTerrainGenerator;
|
||||
demTerrain->setLayer( mLayerDtm );
|
||||
map->setTerrainGenerator( demTerrain );
|
||||
map->setTerrainVerticalScale( 3 );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
|
||||
engine.setRootEntity( scene );
|
||||
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 2000, 60, 225 );
|
||||
|
||||
// When running the test on Travis, it would initially return empty rendered image.
|
||||
// Capturing the initial image and throwing it away fixes that. Hopefully we will
|
||||
// find a better fix in the future.
|
||||
Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
|
||||
QImage img1 = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
renderCheck( "dem_terrain_filtered_1", img1, 40 );
|
||||
|
||||
// Now set the extent to have height > width and redo
|
||||
fullExtent = mLayerDtm->extent();
|
||||
fullExtent.setXMaximum( fullExtent.xMaximum() - fullExtent.width() / 3 );
|
||||
fullExtent.setXMinimum( fullExtent.xMinimum() + fullExtent.width() / 3 );
|
||||
map->setExtent( fullExtent );
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 2000, 60, 45 );
|
||||
|
||||
QImage img2 = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
|
||||
delete scene;
|
||||
delete map;
|
||||
|
||||
QVERIFY( renderCheck( "dem_terrain_filtered_2", img2, 40 ) );
|
||||
}
|
||||
|
||||
void TestQgs3DRendering::testFilteredExtrudedPolygons()
|
||||
{
|
||||
const QgsRectangle fullExtent = QgsRectangle( 321720, 129190, 322560, 130060 );
|
||||
|
||||
Qgs3DMapSettings *map = new Qgs3DMapSettings;
|
||||
map->setCrs( mProject->crs() );
|
||||
map->setExtent( fullExtent );
|
||||
map->setLayers( QList<QgsMapLayer *>() << mLayerBuildings << mLayerRgb );
|
||||
QgsPointLightSettings defaultLight;
|
||||
defaultLight.setIntensity( 0.5 );
|
||||
defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) );
|
||||
map->setLightSources( {defaultLight.clone() } );
|
||||
|
||||
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
|
||||
flatTerrain->setCrs( map->crs() );
|
||||
map->setTerrainGenerator( flatTerrain );
|
||||
|
||||
QgsOffscreen3DEngine engine;
|
||||
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
|
||||
engine.setRootEntity( scene );
|
||||
|
||||
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 250 ), 1500, 45, 0 );
|
||||
|
||||
QgsPhongMaterialSettings materialSettings;
|
||||
materialSettings.setAmbient( Qt::lightGray );
|
||||
QgsPolygon3DSymbol *symbol3d = new QgsPolygon3DSymbol;
|
||||
symbol3d->setMaterialSettings( materialSettings.clone() );
|
||||
symbol3d->setExtrusionHeight( 10.f );
|
||||
QgsVectorLayer3DRenderer *renderer3d = new QgsVectorLayer3DRenderer( symbol3d );
|
||||
mLayerBuildings->setRenderer3D( renderer3d );
|
||||
|
||||
// When running the test on Travis, it would initially return empty rendered image.
|
||||
// Capturing the initial image and throwing it away fixes that. Hopefully we will
|
||||
// find a better fix in the future.
|
||||
Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
|
||||
|
||||
delete scene;
|
||||
delete map;
|
||||
|
||||
QVERIFY( renderCheck( "polygon3d_extrusion_filtered", img, 40 ) );
|
||||
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgs3DRendering )
|
||||
#include "testqgs3drendering.moc"
|
||||
|
BIN
tests/testdata/control_images/3d/expected_dem_terrain_filtered_1/expected_dem_terrain_filtered_1.png
vendored
Normal file
BIN
tests/testdata/control_images/3d/expected_dem_terrain_filtered_1/expected_dem_terrain_filtered_1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
tests/testdata/control_images/3d/expected_dem_terrain_filtered_2/expected_dem_terrain_filtered_2.png
vendored
Normal file
BIN
tests/testdata/control_images/3d/expected_dem_terrain_filtered_2/expected_dem_terrain_filtered_2.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 141 KiB |
Binary file not shown.
After Width: | Height: | Size: 221 KiB |
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
Loading…
x
Reference in New Issue
Block a user