Merge pull request #1380 from Oslandia/mask_renderer

[FEATURE] Inverted polygons renderer
This commit is contained in:
Martin Dobias 2014-05-24 11:26:41 +07:00
commit 0775a89ed5
19 changed files with 1433 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,70 @@
class QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
{
%TypeHeaderCode
#include <qgsinvertedpolygonrenderer.h>
%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<QString> 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;
};

View File

@ -122,6 +122,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

View File

@ -0,0 +1,23 @@
class QgsInvertedPolygonRendererWidget : public QgsRendererV2Widget, private Ui::QgsInvertedPolygonRendererWidgetBase
{
%TypeHeaderCode
#include <qgsinvertedpolygonrendererwidget.h>
%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();
};

View File

@ -36,6 +36,7 @@ SET(QGIS_CORE_SRCS
symbology-ng/qgscategorizedsymbolrendererv2.cpp
symbology-ng/qgsgraduatedsymbolrendererv2.cpp
symbology-ng/qgsrulebasedrendererv2.cpp
symbology-ng/qgsinvertedpolygonrenderer.cpp
symbology-ng/qgsvectorcolorrampv2.cpp
symbology-ng/qgscptcityarchive.cpp
symbology-ng/qgsstylev2.cpp
@ -585,6 +586,7 @@ SET(QGIS_CORE_HDRS
symbology-ng/qgsrendererv2registry.h
symbology-ng/qgsrulebasedrendererv2.h
symbology-ng/qgssinglesymbolrendererv2.h
symbology-ng/qgsinvertedpolygonrenderer.h
symbology-ng/qgsstylev2.h
symbology-ng/qgssvgcache.h
symbology-ng/qgssymbollayerv2.h

View File

@ -0,0 +1,367 @@
/***************************************************************************
qgsinvertedpolygonrenderer.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 "qgsinvertedpolygonrenderer.h"
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgslogger.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgssymbollayerv2.h"
#include "qgsogcutils.h"
#include <QDomDocument>
#include <QDomElement>
QgsInvertedPolygonRenderer::QgsInvertedPolygonRenderer( const QgsFeatureRendererV2* subRenderer )
: QgsFeatureRendererV2( "invertedPolygonRenderer" )
{
if ( subRenderer ) {
setEmbeddedRenderer( subRenderer );
}
else {
mSubRenderer.reset( QgsFeatureRendererV2::defaultRenderer( QGis::Polygon ) );
}
}
QgsInvertedPolygonRenderer::~QgsInvertedPolygonRenderer()
{
}
void QgsInvertedPolygonRenderer::setEmbeddedRenderer( const QgsFeatureRendererV2* subRenderer )
{
if ( subRenderer ) {
mSubRenderer.reset( const_cast<QgsFeatureRendererV2*>(subRenderer)->clone() );
}
else {
mSubRenderer.reset( 0 );
}
}
const QgsFeatureRendererV2* QgsInvertedPolygonRenderer::embeddedRenderer() const
{
return mSubRenderer.data();
}
void QgsInvertedPolygonRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
{
if ( !mSubRenderer ) {
return;
}
mSubRenderer->startRender( context, fields );
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 QgsInvertedPolygonRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
{
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
// 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<const char*>(&sym), sizeof(sym) );
}
}
else
{
QgsSymbolV2* sym = mSubRenderer->symbolForFeature( feature );
if (sym) {
catId.append( reinterpret_cast<const char*>(&sym), sizeof(sym) );
}
}
if ( catId.isEmpty() )
{
return false;
}
// 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
if ( ! mFeaturesCategoryMap.contains(catId) )
{
// the exterior ring must be a square in the destination CRS
CombinedFeature cFeat;
cFeat.multiPolygon.append( mExtentPolygon );
// store the first feature
cFeat.feature = feature;
mFeaturesCategoryMap.insert( catId, cFeat );
}
// 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
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;
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 );
}
}
return true;
}
void QgsInvertedPolygonRenderer::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 );
}
// 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() )
{
// empty feature with default attributes
QgsFeature feat( mFields );
feat.setGeometry( QgsGeometry::fromPolygon( mExtentPolygon ) );
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 );
if ( mTransform )
{
// restore the coordinate transform if needed
context.setCoordinateTransform( mTransform );
}
}
QString QgsInvertedPolygonRenderer::dump() const
{
if ( !mSubRenderer ) {
return "INVERTED: NULL";
}
return "INVERTED [" + mSubRenderer->dump() + "]";
}
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::clone()
{
if ( mSubRenderer.isNull() )
{
return new QgsInvertedPolygonRenderer( 0 );
}
// else
return new QgsInvertedPolygonRenderer( mSubRenderer->clone() );
}
QgsFeatureRendererV2* QgsInvertedPolygonRenderer::create( QDomElement& element )
{
QgsInvertedPolygonRenderer* r = new QgsInvertedPolygonRenderer();
//look for an embedded renderer <renderer-v2>
QDomElement embeddedRendererElem = element.firstChildElement( "renderer-v2" );
if ( !embeddedRendererElem.isNull() )
{
r->setEmbeddedRenderer( QgsFeatureRendererV2::load( embeddedRendererElem ) );
}
return r;
}
QDomElement QgsInvertedPolygonRenderer::save( QDomDocument& doc )
{
QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
rendererElem.setAttribute( "type", "invertedPolygonRenderer" );
if ( mSubRenderer )
{
QDomElement embeddedRendererElem = mSubRenderer->save( doc );
rendererElem.appendChild( embeddedRendererElem );
}
return rendererElem;
}
QgsSymbolV2* QgsInvertedPolygonRenderer::symbolForFeature( QgsFeature& feature )
{
if ( !mSubRenderer ) {
return 0;
}
return mSubRenderer->symbolForFeature( feature );
}
QgsSymbolV2List QgsInvertedPolygonRenderer::symbolsForFeature( QgsFeature& feature )
{
if ( !mSubRenderer ) {
return QgsSymbolV2List();
}
return mSubRenderer->symbolsForFeature( feature );
}
QgsSymbolV2List QgsInvertedPolygonRenderer::symbols()
{
if ( !mSubRenderer ) {
return QgsSymbolV2List();
}
return mSubRenderer->symbols();
}
int QgsInvertedPolygonRenderer::capabilities()
{
if ( !mSubRenderer ) {
return 0;
}
return mSubRenderer->capabilities();
}
QList<QString> QgsInvertedPolygonRenderer::usedAttributes()
{
if ( !mSubRenderer ) {
return QList<QString>();
}
return mSubRenderer->usedAttributes();
}
QgsLegendSymbologyList QgsInvertedPolygonRenderer::legendSymbologyItems( QSize iconSize )
{
if ( !mSubRenderer ) {
return QgsLegendSymbologyList();
}
return mSubRenderer->legendSymbologyItems( iconSize );
}
QgsLegendSymbolList QgsInvertedPolygonRenderer::legendSymbolItems( double scaleDenominator, QString rule )
{
if ( !mSubRenderer ) {
return QgsLegendSymbolList();
}
return mSubRenderer->legendSymbolItems( scaleDenominator, rule );
}
bool QgsInvertedPolygonRenderer::willRenderFeature( QgsFeature& feat )
{
if ( !mSubRenderer ) {
return false;
}
return mSubRenderer->willRenderFeature( feat );
}

View File

@ -0,0 +1,151 @@
/***************************************************************************
qgsinvertedpolygonrenderer.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 QGSINVERTEDPOLYGONRENDERER_H
#define QGSINVERTEDPOLYGONRENDERER_H
#include "qgis.h"
#include "qgsrendererv2.h"
#include "qgssymbolv2.h"
#include "qgsexpression.h"
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include <QScopedPointer>
/**
* 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.
*
* 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 QgsInvertedPolygonRenderer : public QgsFeatureRendererV2
{
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<QString> 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() */
QgsInvertedPolygonRenderer( const QgsInvertedPolygonRenderer& );
/** Private assignment operator. @see clone() */
QgsInvertedPolygonRenderer& operator=( const QgsInvertedPolygonRenderer& );
/** Embedded renderer */
QScopedPointer<QgsFeatureRendererV2> mSubRenderer;
/** 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;
/** 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;
/** 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<FeatureDecoration> mFeatureDecorations;
};
#endif // QGSMASKRENDERERV2_H

View File

@ -20,6 +20,7 @@
#include "qgsgraduatedsymbolrendererv2.h"
#include "qgsrulebasedrendererv2.h"
#include "qgspointdisplacementrenderer.h"
#include "qgsinvertedpolygonrenderer.h"
QgsRendererV2Registry::QgsRendererV2Registry()
{
@ -44,6 +45,10 @@ QgsRendererV2Registry::QgsRendererV2Registry()
addRenderer( new QgsRendererV2Metadata( "pointDisplacement",
QObject::tr( "Point displacement" ),
QgsPointDisplacementRenderer::create ) );
addRenderer( new QgsRendererV2Metadata( "invertedPolygonRenderer",
QObject::tr( "Inverted polygons" ),
QgsInvertedPolygonRenderer::create ) );
}
QgsRendererV2Registry::~QgsRendererV2Registry()

View File

@ -20,6 +20,7 @@ symbology-ng/qgssinglesymbolrendererv2widget.cpp
symbology-ng/qgscategorizedsymbolrendererv2widget.cpp
symbology-ng/qgsgraduatedsymbolrendererv2widget.cpp
symbology-ng/qgsrulebasedrendererv2widget.cpp
symbology-ng/qgsinvertedpolygonrendererwidget.cpp
symbology-ng/qgsrendererv2propertiesdialog.cpp
symbology-ng/qgsstylev2managerdialog.cpp
symbology-ng/qgssymbollevelsv2dialog.cpp
@ -231,6 +232,7 @@ symbology-ng/qgscategorizedsymbolrendererv2widget.h
symbology-ng/qgsdatadefinedsymboldialog.h
symbology-ng/qgsgraduatedsymbolrendererv2widget.h
symbology-ng/qgsrulebasedrendererv2widget.h
symbology-ng/qgsinvertedpolygonrendererwidget.h
symbology-ng/qgsrendererv2widget.h
symbology-ng/qgsrendererv2propertiesdialog.h
symbology-ng/qgsstylev2managerdialog.h

View File

@ -0,0 +1,132 @@
/***************************************************************************
qgsinvertedpolygonrendererwidget.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 "qgsinvertedpolygonrendererwidget.h"
#include "qgsinvertedpolygonrenderer.h"
#include "qgsrendererv2registry.h"
#include "qgssymbolv2.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
QgsRendererV2Widget* QgsInvertedPolygonRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
{
return new QgsInvertedPolygonRendererWidget( layer, style, renderer );
}
QgsInvertedPolygonRendererWidget::QgsInvertedPolygonRendererWidget( 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 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 );
return;
}
setupUi( this );
// try to recognize the previous renderer
// (null renderer means "no previous renderer")
if ( !renderer )
{
// a new renderer
mRenderer.reset( new QgsInvertedPolygonRenderer() );
}
else if ( renderer && renderer->type() != "invertedPolygonRenderer" )
{
// 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 inverted renderer
mRenderer.reset( static_cast<QgsInvertedPolygonRenderer*>(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 != "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 );
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* QgsInvertedPolygonRendererWidget::renderer()
{
if ( mRenderer && mEmbeddedRendererWidget )
{
QgsFeatureRendererV2* embeddedRenderer = mEmbeddedRendererWidget->renderer();
if ( embeddedRenderer )
{
mRenderer->setEmbeddedRenderer( embeddedRenderer->clone() );
}
}
return mRenderer.data();
}
void QgsInvertedPolygonRendererWidget::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<QgsFeatureRendererV2*>(mRenderer->embeddedRenderer())->clone() ) );
if ( mLayout->count() > 1 ) {
// remove the current renderer widget
mLayout->takeAt( 1 );
}
mLayout->addWidget( mEmbeddedRendererWidget.data() );
}
}

View File

@ -0,0 +1,60 @@
/***************************************************************************
qgsinvertedpolygonrendererwidget.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 QGSINVERTEDPOLYGONRENDERERWIDGET_H
#define QGSINVERTEDPOLYGONRENDERERWIDGET_H
#include "ui_qgsinvertedpolygonrendererwidgetbase.h"
#include "qgsinvertedpolygonrenderer.h"
#include "qgsrendererv2widget.h"
class QMenu;
/**
* A widget used represent options of a QgsInvertedPolygonRenderer
*/
class GUI_EXPORT QgsInvertedPolygonRendererWidget : public QgsRendererV2Widget, private Ui::QgsInvertedPolygonRendererWidgetBase
{
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)
*/
QgsInvertedPolygonRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
/** @returns the current feature renderer */
virtual QgsFeatureRendererV2* renderer();
protected:
/** the mask renderer */
QScopedPointer<QgsInvertedPolygonRenderer> mRenderer;
/** the widget used to represent the mask's embedded renderer */
QScopedPointer<QgsRendererV2Widget> mEmbeddedRendererWidget;
private slots:
void on_mRendererComboBox_currentIndexChanged( int index );
};
#endif // QGSMASKRENDERERV2WIDGET_H

View File

@ -23,6 +23,7 @@
#include "qgsgraduatedsymbolrendererv2widget.h"
#include "qgsrulebasedrendererv2widget.h"
#include "qgspointdisplacementrendererwidget.h"
#include "qgsinvertedpolygonrendererwidget.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( "invertedPolygonRenderer", QgsInvertedPolygonRendererWidget::create );
initialized = true;
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsInvertedPolygonRendererWidgetBase</class>
<widget class="QWidget" name="QgsInvertedPolygonRendererWidgetBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>390</width>
<height>79</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="mLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="mRendererLabel">
<property name="text">
<string>Sub renderer:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mRendererComboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -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 )

View File

@ -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 <QtTest>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QObject>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QDesktopServices>
#include <iostream>
//qgis includes...
#include <qgsmaprenderer.h>
#include <qgsmaplayer.h>
#include <qgsvectorlayer.h>
#include <qgsapplication.h>
#include <qgsproviderregistry.h>
#include <qgsmaplayerregistry.h>
//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<QgsMapLayer *>() << mpPolysLayer );
mMapSettings.setLayers( QStringList() << mpPolysLayer->id() );
mReport += "<h1>Inverted Polygon Renderer Tests</h1>\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 += "<h2>Inverted polygon renderer, single sub renderer test</h2>\n";
QVERIFY( setQml( "inverted_polys_single.qml" ) );
QVERIFY( imageCheck( "inverted_polys_single" ) );
}
void TestQgsInvertedPolygon::graduatedSubRenderer()
{
mReport += "<h2>Inverted polygon renderer, graduated sub renderer test</h2>\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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

View File

@ -0,0 +1,228 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis version="2.3.0-Master" minimumScale="1" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="1" maxLabelScale="1e+08" simplifyDrawingTol="1" simplifyMaxScale="1" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
<renderer-v2 type="invertedPolygonRenderer">
<renderer-v2 attr="Name" symbollevels="0" type="categorizedSymbol">
<categories>
<category symbol="0" value="Dam" label="Dam"/>
<category symbol="1" value="Lake" label="Lake"/>
</categories>
<symbols>
<symbol alpha="1" type="fill" name="0">
<layer pass="0" class="ShapeburstFill" locked="0">
<prop k="blur_radius" v="0"/>
<prop k="color1" v="0,0,255,255"/>
<prop k="color2" v="0,255,0,255"/>
<prop k="color_type" v="0"/>
<prop k="discrete" v="0"/>
<prop k="distance_map_unit_scale" v="0,0"/>
<prop k="distance_unit" v="MM"/>
<prop k="ignore_rings" v="0"/>
<prop k="max_distance" v="5"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="shapeburst_color" v="27,54,212,255"/>
<prop k="shapeburst_color2" v="255,255,255,0"/>
<prop k="use_whole_shape" v="0"/>
</layer>
</symbol>
<symbol alpha="1" type="fill" name="1">
<layer pass="0" class="ShapeburstFill" locked="0">
<prop k="blur_radius" v="0"/>
<prop k="color1" v="0,0,255,255"/>
<prop k="color2" v="0,255,0,255"/>
<prop k="color_type" v="0"/>
<prop k="discrete" v="0"/>
<prop k="distance_map_unit_scale" v="0,0"/>
<prop k="distance_unit" v="MM"/>
<prop k="ignore_rings" v="0"/>
<prop k="max_distance" v="5"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="shapeburst_color" v="110,194,217,255"/>
<prop k="shapeburst_color2" v="255,255,255,0"/>
<prop k="use_whole_shape" v="0"/>
</layer>
</symbol>
</symbols>
<rotation/>
<sizescale scalemethod="area"/>
</renderer-v2>
</renderer-v2>
<customproperties>
<property key="labeling" value="pal"/>
<property key="labeling/addDirectionSymbol" value="false"/>
<property key="labeling/angleOffset" value="0"/>
<property key="labeling/blendMode" value="0"/>
<property key="labeling/bufferBlendMode" value="0"/>
<property key="labeling/bufferColorA" value="255"/>
<property key="labeling/bufferColorB" value="255"/>
<property key="labeling/bufferColorG" value="255"/>
<property key="labeling/bufferColorR" value="255"/>
<property key="labeling/bufferDraw" value="false"/>
<property key="labeling/bufferJoinStyle" value="64"/>
<property key="labeling/bufferNoFill" value="false"/>
<property key="labeling/bufferSize" value="1"/>
<property key="labeling/bufferSizeInMapUnits" value="false"/>
<property key="labeling/bufferSizeMapUnitMaxScale" value="0"/>
<property key="labeling/bufferSizeMapUnitMinScale" value="0"/>
<property key="labeling/bufferTransp" value="0"/>
<property key="labeling/centroidWhole" value="false"/>
<property key="labeling/decimals" value="3"/>
<property key="labeling/displayAll" value="false"/>
<property key="labeling/dist" value="0"/>
<property key="labeling/distInMapUnits" value="false"/>
<property key="labeling/distMapUnitMaxScale" value="0"/>
<property key="labeling/distMapUnitMinScale" value="0"/>
<property key="labeling/enabled" value="false"/>
<property key="labeling/fieldName" value=""/>
<property key="labeling/fontBold" value="true"/>
<property key="labeling/fontCapitals" value="0"/>
<property key="labeling/fontFamily" value="Ubuntu"/>
<property key="labeling/fontItalic" value="true"/>
<property key="labeling/fontLetterSpacing" value="0"/>
<property key="labeling/fontLimitPixelSize" value="false"/>
<property key="labeling/fontMaxPixelSize" value="10000"/>
<property key="labeling/fontMinPixelSize" value="3"/>
<property key="labeling/fontSize" value="11"/>
<property key="labeling/fontSizeInMapUnits" value="false"/>
<property key="labeling/fontSizeMapUnitMaxScale" value="0"/>
<property key="labeling/fontSizeMapUnitMinScale" value="0"/>
<property key="labeling/fontStrikeout" value="false"/>
<property key="labeling/fontUnderline" value="false"/>
<property key="labeling/fontWeight" value="75"/>
<property key="labeling/fontWordSpacing" value="0"/>
<property key="labeling/formatNumbers" value="false"/>
<property key="labeling/isExpression" value="false"/>
<property key="labeling/labelOffsetInMapUnits" value="true"/>
<property key="labeling/labelOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/labelOffsetMapUnitMinScale" value="0"/>
<property key="labeling/labelPerPart" value="false"/>
<property key="labeling/leftDirectionSymbol" value="&lt;"/>
<property key="labeling/limitNumLabels" value="false"/>
<property key="labeling/maxCurvedCharAngleIn" value="20"/>
<property key="labeling/maxCurvedCharAngleOut" value="-20"/>
<property key="labeling/maxNumLabels" value="2000"/>
<property key="labeling/mergeLines" value="false"/>
<property key="labeling/minFeatureSize" value="0"/>
<property key="labeling/multilineAlign" value="0"/>
<property key="labeling/multilineHeight" value="1"/>
<property key="labeling/namedStyle" value="Bold Italic"/>
<property key="labeling/obstacle" value="true"/>
<property key="labeling/placeDirectionSymbol" value="0"/>
<property key="labeling/placement" value="0"/>
<property key="labeling/placementFlags" value="0"/>
<property key="labeling/plussign" value="false"/>
<property key="labeling/preserveRotation" value="true"/>
<property key="labeling/previewBkgrdColor" value="#ffffff"/>
<property key="labeling/priority" value="5"/>
<property key="labeling/quadOffset" value="4"/>
<property key="labeling/reverseDirectionSymbol" value="false"/>
<property key="labeling/rightDirectionSymbol" value=">"/>
<property key="labeling/scaleMax" value="10000000"/>
<property key="labeling/scaleMin" value="1"/>
<property key="labeling/scaleVisibility" value="false"/>
<property key="labeling/shadowBlendMode" value="6"/>
<property key="labeling/shadowColorB" value="0"/>
<property key="labeling/shadowColorG" value="0"/>
<property key="labeling/shadowColorR" value="0"/>
<property key="labeling/shadowDraw" value="false"/>
<property key="labeling/shadowOffsetAngle" value="135"/>
<property key="labeling/shadowOffsetDist" value="1"/>
<property key="labeling/shadowOffsetGlobal" value="true"/>
<property key="labeling/shadowOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/shadowOffsetMapUnitMinScale" value="0"/>
<property key="labeling/shadowOffsetUnits" value="1"/>
<property key="labeling/shadowRadius" value="1.5"/>
<property key="labeling/shadowRadiusAlphaOnly" value="false"/>
<property key="labeling/shadowRadiusMapUnitMaxScale" value="0"/>
<property key="labeling/shadowRadiusMapUnitMinScale" value="0"/>
<property key="labeling/shadowRadiusUnits" value="1"/>
<property key="labeling/shadowScale" value="100"/>
<property key="labeling/shadowTransparency" value="30"/>
<property key="labeling/shadowUnder" value="0"/>
<property key="labeling/shapeBlendMode" value="0"/>
<property key="labeling/shapeBorderColorA" value="255"/>
<property key="labeling/shapeBorderColorB" value="128"/>
<property key="labeling/shapeBorderColorG" value="128"/>
<property key="labeling/shapeBorderColorR" value="128"/>
<property key="labeling/shapeBorderWidth" value="0"/>
<property key="labeling/shapeBorderWidthMapUnitMaxScale" value="0"/>
<property key="labeling/shapeBorderWidthMapUnitMinScale" value="0"/>
<property key="labeling/shapeBorderWidthUnits" value="1"/>
<property key="labeling/shapeDraw" value="false"/>
<property key="labeling/shapeFillColorA" value="255"/>
<property key="labeling/shapeFillColorB" value="255"/>
<property key="labeling/shapeFillColorG" value="255"/>
<property key="labeling/shapeFillColorR" value="255"/>
<property key="labeling/shapeJoinStyle" value="64"/>
<property key="labeling/shapeOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/shapeOffsetMapUnitMinScale" value="0"/>
<property key="labeling/shapeOffsetUnits" value="1"/>
<property key="labeling/shapeOffsetX" value="0"/>
<property key="labeling/shapeOffsetY" value="0"/>
<property key="labeling/shapeRadiiMapUnitMaxScale" value="0"/>
<property key="labeling/shapeRadiiMapUnitMinScale" value="0"/>
<property key="labeling/shapeRadiiUnits" value="1"/>
<property key="labeling/shapeRadiiX" value="0"/>
<property key="labeling/shapeRadiiY" value="0"/>
<property key="labeling/shapeRotation" value="0"/>
<property key="labeling/shapeRotationType" value="0"/>
<property key="labeling/shapeSVGFile" value=""/>
<property key="labeling/shapeSizeMapUnitMaxScale" value="0"/>
<property key="labeling/shapeSizeMapUnitMinScale" value="0"/>
<property key="labeling/shapeSizeType" value="0"/>
<property key="labeling/shapeSizeUnits" value="1"/>
<property key="labeling/shapeSizeX" value="0"/>
<property key="labeling/shapeSizeY" value="0"/>
<property key="labeling/shapeTransparency" value="0"/>
<property key="labeling/shapeType" value="0"/>
<property key="labeling/textColorA" value="255"/>
<property key="labeling/textColorB" value="0"/>
<property key="labeling/textColorG" value="0"/>
<property key="labeling/textColorR" value="0"/>
<property key="labeling/textTransp" value="0"/>
<property key="labeling/upsidedownLabels" value="0"/>
<property key="labeling/wrapChar" value=""/>
<property key="labeling/xOffset" value="0"/>
<property key="labeling/yOffset" value="0"/>
</customproperties>
<blendMode>0</blendMode>
<featureBlendMode>0</featureBlendMode>
<layerTransparency>0</layerTransparency>
<displayfield>Name</displayfield>
<label>0</label>
<labelattributes>
<label fieldname="" text="Label"/>
<family fieldname="" name="Lucida Grande"/>
<size fieldname="" units="pt" value="12"/>
<bold fieldname="" on="0"/>
<italic fieldname="" on="0"/>
<underline fieldname="" on="0"/>
<strikeout fieldname="" on="0"/>
<color fieldname="" red="0" blue="0" green="0"/>
<x fieldname=""/>
<y fieldname=""/>
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
<angle fieldname="" value="0" auto="0"/>
<alignment fieldname="" value="center"/>
<buffercolor fieldname="" red="255" blue="255" green="255"/>
<buffersize fieldname="" units="pt" value="1"/>
<bufferenabled fieldname="" on=""/>
<multilineenabled fieldname="" on=""/>
<selectedonly on=""/>
</labelattributes>
<edittypes>
<edittype labelontop="0" editable="1" type="0" name="Name"/>
<edittype labelontop="0" editable="1" type="0" name="Value"/>
</edittypes>
<editform></editform>
<editforminit></editforminit>
<featformsuppress>0</featformsuppress>
<annotationform></annotationform>
<editorlayout>generatedlayout</editorlayout>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<attributeactions/>
</qgis>

201
tests/testdata/inverted_polys_single.qml vendored Normal file
View File

@ -0,0 +1,201 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis version="2.3.0-Master" minimumScale="1" maximumScale="1e+08" simplifyDrawingHints="1" minLabelScale="1" maxLabelScale="1e+08" simplifyDrawingTol="1" simplifyMaxScale="1" hasScaleBasedVisibilityFlag="0" simplifyLocal="1" scaleBasedLabelVisibilityFlag="0">
<renderer-v2 type="invertedPolygonRenderer">
<renderer-v2 symbollevels="0" type="singleSymbol">
<symbols>
<symbol alpha="1" type="fill" name="0">
<layer pass="0" class="SimpleFill" locked="0">
<prop k="border_width_map_unit_scale" v="0,0"/>
<prop k="border_width_unit" v="MM"/>
<prop k="color" v="76,143,160,255"/>
<prop k="color_border" v="0,0,0,255"/>
<prop k="joinstyle" v="bevel"/>
<prop k="offset" v="0,0"/>
<prop k="offset_map_unit_scale" v="0,0"/>
<prop k="offset_unit" v="MM"/>
<prop k="style" v="solid"/>
<prop k="style_border" v="solid"/>
<prop k="width_border" v="0.26"/>
</layer>
</symbol>
</symbols>
<rotation/>
<sizescale scalemethod="area"/>
</renderer-v2>
</renderer-v2>
<customproperties>
<property key="labeling" value="pal"/>
<property key="labeling/addDirectionSymbol" value="false"/>
<property key="labeling/angleOffset" value="0"/>
<property key="labeling/blendMode" value="0"/>
<property key="labeling/bufferBlendMode" value="0"/>
<property key="labeling/bufferColorA" value="255"/>
<property key="labeling/bufferColorB" value="255"/>
<property key="labeling/bufferColorG" value="255"/>
<property key="labeling/bufferColorR" value="255"/>
<property key="labeling/bufferDraw" value="false"/>
<property key="labeling/bufferJoinStyle" value="64"/>
<property key="labeling/bufferNoFill" value="false"/>
<property key="labeling/bufferSize" value="1"/>
<property key="labeling/bufferSizeInMapUnits" value="false"/>
<property key="labeling/bufferSizeMapUnitMaxScale" value="0"/>
<property key="labeling/bufferSizeMapUnitMinScale" value="0"/>
<property key="labeling/bufferTransp" value="0"/>
<property key="labeling/centroidWhole" value="false"/>
<property key="labeling/decimals" value="3"/>
<property key="labeling/displayAll" value="false"/>
<property key="labeling/dist" value="0"/>
<property key="labeling/distInMapUnits" value="false"/>
<property key="labeling/distMapUnitMaxScale" value="0"/>
<property key="labeling/distMapUnitMinScale" value="0"/>
<property key="labeling/enabled" value="false"/>
<property key="labeling/fieldName" value=""/>
<property key="labeling/fontBold" value="true"/>
<property key="labeling/fontCapitals" value="0"/>
<property key="labeling/fontFamily" value="Ubuntu"/>
<property key="labeling/fontItalic" value="true"/>
<property key="labeling/fontLetterSpacing" value="0"/>
<property key="labeling/fontLimitPixelSize" value="false"/>
<property key="labeling/fontMaxPixelSize" value="10000"/>
<property key="labeling/fontMinPixelSize" value="3"/>
<property key="labeling/fontSize" value="11"/>
<property key="labeling/fontSizeInMapUnits" value="false"/>
<property key="labeling/fontSizeMapUnitMaxScale" value="0"/>
<property key="labeling/fontSizeMapUnitMinScale" value="0"/>
<property key="labeling/fontStrikeout" value="false"/>
<property key="labeling/fontUnderline" value="false"/>
<property key="labeling/fontWeight" value="75"/>
<property key="labeling/fontWordSpacing" value="0"/>
<property key="labeling/formatNumbers" value="false"/>
<property key="labeling/isExpression" value="false"/>
<property key="labeling/labelOffsetInMapUnits" value="true"/>
<property key="labeling/labelOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/labelOffsetMapUnitMinScale" value="0"/>
<property key="labeling/labelPerPart" value="false"/>
<property key="labeling/leftDirectionSymbol" value="&lt;"/>
<property key="labeling/limitNumLabels" value="false"/>
<property key="labeling/maxCurvedCharAngleIn" value="20"/>
<property key="labeling/maxCurvedCharAngleOut" value="-20"/>
<property key="labeling/maxNumLabels" value="2000"/>
<property key="labeling/mergeLines" value="false"/>
<property key="labeling/minFeatureSize" value="0"/>
<property key="labeling/multilineAlign" value="0"/>
<property key="labeling/multilineHeight" value="1"/>
<property key="labeling/namedStyle" value="Bold Italic"/>
<property key="labeling/obstacle" value="true"/>
<property key="labeling/placeDirectionSymbol" value="0"/>
<property key="labeling/placement" value="0"/>
<property key="labeling/placementFlags" value="0"/>
<property key="labeling/plussign" value="false"/>
<property key="labeling/preserveRotation" value="true"/>
<property key="labeling/previewBkgrdColor" value="#ffffff"/>
<property key="labeling/priority" value="5"/>
<property key="labeling/quadOffset" value="4"/>
<property key="labeling/reverseDirectionSymbol" value="false"/>
<property key="labeling/rightDirectionSymbol" value=">"/>
<property key="labeling/scaleMax" value="10000000"/>
<property key="labeling/scaleMin" value="1"/>
<property key="labeling/scaleVisibility" value="false"/>
<property key="labeling/shadowBlendMode" value="6"/>
<property key="labeling/shadowColorB" value="0"/>
<property key="labeling/shadowColorG" value="0"/>
<property key="labeling/shadowColorR" value="0"/>
<property key="labeling/shadowDraw" value="false"/>
<property key="labeling/shadowOffsetAngle" value="135"/>
<property key="labeling/shadowOffsetDist" value="1"/>
<property key="labeling/shadowOffsetGlobal" value="true"/>
<property key="labeling/shadowOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/shadowOffsetMapUnitMinScale" value="0"/>
<property key="labeling/shadowOffsetUnits" value="1"/>
<property key="labeling/shadowRadius" value="1.5"/>
<property key="labeling/shadowRadiusAlphaOnly" value="false"/>
<property key="labeling/shadowRadiusMapUnitMaxScale" value="0"/>
<property key="labeling/shadowRadiusMapUnitMinScale" value="0"/>
<property key="labeling/shadowRadiusUnits" value="1"/>
<property key="labeling/shadowScale" value="100"/>
<property key="labeling/shadowTransparency" value="30"/>
<property key="labeling/shadowUnder" value="0"/>
<property key="labeling/shapeBlendMode" value="0"/>
<property key="labeling/shapeBorderColorA" value="255"/>
<property key="labeling/shapeBorderColorB" value="128"/>
<property key="labeling/shapeBorderColorG" value="128"/>
<property key="labeling/shapeBorderColorR" value="128"/>
<property key="labeling/shapeBorderWidth" value="0"/>
<property key="labeling/shapeBorderWidthMapUnitMaxScale" value="0"/>
<property key="labeling/shapeBorderWidthMapUnitMinScale" value="0"/>
<property key="labeling/shapeBorderWidthUnits" value="1"/>
<property key="labeling/shapeDraw" value="false"/>
<property key="labeling/shapeFillColorA" value="255"/>
<property key="labeling/shapeFillColorB" value="255"/>
<property key="labeling/shapeFillColorG" value="255"/>
<property key="labeling/shapeFillColorR" value="255"/>
<property key="labeling/shapeJoinStyle" value="64"/>
<property key="labeling/shapeOffsetMapUnitMaxScale" value="0"/>
<property key="labeling/shapeOffsetMapUnitMinScale" value="0"/>
<property key="labeling/shapeOffsetUnits" value="1"/>
<property key="labeling/shapeOffsetX" value="0"/>
<property key="labeling/shapeOffsetY" value="0"/>
<property key="labeling/shapeRadiiMapUnitMaxScale" value="0"/>
<property key="labeling/shapeRadiiMapUnitMinScale" value="0"/>
<property key="labeling/shapeRadiiUnits" value="1"/>
<property key="labeling/shapeRadiiX" value="0"/>
<property key="labeling/shapeRadiiY" value="0"/>
<property key="labeling/shapeRotation" value="0"/>
<property key="labeling/shapeRotationType" value="0"/>
<property key="labeling/shapeSVGFile" value=""/>
<property key="labeling/shapeSizeMapUnitMaxScale" value="0"/>
<property key="labeling/shapeSizeMapUnitMinScale" value="0"/>
<property key="labeling/shapeSizeType" value="0"/>
<property key="labeling/shapeSizeUnits" value="1"/>
<property key="labeling/shapeSizeX" value="0"/>
<property key="labeling/shapeSizeY" value="0"/>
<property key="labeling/shapeTransparency" value="0"/>
<property key="labeling/shapeType" value="0"/>
<property key="labeling/textColorA" value="255"/>
<property key="labeling/textColorB" value="0"/>
<property key="labeling/textColorG" value="0"/>
<property key="labeling/textColorR" value="0"/>
<property key="labeling/textTransp" value="0"/>
<property key="labeling/upsidedownLabels" value="0"/>
<property key="labeling/wrapChar" value=""/>
<property key="labeling/xOffset" value="0"/>
<property key="labeling/yOffset" value="0"/>
</customproperties>
<blendMode>0</blendMode>
<featureBlendMode>0</featureBlendMode>
<layerTransparency>0</layerTransparency>
<displayfield>Name</displayfield>
<label>0</label>
<labelattributes>
<label fieldname="" text="Label"/>
<family fieldname="" name="Lucida Grande"/>
<size fieldname="" units="pt" value="12"/>
<bold fieldname="" on="0"/>
<italic fieldname="" on="0"/>
<underline fieldname="" on="0"/>
<strikeout fieldname="" on="0"/>
<color fieldname="" red="0" blue="0" green="0"/>
<x fieldname=""/>
<y fieldname=""/>
<offset x="0" y="0" units="pt" yfieldname="" xfieldname=""/>
<angle fieldname="" value="0" auto="0"/>
<alignment fieldname="" value="center"/>
<buffercolor fieldname="" red="255" blue="255" green="255"/>
<buffersize fieldname="" units="pt" value="1"/>
<bufferenabled fieldname="" on=""/>
<multilineenabled fieldname="" on=""/>
<selectedonly on=""/>
</labelattributes>
<edittypes>
<edittype labelontop="0" editable="1" type="0" name="Name"/>
<edittype labelontop="0" editable="1" type="0" name="Value"/>
</edittypes>
<editform></editform>
<editforminit></editforminit>
<featformsuppress>0</featformsuppress>
<annotationform></annotationform>
<editorlayout>generatedlayout</editorlayout>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<attributeactions/>
</qgis>