Skip to content

Commit

Permalink
Merge pull request #4625 from nyalldawson/comp_extent
Browse files Browse the repository at this point in the history
[FEATURE] New decoration type for showing composer map extents
  • Loading branch information
nyalldawson authored Jun 1, 2017
2 parents 5077e12 + babfb6b commit f359ffb
Show file tree
Hide file tree
Showing 10 changed files with 661 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ SET(QGIS_APP_SRCS
qgsdecorationitem.cpp
qgsdecorationcopyright.cpp
qgsdecorationcopyrightdialog.cpp
qgsdecorationlayoutextent.cpp
qgsdecorationlayoutextentdialog.cpp
qgsdecorationnortharrow.cpp
qgsdecorationnortharrowdialog.cpp
qgsdecorationscalebar.cpp
Expand Down Expand Up @@ -209,6 +211,8 @@ SET (QGIS_APP_MOC_HDRS
qgsdecorationitem.h
qgsdecorationcopyright.h
qgsdecorationcopyrightdialog.h
qgsdecorationlayoutextent.h
qgsdecorationlayoutextentdialog.h
qgsdecorationnortharrow.h
qgsdecorationnortharrowdialog.h
qgsdecorationscalebar.h
Expand Down
5 changes: 5 additions & 0 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgsdecorationnortharrow.h"
#include "qgsdecorationscalebar.h"
#include "qgsdecorationgrid.h"
#include "qgsdecorationlayoutextent.h"
#include "qgsencodingfiledialog.h"
#include "qgserror.h"
#include "qgserrordialog.h"
Expand Down Expand Up @@ -3527,11 +3528,15 @@ void QgisApp::createDecorations()
QgsDecorationGrid *mDecorationGrid = new QgsDecorationGrid( this );
connect( mActionDecorationGrid, &QAction::triggered, mDecorationGrid, &QgsDecorationGrid::run );

QgsDecorationLayoutExtent *decorationLayoutExtent = new QgsDecorationLayoutExtent( this );
connect( mActionDecorationLayoutExtent, &QAction::triggered, decorationLayoutExtent, &QgsDecorationLayoutExtent::run );

// add the decorations in a particular order so they are rendered in that order
addDecorationItem( mDecorationGrid );
addDecorationItem( mDecorationCopyright );
addDecorationItem( mDecorationNorthArrow );
addDecorationItem( mDecorationScaleBar );
addDecorationItem( decorationLayoutExtent );
connect( mMapCanvas, &QgsMapCanvas::renderComplete, this, &QgisApp::renderDecorationItems );
connect( this, &QgisApp::newProject, this, &QgisApp::projectReadDecorationItems );
connect( this, &QgisApp::projectRead, this, &QgisApp::projectReadDecorationItems );
Expand Down
7 changes: 4 additions & 3 deletions src/app/qgsdecorationitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class APP_EXPORT QgsDecorationItem : public QObject, public QgsMapDecoration
*/
void setPlacement( Placement placement ) { mPlacement = placement; }

QString name() const { return mName; }

signals:
void toggled( bool t );

Expand All @@ -73,14 +75,13 @@ class APP_EXPORT QgsDecorationItem : public QObject, public QgsMapDecoration
//! Show the dialog box
virtual void run() {}

virtual void setName( const char *name );
virtual QString name() { return mName; }

//! Redraws the decoration
void update();

protected:

void setName( const char *name );

//! True if decoration item has to be displayed
bool mEnabled;

Expand Down
189 changes: 189 additions & 0 deletions src/app/qgsdecorationlayoutextent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/***************************************************************************
qgsdecorationlayoutextent.cpp
----------------------
begin : May 2017
copyright : (C) 2017 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 "qgsdecorationlayoutextent.h"
#include "qgsdecorationlayoutextentdialog.h"

#include "qgslayoutmanager.h"
#include "qgscomposition.h"
#include "qgscomposermap.h"
#include "qgsgeometry.h"
#include "qgscsexception.h"
#include "qgslinesymbollayer.h"
#include "qgscomposer.h"
#include "qgisapp.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgssymbollayerutils.h"
#include "qgsreadwritecontext.h"

#include <QPainter>

QgsDecorationLayoutExtent::QgsDecorationLayoutExtent( QObject *parent )
: QgsDecorationItem( parent )
{
mPlacement = BottomRight;
mMarginUnit = QgsUnitTypes::RenderMillimeters;

setName( "Layout Extent" );
projectRead();
}

void QgsDecorationLayoutExtent::projectRead()
{
QgsDecorationItem::projectRead();

QDomDocument doc;
QDomElement elem;
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
QString xml = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Symbol" ) );
mSymbol.reset( nullptr );
if ( !xml.isEmpty() )
{
doc.setContent( xml );
elem = doc.documentElement();
mSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( elem, rwContext ) );
}
if ( ! mSymbol )
{
mSymbol.reset( new QgsFillSymbol() );
QgsSimpleLineSymbolLayer *layer = new QgsSimpleLineSymbolLayer( QColor( 0, 0, 0, 100 ), 0, Qt::DashLine );
mSymbol->changeSymbolLayer( 0, layer );
}

QString textXml = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Font" ) );
if ( !textXml.isEmpty() )
{
doc.setContent( textXml );
elem = doc.documentElement();
mTextFormat.readXml( elem, rwContext );
}
mLabelExtents = QgsProject::instance()->readBoolEntry( mNameConfig, QStringLiteral( "/Labels" ), true );
}

void QgsDecorationLayoutExtent::saveToProject()
{
QgsDecorationItem::saveToProject();
// write symbol info to xml
QDomDocument doc;
QDomElement elem;
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
if ( mSymbol )
{
elem = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "Symbol" ), mSymbol.get(), doc, rwContext );
doc.appendChild( elem );
// FIXME this works, but XML will not be valid as < is replaced by &lt;
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Symbol" ), doc.toString() );
}

QDomDocument textDoc;
QDomElement textElem = mTextFormat.writeXml( textDoc, rwContext );
textDoc.appendChild( textElem );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Font" ), textDoc.toString() );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Labels" ), mLabelExtents );
}

void QgsDecorationLayoutExtent::run()
{
QgsDecorationLayoutExtentDialog dlg( *this, QgisApp::instance() );
dlg.exec();
}


void QgsDecorationLayoutExtent::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
if ( !enabled() )
return;

if ( !mSymbol )
return;

context.painter()->save();
context.painter()->setRenderHint( QPainter::Antialiasing, true );
mSymbol->startRender( context );

const QgsMapToPixel &m2p = mapSettings.mapToPixel();
QTransform transform = m2p.transform();

// only loop through open composers
Q_FOREACH ( QgsComposer *composer, QgisApp::instance()->printComposers() )
{
QgsComposition *composition = composer->composition();
Q_FOREACH ( const QgsComposerMap *map, composition->composerMapItems() )
{
QPolygonF extent = map->visibleExtentPolygon();
QPointF labelPoint = extent.at( 1 );
QgsGeometry g = QgsGeometry::fromQPolygonF( extent );

if ( map->crs() !=
mapSettings.destinationCrs() )
{
// reproject extent
QgsCoordinateTransform ct( map->crs(),
mapSettings.destinationCrs() );
g = g.densifyByCount( 20 );
try
{
g.transform( ct );
labelPoint = ct.transform( labelPoint.x(), labelPoint.y() ).toQPointF();
}
catch ( QgsCsException & )
{
}
}

g.transform( transform );
labelPoint = transform.map( labelPoint );
extent = g.asQPolygonF();
mSymbol->renderPolygon( extent, nullptr, nullptr, context );

if ( mLabelExtents )
{
QgsTextRenderer::drawText( labelPoint, ( map->mapRotation() - mapSettings.rotation() ) * M_PI / 180.0, QgsTextRenderer::AlignRight, QStringList() << tr( "%1: %2" ).arg( composition->name(), map->displayName() ),
context, mTextFormat );
}
}
}
mSymbol->stopRender( context );
context.painter()->restore();
}

bool QgsDecorationLayoutExtent::labelExtents() const
{
return mLabelExtents;
}

void QgsDecorationLayoutExtent::setLabelExtents( bool labelExtents )
{
mLabelExtents = labelExtents;
}

QgsFillSymbol *QgsDecorationLayoutExtent::symbol() const
{
return mSymbol.get();
}

void QgsDecorationLayoutExtent::setSymbol( QgsFillSymbol *symbol )
{
mSymbol.reset( symbol );
}

98 changes: 98 additions & 0 deletions src/app/qgsdecorationlayoutextent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/***************************************************************************
qgsdecorationlayoutextent.h
-------------------
begin : May 2017
copyright : (C) 2017 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 QGSDECORATIONLAYOUTEXTENT_H
#define QGSDECORATIONLAYOUTEXTENT_H

#include "qgsdecorationitem.h"

#include <QColor>
#include <QFont>
#include <QObject>
#include "qgis_app.h"
#include "qgssymbol.h"
#include "qgstextrenderer.h"
#include <memory>

class QgsDecorationLayoutExtentDialog;

class APP_EXPORT QgsDecorationLayoutExtent : public QgsDecorationItem
{
Q_OBJECT
public:

/**
* Constructor for QgsDecorationLayoutExtent.
*/
QgsDecorationLayoutExtent( QObject *parent = nullptr );

/**
* Returns the fill symbol used for shading layout extents.
* \see setSymbol()
*/
QgsFillSymbol *symbol() const;

/**
* Sets the fill \a symbol used for shading layout extents. Ownership of
* \a symbol is transferred.
* \see symbol()
*/
void setSymbol( QgsFillSymbol *symbol SIP_TRANSFER );

/**
* Returns the text format for extent labels.
* \see setTextFormat()
* \see labelExtents()
*/
QgsTextFormat textFormat() const { return mTextFormat; }

/**
* Sets the text \a format for extent labels.
* \see textFormat()
* \see setLabelExtents()
*/
void setTextFormat( const QgsTextFormat &format ) { mTextFormat = format; }

/**
* Returns true if layout extents should be labeled with the name of the associated layout & map.
* \see setLabelExtents()
* \see textFormat()
*/
bool labelExtents() const;

/**
* Sets whether layout extents should be labeled with the name of the associated layout & map.
* \see labelExtents()
* \see setTextFormat()
*/
void setLabelExtents( bool labelExtents );

public slots:
void projectRead() override;
void saveToProject() override;
void run() override;
void render( const QgsMapSettings &mapSettings, QgsRenderContext &context ) override;

private:
std::unique_ptr< QgsFillSymbol > mSymbol;
QgsTextFormat mTextFormat;
bool mLabelExtents = true;

friend class QgsDecorationLayoutExtentDialog;
};

#endif //QGSDECORATIONLAYOUTEXTENT_H
Loading

0 comments on commit f359ffb

Please sign in to comment.