From e6758d64df49db38e1c6e15bb942d1015233106c Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Tue, 15 Apr 2014 13:55:00 +0200 Subject: [PATCH 1/9] Add a new 'mask' feature renderer that can be used to invert polygon fills --- src/core/CMakeLists.txt | 2 + src/core/symbology-ng/qgsmaskrendererv2.cpp | 303 ++++++++++++++++++ src/core/symbology-ng/qgsmaskrendererv2.h | 138 ++++++++ .../symbology-ng/qgsrendererv2registry.cpp | 5 + src/gui/CMakeLists.txt | 2 + .../symbology-ng/qgsmaskrendererv2widget.cpp | 124 +++++++ .../symbology-ng/qgsmaskrendererv2widget.h | 60 ++++ .../qgsrendererv2propertiesdialog.cpp | 2 + 8 files changed, 636 insertions(+) create mode 100644 src/core/symbology-ng/qgsmaskrendererv2.cpp create mode 100644 src/core/symbology-ng/qgsmaskrendererv2.h create mode 100644 src/gui/symbology-ng/qgsmaskrendererv2widget.cpp create mode 100644 src/gui/symbology-ng/qgsmaskrendererv2widget.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fe8fceca583..a8c9275a541 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -36,6 +36,7 @@ SET(QGIS_CORE_SRCS symbology-ng/qgscategorizedsymbolrendererv2.cpp symbology-ng/qgsgraduatedsymbolrendererv2.cpp symbology-ng/qgsrulebasedrendererv2.cpp + symbology-ng/qgsmaskrendererv2.cpp symbology-ng/qgsvectorcolorrampv2.cpp symbology-ng/qgscptcityarchive.cpp symbology-ng/qgsstylev2.cpp @@ -583,6 +584,7 @@ SET(QGIS_CORE_HDRS symbology-ng/qgsrendererv2registry.h symbology-ng/qgsrulebasedrendererv2.h symbology-ng/qgssinglesymbolrendererv2.h + symbology-ng/qgsmaskrendererv2.h symbology-ng/qgsstylev2.h symbology-ng/qgssvgcache.h symbology-ng/qgssymbollayerv2.h diff --git a/src/core/symbology-ng/qgsmaskrendererv2.cpp b/src/core/symbology-ng/qgsmaskrendererv2.cpp new file mode 100644 index 00000000000..ddd45cd4a3b --- /dev/null +++ b/src/core/symbology-ng/qgsmaskrendererv2.cpp @@ -0,0 +1,303 @@ +/*************************************************************************** + qgsmaskrendererv2.cpp + --------------------- + begin : April 2014 + copyright : (C) 2014 Hugo Mercier / Oslandia + email : hugo dot mercier at oslandia 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 "qgsmaskrendererv2.h" + +#include "qgssymbolv2.h" +#include "qgssymbollayerv2utils.h" + +#include "qgslogger.h" +#include "qgsfeature.h" +#include "qgsvectorlayer.h" +#include "qgssymbollayerv2.h" +#include "qgsogcutils.h" + +#include +#include + +QgsMaskRendererV2::QgsMaskRendererV2( const QgsFeatureRendererV2* subRenderer ) + : QgsFeatureRendererV2( "maskRenderer" ) +{ + if ( subRenderer ) { + setEmbeddedRenderer( subRenderer ); + } + else { + mSubRenderer.reset( QgsFeatureRendererV2::defaultRenderer( QGis::Polygon ) ); + } +} + +QgsMaskRendererV2::~QgsMaskRendererV2() +{ +} + +void QgsMaskRendererV2::setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer ) +{ + if ( subRenderer ) { + mSubRenderer.reset( const_cast(subRenderer)->clone() ); + } + else { + mSubRenderer.reset( 0 ); + } +} + +const QgsFeatureRendererV2* QgsMaskRendererV2::embeddedRenderer() const +{ + return mSubRenderer.data(); +} + +void QgsMaskRendererV2::startRender( QgsRenderContext& context, const QgsFields& fields ) +{ + if ( !mSubRenderer ) { + return; + } + + mSubRenderer->startRender( context, fields ); + mFeaturesCategoryMap.clear(); + mFeatureDecorations.clear(); + mFields = fields; +} + +bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) +{ + Q_UNUSED( context ); + + // Features are grouped by category of symbols (returned by symbol(s)ForFeature) + // This way, users can have multiple inverted polygon fills for a layer, + // for instance, with rule based renderer and different symbols + // that have transparency. + // + // In order to assign a unique category to a set of symbols + // during each rendering session (between startRender() and stopRender()), + // we build an unique id as a QByteArray that is the concatenation + // of each symbol's memory address. + // The only assumption made here is that symbol(s)ForFeature will + // always return the same address for the same symbol(s) shared amongst + // different features. + // This QByteArray can then be used as a key for a QMap where the list of + // features for this category is stored + QByteArray catId; + if ( capabilities() & MoreSymbolsPerFeature ) { + QgsSymbolV2List syms( mSubRenderer->symbolsForFeature( feature ) ); + foreach ( QgsSymbolV2* sym, syms ) + { + // append the memory address + catId.append( reinterpret_cast(&sym), sizeof(sym) ); + } + } + else + { + QgsSymbolV2* sym = mSubRenderer->symbolForFeature( feature ); + if (sym) { + catId.append( reinterpret_cast(&sym), sizeof(sym) ); + } + } + if ( !catId.isEmpty() ) + { + mFeaturesCategoryMap[catId].append( feature ); + } + + // store this feature as a feature to render with decoration if needed + if ( selected || drawVertexMarker ) + { + mFeatureDecorations.append( FeatureDecoration( feature, selected, drawVertexMarker, layer) ); + } + + return true; +} + +void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) +{ + if ( !mSubRenderer ) { + return; + } + + // We build here a "reversed" geometry of all the polygons + // + // The final geometry is a multipolygon F, with : + // * the first polygon of F having the current extent as its exterior ring + // * each polygon's exterior ring is added as interior ring of the first polygon of F + // * each polygon's interior ring is added as new polygons in F + // + // No validity check is done, on purpose, it will be very slow and painting + // operations do not need geometries to be valid + for ( FeatureCategoryMap::iterator cit = mFeaturesCategoryMap.begin(); cit != mFeaturesCategoryMap.end(); ++cit) + { + QgsMultiPolygon geom; + geom.push_back( QgsPolygon() ); + + // build a rectangle out of the current extent + QgsRectangle e = context.extent(); + // add some space to hide borders + e.scale(2.0); + QgsPolyline extent_ring; + extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMinimum()) ); + extent_ring.push_back( QgsPoint(e.xMaximum(), e.yMinimum()) ); + extent_ring.push_back( QgsPoint(e.xMaximum(), e.yMaximum()) ); + extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMaximum()) ); + extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMinimum()) ); + geom[0].push_back( extent_ring ); + + for ( QList::iterator fit = cit.value().begin(); fit != cit.value().end(); ++fit ) + { + if ( ! fit->geometry() ) { + continue; + } + QgsMultiPolygon multi; + if ( (fit->geometry()->wkbType() == QGis::WKBPolygon) || (fit->geometry()->wkbType() == QGis::WKBPolygon25D) ) { + multi.push_back( fit->geometry()->asPolygon() ); + } + else if ( (fit->geometry()->wkbType() == QGis::WKBMultiPolygon) || (fit->geometry()->wkbType() == QGis::WKBMultiPolygon25D) ) { + multi = fit->geometry()->asMultiPolygon(); + } + for ( int i = 0; i < multi.size(); i++ ) { + // add the exterior ring as interior ring + geom[0].push_back( multi[i][0] ); + // add interior rings as new exterior rings + for ( int j = 1; j < multi[i].size(); j++ ) { + QgsPolygon new_poly; + new_poly.push_back( multi[i][j] ); + geom.push_back( new_poly ); + } + } + } + + QgsFeature feat( cit.value()[0] ); + feat.setGeometry( QgsGeometry::fromMultiPolygon( geom ) ); + mSubRenderer->renderFeature( feat, context ); + } + + // when no features are visible, we still have to draw the exterior rectangle + if ( mFeaturesCategoryMap.isEmpty() ) + { + QgsRectangle e = context.extent(); + // add some space to hide borders + e.scale(2.0); + QgsFeature feat( mFields ); + feat.setGeometry( QgsGeometry::fromRect(e) ); + mSubRenderer->renderFeature( feat, context ); + } + + // draw feature decorations + foreach (FeatureDecoration deco, mFeatureDecorations ) + { + mSubRenderer->renderFeature( deco.feature, context, deco.layer, deco.selected, deco.drawMarkers ); + } + + mSubRenderer->stopRender( context ); +} + +QString QgsMaskRendererV2::dump() const +{ + if ( !mSubRenderer ) { + return "MASK: NULL"; + } + return "MASK [" + mSubRenderer->dump() + "]"; +} + +QgsFeatureRendererV2* QgsMaskRendererV2::clone() +{ + QgsMaskRendererV2* r = new QgsMaskRendererV2( mSubRenderer.data() ); + return r; +} + +QgsFeatureRendererV2* QgsMaskRendererV2::create( QDomElement& element ) +{ + QgsMaskRendererV2* r = new QgsMaskRendererV2(); + //look for an embedded renderer + QDomElement embeddedRendererElem = element.firstChildElement( "renderer-v2" ); + if ( !embeddedRendererElem.isNull() ) + { + r->setEmbeddedRenderer( QgsFeatureRendererV2::load( embeddedRendererElem ) ); + } + return r; +} + +QDomElement QgsMaskRendererV2::save( QDomDocument& doc ) +{ + QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME ); + rendererElem.setAttribute( "type", "maskRenderer" ); + + if ( mSubRenderer ) + { + QDomElement embeddedRendererElem = mSubRenderer->save( doc ); + rendererElem.appendChild( embeddedRendererElem ); + } + + return rendererElem; +} + +QgsSymbolV2* QgsMaskRendererV2::symbolForFeature( QgsFeature& feature ) +{ + if ( !mSubRenderer ) { + return 0; + } + return mSubRenderer->symbolForFeature( feature ); +} + +QgsSymbolV2List QgsMaskRendererV2::symbolsForFeature( QgsFeature& feature ) +{ + if ( !mSubRenderer ) { + return QgsSymbolV2List(); + } + return mSubRenderer->symbolsForFeature( feature ); +} + +QgsSymbolV2List QgsMaskRendererV2::symbols() +{ + if ( !mSubRenderer ) { + return QgsSymbolV2List(); + } + return mSubRenderer->symbols(); +} + +int QgsMaskRendererV2::capabilities() +{ + if ( !mSubRenderer ) { + return 0; + } + return mSubRenderer->capabilities(); +} + +QList QgsMaskRendererV2::usedAttributes() +{ + if ( !mSubRenderer ) { + return QList(); + } + return mSubRenderer->usedAttributes(); +} + +QgsLegendSymbologyList QgsMaskRendererV2::legendSymbologyItems( QSize iconSize ) +{ + if ( !mSubRenderer ) { + return QgsLegendSymbologyList(); + } + return mSubRenderer->legendSymbologyItems( iconSize ); +} + +QgsLegendSymbolList QgsMaskRendererV2::legendSymbolItems( double scaleDenominator, QString rule ) +{ + if ( !mSubRenderer ) { + return QgsLegendSymbolList(); + } + return mSubRenderer->legendSymbolItems( scaleDenominator, rule ); +} + +bool QgsMaskRendererV2::willRenderFeature( QgsFeature& feat ) +{ + if ( !mSubRenderer ) { + return false; + } + return mSubRenderer->willRenderFeature( feat ); +} diff --git a/src/core/symbology-ng/qgsmaskrendererv2.h b/src/core/symbology-ng/qgsmaskrendererv2.h new file mode 100644 index 00000000000..db9c02fa8f2 --- /dev/null +++ b/src/core/symbology-ng/qgsmaskrendererv2.h @@ -0,0 +1,138 @@ +/*************************************************************************** + qgsmaskrendererv2.h + --------------------- + begin : April 2014 + copyright : (C) 2014 Hugo Mercier / Oslandia + email : hugo dot mercier at oslandia 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 QGSMASKRENDERERV2_H +#define QGSMASKRENDERERV2_H + +#include "qgis.h" +#include "qgsrendererv2.h" +#include "qgssymbolv2.h" +#include "qgsexpression.h" +#include "qgsfeature.h" +#include + +/** + * QgsMaskRendererV2 is a polygon-only feature renderer used to + * display features inverted, where the exterior is turned to an interior + * and where the exterior theoretically spans the entire plane, allowing + * to mask the surroundings of some features. + * + * It is designed on top of another feature renderer, which is called "embedded" + * Most of the methods are then only proxies to the embedded renderer. + * + * Features are collected to form one "inverted" polygon + * during renderFeature() and rendered on stopRender(). + */ +class CORE_EXPORT QgsMaskRendererV2 : public QgsFeatureRendererV2 +{ + public: + + /** Constructor + * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned + */ + QgsMaskRendererV2( const QgsFeatureRendererV2* embeddedRenderer = 0 ); + virtual ~QgsMaskRendererV2(); + + /** Used to clone this feature renderer.*/ + virtual QgsFeatureRendererV2* clone(); + + virtual void startRender( QgsRenderContext& context, const QgsFields& fields ); + + /** Renders a given feature. + * This will here collect features. The actual rendering will be postponed to stopRender() + * @param feature the feature to render + * @param context the rendering context + * @param layer the symbol layer to render, if that makes sense + * @param selected whether this feature has been selected (this will add decorations) + * @param drawVertexMarker whether this feature has vertex markers (in edit mode usually) + * @returns true if the rendering was ok + */ + virtual bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ); + + /** + * The actual rendering will take place here. + * Features collected during renderFeature() are rendered using the embedded feature renderer + */ + virtual void stopRender( QgsRenderContext& context ); + + /** @returns a textual reprensation of the renderer */ + virtual QString dump() const; + + /** Proxy that will call this method on the embedded renderer. */ + virtual QList usedAttributes(); + /** Proxy that will call this method on the embedded renderer. */ + virtual int capabilities(); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2List symbols(); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature ); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsLegendSymbologyList legendSymbologyItems( QSize iconSize ); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, QString rule = "" ); + /** Proxy that will call this method on the embedded renderer. */ + virtual bool willRenderFeature( QgsFeature& feat ); + + /** Creates a renderer out of an XML, for loading*/ + static QgsFeatureRendererV2* create( QDomElement& element ); + + /** Creates an XML representation of the renderer. Used for saving purpose + * @param doc the XML document where to create the XML subtree + * @returns the created XML subtree + */ + virtual QDomElement save( QDomDocument& doc ); + + /** sets the embedded renderer + * @param subRenderer the embedded renderer (will be cloned) + */ + void setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer ); + /** @returns the current embedded renderer + */ + const QgsFeatureRendererV2* embeddedRenderer() const; + + private: + /** Private copy constructor. @see clone() */ + QgsMaskRendererV2( const QgsMaskRendererV2& ); + /** Private assignment operator. @see clone() */ + QgsMaskRendererV2& operator=( const QgsMaskRendererV2& ); + + /** Embedded renderer */ + QScopedPointer mSubRenderer; + + typedef QMap< QByteArray, QList > FeatureCategoryMap; + /** where features are stored, based on their symbol category */ + FeatureCategoryMap mFeaturesCategoryMap; + + /** fields of each feature*/ + QgsFields mFields; + + /** Class used to represent features that must be rendered + with decorations (selection, vertex markers) + */ + struct FeatureDecoration + { + QgsFeature feature; + bool selected; + bool drawMarkers; + int layer; + FeatureDecoration( QgsFeature& a_feature, bool a_selected, bool a_drawMarkers, int a_layer ) : + feature(a_feature),selected(a_selected), drawMarkers(a_drawMarkers), layer(a_layer) {} + }; + QList mFeatureDecorations; +}; + + +#endif // QGSMASKRENDERERV2_H diff --git a/src/core/symbology-ng/qgsrendererv2registry.cpp b/src/core/symbology-ng/qgsrendererv2registry.cpp index ecce71d2229..0e9a775fc42 100644 --- a/src/core/symbology-ng/qgsrendererv2registry.cpp +++ b/src/core/symbology-ng/qgsrendererv2registry.cpp @@ -20,6 +20,7 @@ #include "qgsgraduatedsymbolrendererv2.h" #include "qgsrulebasedrendererv2.h" #include "qgspointdisplacementrenderer.h" +#include "qgsmaskrendererv2.h" QgsRendererV2Registry::QgsRendererV2Registry() { @@ -44,6 +45,10 @@ QgsRendererV2Registry::QgsRendererV2Registry() addRenderer( new QgsRendererV2Metadata( "pointDisplacement", QObject::tr( "Point displacement" ), QgsPointDisplacementRenderer::create ) ); + + addRenderer( new QgsRendererV2Metadata( "maskRenderer", + QObject::tr( "Mask" ), + QgsMaskRendererV2::create ) ); } QgsRendererV2Registry::~QgsRendererV2Registry() diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index dcfcde2989e..84e0cae8644 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -20,6 +20,7 @@ symbology-ng/qgssinglesymbolrendererv2widget.cpp symbology-ng/qgscategorizedsymbolrendererv2widget.cpp symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp symbology-ng/qgsrulebasedrendererv2widget.cpp +symbology-ng/qgsmaskrendererv2widget.cpp symbology-ng/qgsrendererv2propertiesdialog.cpp symbology-ng/qgsstylev2managerdialog.cpp symbology-ng/qgssymbollevelsv2dialog.cpp @@ -180,6 +181,7 @@ symbology-ng/qgscategorizedsymbolrendererv2widget.h symbology-ng/qgsdatadefinedsymboldialog.h symbology-ng/qgsgraduatedsymbolrendererv2widget.h symbology-ng/qgsrulebasedrendererv2widget.h +symbology-ng/qgsmaskrendererv2widget.h symbology-ng/qgsrendererv2widget.h symbology-ng/qgsrendererv2propertiesdialog.h symbology-ng/qgsstylev2managerdialog.h diff --git a/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp b/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp new file mode 100644 index 00000000000..6072d8260f9 --- /dev/null +++ b/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + qgsmaskrendererv2widget.cpp + --------------------- + begin : April 2014 + copyright : (C) 2014 Hugo Mercier / Oslandia + email : hugo dot mercier at oslandia 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 "qgsmaskrendererv2widget.h" +#include "qgsmaskrendererv2.h" +#include "qgsrendererv2registry.h" + +#include "qgssymbolv2.h" + +#include "qgslogger.h" +#include "qgsvectorlayer.h" + +QgsRendererV2Widget* QgsMaskRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) +{ + return new QgsMaskRendererV2Widget( layer, style, renderer ); +} + +QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) + : QgsRendererV2Widget( layer, style ) +{ + if ( !layer ) { + return; + } + + // the renderer only applies to polygon vector layers + if ( layer->wkbType() != QGis::WKBPolygon && + layer->wkbType() != QGis::WKBPolygon25D && + layer->wkbType() != QGis::WKBMultiPolygon && + layer->wkbType() != QGis::WKBMultiPolygon25D ) + { + //setup blank dialog + mRenderer.reset( 0 ); + QGridLayout* layout = new QGridLayout( this ); + QLabel* label = new QLabel( tr( "The mask renderer only applies to polygon and multipolygon layers. \n" + "'%1' is not a polygon layer and then cannot be displayed" ) + .arg( layer->name() ), this ); + layout->addWidget( label ); + return; + } + setupUi( this ); + + // try to recognize the previous renderer + // (null renderer means "no previous renderer") + if ( !renderer || renderer->type() != "maskRenderer" ) + { + mRenderer.reset( new QgsMaskRendererV2() ); + } + else + { + mRenderer.reset( static_cast( renderer ) ); + } + + int currentEmbeddedIdx = 0; + //insert possible renderer types + QStringList rendererList = QgsRendererV2Registry::instance()->renderersList(); + QStringList::const_iterator it = rendererList.constBegin(); + int idx = 0; + mRendererComboBox->blockSignals( true ); + for ( ; it != rendererList.constEnd(); ++it, ++idx ) + { + if (( *it != "maskRenderer" ) && //< a mask renderer cannot contain another mask renderer + ( *it != "pointDisplacement" )) //< a mask renderer can only contain a polygon renderer + { + QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( *it ); + mRendererComboBox->addItem( m->icon(), m->visibleName(), /* data */ *it ); + const QgsFeatureRendererV2* embeddedRenderer = mRenderer->embeddedRenderer(); + if ( embeddedRenderer && embeddedRenderer->type() == m->name() ) + { + // store the combo box index of the current renderer + currentEmbeddedIdx = idx; + } + } + } + mRendererComboBox->blockSignals( false ); + + int oldIdx = mRendererComboBox->currentIndex(); + mRendererComboBox->setCurrentIndex( currentEmbeddedIdx ); + if ( oldIdx == currentEmbeddedIdx ) + { + // force update + on_mRendererComboBox_currentIndexChanged( currentEmbeddedIdx ); + } +} + +QgsFeatureRendererV2* QgsMaskRendererV2Widget::renderer() +{ + if ( mRenderer && mEmbeddedRendererWidget ) + { + QgsFeatureRendererV2* embeddedRenderer = mEmbeddedRendererWidget->renderer(); + if ( embeddedRenderer ) + { + mRenderer->setEmbeddedRenderer( embeddedRenderer->clone() ); + } + } + return mRenderer.data(); +} + +void QgsMaskRendererV2Widget::on_mRendererComboBox_currentIndexChanged( int index ) +{ + QString rendererId = mRendererComboBox->itemData( index ).toString(); + QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererId ); + if ( m ) + { + mEmbeddedRendererWidget.reset( m->createRendererWidget( mLayer, mStyle, const_cast(mRenderer->embeddedRenderer())->clone() ) ); + + if ( mLayout->count() > 1 ) { + // remove the current renderer widget + mLayout->takeAt( 1 ); + } + mLayout->addWidget( mEmbeddedRendererWidget.data() ); + } +} + diff --git a/src/gui/symbology-ng/qgsmaskrendererv2widget.h b/src/gui/symbology-ng/qgsmaskrendererv2widget.h new file mode 100644 index 00000000000..983d101f5fe --- /dev/null +++ b/src/gui/symbology-ng/qgsmaskrendererv2widget.h @@ -0,0 +1,60 @@ +/*************************************************************************** + qgsmaskrendererv2widget.h + --------------------- + begin : April 2014 + copyright : (C) 2014 Hugo Mercier / Oslandia + email : hugo dot mercier at oslandia 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 QGSMASKRENDERERV2WIDGET_H +#define QGSMASKRENDERERV2WIDGET_H + +#include "ui_qgsmaskrendererwidgetbase.h" +#include "qgsmaskrendererv2.h" +#include "qgsrendererv2widget.h" + +class QMenu; + +/** + * A widget used represent options of a QgsMaskRendererV2 + */ +class GUI_EXPORT QgsMaskRendererV2Widget : public QgsRendererV2Widget, private Ui::QgsMaskRendererWidgetBase +{ + Q_OBJECT + + public: + /** static creation method + * @param layer the layer where this renderer is applied + * @param style + * @param renderer the mask renderer (will take ownership) + */ + static QgsRendererV2Widget* create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + + /** Constructor + * @param layer the layer where this renderer is applied + * @param style + * @param renderer the mask renderer (will take ownership) + */ + QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + + /** @returns the current feature renderer */ + virtual QgsFeatureRendererV2* renderer(); + + protected: + /** the mask renderer */ + QScopedPointer mRenderer; + /** the widget used to represent the mask's embedded renderer */ + QScopedPointer mEmbeddedRendererWidget; + + private slots: + void on_mRendererComboBox_currentIndexChanged( int index ); +}; + + +#endif // QGSMASKRENDERERV2WIDGET_H diff --git a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp index 346a8a316d8..3906ff08c49 100644 --- a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp +++ b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp @@ -23,6 +23,7 @@ #include "qgsgraduatedsymbolrendererv2widget.h" #include "qgsrulebasedrendererv2widget.h" #include "qgspointdisplacementrendererwidget.h" +#include "qgsmaskrendererv2widget.h" #include "qgsapplication.h" #include "qgslogger.h" @@ -66,6 +67,7 @@ static void _initRendererWidgetFunctions() _initRenderer( "graduatedSymbol", QgsGraduatedSymbolRendererV2Widget::create, "rendererGraduatedSymbol.png" ); _initRenderer( "RuleRenderer", QgsRuleBasedRendererV2Widget::create ); _initRenderer( "pointDisplacement", QgsPointDisplacementRendererWidget::create ); + _initRenderer( "maskRenderer", QgsMaskRendererV2Widget::create ); initialized = true; } From 72311961a4d9e1f963dd9e505672c9ab78101610 Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Thu, 22 May 2014 08:15:24 +0200 Subject: [PATCH 2/9] Add missing .ui --- src/ui/qgsmaskrendererwidgetbase.ui | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/ui/qgsmaskrendererwidgetbase.ui diff --git a/src/ui/qgsmaskrendererwidgetbase.ui b/src/ui/qgsmaskrendererwidgetbase.ui new file mode 100644 index 00000000000..d5d23a4af4c --- /dev/null +++ b/src/ui/qgsmaskrendererwidgetbase.ui @@ -0,0 +1,35 @@ + + + QgsMaskRendererWidgetBase + + + + 0 + 0 + 390 + 79 + + + + Form + + + + + + + + Sub renderer: + + + + + + + + + + + + + From c1b4fe85456254a01ef794e113e473e0dd9e168a Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 09:20:34 +0200 Subject: [PATCH 3/9] Mask renderer: fix clone() --- src/core/symbology-ng/qgsmaskrendererv2.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/symbology-ng/qgsmaskrendererv2.cpp b/src/core/symbology-ng/qgsmaskrendererv2.cpp index ddd45cd4a3b..53446fe962a 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.cpp +++ b/src/core/symbology-ng/qgsmaskrendererv2.cpp @@ -208,8 +208,12 @@ QString QgsMaskRendererV2::dump() const QgsFeatureRendererV2* QgsMaskRendererV2::clone() { - QgsMaskRendererV2* r = new QgsMaskRendererV2( mSubRenderer.data() ); - return r; + if ( mSubRenderer.isNull() ) + { + return new QgsMaskRendererV2( 0 ); + } + // else + return new QgsMaskRendererV2( mSubRenderer->clone() ); } QgsFeatureRendererV2* QgsMaskRendererV2::create( QDomElement& element ) From acf2c45d92e7b79419d28ed667e9aabbe29c09f9 Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 11:17:40 +0200 Subject: [PATCH 4/9] Mask renderer: do not store temporary features anymore --- src/core/symbology-ng/qgsmaskrendererv2.cpp | 106 +++++++++++--------- src/core/symbology-ng/qgsmaskrendererv2.h | 9 +- 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/core/symbology-ng/qgsmaskrendererv2.cpp b/src/core/symbology-ng/qgsmaskrendererv2.cpp index 53446fe962a..01ddbc598ab 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.cpp +++ b/src/core/symbology-ng/qgsmaskrendererv2.cpp @@ -73,6 +73,12 @@ bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& co { Q_UNUSED( context ); + // store this feature as a feature to render with decoration if needed + if ( selected || drawVertexMarker ) + { + mFeatureDecorations.append( FeatureDecoration( feature, selected, drawVertexMarker, layer) ); + } + // Features are grouped by category of symbols (returned by symbol(s)ForFeature) // This way, users can have multiple inverted polygon fills for a layer, // for instance, with rule based renderer and different symbols @@ -103,24 +109,10 @@ bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& co catId.append( reinterpret_cast(&sym), sizeof(sym) ); } } - if ( !catId.isEmpty() ) + + if ( catId.isEmpty() ) { - mFeaturesCategoryMap[catId].append( feature ); - } - - // store this feature as a feature to render with decoration if needed - if ( selected || drawVertexMarker ) - { - mFeatureDecorations.append( FeatureDecoration( feature, selected, drawVertexMarker, layer) ); - } - - return true; -} - -void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) -{ - if ( !mSubRenderer ) { - return; + return false; } // We build here a "reversed" geometry of all the polygons @@ -132,12 +124,9 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) // // No validity check is done, on purpose, it will be very slow and painting // operations do not need geometries to be valid - for ( FeatureCategoryMap::iterator cit = mFeaturesCategoryMap.begin(); cit != mFeaturesCategoryMap.end(); ++cit) + if ( ! mFeaturesCategoryMap.contains(catId) ) { - QgsMultiPolygon geom; - geom.push_back( QgsPolygon() ); - - // build a rectangle out of the current extent + // add the exterior ring QgsRectangle e = context.extent(); // add some space to hide borders e.scale(2.0); @@ -147,34 +136,54 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) extent_ring.push_back( QgsPoint(e.xMaximum(), e.yMaximum()) ); extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMaximum()) ); extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMinimum()) ); - geom[0].push_back( extent_ring ); + CombinedFeature cFeat; + QgsPolygon exterior_ring_poly; + exterior_ring_poly.append(extent_ring); + cFeat.multiPolygon.append(exterior_ring_poly); + // store the first feature + cFeat.feature = feature; + mFeaturesCategoryMap.insert( catId, cFeat ); + } - for ( QList::iterator fit = cit.value().begin(); fit != cit.value().end(); ++fit ) - { - if ( ! fit->geometry() ) { - continue; - } - QgsMultiPolygon multi; - if ( (fit->geometry()->wkbType() == QGis::WKBPolygon) || (fit->geometry()->wkbType() == QGis::WKBPolygon25D) ) { - multi.push_back( fit->geometry()->asPolygon() ); - } - else if ( (fit->geometry()->wkbType() == QGis::WKBMultiPolygon) || (fit->geometry()->wkbType() == QGis::WKBMultiPolygon25D) ) { - multi = fit->geometry()->asMultiPolygon(); - } - for ( int i = 0; i < multi.size(); i++ ) { - // add the exterior ring as interior ring - geom[0].push_back( multi[i][0] ); - // add interior rings as new exterior rings - for ( int j = 1; j < multi[i].size(); j++ ) { - QgsPolygon new_poly; - new_poly.push_back( multi[i][j] ); - geom.push_back( new_poly ); - } - } + // update the gometry + CombinedFeature& cFeat = mFeaturesCategoryMap[catId]; + QgsMultiPolygon multi; + QgsGeometry* geom = feature.geometry(); + if ( !geom ) + { + return false; + } + if ( (geom->wkbType() == QGis::WKBPolygon) || + (geom->wkbType() == QGis::WKBPolygon25D) ) { + multi.append(geom->asPolygon() ); + } + else if ( (geom->wkbType() == QGis::WKBMultiPolygon) || + (geom->wkbType() == QGis::WKBMultiPolygon25D) ) { + multi = geom->asMultiPolygon(); + } + for ( int i = 0; i < multi.size(); i++ ) { + // add the exterior ring as interior ring to the first polygon + cFeat.multiPolygon[0].append( multi[i][0] ); + // add interior rings as new polygons + for ( int j = 1; j < multi[i].size(); j++ ) { + QgsPolygon new_poly; + new_poly.append( multi[i][j] ); + cFeat.multiPolygon.append( new_poly ); } + } + return true; +} - QgsFeature feat( cit.value()[0] ); - feat.setGeometry( QgsGeometry::fromMultiPolygon( geom ) ); +void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) +{ + if ( !mSubRenderer ) { + return; + } + + for ( FeatureCategoryMap::iterator cit = mFeaturesCategoryMap.begin(); cit != mFeaturesCategoryMap.end(); ++cit) + { + QgsFeature feat( cit.value().feature ); + feat.setGeometry( QgsGeometry::fromMultiPolygon( cit.value().multiPolygon ) ); mSubRenderer->renderFeature( feat, context ); } @@ -184,9 +193,10 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) QgsRectangle e = context.extent(); // add some space to hide borders e.scale(2.0); + // empty feature with default attributes QgsFeature feat( mFields ); feat.setGeometry( QgsGeometry::fromRect(e) ); - mSubRenderer->renderFeature( feat, context ); + mSubRenderer->renderFeature( feat, context ); } // draw feature decorations diff --git a/src/core/symbology-ng/qgsmaskrendererv2.h b/src/core/symbology-ng/qgsmaskrendererv2.h index db9c02fa8f2..cf623761fa4 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.h +++ b/src/core/symbology-ng/qgsmaskrendererv2.h @@ -20,6 +20,7 @@ #include "qgssymbolv2.h" #include "qgsexpression.h" #include "qgsfeature.h" +#include "qgsgeometry.h" #include /** @@ -112,7 +113,13 @@ class CORE_EXPORT QgsMaskRendererV2 : public QgsFeatureRendererV2 /** Embedded renderer */ QScopedPointer mSubRenderer; - typedef QMap< QByteArray, QList > FeatureCategoryMap; + /** Structure where the reversed geometry is built during renderFeature */ + struct CombinedFeature + { + QgsMultiPolygon multiPolygon; //< the final combined geometry + QgsFeature feature; //< one feature (for attriute-based rendering) + }; + typedef QMap< QByteArray, CombinedFeature > FeatureCategoryMap; /** where features are stored, based on their symbol category */ FeatureCategoryMap mFeaturesCategoryMap; From 3ec7337e66bd57e03852b9856637965228b8e80d Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 17:37:25 +0200 Subject: [PATCH 5/9] Mask renderer: make it work with on-the-fly reprojection --- src/core/symbology-ng/qgsmaskrendererv2.cpp | 88 ++++++++++++++++----- src/core/symbology-ng/qgsmaskrendererv2.h | 6 ++ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/core/symbology-ng/qgsmaskrendererv2.cpp b/src/core/symbology-ng/qgsmaskrendererv2.cpp index 01ddbc598ab..3b885f68006 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.cpp +++ b/src/core/symbology-ng/qgsmaskrendererv2.cpp @@ -67,6 +67,37 @@ void QgsMaskRendererV2::startRender( QgsRenderContext& context, const QgsFields& mFeaturesCategoryMap.clear(); mFeatureDecorations.clear(); mFields = fields; + + // We compute coordinates of the extent which will serve as exterior ring + // for the final polygon + // It must be computed in the destination CRS if reprojection is enabled. + const QgsMapToPixel& mtp( context.mapToPixel() ); + + // convert viewport to dest CRS + QRect e( context.painter()->viewport() ); + // add some space to hide borders and tend to infinity + e.adjust( -e.width()*10, -e.height()*10, e.width()*10, e.height()*10 ); + QgsPolyline exteriorRing; + exteriorRing << mtp.toMapCoordinates( e.topLeft() ); + exteriorRing << mtp.toMapCoordinates( e.topRight() ); + exteriorRing << mtp.toMapCoordinates( e.bottomRight() ); + exteriorRing << mtp.toMapCoordinates( e.bottomLeft() ); + exteriorRing << mtp.toMapCoordinates( e.topLeft() ); + + mTransform = context.coordinateTransform(); + // If reprojection is enabled, we must reproject during renderFeature + // and act as if there is no reprojection + // If we don't do that, there is no need to have a simple rectangular extent + // that covers the whole screen + // (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general) + if (mTransform) + { + // disable projection + context.setCoordinateTransform(0); + } + + mExtentPolygon.clear(); + mExtentPolygon.append(exteriorRing); } bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) @@ -126,20 +157,9 @@ bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& co // operations do not need geometries to be valid if ( ! mFeaturesCategoryMap.contains(catId) ) { - // add the exterior ring - QgsRectangle e = context.extent(); - // add some space to hide borders - e.scale(2.0); - QgsPolyline extent_ring; - extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMinimum()) ); - extent_ring.push_back( QgsPoint(e.xMaximum(), e.yMinimum()) ); - extent_ring.push_back( QgsPoint(e.xMaximum(), e.yMaximum()) ); - extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMaximum()) ); - extent_ring.push_back( QgsPoint(e.xMinimum(), e.yMinimum()) ); + // the exterior ring must be a square in the destination CRS CombinedFeature cFeat; - QgsPolygon exterior_ring_poly; - exterior_ring_poly.append(extent_ring); - cFeat.multiPolygon.append(exterior_ring_poly); + cFeat.multiPolygon.append( mExtentPolygon ); // store the first feature cFeat.feature = feature; mFeaturesCategoryMap.insert( catId, cFeat ); @@ -161,13 +181,37 @@ bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& co (geom->wkbType() == QGis::WKBMultiPolygon25D) ) { multi = geom->asMultiPolygon(); } + for ( int i = 0; i < multi.size(); i++ ) { // add the exterior ring as interior ring to the first polygon - cFeat.multiPolygon[0].append( multi[i][0] ); + if ( mTransform ) { + QgsPolyline new_ls; + QgsPolyline& old_ls = multi[i][0]; + for ( int k = 0; k < old_ls.size(); k++ ) { + new_ls.append( mTransform->transform( old_ls[k] ) ); + } + cFeat.multiPolygon[0].append( new_ls ); + } + else + { + cFeat.multiPolygon[0].append( multi[i][0] ); + } // add interior rings as new polygons for ( int j = 1; j < multi[i].size(); j++ ) { QgsPolygon new_poly; - new_poly.append( multi[i][j] ); + if ( mTransform ) { + QgsPolyline new_ls; + QgsPolyline& old_ls = multi[i][j]; + for ( int k = 0; k < old_ls.size(); k++ ) { + new_ls.append( mTransform->transform( old_ls[k] ) ); + } + new_poly.append( new_ls ); + } + else + { + new_poly.append( multi[i][j] ); + } + cFeat.multiPolygon.append( new_poly ); } } @@ -188,14 +232,14 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) } // when no features are visible, we still have to draw the exterior rectangle + // warning: when sub renderers have more than one possible symbols, + // there is no way to choose a correct one, because there is no attribute here + // in that case, nothing will be rendered if ( mFeaturesCategoryMap.isEmpty() ) { - QgsRectangle e = context.extent(); - // add some space to hide borders - e.scale(2.0); // empty feature with default attributes QgsFeature feat( mFields ); - feat.setGeometry( QgsGeometry::fromRect(e) ); + feat.setGeometry( QgsGeometry::fromPolygon( mExtentPolygon ) ); mSubRenderer->renderFeature( feat, context ); } @@ -206,6 +250,12 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) } mSubRenderer->stopRender( context ); + + if ( mTransform ) + { + // restore the coordinate transform if needed + context.setCoordinateTransform( mTransform ); + } } QString QgsMaskRendererV2::dump() const diff --git a/src/core/symbology-ng/qgsmaskrendererv2.h b/src/core/symbology-ng/qgsmaskrendererv2.h index cf623761fa4..db98ae42913 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.h +++ b/src/core/symbology-ng/qgsmaskrendererv2.h @@ -123,6 +123,12 @@ class CORE_EXPORT QgsMaskRendererV2 : public QgsFeatureRendererV2 /** where features are stored, based on their symbol category */ FeatureCategoryMap mFeaturesCategoryMap; + /** the polygon used as exterior ring that covers the current extent */ + QgsPolygon mExtentPolygon; + + /** the current coordinate transform (or null) */ + const QgsCoordinateTransform* mTransform; + /** fields of each feature*/ QgsFields mFields; From 84b1577420410c9932d6ff5f6058b0f75b7d76c8 Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 18:11:06 +0200 Subject: [PATCH 6/9] Mask renderer: reuse existing renderer as embedded --- src/gui/symbology-ng/qgsmaskrendererv2widget.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp b/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp index 6072d8260f9..34c5e3e1aa3 100644 --- a/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp +++ b/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp @@ -52,13 +52,21 @@ QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyl // try to recognize the previous renderer // (null renderer means "no previous renderer") - if ( !renderer || renderer->type() != "maskRenderer" ) + if ( !renderer ) { + // a new renderer mRenderer.reset( new QgsMaskRendererV2() ); } + else if ( renderer && renderer->type() != "maskRenderer" ) + { + // an existing renderer, but not a mask renderer + // create a mask renderer, with the existing renderer embedded + mRenderer.reset( new QgsMaskRendererV2( renderer ) ); + } else { - mRenderer.reset( static_cast( renderer ) ); + // an existing mask renderer + mRenderer.reset( static_cast(renderer) ); } int currentEmbeddedIdx = 0; From 0ff587c7a0da00c23d50339471f405a1bf00844e Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 18:44:05 +0200 Subject: [PATCH 7/9] Mask renderer: rename to inverted polygon renderer --- src/core/CMakeLists.txt | 4 +- ...rv2.cpp => qgsinvertedpolygonrenderer.cpp} | 56 +++++++++---------- ...dererv2.h => qgsinvertedpolygonrenderer.h} | 18 +++--- .../symbology-ng/qgsrendererv2registry.cpp | 8 +-- src/gui/CMakeLists.txt | 4 +- ...p => qgsinvertedpolygonrendererwidget.cpp} | 36 ++++++------ ...t.h => qgsinvertedpolygonrendererwidget.h} | 18 +++--- .../qgsrendererv2propertiesdialog.cpp | 4 +- ...> qgsinvertedpolygonrendererwidgetbase.ui} | 4 +- 9 files changed, 76 insertions(+), 76 deletions(-) rename src/core/symbology-ng/{qgsmaskrendererv2.cpp => qgsinvertedpolygonrenderer.cpp} (82%) rename src/core/symbology-ng/{qgsmaskrendererv2.h => qgsinvertedpolygonrenderer.h} (91%) rename src/gui/symbology-ng/{qgsmaskrendererv2widget.cpp => qgsinvertedpolygonrendererwidget.cpp} (72%) rename src/gui/symbology-ng/{qgsmaskrendererv2widget.h => qgsinvertedpolygonrendererwidget.h} (75%) rename src/ui/{qgsmaskrendererwidgetbase.ui => qgsinvertedpolygonrendererwidgetbase.ui} (85%) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a8c9275a541..220edc44a98 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -36,7 +36,7 @@ SET(QGIS_CORE_SRCS symbology-ng/qgscategorizedsymbolrendererv2.cpp symbology-ng/qgsgraduatedsymbolrendererv2.cpp symbology-ng/qgsrulebasedrendererv2.cpp - symbology-ng/qgsmaskrendererv2.cpp + symbology-ng/qgsinvertedpolygonrenderer.cpp symbology-ng/qgsvectorcolorrampv2.cpp symbology-ng/qgscptcityarchive.cpp symbology-ng/qgsstylev2.cpp @@ -584,7 +584,7 @@ SET(QGIS_CORE_HDRS symbology-ng/qgsrendererv2registry.h symbology-ng/qgsrulebasedrendererv2.h symbology-ng/qgssinglesymbolrendererv2.h - symbology-ng/qgsmaskrendererv2.h + symbology-ng/qgsinvertedpolygonrenderer.h symbology-ng/qgsstylev2.h symbology-ng/qgssvgcache.h symbology-ng/qgssymbollayerv2.h diff --git a/src/core/symbology-ng/qgsmaskrendererv2.cpp b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp similarity index 82% rename from src/core/symbology-ng/qgsmaskrendererv2.cpp rename to src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp index 3b885f68006..1748f7f0194 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.cpp +++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - qgsmaskrendererv2.cpp + qgsinvertedpolygonrenderer.cpp --------------------- begin : April 2014 copyright : (C) 2014 Hugo Mercier / Oslandia @@ -13,7 +13,7 @@ * * ***************************************************************************/ -#include "qgsmaskrendererv2.h" +#include "qgsinvertedpolygonrenderer.h" #include "qgssymbolv2.h" #include "qgssymbollayerv2utils.h" @@ -27,8 +27,8 @@ #include #include -QgsMaskRendererV2::QgsMaskRendererV2( const QgsFeatureRendererV2* subRenderer ) - : QgsFeatureRendererV2( "maskRenderer" ) +QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* subRenderer ) + : QgsFeatureRendererV2( "invertedPolygonRenderer" ) { if ( subRenderer ) { setEmbeddedRenderer( subRenderer ); @@ -38,11 +38,11 @@ QgsMaskRendererV2::QgsMaskRendererV2( const QgsFeatureRendererV2* subRenderer ) } } -QgsMaskRendererV2::~QgsMaskRendererV2() +QgsInvertedPolygonRenderer::~QgsInvertedPolygonRenderer() { } -void QgsMaskRendererV2::setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer ) +void QgsInvertedPolygonRenderer::setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer ) { if ( subRenderer ) { mSubRenderer.reset( const_cast(subRenderer)->clone() ); @@ -52,12 +52,12 @@ void QgsMaskRendererV2::setEmbeddedRenderer( const QgsFeatureRendererV2* subRend } } -const QgsFeatureRendererV2* QgsMaskRendererV2::embeddedRenderer() const +const QgsFeatureRendererV2* QgsInvertedPolygonRenderer::embeddedRenderer() const { return mSubRenderer.data(); } -void QgsMaskRendererV2::startRender( QgsRenderContext& context, const QgsFields& fields ) +void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const QgsFields& fields ) { if ( !mSubRenderer ) { return; @@ -100,7 +100,7 @@ void QgsMaskRendererV2::startRender( QgsRenderContext& context, const QgsFields& mExtentPolygon.append(exteriorRing); } -bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) +bool QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) { Q_UNUSED( context ); @@ -218,7 +218,7 @@ bool QgsMaskRendererV2::renderFeature( QgsFeature& feature, QgsRenderContext& co return true; } -void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) +void QgsInvertedPolygonRenderer::stopRender( QgsRenderContext& context ) { if ( !mSubRenderer ) { return; @@ -258,27 +258,27 @@ void QgsMaskRendererV2::stopRender( QgsRenderContext& context ) } } -QString QgsMaskRendererV2::dump() const +QString QgsInvertedPolygonRenderer::dump() const { if ( !mSubRenderer ) { - return "MASK: NULL"; + return "INVERTED: NULL"; } - return "MASK [" + mSubRenderer->dump() + "]"; + return "INVERTED [" + mSubRenderer->dump() + "]"; } -QgsFeatureRendererV2* QgsMaskRendererV2::clone() +QgsFeatureRendererV2* QgsInvertedPolygonRenderer::clone() { if ( mSubRenderer.isNull() ) { - return new QgsMaskRendererV2( 0 ); + return new QgsInvertedPolygonRenderer( 0 ); } // else - return new QgsMaskRendererV2( mSubRenderer->clone() ); + return new QgsInvertedPolygonRenderer( mSubRenderer->clone() ); } -QgsFeatureRendererV2* QgsMaskRendererV2::create( QDomElement& element ) +QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element ) { - QgsMaskRendererV2* r = new QgsMaskRendererV2(); + QgsInvertedPolygonRenderer* r = new QgsInvertedPolygonRenderer(); //look for an embedded renderer QDomElement embeddedRendererElem = element.firstChildElement( "renderer-v2" ); if ( !embeddedRendererElem.isNull() ) @@ -288,10 +288,10 @@ QgsFeatureRendererV2* QgsMaskRendererV2::create( QDomElement& element ) return r; } -QDomElement QgsMaskRendererV2::save( QDomDocument& doc ) +QDomElement QgsInvertedPolygonRenderer::save( QDomDocument& doc ) { QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME ); - rendererElem.setAttribute( "type", "maskRenderer" ); + rendererElem.setAttribute( "type", "invertedPolygonRenderer" ); if ( mSubRenderer ) { @@ -302,7 +302,7 @@ QDomElement QgsMaskRendererV2::save( QDomDocument& doc ) return rendererElem; } -QgsSymbolV2* QgsMaskRendererV2::symbolForFeature( QgsFeature& feature ) +QgsSymbolV2* QgsInvertedPolygonRenderer::symbolForFeature( QgsFeature& feature ) { if ( !mSubRenderer ) { return 0; @@ -310,7 +310,7 @@ QgsSymbolV2* QgsMaskRendererV2::symbolForFeature( QgsFeature& feature ) return mSubRenderer->symbolForFeature( feature ); } -QgsSymbolV2List QgsMaskRendererV2::symbolsForFeature( QgsFeature& feature ) +QgsSymbolV2List QgsInvertedPolygonRenderer::symbolsForFeature( QgsFeature& feature ) { if ( !mSubRenderer ) { return QgsSymbolV2List(); @@ -318,7 +318,7 @@ QgsSymbolV2List QgsMaskRendererV2::symbolsForFeature( QgsFeature& feature ) return mSubRenderer->symbolsForFeature( feature ); } -QgsSymbolV2List QgsMaskRendererV2::symbols() +QgsSymbolV2List QgsInvertedPolygonRenderer::symbols() { if ( !mSubRenderer ) { return QgsSymbolV2List(); @@ -326,7 +326,7 @@ QgsSymbolV2List QgsMaskRendererV2::symbols() return mSubRenderer->symbols(); } -int QgsMaskRendererV2::capabilities() +int QgsInvertedPolygonRenderer::capabilities() { if ( !mSubRenderer ) { return 0; @@ -334,7 +334,7 @@ int QgsMaskRendererV2::capabilities() return mSubRenderer->capabilities(); } -QList QgsMaskRendererV2::usedAttributes() +QList QgsInvertedPolygonRenderer::usedAttributes() { if ( !mSubRenderer ) { return QList(); @@ -342,7 +342,7 @@ QList QgsMaskRendererV2::usedAttributes() return mSubRenderer->usedAttributes(); } -QgsLegendSymbologyList QgsMaskRendererV2::legendSymbologyItems( QSize iconSize ) +QgsLegendSymbologyList QgsInvertedPolygonRenderer::legendSymbologyItems( QSize iconSize ) { if ( !mSubRenderer ) { return QgsLegendSymbologyList(); @@ -350,7 +350,7 @@ QgsLegendSymbologyList QgsMaskRendererV2::legendSymbologyItems( QSize iconSize ) return mSubRenderer->legendSymbologyItems( iconSize ); } -QgsLegendSymbolList QgsMaskRendererV2::legendSymbolItems( double scaleDenominator, QString rule ) +QgsLegendSymbolList QgsInvertedPolygonRenderer::legendSymbolItems( double scaleDenominator, QString rule ) { if ( !mSubRenderer ) { return QgsLegendSymbolList(); @@ -358,7 +358,7 @@ QgsLegendSymbolList QgsMaskRendererV2::legendSymbolItems( double scaleDenominato return mSubRenderer->legendSymbolItems( scaleDenominator, rule ); } -bool QgsMaskRendererV2::willRenderFeature( QgsFeature& feat ) +bool QgsInvertedPolygonRenderer::willRenderFeature( QgsFeature& feat ) { if ( !mSubRenderer ) { return false; diff --git a/src/core/symbology-ng/qgsmaskrendererv2.h b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h similarity index 91% rename from src/core/symbology-ng/qgsmaskrendererv2.h rename to src/core/symbology-ng/qgsinvertedpolygonrenderer.h index db98ae42913..575a7db98bc 100644 --- a/src/core/symbology-ng/qgsmaskrendererv2.h +++ b/src/core/symbology-ng/qgsinvertedpolygonrenderer.h @@ -1,5 +1,5 @@ /*************************************************************************** - qgsmaskrendererv2.h + qgsinvertedpolygonrenderer.h --------------------- begin : April 2014 copyright : (C) 2014 Hugo Mercier / Oslandia @@ -12,8 +12,8 @@ * (at your option) any later version. * * * ***************************************************************************/ -#ifndef QGSMASKRENDERERV2_H -#define QGSMASKRENDERERV2_H +#ifndef QGSINVERTEDPOLYGONRENDERER_H +#define QGSINVERTEDPOLYGONRENDERER_H #include "qgis.h" #include "qgsrendererv2.h" @@ -24,7 +24,7 @@ #include /** - * QgsMaskRendererV2 is a polygon-only feature renderer used to + * QgsInvertedPolygonRenderer is a polygon-only feature renderer used to * display features inverted, where the exterior is turned to an interior * and where the exterior theoretically spans the entire plane, allowing * to mask the surroundings of some features. @@ -35,15 +35,15 @@ * Features are collected to form one "inverted" polygon * during renderFeature() and rendered on stopRender(). */ -class CORE_EXPORT QgsMaskRendererV2 : public QgsFeatureRendererV2 +class CORE_EXPORT QgsInvertedPolygonRenderer : public QgsFeatureRendererV2 { public: /** Constructor * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned */ - QgsMaskRendererV2( const QgsFeatureRendererV2* embeddedRenderer = 0 ); - virtual ~QgsMaskRendererV2(); + QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* embeddedRenderer = 0 ); + virtual ~QgsInvertedPolygonRenderer(); /** Used to clone this feature renderer.*/ virtual QgsFeatureRendererV2* clone(); @@ -106,9 +106,9 @@ class CORE_EXPORT QgsMaskRendererV2 : public QgsFeatureRendererV2 private: /** Private copy constructor. @see clone() */ - QgsMaskRendererV2( const QgsMaskRendererV2& ); + QgsInvertedPolygonRenderer( const QgsInvertedPolygonRenderer& ); /** Private assignment operator. @see clone() */ - QgsMaskRendererV2& operator=( const QgsMaskRendererV2& ); + QgsInvertedPolygonRenderer& operator=( const QgsInvertedPolygonRenderer& ); /** Embedded renderer */ QScopedPointer mSubRenderer; diff --git a/src/core/symbology-ng/qgsrendererv2registry.cpp b/src/core/symbology-ng/qgsrendererv2registry.cpp index 0e9a775fc42..52710a59139 100644 --- a/src/core/symbology-ng/qgsrendererv2registry.cpp +++ b/src/core/symbology-ng/qgsrendererv2registry.cpp @@ -20,7 +20,7 @@ #include "qgsgraduatedsymbolrendererv2.h" #include "qgsrulebasedrendererv2.h" #include "qgspointdisplacementrenderer.h" -#include "qgsmaskrendererv2.h" +#include "qgsinvertedpolygonrenderer.h" QgsRendererV2Registry::QgsRendererV2Registry() { @@ -46,9 +46,9 @@ QgsRendererV2Registry::QgsRendererV2Registry() QObject::tr( "Point displacement" ), QgsPointDisplacementRenderer::create ) ); - addRenderer( new QgsRendererV2Metadata( "maskRenderer", - QObject::tr( "Mask" ), - QgsMaskRendererV2::create ) ); + addRenderer( new QgsRendererV2Metadata( "invertedPolygonRenderer", + QObject::tr( "Inverted polygons" ), + QgsInvertedPolygonRenderer::create ) ); } QgsRendererV2Registry::~QgsRendererV2Registry() diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 84e0cae8644..51612497a64 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -20,7 +20,7 @@ symbology-ng/qgssinglesymbolrendererv2widget.cpp symbology-ng/qgscategorizedsymbolrendererv2widget.cpp symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp symbology-ng/qgsrulebasedrendererv2widget.cpp -symbology-ng/qgsmaskrendererv2widget.cpp +symbology-ng/qgsinvertedpolygonrendererwidget.cpp symbology-ng/qgsrendererv2propertiesdialog.cpp symbology-ng/qgsstylev2managerdialog.cpp symbology-ng/qgssymbollevelsv2dialog.cpp @@ -181,7 +181,7 @@ symbology-ng/qgscategorizedsymbolrendererv2widget.h symbology-ng/qgsdatadefinedsymboldialog.h symbology-ng/qgsgraduatedsymbolrendererv2widget.h symbology-ng/qgsrulebasedrendererv2widget.h -symbology-ng/qgsmaskrendererv2widget.h +symbology-ng/qgsinvertedpolygonrendererwidget.h symbology-ng/qgsrendererv2widget.h symbology-ng/qgsrendererv2propertiesdialog.h symbology-ng/qgsstylev2managerdialog.h diff --git a/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp b/src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.cpp similarity index 72% rename from src/gui/symbology-ng/qgsmaskrendererv2widget.cpp rename to src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.cpp index 34c5e3e1aa3..b7141989a11 100644 --- a/src/gui/symbology-ng/qgsmaskrendererv2widget.cpp +++ b/src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - qgsmaskrendererv2widget.cpp + qgsinvertedpolygonrendererwidget.cpp --------------------- begin : April 2014 copyright : (C) 2014 Hugo Mercier / Oslandia @@ -12,8 +12,8 @@ * (at your option) any later version. * * * ***************************************************************************/ -#include "qgsmaskrendererv2widget.h" -#include "qgsmaskrendererv2.h" +#include "qgsinvertedpolygonrendererwidget.h" +#include "qgsinvertedpolygonrenderer.h" #include "qgsrendererv2registry.h" #include "qgssymbolv2.h" @@ -21,12 +21,12 @@ #include "qgslogger.h" #include "qgsvectorlayer.h" -QgsRendererV2Widget* QgsMaskRendererV2Widget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) +QgsRendererV2Widget* QgsInvertedPolygonRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) { - return new QgsMaskRendererV2Widget( layer, style, renderer ); + return new QgsInvertedPolygonRendererWidget( layer, style, renderer ); } -QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) +QgsInvertedPolygonRendererWidget::QgsInvertedPolygonRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ) : QgsRendererV2Widget( layer, style ) { if ( !layer ) { @@ -42,7 +42,7 @@ QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyl //setup blank dialog mRenderer.reset( 0 ); QGridLayout* layout = new QGridLayout( this ); - QLabel* label = new QLabel( tr( "The mask renderer only applies to polygon and multipolygon layers. \n" + QLabel* label = new QLabel( tr( "The inverted polygon renderer only applies to polygon and multipolygon layers. \n" "'%1' is not a polygon layer and then cannot be displayed" ) .arg( layer->name() ), this ); layout->addWidget( label ); @@ -55,18 +55,18 @@ QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyl if ( !renderer ) { // a new renderer - mRenderer.reset( new QgsMaskRendererV2() ); + mRenderer.reset( new QgsInvertedPolygonRenderer() ); } - else if ( renderer && renderer->type() != "maskRenderer" ) + else if ( renderer && renderer->type() != "invertedPolygonRenderer" ) { - // an existing renderer, but not a mask renderer - // create a mask renderer, with the existing renderer embedded - mRenderer.reset( new QgsMaskRendererV2( renderer ) ); + // an existing renderer, but not an inverted renderer + // create an inverted renderer, with the existing renderer embedded + mRenderer.reset( new QgsInvertedPolygonRenderer( renderer ) ); } else { - // an existing mask renderer - mRenderer.reset( static_cast(renderer) ); + // an existing inverted renderer + mRenderer.reset( static_cast(renderer) ); } int currentEmbeddedIdx = 0; @@ -77,8 +77,8 @@ QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyl mRendererComboBox->blockSignals( true ); for ( ; it != rendererList.constEnd(); ++it, ++idx ) { - if (( *it != "maskRenderer" ) && //< a mask renderer cannot contain another mask renderer - ( *it != "pointDisplacement" )) //< a mask renderer can only contain a polygon renderer + if (( *it != "invertedPolygonRenderer" ) && //< an inverted renderer cannot contain another inverted renderer + ( *it != "pointDisplacement" )) //< an inverted renderer can only contain a polygon renderer { QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( *it ); mRendererComboBox->addItem( m->icon(), m->visibleName(), /* data */ *it ); @@ -101,7 +101,7 @@ QgsMaskRendererV2Widget::QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyl } } -QgsFeatureRendererV2* QgsMaskRendererV2Widget::renderer() +QgsFeatureRendererV2* QgsInvertedPolygonRendererWidget::renderer() { if ( mRenderer && mEmbeddedRendererWidget ) { @@ -114,7 +114,7 @@ QgsFeatureRendererV2* QgsMaskRendererV2Widget::renderer() return mRenderer.data(); } -void QgsMaskRendererV2Widget::on_mRendererComboBox_currentIndexChanged( int index ) +void QgsInvertedPolygonRendererWidget::on_mRendererComboBox_currentIndexChanged( int index ) { QString rendererId = mRendererComboBox->itemData( index ).toString(); QgsRendererV2AbstractMetadata* m = QgsRendererV2Registry::instance()->rendererMetadata( rendererId ); diff --git a/src/gui/symbology-ng/qgsmaskrendererv2widget.h b/src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.h similarity index 75% rename from src/gui/symbology-ng/qgsmaskrendererv2widget.h rename to src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.h index 983d101f5fe..fb0b12eba36 100644 --- a/src/gui/symbology-ng/qgsmaskrendererv2widget.h +++ b/src/gui/symbology-ng/qgsinvertedpolygonrendererwidget.h @@ -1,5 +1,5 @@ /*************************************************************************** - qgsmaskrendererv2widget.h + qgsinvertedpolygonrendererwidget.h --------------------- begin : April 2014 copyright : (C) 2014 Hugo Mercier / Oslandia @@ -12,19 +12,19 @@ * (at your option) any later version. * * * ***************************************************************************/ -#ifndef QGSMASKRENDERERV2WIDGET_H -#define QGSMASKRENDERERV2WIDGET_H +#ifndef QGSINVERTEDPOLYGONRENDERERWIDGET_H +#define QGSINVERTEDPOLYGONRENDERERWIDGET_H -#include "ui_qgsmaskrendererwidgetbase.h" -#include "qgsmaskrendererv2.h" +#include "ui_qgsinvertedpolygonrendererwidgetbase.h" +#include "qgsinvertedpolygonrenderer.h" #include "qgsrendererv2widget.h" class QMenu; /** - * A widget used represent options of a QgsMaskRendererV2 + * A widget used represent options of a QgsInvertedPolygonRenderer */ -class GUI_EXPORT QgsMaskRendererV2Widget : public QgsRendererV2Widget, private Ui::QgsMaskRendererWidgetBase +class GUI_EXPORT QgsInvertedPolygonRendererWidget : public QgsRendererV2Widget, private Ui::QgsInvertedPolygonRendererWidgetBase { Q_OBJECT @@ -41,14 +41,14 @@ class GUI_EXPORT QgsMaskRendererV2Widget : public QgsRendererV2Widget, private U * @param style * @param renderer the mask renderer (will take ownership) */ - QgsMaskRendererV2Widget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + QgsInvertedPolygonRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); /** @returns the current feature renderer */ virtual QgsFeatureRendererV2* renderer(); protected: /** the mask renderer */ - QScopedPointer mRenderer; + QScopedPointer mRenderer; /** the widget used to represent the mask's embedded renderer */ QScopedPointer mEmbeddedRendererWidget; diff --git a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp index 3906ff08c49..bed3c58b31f 100644 --- a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp +++ b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp @@ -23,7 +23,7 @@ #include "qgsgraduatedsymbolrendererv2widget.h" #include "qgsrulebasedrendererv2widget.h" #include "qgspointdisplacementrendererwidget.h" -#include "qgsmaskrendererv2widget.h" +#include "qgsinvertedpolygonrendererwidget.h" #include "qgsapplication.h" #include "qgslogger.h" @@ -67,7 +67,7 @@ static void _initRendererWidgetFunctions() _initRenderer( "graduatedSymbol", QgsGraduatedSymbolRendererV2Widget::create, "rendererGraduatedSymbol.png" ); _initRenderer( "RuleRenderer", QgsRuleBasedRendererV2Widget::create ); _initRenderer( "pointDisplacement", QgsPointDisplacementRendererWidget::create ); - _initRenderer( "maskRenderer", QgsMaskRendererV2Widget::create ); + _initRenderer( "invertedPolygonRenderer", QgsInvertedPolygonRendererWidget::create ); initialized = true; } diff --git a/src/ui/qgsmaskrendererwidgetbase.ui b/src/ui/qgsinvertedpolygonrendererwidgetbase.ui similarity index 85% rename from src/ui/qgsmaskrendererwidgetbase.ui rename to src/ui/qgsinvertedpolygonrendererwidgetbase.ui index d5d23a4af4c..79b11ee1929 100644 --- a/src/ui/qgsmaskrendererwidgetbase.ui +++ b/src/ui/qgsinvertedpolygonrendererwidgetbase.ui @@ -1,7 +1,7 @@ - QgsMaskRendererWidgetBase - + QgsInvertedPolygonRendererWidgetBase + 0 From 66ac1eda38b36de4c93be7a1bb11644cd7423886 Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 19:56:18 +0200 Subject: [PATCH 8/9] Inverted polygons renderer: add SIP --- python/core/core.sip | 1 + .../qgsinvertedpolygonrenderer.sip | 70 +++++++++++++++++++ python/gui/gui.sip | 1 + .../qgsinvertedpolygonrendererwidget.sip | 23 ++++++ 4 files changed, 95 insertions(+) create mode 100644 python/core/symbology-ng/qgsinvertedpolygonrenderer.sip create mode 100644 python/gui/symbology-ng/qgsinvertedpolygonrendererwidget.sip diff --git a/python/core/core.sip b/python/core/core.sip index 2608e98e53f..b3d1937fb0e 100644 --- a/python/core/core.sip +++ b/python/core/core.sip @@ -206,6 +206,7 @@ %Include symbology-ng/qgssinglesymbolrendererv2.sip %Include symbology-ng/qgspointdisplacementrenderer.sip %Include symbology-ng/qgsrulebasedrendererv2.sip +%Include symbology-ng/qgsinvertedpolygonrenderer.sip %Include symbology-ng/qgsrendererv2.sip %Include symbology-ng/qgsrendererv2registry.sip diff --git a/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip new file mode 100644 index 00000000000..bf509fa02eb --- /dev/null +++ b/python/core/symbology-ng/qgsinvertedpolygonrenderer.sip @@ -0,0 +1,70 @@ +class QgsInvertedPolygonRenderer : public QgsFeatureRendererV2 +{ +%TypeHeaderCode +#include +%End + public: + + /** Constructor + * @param embeddedRenderer optional embeddedRenderer. If null, a default one will be assigned + */ + QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* embeddedRenderer = 0 ); + virtual ~QgsInvertedPolygonRenderer(); + + /** Used to clone this feature renderer.*/ + virtual QgsFeatureRendererV2* clone(); + + virtual void startRender( QgsRenderContext& context, const QgsFields& fields ); + + /** Renders a given feature. + * This will here collect features. The actual rendering will be postponed to stopRender() + * @param feature the feature to render + * @param context the rendering context + * @param layer the symbol layer to render, if that makes sense + * @param selected whether this feature has been selected (this will add decorations) + * @param drawVertexMarker whether this feature has vertex markers (in edit mode usually) + * @returns true if the rendering was ok + */ + virtual bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false ); + + /** + * The actual rendering will take place here. + * Features collected during renderFeature() are rendered using the embedded feature renderer + */ + virtual void stopRender( QgsRenderContext& context ); + + /** @returns a textual reprensation of the renderer */ + virtual QString dump() const; + + /** Proxy that will call this method on the embedded renderer. */ + virtual QList usedAttributes(); + /** Proxy that will call this method on the embedded renderer. */ + virtual int capabilities(); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2List symbols(); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature ); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + /** Proxy that will call this method on the embedded renderer. */ + virtual QgsLegendSymbologyList legendSymbologyItems( QSize iconSize ); + /** Proxy that will call this method on the embedded renderer. */ + virtual bool willRenderFeature( QgsFeature& feat ); + + /** Creates a renderer out of an XML, for loading*/ + static QgsFeatureRendererV2* create( QDomElement& element ); + + /** Creates an XML representation of the renderer. Used for saving purpose + * @param doc the XML document where to create the XML subtree + * @returns the created XML subtree + */ + virtual QDomElement save( QDomDocument& doc ); + + /** sets the embedded renderer + * @param subRenderer the embedded renderer (will be cloned) + */ + void setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer ); + /** @returns the current embedded renderer + */ + const QgsFeatureRendererV2* embeddedRenderer() const; +}; diff --git a/python/gui/gui.sip b/python/gui/gui.sip index 7f3f22fcf40..99b9e7e0a5c 100644 --- a/python/gui/gui.sip +++ b/python/gui/gui.sip @@ -120,6 +120,7 @@ %Include symbology-ng/qgscptcitycolorrampv2dialog.sip %Include symbology-ng/qgsellipsesymbollayerv2widget.sip %Include symbology-ng/qgsgraduatedsymbolrendererv2widget.sip +%Include symbology-ng/qgsinvertedpolygonrendererwidget.sip %Include symbology-ng/qgslayerpropertieswidget.sip %Include symbology-ng/qgspenstylecombobox.sip %Include symbology-ng/qgspointdisplacementrendererwidget.sip diff --git a/python/gui/symbology-ng/qgsinvertedpolygonrendererwidget.sip b/python/gui/symbology-ng/qgsinvertedpolygonrendererwidget.sip new file mode 100644 index 00000000000..98115f3ec46 --- /dev/null +++ b/python/gui/symbology-ng/qgsinvertedpolygonrendererwidget.sip @@ -0,0 +1,23 @@ +class QgsInvertedPolygonRendererWidget : public QgsRendererV2Widget, private Ui::QgsInvertedPolygonRendererWidgetBase +{ +%TypeHeaderCode +#include +%End + public: + /** static creation method + * @param layer the layer where this renderer is applied + * @param style + * @param renderer the mask renderer (will take ownership) + */ + static QgsRendererV2Widget* create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + + /** Constructor + * @param layer the layer where this renderer is applied + * @param style + * @param renderer the mask renderer (will take ownership) + */ + QgsInvertedPolygonRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); + + /** @returns the current feature renderer */ + virtual QgsFeatureRendererV2* renderer(); +}; From 79061547d46f7d2fa938fecf6ecec2074118d89b Mon Sep 17 00:00:00 2001 From: Hugo Mercier Date: Fri, 23 May 2014 19:57:21 +0200 Subject: [PATCH 9/9] Add unit tests for the inverted polygon renderer --- tests/src/core/CMakeLists.txt | 1 + .../core/testqgsinvertedpolygonrenderer.cpp | 152 ++++++++++++ .../expected_inverted_polys_graduated.png | Bin 0 -> 641536 bytes .../expected_inverted_polys_single.png | Bin 0 -> 641536 bytes tests/testdata/inverted_polys_graduated.qml | 228 ++++++++++++++++++ tests/testdata/inverted_polys_single.qml | 201 +++++++++++++++ 6 files changed, 582 insertions(+) create mode 100644 tests/src/core/testqgsinvertedpolygonrenderer.cpp create mode 100644 tests/testdata/control_images/expected_inverted_polys_graduated/expected_inverted_polys_graduated.png create mode 100644 tests/testdata/control_images/expected_inverted_polys_single/expected_inverted_polys_single.png create mode 100644 tests/testdata/inverted_polys_graduated.qml create mode 100644 tests/testdata/inverted_polys_single.qml diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index c728f34c36e..1baf66e9d46 100644 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -121,3 +121,4 @@ ADD_QGIS_TEST(vectorlayercachetest testqgsvectorlayercache.cpp ) ADD_QGIS_TEST(spatialindextest testqgsspatialindex.cpp) ADD_QGIS_TEST(gradienttest testqgsgradients.cpp ) ADD_QGIS_TEST(shapebursttest testqgsshapeburst.cpp ) +ADD_QGIS_TEST(invertedpolygontest testqgsinvertedpolygonrenderer.cpp ) diff --git a/tests/src/core/testqgsinvertedpolygonrenderer.cpp b/tests/src/core/testqgsinvertedpolygonrenderer.cpp new file mode 100644 index 00000000000..2ade6779b77 --- /dev/null +++ b/tests/src/core/testqgsinvertedpolygonrenderer.cpp @@ -0,0 +1,152 @@ +/*************************************************************************** + testqgsinvertedpolygonrenderer.cpp + -------------------------------------- + Date : 23 may 2014 + Copyright : (C) 2014 by Hugo Mercier / Oslandia + Email : hugo dot mercier at oslandia 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//qgis includes... +#include +#include +#include +#include +#include +#include +//qgis test includes +#include "qgsrenderchecker.h" + +/** \ingroup UnitTests + * This is a unit test for the different renderers for vector layers. + */ +class TestQgsInvertedPolygon: 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 singleSubRenderer(); + void graduatedSubRenderer(); + + private: + bool mTestHasError; + bool setQml( QString qmlFile ); + bool imageCheck( QString theType ); + QgsMapSettings mMapSettings; + QgsVectorLayer * mpPolysLayer; + QString mTestDataDir; + QString mReport; +}; + + +void TestQgsInvertedPolygon::initTestCase() +{ + mTestHasError = false; + // init QGIS's paths - true means that all path will be inited from prefix + QgsApplication::init(); + QgsApplication::initQgis(); + QgsApplication::showSettings(); + + QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + mTestDataDir = myDataDir + QDir::separator(); + + // + //create a poly layer that will be used in all tests... + // + QString myPolysFileName = mTestDataDir + "polys.shp"; + QFileInfo myPolyFileInfo( myPolysFileName ); + mpPolysLayer = new QgsVectorLayer( myPolyFileInfo.filePath(), + myPolyFileInfo.completeBaseName(), "ogr" ); + QgsVectorSimplifyMethod simplifyMethod; + simplifyMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); + mpPolysLayer->setSimplifyMethod( simplifyMethod ); + + // Register the layer with the registry + QgsMapLayerRegistry::instance()->addMapLayers( + QList() << mpPolysLayer ); + + mMapSettings.setLayers( QStringList() << mpPolysLayer->id() ); + mReport += "

Inverted Polygon Renderer Tests

\n"; +} + +void TestQgsInvertedPolygon::cleanupTestCase() +{ + QString myReportFile = QDir::tempPath() + QDir::separator() + "qgistest.html"; + QFile myFile( myReportFile ); + if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) ) + { + QTextStream myQTextStream( &myFile ); + myQTextStream << mReport; + myFile.close(); + } +} + +void TestQgsInvertedPolygon::singleSubRenderer() +{ + mReport += "

Inverted polygon renderer, single sub renderer test

\n"; + QVERIFY( setQml( "inverted_polys_single.qml" ) ); + QVERIFY( imageCheck( "inverted_polys_single" ) ); +} + +void TestQgsInvertedPolygon::graduatedSubRenderer() +{ + mReport += "

Inverted polygon renderer, graduated sub renderer test

\n"; + QVERIFY( setQml( "inverted_polys_graduated.qml" ) ); + QVERIFY( imageCheck( "inverted_polys_graduated" ) ); +} + +// +// Private helper functions not called directly by CTest +// + +bool TestQgsInvertedPolygon::setQml( QString qmlFile ) +{ + //load a qml style and apply to our layer + //the style will correspond to the renderer + //type we are testing + bool myStyleFlag = false; + QString myFileName = mTestDataDir + qmlFile; + QString error = mpPolysLayer->loadNamedStyle( myFileName, myStyleFlag ); + if ( !myStyleFlag ) + { + qDebug( "%s", error.toLocal8Bit().constData() ); + return false; + } + return myStyleFlag; +} + +bool TestQgsInvertedPolygon::imageCheck( QString theTestType ) +{ + //use the QgsRenderChecker test utility class to + //ensure the rendered output matches our control image + mMapSettings.setExtent( mpPolysLayer->extent() ); + QgsRenderChecker myChecker; + myChecker.setControlName( "expected_" + theTestType ); + myChecker.setMapSettings( mMapSettings ); + bool myResultFlag = myChecker.runTest( theTestType ); + mReport += myChecker.report(); + return myResultFlag; +} + +QTEST_MAIN( TestQgsInvertedPolygon ) +#include "moc_testqgsinvertedpolygonrenderer.cxx" diff --git a/tests/testdata/control_images/expected_inverted_polys_graduated/expected_inverted_polys_graduated.png b/tests/testdata/control_images/expected_inverted_polys_graduated/expected_inverted_polys_graduated.png new file mode 100644 index 0000000000000000000000000000000000000000..753ab63360f57dbd12d8f542a42be63b9c82a8cf GIT binary patch literal 641536 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 P@k>AYFaO+M`>FpAoV7%~ literal 0 HcmV?d00001 diff --git a/tests/testdata/control_images/expected_inverted_polys_single/expected_inverted_polys_single.png b/tests/testdata/control_images/expected_inverted_polys_single/expected_inverted_polys_single.png new file mode 100644 index 0000000000000000000000000000000000000000..eb196be77fb7160d052148a8c0060def5a90d3cf GIT binary patch literal 641536 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

>kcbh=ZEWd&o3 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 wbQ^V+$mLeg*q8y5P8nu_00@8p2-t + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Name + + + + + + + + + + 0 + + generatedlayout + + + + diff --git a/tests/testdata/inverted_polys_single.qml b/tests/testdata/inverted_polys_single.qml new file mode 100644 index 00000000000..53c53d0b04e --- /dev/null +++ b/tests/testdata/inverted_polys_single.qml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 0 + Name + + + + + + + + + + 0 + + generatedlayout + + + +