mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[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:
parent
66ffb2a0ae
commit
52159851bf
@ -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
|
||||
|
@ -23,7 +23,8 @@ class QgsSymbolV2
|
||||
{
|
||||
MM,
|
||||
MapUnit,
|
||||
Mixed //mixed units in symbol layers
|
||||
Mixed, //mixed units in symbol layers
|
||||
Pixel
|
||||
};
|
||||
|
||||
enum SymbolType
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
///////////
|
||||
|
||||
|
372
src/ui/symbollayer/widget_rasterfill.ui
Executable file
372
src/ui/symbollayer/widget_rasterfill.ui
Executable 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>
|
@ -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)
|
||||
|
165
tests/src/core/testqgsrasterfill.cpp
Normal file
165
tests/src/core/testqgsrasterfill.cpp
Normal 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"
|
BIN
tests/testdata/control_images/expected_rasterfill/default/expected_rasterfill.png
vendored
Normal file
BIN
tests/testdata/control_images/expected_rasterfill/default/expected_rasterfill.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Loading…
x
Reference in New Issue
Block a user