Implement a registry for terrain objects

This commit is contained in:
Nyall Dawson 2024-11-26 11:59:13 +10:00
parent 8af23b1002
commit 293b7afadd
36 changed files with 905 additions and 0 deletions

View File

@ -29,6 +29,7 @@
%Include auto_generated/symbols/qgspoint3dsymbol.sip
%Include auto_generated/symbols/qgspolygon3dsymbol.sip
%Include auto_generated/symbols/qgspointcloud3dsymbol.sip
%Include auto_generated/terrain/qgs3dterrainregistry.sip
%Include auto_generated/terrain/qgsdemterrainsettings.sip
%Include auto_generated/terrain/qgsflatterrainsettings.sip
%Include auto_generated/terrain/qgsmeshterrainsettings.sip

View File

@ -3,5 +3,6 @@ try:
Qgs3D.instance = staticmethod(Qgs3D.instance)
Qgs3D.initialize = staticmethod(Qgs3D.initialize)
Qgs3D.materialRegistry = staticmethod(Qgs3D.materialRegistry)
Qgs3D.terrainRegistry = staticmethod(Qgs3D.terrainRegistry)
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/terrain/qgs3dterrainregistry.h
try:
Qgs3DTerrainAbstractMetadata.__group__ = ['terrain']
except (NameError, AttributeError):
pass
try:
Qgs3DTerrainRegistry.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsdemterrainsettings.h
try:
QgsDemTerrainSettings.create = staticmethod(QgsDemTerrainSettings.create)
QgsDemTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsflatterrainsettings.h
try:
QgsFlatTerrainSettings.create = staticmethod(QgsFlatTerrainSettings.create)
QgsFlatTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsmeshterrainsettings.h
try:
QgsMeshTerrainSettings.create = staticmethod(QgsMeshTerrainSettings.create)
QgsMeshTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -39,6 +39,11 @@ Initializes the 3D framework.
static QgsMaterialRegistry *materialRegistry();
%Docstring
Returns the material registry, used for managing 3D materials.
%End
static Qgs3DTerrainRegistry *terrainRegistry();
%Docstring
Returns the terrain registry, used for managing 3D terrains.
%End
private:

View File

@ -0,0 +1,120 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/terrain/qgs3dterrainregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
class Qgs3DTerrainAbstractMetadata
{
%Docstring(signature="appended")
Stores metadata about one 3D terrain class.
.. note::
It's necessary to implement :py:func:`~createTerrainSettings` function.
In C++ you can use Qgs3DTerrainMetadata convenience class.
.. versionadded:: 3.42
%End
%TypeHeaderCode
#include "qgs3dterrainregistry.h"
%End
public:
Qgs3DTerrainAbstractMetadata( const QString &type, const QString &visibleName, const QIcon &icon = QIcon() );
%Docstring
Constructor for Qgs3DTerrainAbstractMetadata, with the specified ``type`` and ``visibleName``.
An optional ``icon`` can be specified to represent the material type.
%End
virtual ~Qgs3DTerrainAbstractMetadata();
QString type() const;
%Docstring
Returns the unique terrain type string.
%End
QString visibleName() const;
%Docstring
Returns the terrain's visible (translated) name.
%End
QIcon icon() const;
%Docstring
Returns an icon representing the terrain type, if available.
%End
virtual QgsAbstractTerrainSettings *createTerrainSettings() = 0 /Factory/;
%Docstring
Creates a new instance of this terrain settings type.
Caller takes ownership of the returned object.
%End
};
class Qgs3DTerrainRegistry
{
%Docstring(signature="appended")
Registry of available 3d terrain classes.
Qgs3DTerrainRegistry is not usually directly created, but rather accessed through
Qgs3D.terrainRegistry().
.. versionadded:: 3.42
%End
%TypeHeaderCode
#include "qgs3dterrainregistry.h"
%End
public:
Qgs3DTerrainRegistry();
~Qgs3DTerrainRegistry();
Qgs3DTerrainAbstractMetadata *terrainMetadata( const QString &type ) const;
%Docstring
Returns metadata for specified terrain ``type``. Returns ``None`` if not found
%End
QStringList types() const;
%Docstring
Returns a list of all available terrain types.
%End
bool addType( Qgs3DTerrainAbstractMetadata *metadata /Transfer/ );
%Docstring
Registers a new terrain type. Takes ownership of the ``metadata`` instance.
%End
QgsAbstractTerrainSettings *createTerrainSettings( const QString &type ) const /Factory/;
%Docstring
Creates a new instance of the terrain settings of the specified ``type``.
The caller takes ownership of the returned object.
Returns ``None`` if the specified type is not found in the registry.
%End
private:
Qgs3DTerrainRegistry( const Qgs3DTerrainRegistry &rh );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/terrain/qgs3dterrainregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -26,6 +26,11 @@ Terrain settings for a terrain generator that uses a raster DEM layer to build t
#include "qgsdemterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsDemTerrainSettings object.
%End
QgsDemTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -26,6 +26,11 @@ Terrain settings for a simple flat area terrain.
#include "qgsflatterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsFlatTerrainSettings object.
%End
QgsFlatTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -26,6 +26,11 @@ Terrain settings for a terrain generator that uses uses the Z values of a mesh l
#include "qgsmeshterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsMeshTerrainSettings object.
%End
QgsMeshTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -29,6 +29,7 @@
%Include auto_generated/symbols/qgspoint3dsymbol.sip
%Include auto_generated/symbols/qgspolygon3dsymbol.sip
%Include auto_generated/symbols/qgspointcloud3dsymbol.sip
%Include auto_generated/terrain/qgs3dterrainregistry.sip
%Include auto_generated/terrain/qgsdemterrainsettings.sip
%Include auto_generated/terrain/qgsflatterrainsettings.sip
%Include auto_generated/terrain/qgsmeshterrainsettings.sip

View File

@ -3,5 +3,6 @@ try:
Qgs3D.instance = staticmethod(Qgs3D.instance)
Qgs3D.initialize = staticmethod(Qgs3D.initialize)
Qgs3D.materialRegistry = staticmethod(Qgs3D.materialRegistry)
Qgs3D.terrainRegistry = staticmethod(Qgs3D.terrainRegistry)
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/terrain/qgs3dterrainregistry.h
try:
Qgs3DTerrainAbstractMetadata.__group__ = ['terrain']
except (NameError, AttributeError):
pass
try:
Qgs3DTerrainRegistry.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsdemterrainsettings.h
try:
QgsDemTerrainSettings.create = staticmethod(QgsDemTerrainSettings.create)
QgsDemTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsflatterrainsettings.h
try:
QgsFlatTerrainSettings.create = staticmethod(QgsFlatTerrainSettings.create)
QgsFlatTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -1,5 +1,6 @@
# The following has been generated automatically from src/3d/terrain/qgsmeshterrainsettings.h
try:
QgsMeshTerrainSettings.create = staticmethod(QgsMeshTerrainSettings.create)
QgsMeshTerrainSettings.__group__ = ['terrain']
except (NameError, AttributeError):
pass

View File

@ -39,6 +39,11 @@ Initializes the 3D framework.
static QgsMaterialRegistry *materialRegistry();
%Docstring
Returns the material registry, used for managing 3D materials.
%End
static Qgs3DTerrainRegistry *terrainRegistry();
%Docstring
Returns the terrain registry, used for managing 3D terrains.
%End
private:

View File

@ -0,0 +1,120 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/terrain/qgs3dterrainregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
class Qgs3DTerrainAbstractMetadata
{
%Docstring(signature="appended")
Stores metadata about one 3D terrain class.
.. note::
It's necessary to implement :py:func:`~createTerrainSettings` function.
In C++ you can use Qgs3DTerrainMetadata convenience class.
.. versionadded:: 3.42
%End
%TypeHeaderCode
#include "qgs3dterrainregistry.h"
%End
public:
Qgs3DTerrainAbstractMetadata( const QString &type, const QString &visibleName, const QIcon &icon = QIcon() );
%Docstring
Constructor for Qgs3DTerrainAbstractMetadata, with the specified ``type`` and ``visibleName``.
An optional ``icon`` can be specified to represent the material type.
%End
virtual ~Qgs3DTerrainAbstractMetadata();
QString type() const;
%Docstring
Returns the unique terrain type string.
%End
QString visibleName() const;
%Docstring
Returns the terrain's visible (translated) name.
%End
QIcon icon() const;
%Docstring
Returns an icon representing the terrain type, if available.
%End
virtual QgsAbstractTerrainSettings *createTerrainSettings() = 0 /Factory/;
%Docstring
Creates a new instance of this terrain settings type.
Caller takes ownership of the returned object.
%End
};
class Qgs3DTerrainRegistry
{
%Docstring(signature="appended")
Registry of available 3d terrain classes.
Qgs3DTerrainRegistry is not usually directly created, but rather accessed through
Qgs3D.terrainRegistry().
.. versionadded:: 3.42
%End
%TypeHeaderCode
#include "qgs3dterrainregistry.h"
%End
public:
Qgs3DTerrainRegistry();
~Qgs3DTerrainRegistry();
Qgs3DTerrainAbstractMetadata *terrainMetadata( const QString &type ) const;
%Docstring
Returns metadata for specified terrain ``type``. Returns ``None`` if not found
%End
QStringList types() const;
%Docstring
Returns a list of all available terrain types.
%End
bool addType( Qgs3DTerrainAbstractMetadata *metadata /Transfer/ );
%Docstring
Registers a new terrain type. Takes ownership of the ``metadata`` instance.
%End
QgsAbstractTerrainSettings *createTerrainSettings( const QString &type ) const /Factory/;
%Docstring
Creates a new instance of the terrain settings of the specified ``type``.
The caller takes ownership of the returned object.
Returns ``None`` if the specified type is not found in the registry.
%End
private:
Qgs3DTerrainRegistry( const Qgs3DTerrainRegistry &rh );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/terrain/qgs3dterrainregistry.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -26,6 +26,11 @@ Terrain settings for a terrain generator that uses a raster DEM layer to build t
#include "qgsdemterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsDemTerrainSettings object.
%End
QgsDemTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -26,6 +26,11 @@ Terrain settings for a simple flat area terrain.
#include "qgsflatterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsFlatTerrainSettings object.
%End
QgsFlatTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -26,6 +26,11 @@ Terrain settings for a terrain generator that uses uses the Z values of a mesh l
#include "qgsmeshterrainsettings.h"
%End
public:
static QgsAbstractTerrainSettings *create() /Factory/;
%Docstring
Creates a new instance of a QgsMeshTerrainSettings object.
%End
QgsMeshTerrainSettings *clone() const final /Factory/;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -97,6 +97,7 @@ set(QGIS_3D_SRCS
symbols/qgspointcloud3dsymbol.cpp
symbols/qgspointcloud3dsymbol_p.cpp
terrain/qgs3dterrainregistry.cpp
terrain/qgsdemterraingenerator.cpp
terrain/qgsdemterrainsettings.cpp
terrain/qgsdemterraintilegeometry_p.cpp
@ -192,6 +193,7 @@ set(QGIS_3D_HDRS
symbols/qgspolygon3dsymbol.h
symbols/qgspointcloud3dsymbol.h
terrain/qgs3dterrainregistry.h
terrain/qgsdemterraingenerator.h
terrain/qgsdemterrainsettings.h
terrain/qgsflatterraingenerator.h

View File

@ -42,6 +42,8 @@
#include "qgsphongtexturedmaterialsettings.h"
#include "qgsnullmaterialsettings.h"
#include "qgs3dterrainregistry.h"
#include "qgsstyle.h"
Qgs3D *Qgs3D::instance()
@ -88,7 +90,13 @@ QgsMaterialRegistry *Qgs3D::materialRegistry()
return instance()->mMaterialRegistry;
}
Qgs3DTerrainRegistry *Qgs3D::terrainRegistry()
{
return instance()->mTerrainRegistry;
}
Qgs3D::Qgs3D()
{
mMaterialRegistry = new QgsMaterialRegistry();
mTerrainRegistry = new Qgs3DTerrainRegistry();
}

View File

@ -22,6 +22,7 @@
#include "qgis_sip.h"
class QgsMaterialRegistry;
class Qgs3DTerrainRegistry;
/**
* \ingroup gui
@ -52,6 +53,11 @@ class _3D_EXPORT Qgs3D
*/
static QgsMaterialRegistry *materialRegistry();
/**
* Returns the terrain registry, used for managing 3D terrains.
*/
static Qgs3DTerrainRegistry *terrainRegistry();
private:
Qgs3D();
@ -62,6 +68,7 @@ class _3D_EXPORT Qgs3D
bool mInitialized = false;
QgsMaterialRegistry *mMaterialRegistry = nullptr;
Qgs3DTerrainRegistry *mTerrainRegistry = nullptr;
};
#endif // QGS3D_H

View File

@ -16,6 +16,7 @@
#include "qgs3dmapsettings.h"
#include "moc_qgs3dmapsettings.cpp"
#include "qgs3d.h"
#include "qgs3dutils.h"
#include "qgsflatterraingenerator.h"
#include "qgsdemterraingenerator.h"
@ -35,6 +36,7 @@
#include "qgsmaplayerlistutils_p.h"
#include "qgsterrainsettings.h"
#include "qgsflatterrainsettings.h"
#include "qgs3dterrainregistry.h"
#include <QDomDocument>
#include <QDomElement>
@ -272,6 +274,14 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
flatGen->setCrs( mCrs );
setTerrainGenerator( flatGen );
}
std::unique_ptr<QgsAbstractTerrainSettings> terrainSettings( Qgs3D::terrainRegistry()->createTerrainSettings( terrainGenType ) );
if ( terrainSettings )
{
terrainSettings->readXml( elemTerrainGenerator, context );
setTerrainSettings( terrainSettings.release() );
}
mTerrainGenerator->readXml( elemTerrainGenerator );
QDomElement elemSkybox = elem.firstChildElement( QStringLiteral( "skybox" ) );

View File

@ -0,0 +1,70 @@
/***************************************************************************
qgs3dterrainregistry.cpp
--------------------------------------
Date : November 2024
Copyright : (C) 2024 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgs3dterrainregistry.h"
#include "qgis.h"
#include "qgsflatterrainsettings.h"
#include "qgsdemterrainsettings.h"
#include "qgsmeshterrainsettings.h"
#include <QDomElement>
Qgs3DTerrainRegistry::Qgs3DTerrainRegistry()
{
addType( new Qgs3DTerrainMetadata( QStringLiteral( "flat" ), QObject::tr( "Flat Terrain" ), &QgsFlatTerrainSettings::create ) );
addType( new Qgs3DTerrainMetadata( QStringLiteral( "dem" ), QObject::tr( "DEM (Raster Layer)" ), &QgsDemTerrainSettings::create ) );
addType( new Qgs3DTerrainMetadata( QStringLiteral( "mesh" ), QObject::tr( "Mesh" ), &QgsMeshTerrainSettings::create ) );
}
Qgs3DTerrainRegistry::~Qgs3DTerrainRegistry()
{
qDeleteAll( mMetadata );
}
bool Qgs3DTerrainRegistry::addType( Qgs3DTerrainAbstractMetadata *metadata )
{
if ( !metadata || mMetadata.contains( metadata->type() ) )
return false;
mMetadata[metadata->type()] = metadata;
mTerrainOrder << metadata->type();
return true;
}
QgsAbstractTerrainSettings *Qgs3DTerrainRegistry::createTerrainSettings( const QString &type ) const
{
if ( !mMetadata.contains( type ) )
return nullptr;
return mMetadata[type]->createTerrainSettings();
}
Qgs3DTerrainAbstractMetadata *Qgs3DTerrainRegistry::terrainMetadata( const QString &type ) const
{
return mMetadata.value( type );
}
QStringList Qgs3DTerrainRegistry::types() const
{
QStringList types;
for ( const QString &type : mTerrainOrder )
{
if ( mMetadata.value( type ) )
types << type;
}
return types;
}

View File

@ -0,0 +1,174 @@
/***************************************************************************
qgs3dterrainregistry.h
--------------------------------------
Date : November 2024
Copyright : (C) 2024 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGS3DTERRAINREGISTRY_H
#define QGS3DTERRAINREGISTRY_H
#include "qgis_3d.h"
#include "qgis_sip.h"
#include <QString>
#include <QDomElement>
#include <QMap>
#include <QIcon>
class QgsReadWriteContext;
class QgsAbstractTerrainSettings;
/**
* \ingroup core
* \brief Stores metadata about one 3D terrain class.
*
* \note It's necessary to implement createTerrainSettings() function.
* In C++ you can use Qgs3DTerrainMetadata convenience class.
*
* \since QGIS 3.42
*/
class _3D_EXPORT Qgs3DTerrainAbstractMetadata
{
public:
/**
* Constructor for Qgs3DTerrainAbstractMetadata, with the specified \a type and \a visibleName.
*
* An optional \a icon can be specified to represent the material type.
*/
Qgs3DTerrainAbstractMetadata( const QString &type, const QString &visibleName, const QIcon &icon = QIcon() )
: mType( type )
, mVisibleName( visibleName )
, mIcon( icon )
{}
virtual ~Qgs3DTerrainAbstractMetadata() = default;
/**
* Returns the unique terrain type string.
*/
QString type() const { return mType; }
/**
* Returns the terrain's visible (translated) name.
*/
QString visibleName() const { return mVisibleName; }
/**
* Returns an icon representing the terrain type, if available.
*/
QIcon icon() const { return mIcon; }
/**
* Creates a new instance of this terrain settings type.
*
* Caller takes ownership of the returned object.
*/
virtual QgsAbstractTerrainSettings *createTerrainSettings() = 0 SIP_FACTORY;
private:
QString mType;
QString mVisibleName;
QIcon mIcon;
};
//! Terrain settings creation function
typedef QgsAbstractTerrainSettings *( *QgsTerrainSettingsCreateFunc )() SIP_SKIP;
#ifndef SIP_RUN
/**
* \ingroup core
* \brief Convenience metadata class that uses static functions to create 3D terrain objects.
*
* \note Not available in Python bindings.
*
* \since QGIS 3.42
*/
class _3D_EXPORT Qgs3DTerrainMetadata : public Qgs3DTerrainAbstractMetadata
{
public:
/**
* Constructor for Qgs3DTerrainMetadata, with the specified \a type and \a visibleName.
*
* The \a pfCreate argument is used to specify
* a static function for creating the terrain settings.
*
* An optional \a icon can be specified to represent the terrain type.
*/
Qgs3DTerrainMetadata( const QString &type, const QString &visibleName, QgsTerrainSettingsCreateFunc pfCreate, const QIcon &icon = QIcon() )
: Qgs3DTerrainAbstractMetadata( type, visibleName, icon )
, mCreateFunc( pfCreate )
{}
/**
* Returns the terrain setting's creation function.
*/
QgsTerrainSettingsCreateFunc createFunction() const { return mCreateFunc; }
QgsAbstractTerrainSettings *createTerrainSettings() override SIP_FACTORY { return mCreateFunc ? mCreateFunc() : nullptr; }
private:
QgsTerrainSettingsCreateFunc mCreateFunc;
};
#endif
/**
* \ingroup core
* \brief Registry of available 3d terrain classes.
*
* Qgs3DTerrainRegistry is not usually directly created, but rather accessed through
* Qgs3D::terrainRegistry().
*
* \since QGIS 3.42
*/
class _3D_EXPORT Qgs3DTerrainRegistry
{
public:
Qgs3DTerrainRegistry();
~Qgs3DTerrainRegistry();
Qgs3DTerrainRegistry( const Qgs3DTerrainRegistry &rh ) = delete;
Qgs3DTerrainRegistry &operator=( const Qgs3DTerrainRegistry &rh ) = delete;
//! Returns metadata for specified terrain \a type. Returns NULLPTR if not found
Qgs3DTerrainAbstractMetadata *terrainMetadata( const QString &type ) const;
/**
* Returns a list of all available terrain types.
*/
QStringList types() const;
//! Registers a new terrain type. Takes ownership of the \a metadata instance.
bool addType( Qgs3DTerrainAbstractMetadata *metadata SIP_TRANSFER );
/**
* Creates a new instance of the terrain settings of the specified \a type.
*
* The caller takes ownership of the returned object.
*
* Returns NULLPTR if the specified type is not found in the registry.
*/
QgsAbstractTerrainSettings *createTerrainSettings( const QString &type ) const SIP_FACTORY;
private:
#ifdef SIP_RUN
Qgs3DTerrainRegistry( const Qgs3DTerrainRegistry &rh );
#endif
QMap<QString, Qgs3DTerrainAbstractMetadata *> mMetadata;
//! List of terrains, maintained in the order that they have been added
QStringList mTerrainOrder;
};
#endif // QGS3DTERRAINREGISTRY_H

View File

@ -16,6 +16,11 @@
#include "qgsdemterrainsettings.h"
#include "qgsrasterlayer.h"
QgsAbstractTerrainSettings *QgsDemTerrainSettings::create()
{
return new QgsDemTerrainSettings();
}
QgsDemTerrainSettings *QgsDemTerrainSettings::clone() const
{
return new QgsDemTerrainSettings( *this );

View File

@ -35,6 +35,11 @@ class QgsRasterLayer;
class _3D_EXPORT QgsDemTerrainSettings : public QgsAbstractTerrainSettings
{
public:
/**
* Creates a new instance of a QgsDemTerrainSettings object.
*/
static QgsAbstractTerrainSettings *create() SIP_FACTORY;
QgsDemTerrainSettings *clone() const final SIP_FACTORY;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -15,6 +15,11 @@
#include "qgsflatterrainsettings.h"
QgsAbstractTerrainSettings *QgsFlatTerrainSettings::create()
{
return new QgsFlatTerrainSettings();
}
QgsFlatTerrainSettings *QgsFlatTerrainSettings::clone() const
{
return new QgsFlatTerrainSettings( *this );

View File

@ -36,6 +36,11 @@ class QgsProject;
class _3D_EXPORT QgsFlatTerrainSettings : public QgsAbstractTerrainSettings
{
public:
/**
* Creates a new instance of a QgsFlatTerrainSettings object.
*/
static QgsAbstractTerrainSettings *create() SIP_FACTORY;
QgsFlatTerrainSettings *clone() const final SIP_FACTORY;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -16,6 +16,11 @@
#include "qgsmeshterrainsettings.h"
#include "qgsmeshlayer.h"
QgsAbstractTerrainSettings *QgsMeshTerrainSettings::create()
{
return new QgsMeshTerrainSettings();
}
QgsMeshTerrainSettings *QgsMeshTerrainSettings::clone() const
{
return new QgsMeshTerrainSettings( *this );

View File

@ -38,6 +38,11 @@ class QgsMeshLayer;
class _3D_EXPORT QgsMeshTerrainSettings : public QgsAbstractTerrainSettings
{
public:
/**
* Creates a new instance of a QgsMeshTerrainSettings object.
*/
static QgsAbstractTerrainSettings *create() SIP_FACTORY;
QgsMeshTerrainSettings *clone() const final SIP_FACTORY;
QString type() const final;
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) final;

View File

@ -535,6 +535,7 @@ endif()
if (WITH_3D)
ADD_PYTHON_TEST(PyQgs3DMaterials test_qgs3dmaterials.py)
ADD_PYTHON_TEST(PyQgs3DTerrain test_qgs3dterrain.py)
ADD_PYTHON_TEST(PyQgsMesh3DSymbol test_qgsmesh3dsymbol.py)
endif()

View File

@ -0,0 +1,295 @@
"""QGIS Unit tests for 3D terrrain objects.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
from qgis.PyQt.QtXml import (
QDomDocument,
)
from qgis.core import QgsProject, QgsRasterLayer, QgsMeshLayer, QgsReadWriteContext
from qgis._3d import (
QgsFlatTerrainSettings,
QgsDemTerrainSettings,
QgsMeshTerrainSettings,
Qgs3DTerrainRegistry,
)
import unittest
from qgis.testing import start_app, QgisTestCase
from utilities import unitTestDataPath
start_app()
TEST_DATA_DIR = unitTestDataPath()
class TestQgs3DTerrain(QgisTestCase):
def test_flat_terrain(self):
settings = QgsFlatTerrainSettings.create()
self.assertIsInstance(settings, QgsFlatTerrainSettings)
self.assertEqual(settings.verticalScale(), 1)
settings.setVerticalScale(3)
self.assertEqual(settings.verticalScale(), 3)
self.assertEqual(settings.mapTileResolution(), 512)
settings.setMapTileResolution(36)
self.assertEqual(settings.mapTileResolution(), 36)
self.assertEqual(settings.maximumScreenError(), 3.0)
settings.setMaximumScreenError(4.5)
self.assertEqual(settings.maximumScreenError(), 4.5)
self.assertEqual(settings.maximumGroundError(), 1)
settings.setMaximumGroundError(14.5)
self.assertEqual(settings.maximumGroundError(), 14.5)
self.assertEqual(settings.elevationOffset(), 0)
settings.setElevationOffset(24.5)
self.assertEqual(settings.elevationOffset(), 24.5)
# clone
settings2 = settings.clone()
self.assertEqual(settings2.verticalScale(), 3)
self.assertEqual(settings2.mapTileResolution(), 36)
self.assertEqual(settings2.maximumScreenError(), 4.5)
self.assertEqual(settings2.maximumGroundError(), 14.5)
self.assertEqual(settings2.elevationOffset(), 24.5)
self.assertTrue(settings2.equals(settings))
# equals
settings2.setVerticalScale(4)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMapTileResolution(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumScreenError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumGroundError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setElevationOffset(136)
self.assertFalse(settings2.equals(settings))
# read/write xml
doc = QDomDocument("testdoc")
elem = doc.createElement("test")
settings.writeXml(elem, QgsReadWriteContext())
settings3 = QgsFlatTerrainSettings()
settings3.readXml(elem, QgsReadWriteContext())
self.assertEqual(settings3.verticalScale(), 3)
self.assertEqual(settings3.mapTileResolution(), 36)
self.assertEqual(settings3.maximumScreenError(), 4.5)
self.assertEqual(settings3.maximumGroundError(), 14.5)
self.assertEqual(settings3.elevationOffset(), 24.5)
def test_dem_terrain(self):
p = QgsProject()
rl = QgsRasterLayer(
self.get_test_data_path("raster/dem.tif").as_posix(), "layer1"
)
self.assertTrue(rl.isValid())
rl2 = QgsRasterLayer(
self.get_test_data_path("raster/dem.tif").as_posix(), "layer2"
)
self.assertTrue(rl2.isValid())
p.addMapLayer(rl)
p.addMapLayer(rl2)
settings = QgsDemTerrainSettings.create()
self.assertIsInstance(settings, QgsDemTerrainSettings)
settings.setLayer(rl)
self.assertEqual(settings.layer(), rl)
self.assertEqual(settings.resolution(), 16)
settings.setResolution(66)
self.assertEqual(settings.resolution(), 66)
self.assertEqual(settings.skirtHeight(), 10)
settings.setSkirtHeight(366)
self.assertEqual(settings.skirtHeight(), 366)
self.assertEqual(settings.verticalScale(), 1)
settings.setVerticalScale(3)
self.assertEqual(settings.verticalScale(), 3)
self.assertEqual(settings.mapTileResolution(), 512)
settings.setMapTileResolution(36)
self.assertEqual(settings.mapTileResolution(), 36)
self.assertEqual(settings.maximumScreenError(), 3.0)
settings.setMaximumScreenError(4.5)
self.assertEqual(settings.maximumScreenError(), 4.5)
self.assertEqual(settings.maximumGroundError(), 1)
settings.setMaximumGroundError(14.5)
self.assertEqual(settings.maximumGroundError(), 14.5)
self.assertEqual(settings.elevationOffset(), 0)
settings.setElevationOffset(24.5)
self.assertEqual(settings.elevationOffset(), 24.5)
# clone
settings2 = settings.clone()
self.assertEqual(settings2.layer(), rl)
self.assertEqual(settings2.resolution(), 66)
self.assertEqual(settings2.skirtHeight(), 366)
self.assertEqual(settings2.verticalScale(), 3)
self.assertEqual(settings2.mapTileResolution(), 36)
self.assertEqual(settings2.maximumScreenError(), 4.5)
self.assertEqual(settings2.maximumGroundError(), 14.5)
self.assertEqual(settings2.elevationOffset(), 24.5)
self.assertTrue(settings2.equals(settings))
# equals
settings2.setLayer(rl2)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setResolution(555)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setSkirtHeight(1555)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setVerticalScale(4)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMapTileResolution(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumScreenError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumGroundError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setElevationOffset(136)
self.assertFalse(settings2.equals(settings))
# read/write xml
doc = QDomDocument("testdoc")
elem = doc.createElement("test")
settings.writeXml(elem, QgsReadWriteContext())
settings3 = QgsDemTerrainSettings()
settings3.readXml(elem, QgsReadWriteContext())
self.assertIsNone(settings3.layer())
settings3.resolveReferences(p)
self.assertEqual(settings3.layer(), rl)
self.assertEqual(settings3.resolution(), 66)
self.assertEqual(settings3.skirtHeight(), 366)
self.assertEqual(settings3.verticalScale(), 3)
self.assertEqual(settings3.mapTileResolution(), 36)
self.assertEqual(settings3.maximumScreenError(), 4.5)
self.assertEqual(settings3.maximumGroundError(), 14.5)
self.assertEqual(settings3.elevationOffset(), 24.5)
def test_mesh_terrain(self):
p = QgsProject()
ml = QgsMeshLayer(
self.get_test_data_path("mesh/quad_and_triangle.2dm").as_posix(), "layer1"
)
self.assertTrue(ml.isValid())
ml2 = QgsMeshLayer(
self.get_test_data_path("mesh/quad_and_triangle.2dm").as_posix(), "layer2"
)
self.assertTrue(ml2.isValid())
p.addMapLayer(ml)
p.addMapLayer(ml2)
settings = QgsMeshTerrainSettings.create()
self.assertIsInstance(settings, QgsMeshTerrainSettings)
settings.setLayer(ml)
self.assertEqual(settings.layer(), ml)
self.assertEqual(settings.verticalScale(), 1)
settings.setVerticalScale(3)
self.assertEqual(settings.verticalScale(), 3)
self.assertEqual(settings.mapTileResolution(), 512)
settings.setMapTileResolution(36)
self.assertEqual(settings.mapTileResolution(), 36)
self.assertEqual(settings.maximumScreenError(), 3.0)
settings.setMaximumScreenError(4.5)
self.assertEqual(settings.maximumScreenError(), 4.5)
self.assertEqual(settings.maximumGroundError(), 1)
settings.setMaximumGroundError(14.5)
self.assertEqual(settings.maximumGroundError(), 14.5)
self.assertEqual(settings.elevationOffset(), 0)
settings.setElevationOffset(24.5)
self.assertEqual(settings.elevationOffset(), 24.5)
# clone
settings2 = settings.clone()
self.assertEqual(settings2.layer(), ml)
self.assertEqual(settings2.verticalScale(), 3)
self.assertEqual(settings2.mapTileResolution(), 36)
self.assertEqual(settings2.maximumScreenError(), 4.5)
self.assertEqual(settings2.maximumGroundError(), 14.5)
self.assertEqual(settings2.elevationOffset(), 24.5)
self.assertTrue(settings2.equals(settings))
# equals
settings2.setLayer(ml2)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setVerticalScale(4)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMapTileResolution(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumScreenError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setMaximumGroundError(136)
self.assertFalse(settings2.equals(settings))
settings2 = settings.clone()
settings2.setElevationOffset(136)
self.assertFalse(settings2.equals(settings))
# read/write xml
doc = QDomDocument("testdoc")
elem = doc.createElement("test")
settings.writeXml(elem, QgsReadWriteContext())
settings3 = QgsMeshTerrainSettings()
settings3.readXml(elem, QgsReadWriteContext())
self.assertIsNone(settings3.layer())
settings3.resolveReferences(p)
self.assertEqual(settings3.layer(), ml)
self.assertEqual(settings3.verticalScale(), 3)
self.assertEqual(settings3.mapTileResolution(), 36)
self.assertEqual(settings3.maximumScreenError(), 4.5)
self.assertEqual(settings3.maximumGroundError(), 14.5)
self.assertEqual(settings3.elevationOffset(), 24.5)
def test_registry(self):
registry = Qgs3DTerrainRegistry()
# registry should be populated with known types
self.assertIn("flat", registry.types())
self.assertIn("dem", registry.types())
self.assertIn("mesh", registry.types())
# check settings
settings = registry.createTerrainSettings("flat")
self.assertIsInstance(settings, QgsFlatTerrainSettings)
settings = registry.createTerrainSettings("dem")
self.assertIsInstance(settings, QgsDemTerrainSettings)
settings = registry.createTerrainSettings("mesh")
self.assertIsInstance(settings, QgsMeshTerrainSettings)
if __name__ == "__main__":
unittest.main()