Skip to content

Commit d4ef55d

Browse files
authoredNov 22, 2018
Merge pull request #8509 from wonder-sk/terrain-map-themes
[FEATURE] Optionally use a map theme to render terrain textures
2 parents fee3935 + afd3525 commit d4ef55d

File tree

10 files changed

+201
-6
lines changed

10 files changed

+201
-6
lines changed
 

‎src/3d/qgs3dmapsettings.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
3939
, mMaxTerrainGroundError( other.mMaxTerrainGroundError )
4040
, mTerrainShadingEnabled( other.mTerrainShadingEnabled )
4141
, mTerrainShadingMaterial( other.mTerrainShadingMaterial )
42+
, mTerrainMapTheme( other.mTerrainMapTheme )
4243
, mShowTerrainBoundingBoxes( other.mShowTerrainBoundingBoxes )
4344
, mShowTerrainTileInfo( other.mShowTerrainTileInfo )
4445
, mShowCameraViewCenter( other.mShowCameraViewCenter )
@@ -48,6 +49,9 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
4849
, mSkyboxEnabled( other.mSkyboxEnabled )
4950
, mSkyboxFileBase( other.mSkyboxFileBase )
5051
, mSkyboxFileExtension( other.mSkyboxFileExtension )
52+
, mTransformContext( other.mTransformContext )
53+
, mPathResolver( other.mPathResolver )
54+
, mMapThemes( other.mMapThemes )
5155
{
5256
Q_FOREACH ( QgsAbstract3DRenderer *renderer, other.mRenderers )
5357
{
@@ -87,6 +91,7 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
8791
QDomElement elemTerrainShadingMaterial = elemTerrain.firstChildElement( QStringLiteral( "shading-material" ) );
8892
if ( !elemTerrainShadingMaterial.isNull() )
8993
mTerrainShadingMaterial.readXml( elemTerrainShadingMaterial );
94+
mTerrainMapTheme = elemTerrain.attribute( QStringLiteral( "map-theme" ) );
9095
mShowLabels = elemTerrain.attribute( QStringLiteral( "show-labels" ), QStringLiteral( "0" ) ).toInt();
9196

9297
mPointLights.clear();
@@ -204,6 +209,7 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
204209
QDomElement elemTerrainShadingMaterial = doc.createElement( QStringLiteral( "shading-material" ) );
205210
mTerrainShadingMaterial.writeXml( elemTerrainShadingMaterial );
206211
elemTerrain.appendChild( elemTerrainShadingMaterial );
212+
elemTerrain.setAttribute( QStringLiteral( "map-theme" ), mTerrainMapTheme );
207213
elemTerrain.setAttribute( QStringLiteral( "show-labels" ), mShowLabels ? 1 : 0 );
208214

209215
QDomElement elemPointLights = doc.createElement( QStringLiteral( "point-lights" ) );
@@ -432,6 +438,15 @@ void Qgs3DMapSettings::setTerrainShadingMaterial( const QgsPhongMaterialSettings
432438
emit terrainShadingChanged();
433439
}
434440

441+
void Qgs3DMapSettings::setTerrainMapTheme( const QString &theme )
442+
{
443+
if ( mTerrainMapTheme == theme )
444+
return;
445+
446+
mTerrainMapTheme = theme;
447+
emit terrainMapThemeChanged();
448+
}
449+
435450
void Qgs3DMapSettings::setRenderers( const QList<QgsAbstract3DRenderer *> &renderers )
436451
{
437452
mRenderers = renderers;

‎src/3d/qgs3dmapsettings.h

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
126126
*/
127127
void setPathResolver( const QgsPathResolver &resolver ) { mPathResolver = resolver; }
128128

129+
/**
130+
* Returns pointer to the collection of map themes. Normally this would be QgsProject::mapThemeCollection()
131+
* of the currently used project. Without a valid map theme collection object it is not possible
132+
* to resolve map themes from their names.
133+
* \since QGIS 3.6
134+
*/
135+
QgsMapThemeCollection *mapThemeCollection() const { return mMapThemes; }
136+
137+
/**
138+
* Sets pointer to the collection of map themes.
139+
* \see mapThemeCollection()
140+
* \since QGIS 3.6
141+
*/
142+
void setMapThemeCollection( QgsMapThemeCollection *mapThemes ) { mMapThemes = mapThemes; }
143+
129144
//! Sets background color of the 3D map view
130145
void setBackgroundColor( const QColor &color );
131146
//! Returns background color of the 3D map view
@@ -148,9 +163,16 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
148163
//! Returns vertical scale (exaggeration) of terrain
149164
double terrainVerticalScale() const;
150165

151-
//! Sets the list of map layers to be rendered as a texture of the terrain
166+
/**
167+
* Sets the list of map layers to be rendered as a texture of the terrain
168+
* \note If terrain map theme is set, it has a priority over the list of layers specified here.
169+
*/
152170
void setLayers( const QList<QgsMapLayer *> &layers );
153-
//! Returns the list of map layers to be rendered as a texture of the terrain
171+
172+
/**
173+
* Returns the list of map layers to be rendered as a texture of the terrain
174+
* \note If terrain map theme is set, it has a priority over the list of layers specified here.
175+
*/
154176
QList<QgsMapLayer *> layers() const;
155177

156178
/**
@@ -231,6 +253,25 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
231253
*/
232254
QgsPhongMaterialSettings terrainShadingMaterial() const { return mTerrainShadingMaterial; }
233255

256+
/**
257+
* Sets name of the map theme.
258+
* \see terrainMapTheme()
259+
* \since QGIS 3.6
260+
*/
261+
void setTerrainMapTheme( const QString &theme );
262+
263+
/**
264+
* Returns name of the map theme (from the active project) that will be used for terrain's texture.
265+
* Empty map theme name means that the map theme is not overridden and the current map theme will be used.
266+
* \note Support for map themes only works if mapThemeCollection() is a valid object (otherwise it is not possible to resolve map themes from names)
267+
* \since QGIS 3.6
268+
*/
269+
QString terrainMapTheme() const { return mTerrainMapTheme; }
270+
271+
//
272+
// misc configuration
273+
//
274+
234275
//! Sets list of extra 3D renderers to use in the scene. Takes ownership of the objects.
235276
void setRenderers( const QList<QgsAbstract3DRenderer *> &renderers SIP_TRANSFER );
236277
//! Returns list of extra 3D renderers
@@ -310,6 +351,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
310351
* \since QGIS 3.6
311352
*/
312353
void terrainShadingChanged();
354+
355+
/**
356+
* Emitted when terrain's map theme has changed
357+
* \since QGIS 3.6
358+
*/
359+
void terrainMapThemeChanged();
313360
//! Emitted when the flag whether terrain's bounding boxes are shown has changed
314361
void showTerrainBoundingBoxesChanged();
315362
//! Emitted when the flag whether terrain's tile info is shown has changed
@@ -342,6 +389,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
342389
float mMaxTerrainGroundError = 1.f; //!< Maximum allowed horizontal map error in map units (determines how many zoom levels will be used)
343390
bool mTerrainShadingEnabled = false; //!< Whether terrain should be shaded taking lights into account
344391
QgsPhongMaterialSettings mTerrainShadingMaterial; //!< Material to use for the terrain (if shading is enabled). Diffuse color is ignored.
392+
QString mTerrainMapTheme; //!< Name of map theme used for terrain's texture (empty means use the current map theme)
345393
bool mShowTerrainBoundingBoxes = false; //!< Whether to show bounding boxes of entities - useful for debugging
346394
bool mShowTerrainTileInfo = false; //!< Whether to draw extra information about terrain tiles to the textures - useful for debugging
347395
bool mShowCameraViewCenter = false; //!< Whether to show camera view center as a sphere - useful for debugging
@@ -355,6 +403,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
355403
//! Coordinate transform context
356404
QgsCoordinateTransformContext mTransformContext;
357405
QgsPathResolver mPathResolver;
406+
QgsMapThemeCollection *mMapThemes = nullptr; //!< Pointer to map themes (e.g. from the current project) to resolve map theme content from the name
358407
};
359408

360409

‎src/3d/qgslayoutitem3dmap.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,13 @@ bool QgsLayoutItem3DMap::readPropertiesFromElement( const QDomElement &element,
217217
mSettings.reset( new Qgs3DMapSettings );
218218
mSettings->readXml( elemSettings, context );
219219
if ( mLayout->project() )
220+
{
220221
mSettings->resolveReferences( *mLayout->project() );
222+
223+
mSettings->setTransformContext( mLayout->project()->transformContext() );
224+
mSettings->setPathResolver( mLayout->project()->pathResolver() );
225+
mSettings->setMapThemeCollection( mLayout->project()->mapThemeCollection() );
226+
}
221227
}
222228

223229
QDomElement elemCameraPose = element.firstChildElement( QStringLiteral( "camera-pose" ) );

‎src/3d/terrain/qgsterrainentity_p.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ QgsTerrainEntity::QgsTerrainEntity( int maxLevel, const Qgs3DMapSettings &map, Q
6969
connect( &map, &Qgs3DMapSettings::showLabelsChanged, this, &QgsTerrainEntity::invalidateMapImages );
7070
connect( &map, &Qgs3DMapSettings::layersChanged, this, &QgsTerrainEntity::onLayersChanged );
7171
connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &QgsTerrainEntity::invalidateMapImages );
72+
connect( &map, &Qgs3DMapSettings::terrainMapThemeChanged, this, &QgsTerrainEntity::invalidateMapImages );
7273

7374
connectToLayersRepaintRequest();
7475

‎src/3d/terrain/qgsterraintexturegenerator_p.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <qgsmaprenderercustompainterjob.h>
1919
#include <qgsmaprenderersequentialjob.h>
2020
#include <qgsmapsettings.h>
21+
#include <qgsmapthemecollection.h>
2122
#include <qgsproject.h>
2223

2324
#include "qgs3dmapsettings.h"
@@ -127,13 +128,26 @@ void QgsTerrainTextureGenerator::onRenderingFinished()
127128
QgsMapSettings QgsTerrainTextureGenerator::baseMapSettings()
128129
{
129130
QgsMapSettings mapSettings;
130-
mapSettings.setLayers( mMap.layers() );
131+
131132
mapSettings.setOutputSize( QSize( mMap.mapTileResolution(), mMap.mapTileResolution() ) );
132133
mapSettings.setDestinationCrs( mMap.crs() );
133134
mapSettings.setBackgroundColor( mMap.backgroundColor() );
134135
mapSettings.setFlag( QgsMapSettings::DrawLabeling, mMap.showLabels() );
135136
mapSettings.setTransformContext( mMap.transformContext() );
136137
mapSettings.setPathResolver( mMap.pathResolver() );
138+
139+
QgsMapThemeCollection *mapThemes = mMap.mapThemeCollection();
140+
QString mapThemeName = mMap.terrainMapTheme();
141+
if ( mapThemeName.isEmpty() || !mapThemes || !mapThemes->hasMapTheme( mapThemeName ) )
142+
{
143+
mapSettings.setLayers( mMap.layers() );
144+
}
145+
else
146+
{
147+
mapSettings.setLayers( mapThemes->mapThemeVisibleLayers( mapThemeName ) );
148+
mapSettings.setLayerStyleOverrides( mapThemes->mapThemeStyleOverrides( mapThemeName ) );
149+
}
150+
137151
return mapSettings;
138152
}
139153

‎src/app/3d/qgs3dmapconfigwidget.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
#include "qgs3dutils.h"
2222

2323
#include "qgsmapcanvas.h"
24+
#include "qgsmapthemecollection.h"
2425
#include "qgsrasterlayer.h"
25-
//#include "qgsproject.h"
26+
#include "qgsproject.h"
2627

2728
Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas *mainCanvas, QWidget *parent )
2829
: QWidget( parent )
@@ -73,6 +74,14 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
7374
widgetTerrainMaterial->setDiffuseVisible( false );
7475
widgetTerrainMaterial->setMaterial( mMap->terrainShadingMaterial() );
7576

77+
// populate combo box with map themes
78+
const QStringList mapThemeNames = QgsProject::instance()->mapThemeCollection()->mapThemes();
79+
cboTerrainMapTheme->addItem( QString() ); // empty item for no map theme
80+
for ( QString themeName : mapThemeNames )
81+
cboTerrainMapTheme->addItem( themeName );
82+
83+
cboTerrainMapTheme->setCurrentText( mMap->terrainMapTheme() );
84+
7685
widgetLights->setPointLights( mMap->pointLights() );
7786

7887
connect( cboTerrainLayer, static_cast<void ( QComboBox::* )( int )>( &QgsMapLayerComboBox::currentIndexChanged ), this, &Qgs3DMapConfigWidget::onTerrainLayerChanged );
@@ -144,6 +153,8 @@ void Qgs3DMapConfigWidget::apply()
144153
mMap->setTerrainShadingEnabled( groupTerrainShading->isChecked() );
145154
mMap->setTerrainShadingMaterial( widgetTerrainMaterial->material() );
146155

156+
mMap->setTerrainMapTheme( cboTerrainMapTheme->currentText() );
157+
147158
mMap->setPointLights( widgetLights->pointLights() );
148159
}
149160

‎src/app/qgisapp.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11141,8 +11141,10 @@ void QgisApp::new3DMapCanvas()
1114111141
map->setSelectionColor( mMapCanvas->selectionColor() );
1114211142
map->setBackgroundColor( mMapCanvas->canvasColor() );
1114311143
map->setLayers( mMapCanvas->layers() );
11144+
1114411145
map->setTransformContext( QgsProject::instance()->transformContext() );
1114511146
map->setPathResolver( QgsProject::instance()->pathResolver() );
11147+
map->setMapThemeCollection( QgsProject::instance()->mapThemeCollection() );
1114611148
connect( QgsProject::instance(), &QgsProject::transformContextChanged, map, [map]
1114711149
{
1114811150
map->setTransformContext( QgsProject::instance()->transformContext() );
@@ -13562,6 +13564,14 @@ void QgisApp::readProject( const QDomDocument &doc )
1356213564
map->readXml( elem3D, readWriteContext );
1356313565
map->resolveReferences( *QgsProject::instance() );
1356413566

13567+
map->setTransformContext( QgsProject::instance()->transformContext() );
13568+
map->setPathResolver( QgsProject::instance()->pathResolver() );
13569+
map->setMapThemeCollection( QgsProject::instance()->mapThemeCollection() );
13570+
connect( QgsProject::instance(), &QgsProject::transformContextChanged, map, [map]
13571+
{
13572+
map->setTransformContext( QgsProject::instance()->transformContext() );
13573+
} );
13574+
1356513575
// these things are not saved in project
1356613576
map->setSelectionColor( mMapCanvas->selectionColor() );
1356713577
map->setBackgroundColor( mMapCanvas->canvasColor() );

‎src/ui/3d/map3dconfigwidget.ui

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
<x>0</x>
88
<y>0</y>
99
<width>691</width>
10-
<height>1135</height>
10+
<height>1122</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string>Configure 3D Map Rendering</string>
1515
</property>
1616
<layout class="QVBoxLayout" name="verticalLayout">
1717
<item>
18-
<widget class="QGroupBox" name="groupBox">
18+
<widget class="QgsCollapsibleGroupBox" name="groupTerrain">
1919
<property name="title">
2020
<string>Terrain</string>
2121
</property>
@@ -84,6 +84,20 @@
8484
</property>
8585
</widget>
8686
</item>
87+
<item row="4" column="0">
88+
<widget class="QLabel" name="label_9">
89+
<property name="text">
90+
<string>Map theme</string>
91+
</property>
92+
</widget>
93+
</item>
94+
<item row="4" column="1" colspan="2">
95+
<widget class="QComboBox" name="cboTerrainMapTheme">
96+
<property name="editable">
97+
<bool>false</bool>
98+
</property>
99+
</widget>
100+
</item>
87101
</layout>
88102
</widget>
89103
</item>
@@ -275,12 +289,15 @@
275289
<tabstop>spinTerrainScale</tabstop>
276290
<tabstop>spinTerrainResolution</tabstop>
277291
<tabstop>spinTerrainSkirtHeight</tabstop>
292+
<tabstop>cboTerrainMapTheme</tabstop>
293+
<tabstop>groupTerrainShading</tabstop>
278294
<tabstop>spinMapResolution</tabstop>
279295
<tabstop>spinScreenError</tabstop>
280296
<tabstop>spinGroundError</tabstop>
281297
<tabstop>chkShowLabels</tabstop>
282298
<tabstop>chkShowTileInfo</tabstop>
283299
<tabstop>chkShowBoundingBoxes</tabstop>
300+
<tabstop>chkShowCameraViewCenter</tabstop>
284301
</tabstops>
285302
<resources/>
286303
<connections/>

‎tests/src/3d/testqgs3drendering.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
#include "qgstest.h"
1717
#include "qgsrenderchecker.h"
1818

19+
#include "qgsmaplayerstylemanager.h"
20+
#include "qgsmapthemecollection.h"
1921
#include "qgsproject.h"
2022
#include "qgsrasterlayer.h"
23+
#include "qgsrastershader.h"
24+
#include "qgssinglebandpseudocolorrenderer.h"
2125
#include "qgsvectorlayer.h"
2226

2327
#include "qgs3dmapscene.h"
@@ -43,6 +47,7 @@ class TestQgs3DRendering : public QObject
4347
void testFlatTerrain();
4448
void testDemTerrain();
4549
void testExtrudedPolygons();
50+
void testMapTheme();
4651

4752
private:
4853
bool renderCheck( const QString &testName, QImage &image, int mismatchCount = 0 );
@@ -88,6 +93,39 @@ void TestQgs3DRendering::initTestCase()
8893
mLayerBuildings->setRenderer3D( renderer3d );
8994

9095
mProject->setCrs( mLayerDtm->crs() );
96+
97+
//
98+
// prepare styles for DTM layer
99+
//
100+
101+
mLayerDtm->styleManager()->addStyleFromLayer( "grayscale" );
102+
103+
double vMin = 44, vMax = 198;
104+
QColor cMin = Qt::red, cMax = Qt::yellow;
105+
106+
// change renderer for the new style
107+
std::unique_ptr<QgsColorRampShader> colorRampShader( new QgsColorRampShader( vMin, vMax ) );
108+
colorRampShader->setColorRampItemList( QList<QgsColorRampShader::ColorRampItem>()
109+
<< QgsColorRampShader::ColorRampItem( vMin, cMin )
110+
<< QgsColorRampShader::ColorRampItem( vMax, cMax ) );
111+
std::unique_ptr<QgsRasterShader> shader( new QgsRasterShader( vMin, vMax ) );
112+
shader->setRasterShaderFunction( colorRampShader.release() );
113+
QgsSingleBandPseudoColorRenderer *r = new QgsSingleBandPseudoColorRenderer( mLayerDtm->renderer()->input(), 1, shader.release() );
114+
mLayerDtm->setRenderer( r );
115+
mLayerDtm->styleManager()->addStyleFromLayer( "my_style" );
116+
117+
mLayerDtm->styleManager()->setCurrentStyle( "grayscale" );
118+
119+
//
120+
// add map theme
121+
//
122+
123+
QgsMapThemeCollection::MapThemeLayerRecord layerRecord( mLayerDtm );
124+
layerRecord.usingCurrentStyle = true;
125+
layerRecord.currentStyle = "my_style";
126+
QgsMapThemeCollection::MapThemeRecord record;
127+
record.addLayerRecord( layerRecord );
128+
mProject->mapThemeCollection()->insert( "theme_dtm", record );
91129
}
92130

93131
//runs after all tests
@@ -204,6 +242,40 @@ void TestQgs3DRendering::testExtrudedPolygons()
204242
QVERIFY( renderCheck( "polygon3d_extrusion", img, 40 ) );
205243
}
206244

245+
void TestQgs3DRendering::testMapTheme()
246+
{
247+
QgsRectangle fullExtent = mLayerDtm->extent();
248+
249+
Qgs3DMapSettings *map = new Qgs3DMapSettings;
250+
map->setCrs( mProject->crs() );
251+
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
252+
map->setLayers( QList<QgsMapLayer *>() << mLayerRgb );
253+
254+
// set theme - this should override what we set in setLayers()
255+
map->setMapThemeCollection( mProject->mapThemeCollection() );
256+
map->setTerrainMapTheme( "theme_dtm" );
257+
258+
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
259+
flatTerrain->setCrs( map->crs() );
260+
flatTerrain->setExtent( fullExtent );
261+
map->setTerrainGenerator( flatTerrain );
262+
263+
QgsOffscreen3DEngine engine;
264+
Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine );
265+
engine.setRootEntity( scene );
266+
267+
// look from the top
268+
scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 0 ), 2500, 0, 0 );
269+
270+
// When running the test on Travis, it would initially return empty rendered image.
271+
// Capturing the initial image and throwing it away fixes that. Hopefully we will
272+
// find a better fix in the future.
273+
Qgs3DUtils::captureSceneImage( engine, scene );
274+
275+
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );
276+
QVERIFY( renderCheck( "terrain_theme", img, 40 ) );
277+
}
278+
207279

208280
bool TestQgs3DRendering::renderCheck( const QString &testName, QImage &image, int mismatchCount )
209281
{
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.