[FEATURE] Raster image symbol fill type

Allows for filling polygons with a tiled raster image. Options
include (data defined) file name, opacity, image size (in pixels,
mm or map units), coordinate mode (feature or view) and rotation.
This commit is contained in:
Nyall Dawson 2014-11-17 23:30:00 +11:00
parent 66ffb2a0ae
commit 52159851bf
14 changed files with 1561 additions and 2 deletions

View File

@ -461,6 +461,175 @@ class QgsImageFillSymbolLayer: QgsFillSymbolLayerV2
virtual Qt::PenStyle dxfPenStyle() const;
};
/** \ingroup core
* \class QgsRasterFillSymbolLayer
* \brief A class for filling symbols with a repeated raster image.
* \note Added in version 2.7
*/
class QgsRasterFillSymbolLayer: QgsImageFillSymbolLayer
{
%TypeHeaderCode
#include <qgsfillsymbollayerv2.h>
%End
public:
enum FillCoordinateMode
{
Feature,
Viewport
};
QgsRasterFillSymbolLayer( const QString& imageFilePath = QString() );
~QgsRasterFillSymbolLayer();
static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
// implemented from base classes
QString layerType() const;
void renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context );
void startRender( QgsSymbolV2RenderContext& context );
void stopRender( QgsSymbolV2RenderContext& context );
QgsStringMap properties() const;
QgsSymbolLayerV2* clone() const;
virtual double estimateMaxBleed() const;
//override QgsImageFillSymbolLayer's support for sub symbols
virtual QgsSymbolV2* subSymbol();
virtual bool setSubSymbol( QgsSymbolV2* symbol );
/**Sets the path to the raster image used for the fill.
* @param imagePath path to image file
* @see imageFilePath
*/
void setImageFilePath( const QString& imagePath );
/**The path to the raster image used for the fill.
* @returns path to image file
* @see setImageFilePath
*/
QString imageFilePath() const;
/**Set the coordinate mode for fill. Controls how the top left corner of the image
* fill is positioned relative to the feature.
* @param mode coordinate mode
* @see coordinateMode
*/
void setCoordinateMode( const FillCoordinateMode mode );
/**Coordinate mode for fill. Controls how the top left corner of the image
* fill is positioned relative to the feature.
* @returns coordinate mode
* @see setCoordinateMode
*/
FillCoordinateMode coordinateMode() const;
/**Sets the opacity for the raster image used in the fill.
* @param alpha opacity value between 0 (fully transparent) and 1 (fully opaque)
* @see alpha
*/
void setAlpha( const double alpha );
/**The opacity for the raster image used in the fill.
* @returns opacity value between 0 (fully transparent) and 1 (fully opaque)
* @see setAlpha
*/
double alpha() const;
/**Sets the offset for the fill.
* @param offset offset for fill
* @see offset
* @see setOffsetUnit
* @see setOffsetMapUnitScale
*/
void setOffset( const QPointF& offset );
/**Returns the offset for the fill.
* @returns offset for fill
* @see setOffset
* @see offsetUnit
* @see offsetMapUnitScale
*/
QPointF offset() const;
/**Sets the units for the fill's offset.
* @param unit units for offset
* @see offsetUnit
* @see setOffset
* @see setOffsetMapUnitScale
*/
void setOffsetUnit( const QgsSymbolV2::OutputUnit unit );
/**Returns the units for the fill's offset.
* @returns units for offset
* @see setOffsetUnit
* @see offset
* @see offsetMapUnitScale
*/
QgsSymbolV2::OutputUnit offsetUnit() const;
/**Sets the map unit scale for the fill's offset.
* @param scale map unit scale for offset
* @see offsetMapUnitScale
* @see setOffset
* @see setOffsetUnit
*/
void setOffsetMapUnitScale( const QgsMapUnitScale& scale );
/**Returns the map unit scale for the fill's offset.
* @returns map unit scale for offset
* @see setOffsetMapUnitScale
* @see offset
* @see offsetUnit
*/
const QgsMapUnitScale& offsetMapUnitScale() const;
/**Sets the width for scaling the image used in the fill. The image's height will also be
* scaled to maintain the image's aspect ratio.
* @param width width for scaling the image
* @see width
* @see setWidthUnit
* @see setWidthMapUnitScale
*/
void setWidth( const double width );
/**Returns the width used for scaling the image used in the fill. The image's height is
* scaled to maintain the image's aspect ratio.
* @returns width used for scaling the image
* @see setWidth
* @see widthUnit
* @see widthMapUnitScale
*/
double width() const;
/**Sets the units for the image's width.
* @param unit units for width
* @see widthUnit
* @see setWidth
* @see setWidthMapUnitScale
*/
void setWidthUnit( const QgsSymbolV2::OutputUnit unit );
/**Returns the units for the image's width.
* @returns units for width
* @see setWidthUnit
* @see width
* @see widthMapUnitScale
*/
QgsSymbolV2::OutputUnit widthUnit() const;
/**Sets the map unit scale for the image's width.
* @param scale map unit scale for width
* @see widthMapUnitScale
* @see setWidth
* @see setWidthUnit
*/
void setWidthMapUnitScale( const QgsMapUnitScale& scale );
/**Returns the map unit scale for the image's width.
* @returns map unit scale for width
* @see setWidthMapUnitScale
* @see width
* @see widthUnit
*/
const QgsMapUnitScale& widthMapUnitScale() const;
protected:
void applyDataDefinedSettings( const QgsSymbolV2RenderContext& context );
};
/**A class for svg fill patterns. The class automatically scales the pattern to
the appropriate pixel dimensions of the output device*/
class QgsSVGFillSymbolLayer: QgsImageFillSymbolLayer

View File

@ -23,7 +23,8 @@ class QgsSymbolV2
{
MM,
MapUnit,
Mixed //mixed units in symbol layers
Mixed, //mixed units in symbol layers
Pixel
};
enum SymbolType

View File

@ -3445,3 +3445,288 @@ QgsMapUnitScale QgsCentroidFillSymbolLayerV2::mapUnitScale() const
}
QgsRasterFillSymbolLayer::QgsRasterFillSymbolLayer( const QString &imageFilePath )
: QgsImageFillSymbolLayer()
, mImageFilePath( imageFilePath )
, mCoordinateMode( QgsRasterFillSymbolLayer::Feature )
, mAlpha( 1.0 )
, mOffsetUnit( QgsSymbolV2::MM )
, mWidth( 0.0 )
, mWidthUnit( QgsSymbolV2::Pixel )
{
QgsImageFillSymbolLayer::setSubSymbol( 0 ); //disable sub symbol
}
QgsRasterFillSymbolLayer::~QgsRasterFillSymbolLayer()
{
}
QgsSymbolLayerV2 *QgsRasterFillSymbolLayer::create( const QgsStringMap &properties )
{
FillCoordinateMode mode = QgsRasterFillSymbolLayer::Feature;
double alpha = 1.0;
QPointF offset;
double angle = 0.0;
double width = 0.0;
QString imagePath;
if ( properties.contains( "imageFile" ) )
{
imagePath = properties["imageFile"];
}
if ( properties.contains( "coordinate_mode" ) )
{
mode = ( FillCoordinateMode )properties["coordinate_mode"].toInt();
}
if ( properties.contains( "alpha" ) )
{
alpha = properties["alpha"].toDouble();
}
if ( properties.contains( "offset" ) )
{
offset = QgsSymbolLayerV2Utils::decodePoint( properties["offset"] );
}
if ( properties.contains( "angle" ) )
{
angle = properties["angle"].toDouble();
}
if ( properties.contains( "width" ) )
{
width = properties["width"].toDouble();
}
QgsRasterFillSymbolLayer* symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
symbolLayer->setCoordinateMode( mode );
symbolLayer->setAlpha( alpha );
symbolLayer->setOffset( offset );
symbolLayer->setAngle( angle );
symbolLayer->setWidth( width );
if ( properties.contains( "offset_unit" ) )
{
symbolLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
}
if ( properties.contains( "offset_map_unit_scale" ) )
{
symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
}
if ( properties.contains( "width_unit" ) )
{
symbolLayer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["width_unit"] ) );
}
if ( properties.contains( "width_map_unit_scale" ) )
{
symbolLayer->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["width_map_unit_scale"] ) );
}
//data defined
if ( properties.contains( "file_expression" ) )
{
symbolLayer->setDataDefinedProperty( "file", properties["file_expression"] );
}
if ( properties.contains( "alpha_expression" ) )
{
symbolLayer->setDataDefinedProperty( "alpha", properties["alpha_expression"] );
}
if ( properties.contains( "angle_expression" ) )
{
symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
}
if ( properties.contains( "width_expression" ) )
{
symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
}
return symbolLayer;
}
bool QgsRasterFillSymbolLayer::setSubSymbol( QgsSymbolV2 *symbol )
{
Q_UNUSED( symbol );
return true;
}
QString QgsRasterFillSymbolLayer::layerType() const
{
return "RasterFill";
}
void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolV2RenderContext &context )
{
QPainter* p = context.renderContext().painter();
if ( !p )
{
return;
}
QPointF offset;
if ( !mOffset.isNull() )
{
offset.setX( mOffset.x() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit, mOffsetMapUnitScale ) );
offset.setY( mOffset.y() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit, mOffsetMapUnitScale ) );
p->translate( offset );
}
if ( mCoordinateMode == Feature )
{
QRectF boundingRect = points.boundingRect();
mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
boundingRect.top() - mBrush.transform().dy() ) );
}
QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
if ( !mOffset.isNull() )
{
p->translate( -offset );
}
}
void QgsRasterFillSymbolLayer::startRender( QgsSymbolV2RenderContext &context )
{
prepareExpressions( context.fields(), context.renderContext().rendererScale() );
applyPattern( mBrush, mImageFilePath, mWidth, mAlpha, context );
}
void QgsRasterFillSymbolLayer::stopRender( QgsSymbolV2RenderContext &context )
{
Q_UNUSED( context );
}
QgsStringMap QgsRasterFillSymbolLayer::properties() const
{
QgsStringMap map;
map["imageFile"] = mImageFilePath;
map["coordinate_mode"] = QString::number( mCoordinateMode );
map["alpha"] = QString::number( mAlpha );
map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
map["angle"] = QString::number( mAngle );
map["width"] = QString::number( mWidth );
map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
saveDataDefinedProperties( map );
return map;
}
QgsSymbolLayerV2 *QgsRasterFillSymbolLayer::clone() const
{
QgsRasterFillSymbolLayer* sl = new QgsRasterFillSymbolLayer( mImageFilePath );
sl->setCoordinateMode( mCoordinateMode );
sl->setAlpha( mAlpha );
sl->setOffset( mOffset );
sl->setOffsetUnit( mOffsetUnit );
sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
sl->setAngle( mAngle );
sl->setWidth( mWidth );
sl->setWidthUnit( mWidthUnit );
sl->setWidthMapUnitScale( mWidthMapUnitScale );
copyDataDefinedProperties( sl );
return sl;
}
double QgsRasterFillSymbolLayer::estimateMaxBleed() const
{
return mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
}
void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
{
mImageFilePath = imagePath;
}
void QgsRasterFillSymbolLayer::setCoordinateMode( const QgsRasterFillSymbolLayer::FillCoordinateMode mode )
{
mCoordinateMode = mode;
}
void QgsRasterFillSymbolLayer::setAlpha( const double alpha )
{
mAlpha = alpha;
}
void QgsRasterFillSymbolLayer::applyDataDefinedSettings( const QgsSymbolV2RenderContext &context )
{
if ( mDataDefinedProperties.isEmpty() )
return; // shortcut
QgsExpression* widthExpression = expression( "width" );
QgsExpression* fileExpression = expression( "file" );
QgsExpression* alphaExpression = expression( "alpha" );
QgsExpression* angleExpression = expression( "angle" );
if ( !widthExpression && !angleExpression && !alphaExpression && !fileExpression )
{
return; //no data defined settings
}
if ( angleExpression )
{
mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
}
if ( !widthExpression && !alphaExpression && !fileExpression )
{
return; //nothing further to do
}
double width = mWidth;
if ( widthExpression )
{
width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
}
double alpha = mAlpha;
if ( alphaExpression )
{
alpha = alphaExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
}
QString file = mImageFilePath;
if ( fileExpression )
{
file = fileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
}
applyPattern( mBrush, file, width, alpha, context );
}
void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolV2RenderContext &context )
{
QImage image( imageFilePath );
if ( image.isNull() )
{
return;
}
if ( !image.hasAlphaChannel() )
{
image = image.convertToFormat( QImage::Format_ARGB32 );
}
double pixelWidth;
if ( width > 0 )
{
pixelWidth = width * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( context.renderContext(), mWidthUnit, mWidthMapUnitScale );
}
else
{
pixelWidth = image.width();
}
//reduce alpha of image
if ( alpha < 1.0 )
{
QPainter p;
p.begin( &image );
p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
QColor alphaColor( 0, 0, 0 );
alphaColor.setAlphaF( alpha );
p.fillRect( image.rect(), alphaColor );
p.end();
}
//resize image if required
if ( !qgsDoubleNear( pixelWidth, image.width() ) )
{
image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
}
brush.setTextureImage( image );
}

View File

@ -550,6 +550,7 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayerV2 : public QgsFillSymbolLayerV2
class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayerV2
{
public:
QgsImageFillSymbolLayer();
virtual ~QgsImageFillSymbolLayer();
void renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context );
@ -590,6 +591,190 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayerV2
virtual void applyDataDefinedSettings( const QgsSymbolV2RenderContext& context ) { Q_UNUSED( context ); }
};
/** \ingroup core
* \class QgsRasterFillSymbolLayer
* \brief A class for filling symbols with a repeated raster image.
* \note Added in version 2.7
*/
class CORE_EXPORT QgsRasterFillSymbolLayer: public QgsImageFillSymbolLayer
{
public:
enum FillCoordinateMode
{
Feature,
Viewport
};
QgsRasterFillSymbolLayer( const QString& imageFilePath = QString() );
~QgsRasterFillSymbolLayer();
static QgsSymbolLayerV2* create( const QgsStringMap& properties = QgsStringMap() );
// implemented from base classes
QString layerType() const;
void renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context );
void startRender( QgsSymbolV2RenderContext& context );
void stopRender( QgsSymbolV2RenderContext& context );
QgsStringMap properties() const;
QgsSymbolLayerV2* clone() const;
virtual double estimateMaxBleed() const;
//override QgsImageFillSymbolLayer's support for sub symbols
virtual QgsSymbolV2* subSymbol() { return 0; }
virtual bool setSubSymbol( QgsSymbolV2* symbol );
/**Sets the path to the raster image used for the fill.
* @param imagePath path to image file
* @see imageFilePath
*/
void setImageFilePath( const QString& imagePath );
/**The path to the raster image used for the fill.
* @returns path to image file
* @see setImageFilePath
*/
QString imageFilePath() const { return mImageFilePath; }
/**Set the coordinate mode for fill. Controls how the top left corner of the image
* fill is positioned relative to the feature.
* @param mode coordinate mode
* @see coordinateMode
*/
void setCoordinateMode( const FillCoordinateMode mode );
/**Coordinate mode for fill. Controls how the top left corner of the image
* fill is positioned relative to the feature.
* @returns coordinate mode
* @see setCoordinateMode
*/
FillCoordinateMode coordinateMode() const { return mCoordinateMode; }
/**Sets the opacity for the raster image used in the fill.
* @param alpha opacity value between 0 (fully transparent) and 1 (fully opaque)
* @see alpha
*/
void setAlpha( const double alpha );
/**The opacity for the raster image used in the fill.
* @returns opacity value between 0 (fully transparent) and 1 (fully opaque)
* @see setAlpha
*/
double alpha() const { return mAlpha; }
/**Sets the offset for the fill.
* @param offset offset for fill
* @see offset
* @see setOffsetUnit
* @see setOffsetMapUnitScale
*/
void setOffset( const QPointF& offset ) { mOffset = offset; }
/**Returns the offset for the fill.
* @returns offset for fill
* @see setOffset
* @see offsetUnit
* @see offsetMapUnitScale
*/
QPointF offset() const { return mOffset; }
/**Sets the units for the fill's offset.
* @param unit units for offset
* @see offsetUnit
* @see setOffset
* @see setOffsetMapUnitScale
*/
void setOffsetUnit( const QgsSymbolV2::OutputUnit unit ) { mOffsetUnit = unit; }
/**Returns the units for the fill's offset.
* @returns units for offset
* @see setOffsetUnit
* @see offset
* @see offsetMapUnitScale
*/
QgsSymbolV2::OutputUnit offsetUnit() const { return mOffsetUnit; }
/**Sets the map unit scale for the fill's offset.
* @param scale map unit scale for offset
* @see offsetMapUnitScale
* @see setOffset
* @see setOffsetUnit
*/
void setOffsetMapUnitScale( const QgsMapUnitScale& scale ) { mOffsetMapUnitScale = scale; }
/**Returns the map unit scale for the fill's offset.
* @returns map unit scale for offset
* @see setOffsetMapUnitScale
* @see offset
* @see offsetUnit
*/
const QgsMapUnitScale& offsetMapUnitScale() const { return mOffsetMapUnitScale; }
/**Sets the width for scaling the image used in the fill. The image's height will also be
* scaled to maintain the image's aspect ratio.
* @param width width for scaling the image
* @see width
* @see setWidthUnit
* @see setWidthMapUnitScale
*/
void setWidth( const double width ) { mWidth = width; }
/**Returns the width used for scaling the image used in the fill. The image's height is
* scaled to maintain the image's aspect ratio.
* @returns width used for scaling the image
* @see setWidth
* @see widthUnit
* @see widthMapUnitScale
*/
double width() const { return mWidth; }
/**Sets the units for the image's width.
* @param unit units for width
* @see widthUnit
* @see setWidth
* @see setWidthMapUnitScale
*/
void setWidthUnit( const QgsSymbolV2::OutputUnit unit ) { mWidthUnit = unit; }
/**Returns the units for the image's width.
* @returns units for width
* @see setWidthUnit
* @see width
* @see widthMapUnitScale
*/
QgsSymbolV2::OutputUnit widthUnit() const { return mWidthUnit; }
/**Sets the map unit scale for the image's width.
* @param scale map unit scale for width
* @see widthMapUnitScale
* @see setWidth
* @see setWidthUnit
*/
void setWidthMapUnitScale( const QgsMapUnitScale& scale ) { mWidthMapUnitScale = scale; }
/**Returns the map unit scale for the image's width.
* @returns map unit scale for width
* @see setWidthMapUnitScale
* @see width
* @see widthUnit
*/
const QgsMapUnitScale& widthMapUnitScale() const { return mWidthMapUnitScale; }
protected:
/**Path to the image file*/
QString mImageFilePath;
FillCoordinateMode mCoordinateMode;
double mAlpha;
QPointF mOffset;
QgsSymbolV2::OutputUnit mOffsetUnit;
QgsMapUnitScale mOffsetMapUnitScale;
double mWidth;
QgsSymbolV2::OutputUnit mWidthUnit;
QgsMapUnitScale mWidthMapUnitScale;
void applyDataDefinedSettings( const QgsSymbolV2RenderContext& context );
private:
/**Applies the image pattern to the brush*/
void applyPattern( QBrush& brush, const QString& imageFilePath, const double width, const double alpha,
const QgsSymbolV2RenderContext& context );
};
/**A class for svg fill patterns. The class automatically scales the pattern to
the appropriate pixel dimensions of the output device*/
class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsImageFillSymbolLayer

View File

@ -46,6 +46,8 @@ QgsSymbolLayerV2Registry::QgsSymbolLayerV2Registry()
QgsGradientFillSymbolLayerV2::create ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "ShapeburstFill", QObject::tr( "Shapeburst fill" ), QgsSymbolV2::Fill,
QgsShapeburstFillSymbolLayerV2::create ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "RasterFill", QObject::tr( "Raster image fill" ), QgsSymbolV2::Fill,
QgsRasterFillSymbolLayer::create ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "SVGFill", QObject::tr( "SVG fill" ), QgsSymbolV2::Fill,
QgsSVGFillSymbolLayer::create, QgsSVGFillSymbolLayer::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerV2Metadata( "CentroidFill", QObject::tr( "Centroid fill" ), QgsSymbolV2::Fill,

View File

@ -347,6 +347,8 @@ QString QgsSymbolLayerV2Utils::encodeOutputUnit( QgsSymbolV2::OutputUnit unit )
return "MM";
case QgsSymbolV2::MapUnit:
return "MapUnit";
case QgsSymbolV2::Pixel:
return "Pixel";
default:
return "MM";
}
@ -362,6 +364,10 @@ QgsSymbolV2::OutputUnit QgsSymbolLayerV2Utils::decodeOutputUnit( QString str )
{
return QgsSymbolV2::MapUnit;
}
else if ( str == "Pixel" )
{
return QgsSymbolV2::Pixel;
}
// millimeters are default
return QgsSymbolV2::MM;
@ -3261,6 +3267,10 @@ double QgsSymbolLayerV2Utils::pixelSizeScaleFactor( const QgsRenderContext& c, Q
{
return ( c.scaleFactor() * c.rasterScaleFactor() );
}
else if ( u == QgsSymbolV2::Pixel )
{
return 1.0;
}
else //QgsSymbol::MapUnit
{
double mup = scale.computeMapUnitsPerPixel( c );

View File

@ -48,7 +48,8 @@ class CORE_EXPORT QgsSymbolV2
{
MM = 0,
MapUnit,
Mixed //mixed units in symbol layers
Mixed, //mixed units in symbol layers
Pixel
};
enum SymbolType

View File

@ -69,6 +69,7 @@ static void _initWidgetFunctions()
_initWidgetFunction( "SimpleFill", QgsSimpleFillSymbolLayerV2Widget::create );
_initWidgetFunction( "GradientFill", QgsGradientFillSymbolLayerV2Widget::create );
_initWidgetFunction( "ShapeburstFill", QgsShapeburstFillSymbolLayerV2Widget::create );
_initWidgetFunction( "RasterFill", QgsRasterFillSymbolLayerWidget::create );
_initWidgetFunction( "SVGFill", QgsSVGFillSymbolLayerWidget::create );
_initWidgetFunction( "CentroidFill", QgsCentroidFillSymbolLayerV2Widget::create );
_initWidgetFunction( "LinePatternFill", QgsLinePatternFillSymbolLayerWidget::create );

View File

@ -44,6 +44,7 @@
#include <QSettings>
#include <QStandardItemModel>
#include <QSvgRenderer>
#include <QMessageBox>
QString QgsSymbolLayerV2Widget::dataDefinedPropertyLabel( const QString &entryName )
{
@ -2912,3 +2913,332 @@ void QgsCentroidFillSymbolLayerV2Widget::on_mDrawInsideCheckBox_stateChanged( in
mLayer->setPointOnSurface( state == Qt::Checked );
emit changed();
}
///////////////
QgsRasterFillSymbolLayerWidget::QgsRasterFillSymbolLayerWidget( const QgsVectorLayer *vl, QWidget *parent )
: QgsSymbolLayerV2Widget( parent, vl )
{
mLayer = 0;
setupUi( this );
mWidthUnitWidget->setUnits( QStringList() << tr( "Pixels" ) << tr( "Millimeter" ) << tr( "Map unit" ), 1 );
mOffsetUnitWidget->setUnits( QStringList() << tr( "Millimeter" ) << tr( "Map unit" ), 1 );
connect( cboCoordinateMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( setCoordinateMode( int ) ) );
connect( mSpinOffsetX, SIGNAL( valueChanged( double ) ), this, SLOT( offsetChanged() ) );
connect( mSpinOffsetY, SIGNAL( valueChanged( double ) ), this, SLOT( offsetChanged() ) );
}
void QgsRasterFillSymbolLayerWidget::setSymbolLayer( QgsSymbolLayerV2 *layer )
{
if ( !layer )
{
return;
}
if ( layer->layerType() != "RasterFill" )
{
return;
}
mLayer = dynamic_cast<QgsRasterFillSymbolLayer*>( layer );
if ( !mLayer )
{
return;
}
mImageLineEdit->blockSignals( true );
mImageLineEdit->setText( mLayer->imageFilePath() );
mImageLineEdit->blockSignals( false );
cboCoordinateMode->blockSignals( true );
switch ( mLayer->coordinateMode() )
{
case QgsRasterFillSymbolLayer::Viewport:
cboCoordinateMode->setCurrentIndex( 1 );
break;
case QgsRasterFillSymbolLayer::Feature:
default:
cboCoordinateMode->setCurrentIndex( 0 );
break;
}
cboCoordinateMode->blockSignals( false );
mSpinTransparency->blockSignals( true );
mSpinTransparency->setValue( mLayer->alpha() * 100.0 );
mSpinTransparency->blockSignals( false );
mSliderTransparency->blockSignals( true );
mSliderTransparency->setValue( mLayer->alpha() * 100.0 );
mSliderTransparency->blockSignals( false );
mRotationSpinBox->blockSignals( true );
mRotationSpinBox->setValue( mLayer->angle() );
mRotationSpinBox->blockSignals( false );
mSpinOffsetX->blockSignals( true );
mSpinOffsetX->setValue( mLayer->offset().x() );
mSpinOffsetX->blockSignals( false );
mSpinOffsetY->blockSignals( true );
mSpinOffsetY->setValue( mLayer->offset().y() );
mSpinOffsetY->blockSignals( false );
mOffsetUnitWidget->blockSignals( true );
mOffsetUnitWidget->setUnit( mLayer->offsetUnit() );
mOffsetUnitWidget->setMapUnitScale( mLayer->offsetMapUnitScale() );
mOffsetUnitWidget->blockSignals( false );
mWidthSpinBox->blockSignals( true );
mWidthSpinBox->setValue( mLayer->width() );
mWidthSpinBox->blockSignals( false );
mWidthUnitWidget->blockSignals( true );
switch ( mLayer->widthUnit() )
{
case QgsSymbolV2::MM:
mWidthUnitWidget->setUnit( 1 );
break;
case QgsSymbolV2::MapUnit:
mWidthUnitWidget->setUnit( 2 );
break;
case QgsSymbolV2::Pixel:
default:
mWidthUnitWidget->setUnit( 0 );
break;
}
mWidthUnitWidget->setMapUnitScale( mLayer->widthMapUnitScale() );
mWidthUnitWidget->blockSignals( false );
updatePreviewImage();
}
QgsSymbolLayerV2 *QgsRasterFillSymbolLayerWidget::symbolLayer()
{
return mLayer;
}
void QgsRasterFillSymbolLayerWidget::on_mBrowseToolButton_clicked()
{
QSettings s;
QString openDir;
QString lineEditText = mImageLineEdit->text();
if ( !lineEditText.isEmpty() )
{
QFileInfo openDirFileInfo( lineEditText );
openDir = openDirFileInfo.path();
}
if ( openDir.isEmpty() )
{
openDir = s.value( "/UI/lastRasterFillImageDir", "" ).toString();
}
//show file dialog
QString filePath = QFileDialog::getOpenFileName( 0, tr( "Select image file" ), openDir );
if ( !filePath.isNull() )
{
//check if file exists
QFileInfo fileInfo( filePath );
if ( !fileInfo.exists() || !fileInfo.isReadable() )
{
QMessageBox::critical( 0, "Invalid file", "Error, file does not exist or is not readable" );
return;
}
s.setValue( "/UI/lastRasterFillImageDir", fileInfo.absolutePath() );
mImageLineEdit->setText( filePath );
on_mImageLineEdit_editingFinished();
}
}
void QgsRasterFillSymbolLayerWidget::on_mImageLineEdit_editingFinished()
{
if ( !mLayer )
{
return;
}
QFileInfo fi( mImageLineEdit->text() );
if ( !fi.exists() )
{
QUrl url( mImageLineEdit->text() );
if ( !url.isValid() )
{
return;
}
}
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
mLayer->setImageFilePath( mImageLineEdit->text() );
updatePreviewImage();
QApplication::restoreOverrideCursor();
emit changed();
}
void QgsRasterFillSymbolLayerWidget::setCoordinateMode( int index )
{
switch ( index )
{
case 0:
//feature coordinate mode
mLayer->setCoordinateMode( QgsRasterFillSymbolLayer::Feature );
break;
case 1:
//viewport coordinate mode
mLayer->setCoordinateMode( QgsRasterFillSymbolLayer::Viewport );
break;
}
emit changed();
}
void QgsRasterFillSymbolLayerWidget::on_mSpinTransparency_valueChanged( int value )
{
if ( !mLayer )
{
return;
}
mLayer->setAlpha( value / 100.0 );
emit changed();
updatePreviewImage();
}
void QgsRasterFillSymbolLayerWidget::offsetChanged()
{
mLayer->setOffset( QPointF( mSpinOffsetX->value(), mSpinOffsetY->value() ) );
emit changed();
}
void QgsRasterFillSymbolLayerWidget::on_mOffsetUnitWidget_changed()
{
if ( !mLayer )
{
return;
}
QgsSymbolV2::OutputUnit unit = static_cast<QgsSymbolV2::OutputUnit>( mOffsetUnitWidget->getUnit() );
mLayer->setOffsetUnit( unit );
mLayer->setOffsetMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
emit changed();
}
void QgsRasterFillSymbolLayerWidget::on_mRotationSpinBox_valueChanged( double d )
{
if ( mLayer )
{
mLayer->setAngle( d );
emit changed();
}
}
void QgsRasterFillSymbolLayerWidget::on_mWidthUnitWidget_changed()
{
if ( !mLayer )
{
return;
}
QgsSymbolV2::OutputUnit unit;
switch ( mWidthUnitWidget->getUnit() )
{
case 0:
unit = QgsSymbolV2::Pixel;
break;
case 1:
unit = QgsSymbolV2::MM;
break;
case 2:
unit = QgsSymbolV2::MapUnit;
break;
}
mLayer->setWidthUnit( unit );
mLayer->setWidthMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
emit changed();
}
void QgsRasterFillSymbolLayerWidget::on_mWidthSpinBox_valueChanged( double d )
{
if ( !mLayer )
{
return;
}
mLayer->setWidth( d );
emit changed();
}
void QgsRasterFillSymbolLayerWidget::on_mDataDefinedPropertiesButton_clicked()
{
if ( !mLayer )
{
return;
}
QList< QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry > dataDefinedProperties;
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "file", tr( "File" ), mLayer->dataDefinedPropertyString( "file" ),
QgsDataDefinedSymbolDialog::fileNameHelpText() );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "alpha", tr( "Opacity" ), mLayer->dataDefinedPropertyString( "alpha" ),
QgsDataDefinedSymbolDialog::doubleHelpText() );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "angle", tr( "Angle" ), mLayer->dataDefinedPropertyString( "angle" ),
QgsDataDefinedSymbolDialog::doubleHelpText() );
dataDefinedProperties << QgsDataDefinedSymbolDialog::DataDefinedSymbolEntry( "width", tr( "Width" ), mLayer->dataDefinedPropertyString( "width" ),
QgsDataDefinedSymbolDialog::doubleHelpText() );
QgsDataDefinedSymbolDialog d( dataDefinedProperties, mVectorLayer );
if ( d.exec() == QDialog::Accepted )
{
//empty all existing properties first
mLayer->removeDataDefinedProperties();
QMap<QString, QString> properties = d.dataDefinedProperties();
QMap<QString, QString>::const_iterator it = properties.constBegin();
for ( ; it != properties.constEnd(); ++it )
{
if ( !it.value().isEmpty() )
{
mLayer->setDataDefinedProperty( it.key(), it.value() );
}
}
emit changed();
}
}
void QgsRasterFillSymbolLayerWidget::updatePreviewImage()
{
if ( !mLayer )
{
return;
}
QImage image( mLayer->imageFilePath() );
if ( image.isNull() )
{
mLabelImagePreview->setPixmap( 0 );
return;
}
if ( image.height() > 150 || image.width() > 150 )
{
image = image.scaled( 150, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}
QImage previewImage( 150, 150, QImage::Format_ARGB32 );
previewImage.fill( Qt::transparent );
QRect imageRect(( 150 - image.width() ) / 2.0, ( 150 - image.height() ) / 2.0, image.width(), image.height() );
QPainter p;
p.begin( &previewImage );
//draw a checkerboard background
uchar pixDataRGB[] = { 150, 150, 150, 150,
100, 100, 100, 150,
100, 100, 100, 150,
150, 150, 150, 150
};
QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
QPixmap pix = QPixmap::fromImage( img.scaled( 8, 8 ) );
QBrush checkerBrush;
checkerBrush.setTexture( pix );
p.fillRect( imageRect, checkerBrush );
if ( mLayer->alpha() < 1.0 )
{
p.setOpacity( mLayer->alpha() );
}
p.drawImage( imageRect.left(), imageRect.top(), image );
p.end();
mLabelImagePreview->setPixmap( QPixmap::fromImage( previewImage ) );
}

View File

@ -320,6 +320,43 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerV2Widget : public QgsSymbolLayerV2Widget
QgsSvgMarkerSymbolLayerV2* mLayer;
};
///////////
#include "ui_widget_rasterfill.h"
class QgsRasterFillSymbolLayer;
class GUI_EXPORT QgsRasterFillSymbolLayerWidget : public QgsSymbolLayerV2Widget, private Ui::WidgetRasterFill
{
Q_OBJECT
public:
QgsRasterFillSymbolLayerWidget( const QgsVectorLayer* vl, QWidget* parent = NULL );
static QgsSymbolLayerV2Widget* create( const QgsVectorLayer* vl ) { return new QgsRasterFillSymbolLayerWidget( vl ); }
// from base class
virtual void setSymbolLayer( QgsSymbolLayerV2* layer );
virtual QgsSymbolLayerV2* symbolLayer();
protected:
QgsRasterFillSymbolLayer* mLayer;
private slots:
void on_mBrowseToolButton_clicked();
void on_mImageLineEdit_editingFinished();
void setCoordinateMode( int index );
void on_mSpinTransparency_valueChanged( int value );
void offsetChanged();
void on_mOffsetUnitWidget_changed();
void on_mRotationSpinBox_valueChanged( double d );
void on_mWidthUnitWidget_changed();
void on_mWidthSpinBox_valueChanged( double d );
void on_mDataDefinedPropertiesButton_clicked();
private:
void updatePreviewImage();
};
///////////

View File

@ -0,0 +1,372 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetRasterFill</class>
<widget class="QWidget" name="WidgetRasterFill">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>294</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mImageLineEdit"/>
</item>
<item>
<widget class="QToolButton" name="mBrowseToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="mTextureWidthLabel">
<property name="text">
<string>Image width</string>
</property>
<property name="buddy">
<cstring>mWidthSpinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="mRotationSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Coord mode</string>
</property>
<property name="buddy">
<cstring>cboCoordinateMode</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="cboCoordinateMode">
<item>
<property name="text">
<string>Object</string>
</property>
</item>
<item>
<property name="text">
<string>Viewport</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="mRotationLabel">
<property name="text">
<string>Rotation</string>
</property>
<property name="buddy">
<cstring>mRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="5">
<widget class="QLabel" name="mLabelImagePreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QgsDoubleSpinBox" name="mWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="specialValueText">
<string>Original</string>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QgsUnitSelectionWidget" name="mWidthUnitWidget" native="true"/>
</item>
</layout>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
<item>
<widget class="QSlider" name="mSliderTransparency">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="mSpinTransparency">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Opacity</string>
</property>
<property name="buddy">
<cstring>mSliderTransparency</cstring>
</property>
</widget>
</item>
<item row="4" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Offset X,Y</string>
</property>
<property name="buddy">
<cstring>mSpinOffsetX</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QDoubleSpinBox" name="mSpinOffsetX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-999.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="mSpinOffsetY">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-999.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QgsUnitSelectionWidget" name="mOffsetUnitWidget" native="true"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="mDataDefinedPropertiesButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Data defined properties...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsUnitSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mImageLineEdit</tabstop>
<tabstop>mBrowseToolButton</tabstop>
<tabstop>mWidthSpinBox</tabstop>
<tabstop>mWidthUnitWidget</tabstop>
<tabstop>mRotationSpinBox</tabstop>
<tabstop>cboCoordinateMode</tabstop>
<tabstop>mSliderTransparency</tabstop>
<tabstop>mSpinTransparency</tabstop>
<tabstop>mSpinOffsetX</tabstop>
<tabstop>mSpinOffsetY</tabstop>
<tabstop>mOffsetUnitWidget</tabstop>
<tabstop>mDataDefinedPropertiesButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>mSliderTransparency</sender>
<signal>valueChanged(int)</signal>
<receiver>mSpinTransparency</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>311</x>
<y>160</y>
</hint>
<hint type="destinationlabel">
<x>392</x>
<y>160</y>
</hint>
</hints>
</connection>
<connection>
<sender>mSpinTransparency</sender>
<signal>valueChanged(int)</signal>
<receiver>mSliderTransparency</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>392</x>
<y>160</y>
</hint>
<hint type="destinationlabel">
<x>311</x>
<y>160</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -132,6 +132,7 @@ ADD_QGIS_TEST(vectorlayercachetest testqgsvectorlayercache.cpp )
# ADD_QGIS_TEST(maprendererjobtest testmaprendererjob.cpp )
ADD_QGIS_TEST(spatialindextest testqgsspatialindex.cpp)
ADD_QGIS_TEST(gradienttest testqgsgradients.cpp )
ADD_QGIS_TEST(rasterfilltest testqgsrasterfill.cpp )
ADD_QGIS_TEST(shapebursttest testqgsshapeburst.cpp )
ADD_QGIS_TEST(invertedpolygontest testqgsinvertedpolygonrenderer.cpp )
ADD_QGIS_TEST(colorschemeregistry testqgscolorschemeregistry.cpp)

View File

@ -0,0 +1,165 @@
/***************************************************************************
testqgsrasterfill.cpp
---------------------
Date : November 2014
Copyright : (C) 2014 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QObject>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QDesktopServices>
#include <iostream>
//qgis includes...
#include <qgsmapsettings.h>
#include <qgsmaplayer.h>
#include <qgsvectorlayer.h>
#include <qgsapplication.h>
#include <qgsproviderregistry.h>
#include <qgsmaplayerregistry.h>
#include <qgssymbolv2.h>
#include <qgssinglesymbolrendererv2.h>
#include <qgsfillsymbollayerv2.h>
//qgis test includes
#include "qgsmultirenderchecker.h"
/** \ingroup UnitTests
* This is a unit test for raster fill types.
*/
class TestQgsRasterFill: 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 rasterFillSymbol();
private:
bool mTestHasError;
bool setQml( QString theType );
bool imageCheck( QString theType );
QgsMapSettings mMapSettings;
QgsVectorLayer * mpPolysLayer;
QgsRasterFillSymbolLayer* mRasterFill;
QgsFillSymbolV2* mFillSymbol;
QgsSingleSymbolRendererV2* mSymbolRenderer;
QString mTestDataDir;
QString mReport;
};
void TestQgsRasterFill::initTestCase()
{
mTestHasError = false;
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init();
QgsApplication::initQgis();
QgsApplication::showSettings();
//create some objects that will be used in all tests...
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 );
//setup raster fill
mRasterFill = new QgsRasterFillSymbolLayer();
mFillSymbol = new QgsFillSymbolV2();
mFillSymbol->changeSymbolLayer( 0, mRasterFill );
mSymbolRenderer = new QgsSingleSymbolRendererV2( mFillSymbol );
mpPolysLayer->setRendererV2( mSymbolRenderer );
// We only need maprender instead of mapcanvas
// since maprender does not require a qui
// and is more light weight
//
mMapSettings.setLayers( QStringList() << mpPolysLayer->id() );
mReport += "<h1>Raster Fill Renderer Tests</h1>\n";
}
void TestQgsRasterFill::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 TestQgsRasterFill::rasterFillSymbol()
{
mReport += "<h2>Raster fill symbol renderer test</h2>\n";
mRasterFill->setImageFilePath( mTestDataDir + QString( "sample_image.png" ) );
mRasterFill->setWidth( 30.0 );
bool result = imageCheck( "rasterfill" );
QVERIFY( result );
}
//
// Private helper functions not called directly by CTest
//
bool TestQgsRasterFill::setQml( QString theType )
{
//load a qml style and apply to our layer
//the style will correspond to the renderer
//type we are testing
QString myFileName = mTestDataDir + "polys_" + theType + "_symbol.qml";
bool myStyleFlag = false;
QString error = mpPolysLayer->loadNamedStyle( myFileName, myStyleFlag );
if ( !myStyleFlag )
{
qDebug( "%s", error.toLocal8Bit().constData() );
}
return myStyleFlag;
}
bool TestQgsRasterFill::imageCheck( QString theTestType )
{
//use the QgsRenderChecker test utility class to
//ensure the rendered output matches our control image
mMapSettings.setExtent( mpPolysLayer->extent() );
QgsMultiRenderChecker myChecker;
myChecker.setControlName( "expected_" + theTestType );
myChecker.setMapSettings( mMapSettings );
myChecker.setColorTolerance( 20 );
bool myResultFlag = myChecker.runTest( theTestType, 500 );
mReport += myChecker.report();
return myResultFlag;
}
QTEST_MAIN( TestQgsRasterFill )
#include "testqgsrasterfill.moc"

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB