Skip to content

Commit

Permalink
Add 3D map print layout item
Browse files Browse the repository at this point in the history
So far this is just support in the layout framework, not yet exposed to the GUI.
  • Loading branch information
wonder-sk committed Aug 21, 2018
1 parent 9fbef83 commit 2e56fad
Show file tree
Hide file tree
Showing 10 changed files with 307 additions and 0 deletions.
Expand Up @@ -178,6 +178,8 @@ of layout items.
LayoutAttributeTable,
LayoutTextTable,

Layout3DMap,

// item
PluginItem,
};
Expand Down
3 changes: 3 additions & 0 deletions src/3d/CMakeLists.txt
Expand Up @@ -9,6 +9,7 @@ SET(QGIS_3D_SRCS
qgs3dutils.cpp
qgscameracontroller.cpp
qgscamerapose.cpp
qgslayoutitem3dmap.cpp
qgsoffscreen3dengine.cpp
qgsphongmaterialsettings.cpp
qgsraycastingutils_p.cpp
Expand Down Expand Up @@ -83,6 +84,7 @@ SET(QGIS_3D_HDRS
qgs3dutils.h
qgscameracontroller.h
qgscamerapose.h
qgslayoutitem3dmap.h
qgsoffscreen3dengine.h
qgsphongmaterialsettings.h
qgsraycastingutils_p.h
Expand Down Expand Up @@ -131,6 +133,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/core/symbology
${CMAKE_SOURCE_DIR}/src/core/metadata
${CMAKE_SOURCE_DIR}/src/core/expression
${CMAKE_SOURCE_DIR}/src/core/layout
${CMAKE_SOURCE_DIR}/src/core/3d
${CMAKE_BINARY_DIR}/src/core
${CMAKE_BINARY_DIR}/src/3d
Expand Down
67 changes: 67 additions & 0 deletions src/3d/qgslayoutitem3dmap.cpp
@@ -0,0 +1,67 @@
/***************************************************************************
qgslayoutitem3dmap.cpp
--------------------------------------
Date : August 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk 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 "qgslayoutitem3dmap.h"

#include "qgs3dmapscene.h"
#include "qgs3dutils.h"
#include "qgscameracontroller.h"
#include "qgslayout.h"
#include "qgslayoutitemregistry.h"
#include "qgsoffscreen3dengine.h"


QgsLayoutItem3DMap::QgsLayoutItem3DMap( QgsLayout *layout )
: QgsLayoutItem( layout )
{

}

QgsLayoutItem3DMap *QgsLayoutItem3DMap::create( QgsLayout *layout )
{
return new QgsLayoutItem3DMap( layout );
}

int QgsLayoutItem3DMap::type() const
{
return QgsLayoutItemRegistry::Layout3DMap;
}

void QgsLayoutItem3DMap::draw( QgsLayoutItemRenderContext &context )
{
QgsOffscreen3DEngine engine;
QSizeF sizePixels = mLayout->renderContext().measurementConverter().convert( sizeWithUnits(), QgsUnitTypes::LayoutPixels ).toQSizeF();
engine.setSize( QSize( qCeil( sizePixels.width() ), qCeil( sizePixels.height() ) ) );

Qgs3DMapScene *scene = new Qgs3DMapScene( *mSettings, &engine );
engine.setRootEntity( scene );

scene->cameraController()->setCameraPose( mCameraPose );

// XXX this should not be needed, but without it the scene often
// is not completely ready (e.g. a missing terrain tile).
// leaving it here until a more robust solution is found...
Qgs3DUtils::captureSceneImage( engine, scene );

QImage img = Qgs3DUtils::captureSceneImage( engine, scene );

QgsRenderContext &ctx = context.renderContext();
ctx.painter()->drawImage( 0, 0, img );
}

void QgsLayoutItem3DMap::setMapSettings( Qgs3DMapSettings *settings )
{
mSettings.reset( settings );
}
94 changes: 94 additions & 0 deletions src/3d/qgslayoutitem3dmap.h
@@ -0,0 +1,94 @@
/***************************************************************************
qgslayoutitem3dmap.h
--------------------------------------
Date : August 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk 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 QGSLAYOUTITEM3DMAP_H
#define QGSLAYOUTITEM3DMAP_H

#include "qgis_3d.h"

#include "qgslayoutitem.h"

#include "qgscamerapose.h"


class Qgs3DMapSettings;

/**
* \ingroup 3d
*
* Implements support of 3D map views in print layouts
*
* \since QGIS 3.4
*/
class _3D_EXPORT QgsLayoutItem3DMap : public QgsLayoutItem
{

#ifdef SIP_RUN
SIP_CONVERT_TO_SUBCLASS_CODE
// the conversions have to be static, because they're using multiple inheritance
// (seen in PyQt4 .sip files for some QGraphicsItem classes)
switch ( sipCpp->type() )
{
// really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
case QGraphicsItem::UserType + 115:
sipType = sipType_QgsLayoutItem3DMap;
*sipCppRet = static_cast<QgsLayoutItem3DMap *>( sipCpp );
break;
default:
sipType = 0;
}
SIP_END
#endif

public:

/**
* Constructor for QgsLayoutItem3DMap, with the specified parent \a layout.
*
* Ownership is transferred to the layout.
*/
QgsLayoutItem3DMap( QgsLayout *layout SIP_TRANSFERTHIS );

/**
* Returns a new 3D map item for the specified \a layout.
*
* The caller takes responsibility for deleting the returned object.
*/
static QgsLayoutItem3DMap *create( QgsLayout *layout ) SIP_FACTORY;

virtual int type() const override;

virtual void draw( QgsLayoutItemRenderContext &context ) override;

//! Configures camera view
void setCameraPose( const QgsCameraPose &pose ) { mCameraPose = pose; }
//! Returns camera view
QgsCameraPose cameraPose() const { return mCameraPose; }

/**
* Configures map scene
*
* Ownership is transferred to the item.
*/
void setMapSettings( Qgs3DMapSettings *settings SIP_TRANSFER );
//! Returns map scene. May be a null pointer if not yet configured.
Qgs3DMapSettings *mapSettings() const { return mSettings.get(); }

private:
std::unique_ptr<Qgs3DMapSettings> mSettings;
QgsCameraPose mCameraPose;
};

#endif // QGSLAYOUTITEM3DMAP_H
7 changes: 7 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -90,6 +90,7 @@
#include "qgs3dmapsettings.h"
#include "qgscameracontroller.h"
#include "qgsflatterraingenerator.h"
#include "qgslayoutitem3dmap.h"
#include "qgsvectorlayer3drenderer.h"
#include "processing/qgs3dalgorithms.h"
#endif
Expand Down Expand Up @@ -10798,6 +10799,12 @@ void QgisApp::initNativeProcessing()

void QgisApp::initLayouts()
{
#ifdef HAVE_3D
QgsApplication::layoutItemRegistry()->addLayoutItemType(
new QgsLayoutItemMetadata( QgsLayoutItemRegistry::Layout3DMap, QObject::tr( "3D Map" ), QgsLayoutItem3DMap::create )
);
#endif

QgsLayoutAppUtils::registerGuiForKnownItemTypes();

mLayoutQptDropHandler = new QgsLayoutQptDropHandler( this );
Expand Down
1 change: 1 addition & 0 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -183,6 +183,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
SIP_END
#endif


Q_OBJECT
Q_PROPERTY( bool locked READ isLocked WRITE setLocked NOTIFY lockChanged )

Expand Down
2 changes: 2 additions & 0 deletions src/core/layout/qgslayoutitemregistry.h
Expand Up @@ -320,6 +320,8 @@ class CORE_EXPORT QgsLayoutItemRegistry : public QObject
LayoutAttributeTable, //!< Attribute table
LayoutTextTable, //!< Preset text table

Layout3DMap, //!< 3D map item

// item types provided by plugins
PluginItem = LayoutTextTable + 10000, //!< Starting point for plugin item types
};
Expand Down
1 change: 1 addition & 0 deletions tests/src/3d/CMakeLists.txt
Expand Up @@ -71,4 +71,5 @@ ENDMACRO (ADD_QGIS_TEST)

ADD_QGIS_TEST(3dutilstest testqgs3dutils.cpp)
ADD_QGIS_TEST(3drenderingtest testqgs3drendering.cpp)
ADD_QGIS_TEST(layout3dmaptest testqgslayout3dmap.cpp)
ADD_QGIS_TEST(tessellatortest testqgstessellator.cpp)
130 changes: 130 additions & 0 deletions tests/src/3d/testqgslayout3dmap.cpp
@@ -0,0 +1,130 @@
/***************************************************************************
testqgslayout3dmap.cpp
-----------------------
begin : August 2018
copyright : (C) 2018 by Martin Dobias
email : wonder dot sk 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 "qgs3dmapsettings.h"
#include "qgsapplication.h"
#include "qgsflatterraingenerator.h"
#include "qgslayoutframe.h"
#include "qgslayoutitem3dmap.h"
#include "qgslayout.h"
#include "qgsmultirenderchecker.h"
#include "qgsfontutils.h"
#include "qgsproject.h"
#include "qgsrasterlayer.h"

#include <QObject>
#include "qgstest.h"

class TestQgsLayout3DMap : public QObject
{
Q_OBJECT

private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.

void testBasic();

private:
QString mReport;
QFont mTestFont;
std::unique_ptr<QgsProject> mProject;
QgsRasterLayer *mLayerDtm;
};

void TestQgsLayout3DMap::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();

mProject.reset( new QgsProject );

QString dataDir( TEST_DATA_DIR );
mLayerDtm = new QgsRasterLayer( dataDir + "/3d/dtm.tif", "rgb", "gdal" );
QVERIFY( mLayerDtm->isValid() );
mProject->addMapLayer( mLayerDtm );

mProject->setCrs( mLayerDtm->crs() );

mReport = QStringLiteral( "<h1>Layout 3D Map Tests</h1>\n" );

QgsFontUtils::loadStandardTestFonts( QStringList() << QStringLiteral( "Oblique" ) );
mTestFont = QgsFontUtils::getStandardTestFont( QStringLiteral( "Oblique " ) );
}

void TestQgsLayout3DMap::cleanupTestCase()
{
mProject.reset();

QString myReportFile = QDir::tempPath() + "/qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
QgsApplication::exitQgis();
}

void TestQgsLayout3DMap::init()
{

}

void TestQgsLayout3DMap::cleanup()
{

}

void TestQgsLayout3DMap::testBasic()
{
QgsRectangle fullExtent = mLayerDtm->extent();

Qgs3DMapSettings *map = new Qgs3DMapSettings;
map->setCrs( mProject->crs() );
map->setOrigin( QgsVector3D( fullExtent.center().x(), fullExtent.center().y(), 0 ) );
map->setLayers( QList<QgsMapLayer *>() << mLayerDtm );

QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs( map->crs() );
flatTerrain->setExtent( fullExtent );
map->setTerrainGenerator( flatTerrain );

QgsCameraPose cam;
cam.setDistanceFromCenterPoint( 2500 );

QgsLayout l( mProject.get() );
l.initializeDefaults();

QgsLayoutItem3DMap *map3dItem = new QgsLayoutItem3DMap( &l );
map3dItem->attemptSetSceneRect( QRectF( 0, 0, 297, 210 ) );
map3dItem->setCameraPose( cam );
map3dItem->setMapSettings( map );
l.addLayoutItem( map3dItem );

QgsLayoutChecker checker( QStringLiteral( "composer3d_basic" ), &l );
checker.setControlPathPrefix( QStringLiteral( "composer_3d" ) );
bool result = checker.testLayout( mReport, 0, 100 );
QVERIFY( result );
}


QGSTEST_MAIN( TestQgsLayout3DMap )
#include "testqgslayout3dmap.moc"
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2e56fad

Please sign in to comment.