mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-20 00:03:07 -04:00
[FEATURE] Raster image marker
This commit is contained in:
parent
a8be1ff37c
commit
c6425338ee
@ -697,6 +697,166 @@ Calculates the marker aspect ratio between width and height.
|
||||
|
||||
|
||||
|
||||
class QgsRasterMarkerSymbolLayer : QgsMarkerSymbolLayer
|
||||
{
|
||||
%Docstring
|
||||
Raster marker symbol layer class.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsmarkersymbollayer.h"
|
||||
%End
|
||||
public:
|
||||
QgsRasterMarkerSymbolLayer( const QString &path = QString(),
|
||||
double size = DEFAULT_SVGMARKER_SIZE,
|
||||
double angle = DEFAULT_SVGMARKER_ANGLE,
|
||||
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD );
|
||||
%Docstring
|
||||
Constructs raster marker symbol layer with picture from given absolute path to a raster image file
|
||||
%End
|
||||
|
||||
|
||||
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/;
|
||||
%Docstring
|
||||
Creates a raster marker symbol layer from a string map of properties.
|
||||
|
||||
:param properties: QgsStringMap properties object
|
||||
%End
|
||||
|
||||
static void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving );
|
||||
%Docstring
|
||||
Turns relative paths in properties map to absolute when reading and vice versa when writing.
|
||||
Used internally when reading/writing symbols.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
|
||||
virtual QString layerType() const;
|
||||
|
||||
|
||||
virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context );
|
||||
|
||||
|
||||
virtual QgsStringMap properties() const;
|
||||
|
||||
|
||||
virtual QgsRasterMarkerSymbolLayer *clone() const /Factory/;
|
||||
|
||||
|
||||
double calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const;
|
||||
%Docstring
|
||||
Calculates the marker aspect ratio between width and height.
|
||||
|
||||
:param context: symbol render context
|
||||
:param scaledSize: size of symbol to render
|
||||
:param hasDataDefinedAspectRatio: will be set to true if marker has data defined aspectRatio
|
||||
%End
|
||||
|
||||
QString path() const;
|
||||
%Docstring
|
||||
Returns the marker raster image path.
|
||||
|
||||
.. seealso:: :py:func:`setPath`
|
||||
%End
|
||||
|
||||
void setPath( const QString &path );
|
||||
%Docstring
|
||||
Set the marker raster image path.
|
||||
|
||||
:param path: raster image path
|
||||
|
||||
.. seealso:: :py:func:`path`
|
||||
%End
|
||||
|
||||
double opacity() const;
|
||||
%Docstring
|
||||
Returns the marker opacity.
|
||||
|
||||
:return: opacity value between 0 (fully transparent) and 1 (fully opaque)
|
||||
|
||||
.. seealso:: :py:func:`setOpacity`
|
||||
%End
|
||||
|
||||
void setOpacity( double opacity );
|
||||
%Docstring
|
||||
Set the marker opacity.
|
||||
|
||||
:param opacity: opacity value between 0 (fully transparent) and 1 (fully opaque)
|
||||
|
||||
.. seealso:: :py:func:`opacity`
|
||||
%End
|
||||
|
||||
double defaultAspectRatio() const;
|
||||
%Docstring
|
||||
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
|
||||
|
||||
.. seealso:: :py:func:`updateDefaultAspectRatio`
|
||||
%End
|
||||
|
||||
double updateDefaultAspectRatio();
|
||||
%Docstring
|
||||
Calculates the default marker aspect ratio between width and height.
|
||||
|
||||
:return: the default aspect ratio value
|
||||
|
||||
.. seealso:: :py:func:`defaultAspectRatio`
|
||||
%End
|
||||
|
||||
bool preservedAspectRatio() const;
|
||||
%Docstring
|
||||
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
|
||||
|
||||
.. seealso:: :py:func:`setPreservedAspectRatio`
|
||||
%End
|
||||
|
||||
bool setPreservedAspectRatio( bool par );
|
||||
%Docstring
|
||||
Set preserved the marker aspect ratio between width and height.
|
||||
|
||||
:param par: Preserved Aspect Ratio
|
||||
|
||||
:return: the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0
|
||||
|
||||
.. seealso:: :py:func:`preservedAspectRatio`
|
||||
%End
|
||||
|
||||
double fixedAspectRatio() const;
|
||||
%Docstring
|
||||
Returns the marker aspect ratio between width and height to be used in rendering,
|
||||
if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
|
||||
|
||||
.. seealso:: :py:func:`setFixedAspectRatio`
|
||||
%End
|
||||
|
||||
void setFixedAspectRatio( double ratio );
|
||||
%Docstring
|
||||
Set the marker aspect ratio between width and height to be used in rendering,
|
||||
if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
|
||||
|
||||
:param ratio: Fixed Aspect Ratio
|
||||
|
||||
.. seealso:: :py:func:`fixedAspectRatio`
|
||||
%End
|
||||
|
||||
virtual void setMapUnitScale( const QgsMapUnitScale &scale );
|
||||
|
||||
virtual QgsMapUnitScale mapUnitScale() const;
|
||||
|
||||
|
||||
virtual QRectF bounds( QPointF point, QgsSymbolRenderContext &context );
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsFontMarkerSymbolLayer : QgsMarkerSymbolLayer
|
||||
{
|
||||
|
@ -33,6 +33,8 @@ class QgsSymbolLayer
|
||||
sipType = sipType_QgsFilledMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "SvgMarker" )
|
||||
sipType = sipType_QgsSvgMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "RasterMarker" )
|
||||
sipType = sipType_QgsRasterMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "VectorField" )
|
||||
sipType = sipType_QgsVectorFieldSymbolLayer;
|
||||
else
|
||||
@ -463,6 +465,9 @@ Abstract base class for marker symbol layers.
|
||||
virtual void startRender( QgsSymbolRenderContext &context );
|
||||
|
||||
|
||||
virtual void stopRender( QgsSymbolRenderContext &context );
|
||||
|
||||
|
||||
virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ) = 0;
|
||||
%Docstring
|
||||
Renders a marker at the specified point. Derived classes must implement this to
|
||||
|
@ -417,6 +417,47 @@ Creates a new QgsSvgMarkerSymbolLayerWidget.
|
||||
|
||||
|
||||
|
||||
class QgsRasterMarkerSymbolLayerWidget : QgsSymbolLayerWidget
|
||||
{
|
||||
%Docstring
|
||||
Widget for configuring QgsRasterMarkerSymbolLayer symbol layers.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgssymbollayerwidget.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsRasterMarkerSymbolLayerWidget.
|
||||
|
||||
:param vl: associated vector layer
|
||||
:param parent: parent widget
|
||||
%End
|
||||
|
||||
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) /Factory/;
|
||||
%Docstring
|
||||
Creates a new QgsRasterMarkerSymbolLayerWidget.
|
||||
|
||||
:param vl: associated vector layer
|
||||
%End
|
||||
|
||||
virtual void setSymbolLayer( QgsSymbolLayer *layer );
|
||||
|
||||
virtual QgsSymbolLayer *symbolLayer();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsRasterFillSymbolLayerWidget : QgsSymbolLayerWidget
|
||||
{
|
||||
|
||||
|
@ -132,6 +132,7 @@ QImage QgsImageCache::pathAsImage( const QString &file, const QSize size, const
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( "HITTTTTTTTTTTTTTTTTTTTTTTTT!!!!!!!!!!!!!!" );
|
||||
result = currentEntry->image;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "qgsdxfexport.h"
|
||||
#include "qgsdxfpaintdevice.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsimagecache.h"
|
||||
#include "qgsimageoperation.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgssvgcache.h"
|
||||
@ -2545,6 +2547,377 @@ QRectF QgsSvgMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &c
|
||||
|
||||
//////////
|
||||
|
||||
QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
|
||||
: mPath( path )
|
||||
{
|
||||
mSize = size;
|
||||
mAngle = angle;
|
||||
mOffset = QPointF( 0, 0 );
|
||||
mScaleMethod = scaleMethod;
|
||||
updateDefaultAspectRatio();
|
||||
}
|
||||
|
||||
|
||||
QgsSymbolLayer *QgsRasterMarkerSymbolLayer::create( const QgsStringMap &props )
|
||||
{
|
||||
QString path;
|
||||
double size = DEFAULT_RASTERMARKER_SIZE;
|
||||
double angle = DEFAULT_RASTERMARKER_ANGLE;
|
||||
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
|
||||
|
||||
if ( props.contains( QStringLiteral( "imageFile" ) ) )
|
||||
path = props[QStringLiteral( "imageFile" )];
|
||||
if ( props.contains( QStringLiteral( "size" ) ) )
|
||||
size = props[QStringLiteral( "size" )].toDouble();
|
||||
if ( props.contains( QStringLiteral( "angle" ) ) )
|
||||
angle = props[QStringLiteral( "angle" )].toDouble();
|
||||
if ( props.contains( QStringLiteral( "scale_method" ) ) )
|
||||
scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
|
||||
|
||||
QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
|
||||
|
||||
if ( props.contains( QStringLiteral( "alpha" ) ) )
|
||||
{
|
||||
m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
|
||||
}
|
||||
|
||||
if ( props.contains( QStringLiteral( "size_unit" ) ) )
|
||||
m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
|
||||
if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
|
||||
m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
|
||||
if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
|
||||
m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
|
||||
|
||||
if ( props.contains( QStringLiteral( "offset" ) ) )
|
||||
m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
|
||||
if ( props.contains( QStringLiteral( "offset_unit" ) ) )
|
||||
m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
|
||||
if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
|
||||
m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
|
||||
|
||||
if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
|
||||
{
|
||||
m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
|
||||
}
|
||||
if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
|
||||
{
|
||||
m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
|
||||
}
|
||||
|
||||
m->restoreOldDataDefinedProperties( props );
|
||||
m->updateDefaultAspectRatio();
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayer::resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving )
|
||||
{
|
||||
QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
|
||||
if ( it != properties.end() )
|
||||
{
|
||||
if ( saving )
|
||||
it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
|
||||
else
|
||||
it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
|
||||
{
|
||||
mPath = path;
|
||||
updateDefaultAspectRatio();
|
||||
}
|
||||
|
||||
bool QgsRasterMarkerSymbolLayer::setPreservedAspectRatio( bool par )
|
||||
{
|
||||
bool aPreservedAspectRatio = preservedAspectRatio();
|
||||
if ( aPreservedAspectRatio && !par )
|
||||
{
|
||||
mFixedAspectRatio = mDefaultAspectRatio;
|
||||
}
|
||||
else if ( !aPreservedAspectRatio && par )
|
||||
{
|
||||
mFixedAspectRatio = 0.0;
|
||||
}
|
||||
return preservedAspectRatio();
|
||||
}
|
||||
|
||||
double QgsRasterMarkerSymbolLayer::updateDefaultAspectRatio()
|
||||
{
|
||||
if ( mDefaultAspectRatio == 0.0 )
|
||||
{
|
||||
QSize size = QgsApplication::imageCache()->originalSize( mPath );
|
||||
mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
|
||||
}
|
||||
return mDefaultAspectRatio;
|
||||
}
|
||||
|
||||
QString QgsRasterMarkerSymbolLayer::layerType() const
|
||||
{
|
||||
return QStringLiteral( "RasterMarker" );
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
|
||||
{
|
||||
QPainter *p = context.renderContext().painter();
|
||||
if ( !p )
|
||||
return;
|
||||
|
||||
bool hasDataDefinedSize = false;
|
||||
double scaledSize = calculateSize( context, hasDataDefinedSize );
|
||||
double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
|
||||
bool hasDataDefinedAspectRatio = false;
|
||||
double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
|
||||
double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
|
||||
|
||||
//don't render symbols with size below one or above 10,000 pixels
|
||||
if ( static_cast< int >( width ) < 1 || 10000.0 < width )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p->save();
|
||||
|
||||
QString path = mPath;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
|
||||
{
|
||||
context.setOriginalValueVariable( mPath );
|
||||
path = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath );
|
||||
if ( preservedAspectRatio() && path != mPath )
|
||||
{
|
||||
QSize size = QgsApplication::imageCache()->originalSize( path );
|
||||
if ( !size.isNull() && size.isValid() && size.width() > 0 )
|
||||
{
|
||||
height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPointF outputOffset;
|
||||
double angle = 0.0;
|
||||
calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
|
||||
|
||||
p->translate( point + outputOffset );
|
||||
|
||||
bool rotated = !qgsDoubleNear( angle, 0 );
|
||||
if ( rotated )
|
||||
p->rotate( angle );
|
||||
|
||||
double opacity = mOpacity;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity ) )
|
||||
{
|
||||
context.setOriginalValueVariable( mOpacity );
|
||||
opacity = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyOpacity, context.renderContext().expressionContext(), opacity * 100 ) / 100.0;
|
||||
}
|
||||
opacity *= context.opacity();
|
||||
|
||||
bool cached;
|
||||
QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached );
|
||||
if ( !img.isNull() )
|
||||
{
|
||||
if ( context.selected() )
|
||||
QgsImageOperation::adjustHueSaturation( img, 1.0, context.renderContext().selectionColor(), 1.0 );
|
||||
|
||||
p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
|
||||
}
|
||||
|
||||
p->restore();
|
||||
}
|
||||
|
||||
double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
|
||||
{
|
||||
double scaledSize = mSize;
|
||||
hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
|
||||
|
||||
bool ok = true;
|
||||
if ( hasDataDefinedSize )
|
||||
{
|
||||
context.setOriginalValueVariable( mSize );
|
||||
scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
|
||||
}
|
||||
else
|
||||
{
|
||||
hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
|
||||
if ( hasDataDefinedSize )
|
||||
{
|
||||
context.setOriginalValueVariable( mSize );
|
||||
scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyWidth, context.renderContext().expressionContext(), mSize, &ok );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hasDataDefinedSize && ok )
|
||||
{
|
||||
switch ( mScaleMethod )
|
||||
{
|
||||
case QgsSymbol::ScaleArea:
|
||||
scaledSize = std::sqrt( scaledSize );
|
||||
break;
|
||||
case QgsSymbol::ScaleDiameter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return scaledSize;
|
||||
}
|
||||
|
||||
double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
|
||||
{
|
||||
hasDataDefinedAspectRatio = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight );
|
||||
if ( !hasDataDefinedAspectRatio )
|
||||
return mFixedAspectRatio;
|
||||
|
||||
if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
|
||||
return 0.0;
|
||||
|
||||
double scaledAspectRatio = mDefaultAspectRatio;
|
||||
if ( mFixedAspectRatio > 0.0 )
|
||||
scaledAspectRatio = mFixedAspectRatio;
|
||||
|
||||
double defaultHeight = mSize * scaledAspectRatio;
|
||||
scaledAspectRatio = defaultHeight / scaledSize;
|
||||
|
||||
bool ok = true;
|
||||
double scaledHeight = scaledSize * scaledAspectRatio;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
|
||||
{
|
||||
context.setOriginalValueVariable( defaultHeight );
|
||||
scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
|
||||
}
|
||||
|
||||
if ( hasDataDefinedAspectRatio && ok )
|
||||
{
|
||||
switch ( mScaleMethod )
|
||||
{
|
||||
case QgsSymbol::ScaleArea:
|
||||
scaledHeight = sqrt( scaledHeight );
|
||||
break;
|
||||
case QgsSymbol::ScaleDiameter:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scaledAspectRatio = scaledHeight / scaledSize;
|
||||
|
||||
return scaledAspectRatio;
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
|
||||
{
|
||||
//offset
|
||||
double offsetX = 0;
|
||||
double offsetY = 0;
|
||||
markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
|
||||
offset = QPointF( offsetX, offsetY );
|
||||
|
||||
angle = mAngle + mLineAngle;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
|
||||
{
|
||||
context.setOriginalValueVariable( mAngle );
|
||||
angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
|
||||
}
|
||||
|
||||
bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
|
||||
if ( hasDataDefinedRotation )
|
||||
{
|
||||
const QgsFeature *f = context.feature();
|
||||
if ( f )
|
||||
{
|
||||
if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
|
||||
{
|
||||
const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
|
||||
angle += m2p.mapRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( angle )
|
||||
offset = _rotatedOffset( offset, angle );
|
||||
}
|
||||
|
||||
|
||||
QgsStringMap QgsRasterMarkerSymbolLayer::properties() const
|
||||
{
|
||||
QgsStringMap map;
|
||||
map[QStringLiteral( "imageFile" )] = mPath;
|
||||
map[QStringLiteral( "size" )] = QString::number( mSize );
|
||||
map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
|
||||
map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
|
||||
map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
|
||||
map[QStringLiteral( "angle" )] = QString::number( mAngle );
|
||||
map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
|
||||
map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
|
||||
map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
|
||||
map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
|
||||
map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
|
||||
map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
|
||||
map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
|
||||
return map;
|
||||
}
|
||||
|
||||
QgsRasterMarkerSymbolLayer *QgsRasterMarkerSymbolLayer::clone() const
|
||||
{
|
||||
QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( mPath, mSize, mAngle );
|
||||
m->setFixedAspectRatio( mFixedAspectRatio );
|
||||
m->setOpacity( mOpacity );
|
||||
m->setOffset( mOffset );
|
||||
m->setOffsetUnit( mOffsetUnit );
|
||||
m->setOffsetMapUnitScale( mOffsetMapUnitScale );
|
||||
m->setSizeUnit( mSizeUnit );
|
||||
m->setSizeMapUnitScale( mSizeMapUnitScale );
|
||||
m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
|
||||
m->setVerticalAnchorPoint( mVerticalAnchorPoint );
|
||||
copyDataDefinedProperties( m );
|
||||
copyPaintEffect( m );
|
||||
return m;
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
|
||||
{
|
||||
QgsMarkerSymbolLayer::setMapUnitScale( scale );
|
||||
}
|
||||
|
||||
QgsMapUnitScale QgsRasterMarkerSymbolLayer::mapUnitScale() const
|
||||
{
|
||||
return QgsMarkerSymbolLayer::mapUnitScale();
|
||||
}
|
||||
|
||||
QRectF QgsRasterMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
|
||||
{
|
||||
bool hasDataDefinedSize = false;
|
||||
double scaledSize = calculateSize( context, hasDataDefinedSize );
|
||||
double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
|
||||
bool hasDataDefinedAspectRatio = false;
|
||||
double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
|
||||
double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
|
||||
|
||||
//don't render symbols with size below one or above 10,000 pixels
|
||||
if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
|
||||
{
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
QPointF outputOffset;
|
||||
double angle = 0.0;
|
||||
calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
|
||||
|
||||
QMatrix transform;
|
||||
|
||||
// move to the desired position
|
||||
transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
|
||||
|
||||
if ( !qgsDoubleNear( angle, 0.0 ) )
|
||||
transform.rotate( angle );
|
||||
|
||||
QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
|
||||
-height / 2.0,
|
||||
width,
|
||||
height ) );
|
||||
|
||||
return symbolBounds;
|
||||
}
|
||||
|
||||
//////////
|
||||
|
||||
QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
|
||||
{
|
||||
mFontFamily = fontFamily;
|
||||
|
@ -636,6 +636,150 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer
|
||||
};
|
||||
|
||||
|
||||
//////////
|
||||
|
||||
#define DEFAULT_RASTERMARKER_SIZE 2*DEFAULT_POINT_SIZE
|
||||
#define DEFAULT_RASTERMARKER_ANGLE 0
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsRasterMarkerSymbolLayer
|
||||
* \brief Raster marker symbol layer class.
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
class CORE_EXPORT QgsRasterMarkerSymbolLayer : public QgsMarkerSymbolLayer
|
||||
{
|
||||
public:
|
||||
//! Constructs raster marker symbol layer with picture from given absolute path to a raster image file
|
||||
QgsRasterMarkerSymbolLayer( const QString &path = QString(),
|
||||
double size = DEFAULT_SVGMARKER_SIZE,
|
||||
double angle = DEFAULT_SVGMARKER_ANGLE,
|
||||
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD );
|
||||
|
||||
// static stuff
|
||||
|
||||
/**
|
||||
* Creates a raster marker symbol layer from a string map of properties.
|
||||
* \param properties QgsStringMap properties object
|
||||
*/
|
||||
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Turns relative paths in properties map to absolute when reading and vice versa when writing.
|
||||
* Used internally when reading/writing symbols.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
static void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving );
|
||||
|
||||
// implemented from base classes
|
||||
|
||||
QString layerType() const override;
|
||||
|
||||
void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override;
|
||||
|
||||
QgsStringMap properties() const override;
|
||||
|
||||
QgsRasterMarkerSymbolLayer *clone() const override SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Calculates the marker aspect ratio between width and height.
|
||||
* \param context symbol render context
|
||||
* \param scaledSize size of symbol to render
|
||||
* \param hasDataDefinedAspectRatio will be set to true if marker has data defined aspectRatio
|
||||
*/
|
||||
double calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const;
|
||||
|
||||
/**
|
||||
* Returns the marker raster image path.
|
||||
* \see setPath()
|
||||
*/
|
||||
QString path() const { return mPath; }
|
||||
|
||||
/**
|
||||
* Set the marker raster image path.
|
||||
* \param path raster image path
|
||||
* \see path()
|
||||
*/
|
||||
void setPath( const QString &path );
|
||||
|
||||
/**
|
||||
* Returns the marker opacity.
|
||||
* \returns opacity value between 0 (fully transparent) and 1 (fully opaque)
|
||||
* \see setOpacity()
|
||||
*/
|
||||
double opacity() const { return mOpacity; }
|
||||
|
||||
/**
|
||||
* Set the marker opacity.
|
||||
* \param opacity opacity value between 0 (fully transparent) and 1 (fully opaque)
|
||||
* \see opacity()
|
||||
*/
|
||||
void setOpacity( double opacity ) { mOpacity = opacity; }
|
||||
|
||||
/**
|
||||
* Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
|
||||
* \see updateDefaultAspectRatio()
|
||||
*/
|
||||
double defaultAspectRatio() const { return mDefaultAspectRatio; }
|
||||
|
||||
/**
|
||||
* Calculates the default marker aspect ratio between width and height.
|
||||
* \returns the default aspect ratio value
|
||||
* \see defaultAspectRatio()
|
||||
*/
|
||||
double updateDefaultAspectRatio();
|
||||
|
||||
/**
|
||||
* Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
|
||||
* \see setPreservedAspectRatio()
|
||||
*/
|
||||
bool preservedAspectRatio() const { return mFixedAspectRatio <= 0.0; }
|
||||
|
||||
/**
|
||||
* Set preserved the marker aspect ratio between width and height.
|
||||
* \param par Preserved Aspect Ratio
|
||||
* \returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0
|
||||
* \see preservedAspectRatio()
|
||||
*/
|
||||
bool setPreservedAspectRatio( bool par );
|
||||
|
||||
/**
|
||||
* Returns the marker aspect ratio between width and height to be used in rendering,
|
||||
* if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
|
||||
* \see setFixedAspectRatio() QgsSvgCache
|
||||
*/
|
||||
double fixedAspectRatio() const { return mFixedAspectRatio; }
|
||||
|
||||
/**
|
||||
* Set the marker aspect ratio between width and height to be used in rendering,
|
||||
* if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
|
||||
* \param ratio Fixed Aspect Ratio
|
||||
* \see fixedAspectRatio() QgsSvgCache
|
||||
*/
|
||||
void setFixedAspectRatio( double ratio ) { mFixedAspectRatio = ratio; }
|
||||
|
||||
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
|
||||
QgsMapUnitScale mapUnitScale() const override;
|
||||
|
||||
QRectF bounds( QPointF point, QgsSymbolRenderContext &context ) override;
|
||||
|
||||
protected:
|
||||
|
||||
QString mPath;
|
||||
//! The marker default opacity
|
||||
double mOpacity = 1.0;
|
||||
//! The marker default aspect ratio
|
||||
double mDefaultAspectRatio = 0.0;
|
||||
//! The marker fixed aspect ratio
|
||||
double mFixedAspectRatio = 0.0;
|
||||
|
||||
private:
|
||||
double calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const;
|
||||
void calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
//////////
|
||||
|
||||
#define POINT2MM(x) ( (x) * 25.4 / 72 ) // point is 1/72 of inch
|
||||
|
@ -411,6 +411,11 @@ void QgsMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
|
||||
Q_UNUSED( context );
|
||||
}
|
||||
|
||||
void QgsMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
}
|
||||
|
||||
void QgsMarkerSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
|
||||
{
|
||||
startRender( context );
|
||||
|
@ -70,6 +70,8 @@ class CORE_EXPORT QgsSymbolLayer
|
||||
sipType = sipType_QgsFilledMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "SvgMarker" )
|
||||
sipType = sipType_QgsSvgMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "RasterMarker" )
|
||||
sipType = sipType_QgsRasterMarkerSymbolLayer;
|
||||
else if ( sipCpp->layerType() == "VectorField" )
|
||||
sipType = sipType_QgsVectorFieldSymbolLayer;
|
||||
else
|
||||
@ -476,6 +478,8 @@ class CORE_EXPORT QgsMarkerSymbolLayer : public QgsSymbolLayer
|
||||
|
||||
void startRender( QgsSymbolRenderContext &context ) override;
|
||||
|
||||
void stopRender( QgsSymbolRenderContext &context ) override;
|
||||
|
||||
/**
|
||||
* Renders a marker at the specified point. Derived classes must implement this to
|
||||
* handle drawing the point.
|
||||
|
@ -38,6 +38,8 @@ QgsSymbolLayerRegistry::QgsSymbolLayerRegistry()
|
||||
QgsFilledMarkerSymbolLayer::create ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "SvgMarker" ), QObject::tr( "SVG marker" ), QgsSymbol::Marker,
|
||||
QgsSvgMarkerSymbolLayer::create, QgsSvgMarkerSymbolLayer::createFromSld, QgsSvgMarkerSymbolLayer::resolvePaths ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "RasterMarker" ), QObject::tr( "Raster image marker" ), QgsSymbol::Marker,
|
||||
QgsRasterMarkerSymbolLayer::create, nullptr, QgsRasterFillSymbolLayer::resolvePaths ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "FontMarker" ), QObject::tr( "Font marker" ), QgsSymbol::Marker,
|
||||
QgsFontMarkerSymbolLayer::create, QgsFontMarkerSymbolLayer::createFromSld ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "EllipseMarker" ), QObject::tr( "Ellipse marker" ), QgsSymbol::Marker,
|
||||
|
@ -70,6 +70,7 @@ static void _initWidgetFunctions()
|
||||
_initWidgetFunction( QStringLiteral( "SimpleMarker" ), QgsSimpleMarkerSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "FilledMarker" ), QgsFilledMarkerSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "SvgMarker" ), QgsSvgMarkerSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "RasterMarker" ), QgsRasterMarkerSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "FontMarker" ), QgsFontMarkerSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "EllipseMarker" ), QgsEllipseSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "VectorField" ), QgsVectorFieldSymbolLayerWidget::create );
|
||||
|
@ -3072,6 +3072,324 @@ void QgsCentroidFillSymbolLayerWidget::mDrawAllPartsCheckBox_stateChanged( int s
|
||||
emit changed();
|
||||
}
|
||||
|
||||
///////////
|
||||
|
||||
QgsRasterMarkerSymbolLayerWidget::QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )
|
||||
: QgsSymbolLayerWidget( parent, vl )
|
||||
{
|
||||
mLayer = nullptr;
|
||||
|
||||
setupUi( this );
|
||||
|
||||
connect( mBrowseToolButton, &QToolButton::clicked, this, &QgsRasterMarkerSymbolLayerWidget::mBrowseToolButton_clicked );
|
||||
connect( mImageLineEdit, &QLineEdit::editingFinished, this, &QgsRasterMarkerSymbolLayerWidget::mImageLineEdit_editingFinished );
|
||||
connect( mOffsetUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsRasterMarkerSymbolLayerWidget::mOffsetUnitWidget_changed );
|
||||
connect( mRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setAngle );
|
||||
connect( mSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsRasterMarkerSymbolLayerWidget::mSizeUnitWidget_changed );
|
||||
connect( mWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setWidth );
|
||||
connect( mHeightSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setHeight );
|
||||
connect( mLockAspectRatio, static_cast < void ( QgsRatioLockButton::* )( bool ) > ( &QgsRatioLockButton::lockChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setLockAspectRatio );
|
||||
|
||||
mSizeUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderPixels << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits
|
||||
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
|
||||
mOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
|
||||
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
|
||||
|
||||
mSpinOffsetX->setClearValue( 0.0 );
|
||||
mSpinOffsetY->setClearValue( 0.0 );
|
||||
mRotationSpinBox->setClearValue( 0.0 );
|
||||
|
||||
connect( mSpinOffsetX, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setOffset );
|
||||
connect( mSpinOffsetY, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setOffset );
|
||||
connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsRasterMarkerSymbolLayerWidget::setOpacity );
|
||||
|
||||
connect( mHorizontalAnchorComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMarkerSymbolLayerWidget::mHorizontalAnchorComboBox_currentIndexChanged );
|
||||
connect( mVerticalAnchorComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMarkerSymbolLayerWidget::mVerticalAnchorComboBox_currentIndexChanged );
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
|
||||
{
|
||||
if ( !layer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( layer->layerType() != QLatin1String( "RasterMarker" ) )
|
||||
return;
|
||||
|
||||
// layer type is correct, we can do the cast
|
||||
mLayer = static_cast<QgsRasterMarkerSymbolLayer *>( layer );
|
||||
|
||||
// set values
|
||||
whileBlocking( mImageLineEdit )->setText( mLayer->path() );
|
||||
|
||||
whileBlocking( mWidthSpinBox )->setValue( mLayer->size() );
|
||||
bool preservedAspectRatio = mLayer->preservedAspectRatio();
|
||||
mHeightSpinBox->blockSignals( true );
|
||||
if ( preservedAspectRatio )
|
||||
{
|
||||
mHeightSpinBox->setValue( mLayer->size() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mHeightSpinBox->setValue( mLayer->size() * mLayer->fixedAspectRatio() );
|
||||
}
|
||||
mHeightSpinBox->setEnabled( mLayer->defaultAspectRatio() > 0.0 );
|
||||
mHeightSpinBox->blockSignals( false );
|
||||
whileBlocking( mLockAspectRatio )->setLocked( preservedAspectRatio );
|
||||
|
||||
whileBlocking( mRotationSpinBox )->setValue( mLayer->angle() );
|
||||
whileBlocking( mOpacityWidget )->setOpacity( mLayer->opacity() );
|
||||
|
||||
whileBlocking( mSpinOffsetX )->setValue( mLayer->offset().x() );
|
||||
whileBlocking( mSpinOffsetY )->setValue( mLayer->offset().y() );
|
||||
|
||||
mSizeUnitWidget->blockSignals( true );
|
||||
mSizeUnitWidget->setUnit( mLayer->sizeUnit() );
|
||||
mSizeUnitWidget->setMapUnitScale( mLayer->sizeMapUnitScale() );
|
||||
mSizeUnitWidget->blockSignals( false );
|
||||
mOffsetUnitWidget->blockSignals( true );
|
||||
mOffsetUnitWidget->setUnit( mLayer->offsetUnit() );
|
||||
mOffsetUnitWidget->setMapUnitScale( mLayer->offsetMapUnitScale() );
|
||||
mOffsetUnitWidget->blockSignals( false );
|
||||
|
||||
//anchor points
|
||||
whileBlocking( mHorizontalAnchorComboBox )->setCurrentIndex( mLayer->horizontalAnchorPoint() );
|
||||
whileBlocking( mVerticalAnchorComboBox )->setCurrentIndex( mLayer->verticalAnchorPoint() );
|
||||
|
||||
registerDataDefinedButton( mWidthDDBtn, QgsSymbolLayer::PropertyWidth );
|
||||
registerDataDefinedButton( mHeightDDBtn, QgsSymbolLayer::PropertyHeight );
|
||||
registerDataDefinedButton( mRotationDDBtn, QgsSymbolLayer::PropertyAngle );
|
||||
registerDataDefinedButton( mOpacityDDBtn, QgsSymbolLayer::PropertyOpacity );
|
||||
registerDataDefinedButton( mOffsetDDBtn, QgsSymbolLayer::PropertyOffset );
|
||||
registerDataDefinedButton( mFilenameDDBtn, QgsSymbolLayer::PropertyName );
|
||||
registerDataDefinedButton( mHorizontalAnchorDDBtn, QgsSymbolLayer::PropertyHorizontalAnchor );
|
||||
registerDataDefinedButton( mVerticalAnchorDDBtn, QgsSymbolLayer::PropertyVerticalAnchor );
|
||||
|
||||
updatePreviewImage();
|
||||
}
|
||||
|
||||
QgsSymbolLayer *QgsRasterMarkerSymbolLayerWidget::symbolLayer()
|
||||
{
|
||||
return mLayer;
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mBrowseToolButton_clicked()
|
||||
{
|
||||
QgsSettings s;
|
||||
QString openDir;
|
||||
QString lineEditText = mImageLineEdit->text();
|
||||
if ( !lineEditText.isEmpty() )
|
||||
{
|
||||
QFileInfo openDirFileInfo( lineEditText );
|
||||
openDir = openDirFileInfo.path();
|
||||
}
|
||||
|
||||
if ( openDir.isEmpty() )
|
||||
{
|
||||
openDir = s.value( QStringLiteral( "/UI/lastRasterMarkerImageDir" ), QDir::homePath() ).toString();
|
||||
}
|
||||
|
||||
//show file dialog
|
||||
QString filePath = QFileDialog::getOpenFileName( nullptr, tr( "Select Image File" ), openDir );
|
||||
if ( !filePath.isNull() )
|
||||
{
|
||||
//check if file exists
|
||||
QFileInfo fileInfo( filePath );
|
||||
if ( !fileInfo.exists() || !fileInfo.isReadable() )
|
||||
{
|
||||
QMessageBox::critical( nullptr, QStringLiteral( "Select Image File" ), QStringLiteral( "Error, file does not exist or is not readable." ) );
|
||||
return;
|
||||
}
|
||||
|
||||
s.setValue( QStringLiteral( "/UI/lastRasterMarkerImageDir" ), fileInfo.absolutePath() );
|
||||
mImageLineEdit->setText( filePath );
|
||||
mImageLineEdit_editingFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mImageLineEdit_editingFinished()
|
||||
{
|
||||
QFileInfo fi( mImageLineEdit->text() );
|
||||
if ( !fi.exists() )
|
||||
{
|
||||
QUrl url( mImageLineEdit->text() );
|
||||
if ( !url.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
|
||||
mLayer->setPath( mImageLineEdit->text() );
|
||||
updatePreviewImage();
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::updatePreviewImage()
|
||||
{
|
||||
QImage image( mLayer->path() );
|
||||
if ( image.isNull() )
|
||||
{
|
||||
mLabelImagePreview->setPixmap( QPixmap() );
|
||||
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->opacity() < 1.0 )
|
||||
{
|
||||
p.setOpacity( mLayer->opacity() );
|
||||
}
|
||||
|
||||
p.drawImage( imageRect.left(), imageRect.top(), image );
|
||||
p.end();
|
||||
mLabelImagePreview->setPixmap( QPixmap::fromImage( previewImage ) );
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setWidth()
|
||||
{
|
||||
double defaultAspectRatio = mLayer->defaultAspectRatio();
|
||||
double fixedAspectRatio = 0.0;
|
||||
mHeightSpinBox->blockSignals( true );
|
||||
if ( defaultAspectRatio <= 0.0 )
|
||||
{
|
||||
mHeightSpinBox->setValue( mWidthSpinBox->value() );
|
||||
}
|
||||
else if ( mLockAspectRatio->locked() )
|
||||
{
|
||||
mHeightSpinBox->setValue( mWidthSpinBox->value() * defaultAspectRatio );
|
||||
}
|
||||
else
|
||||
{
|
||||
fixedAspectRatio = mHeightSpinBox->value() / mWidthSpinBox->value();
|
||||
}
|
||||
mHeightSpinBox->blockSignals( false );
|
||||
mLayer->setSize( mWidthSpinBox->value() );
|
||||
mLayer->setFixedAspectRatio( fixedAspectRatio );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setHeight()
|
||||
{
|
||||
double defaultAspectRatio = mLayer->defaultAspectRatio();
|
||||
double fixedAspectRatio = 0.0;
|
||||
mWidthSpinBox->blockSignals( true );
|
||||
if ( defaultAspectRatio <= 0.0 )
|
||||
{
|
||||
mWidthSpinBox->setValue( mHeightSpinBox->value() );
|
||||
}
|
||||
else if ( mLockAspectRatio->locked() )
|
||||
{
|
||||
mWidthSpinBox->setValue( mHeightSpinBox->value() / defaultAspectRatio );
|
||||
}
|
||||
else
|
||||
{
|
||||
fixedAspectRatio = mHeightSpinBox->value() / mWidthSpinBox->value();
|
||||
}
|
||||
mWidthSpinBox->blockSignals( false );
|
||||
mLayer->setSize( mWidthSpinBox->value() );
|
||||
mLayer->setFixedAspectRatio( fixedAspectRatio );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setLockAspectRatio( const bool locked )
|
||||
{
|
||||
double defaultAspectRatio = mLayer->defaultAspectRatio();
|
||||
if ( defaultAspectRatio <= 0.0 )
|
||||
{
|
||||
whileBlocking( mLockAspectRatio )->setLocked( true );
|
||||
}
|
||||
else if ( locked )
|
||||
{
|
||||
mLayer->setFixedAspectRatio( 0.0 );
|
||||
setWidth();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLayer->setFixedAspectRatio( mHeightSpinBox->value() / mWidthSpinBox->value() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setAngle()
|
||||
{
|
||||
mLayer->setAngle( mRotationSpinBox->value() );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setOpacity( double value )
|
||||
{
|
||||
mLayer->setOpacity( value );
|
||||
emit changed();
|
||||
updatePreviewImage();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::setOffset()
|
||||
{
|
||||
mLayer->setOffset( QPointF( mSpinOffsetX->value(), mSpinOffsetY->value() ) );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mSizeUnitWidget_changed()
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setSizeUnit( mSizeUnitWidget->unit() );
|
||||
mLayer->setSizeMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mOffsetUnitWidget_changed()
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setOffsetUnit( mOffsetUnitWidget->unit() );
|
||||
mLayer->setOffsetMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mHorizontalAnchorComboBox_currentIndexChanged( int index )
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( index ) );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRasterMarkerSymbolLayerWidget::mVerticalAnchorComboBox_currentIndexChanged( int index )
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( index ) );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
||||
QgsRasterFillSymbolLayerWidget::QgsRasterFillSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )
|
||||
|
@ -571,6 +571,62 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, pr
|
||||
|
||||
///////////
|
||||
|
||||
#include "ui_widget_rastermarker.h"
|
||||
|
||||
class QgsRasterMarkerSymbolLayer;
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
* \class QgsRasterMarkerSymbolLayerWidget
|
||||
* \brief Widget for configuring QgsRasterMarkerSymbolLayer symbol layers.
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
class GUI_EXPORT QgsRasterMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, private Ui::WidgetRasterMarker
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsRasterMarkerSymbolLayerWidget.
|
||||
* \param vl associated vector layer
|
||||
* \param parent parent widget
|
||||
*/
|
||||
QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Creates a new QgsRasterMarkerSymbolLayerWidget.
|
||||
* \param vl associated vector layer
|
||||
*/
|
||||
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) SIP_FACTORY { return new QgsRasterMarkerSymbolLayerWidget( vl ); }
|
||||
|
||||
// from base class
|
||||
void setSymbolLayer( QgsSymbolLayer *layer ) override;
|
||||
QgsSymbolLayer *symbolLayer() override;
|
||||
|
||||
protected:
|
||||
|
||||
QgsRasterMarkerSymbolLayer *mLayer = nullptr;
|
||||
|
||||
private slots:
|
||||
void mBrowseToolButton_clicked();
|
||||
void mImageLineEdit_editingFinished();
|
||||
void mSizeUnitWidget_changed();
|
||||
void mOffsetUnitWidget_changed();
|
||||
void mHorizontalAnchorComboBox_currentIndexChanged( int index );
|
||||
void mVerticalAnchorComboBox_currentIndexChanged( int index );
|
||||
void setWidth();
|
||||
void setHeight();
|
||||
void setLockAspectRatio( bool locked );
|
||||
void setAngle();
|
||||
void setOffset();
|
||||
void setOpacity( double value );
|
||||
void updatePreviewImage();
|
||||
|
||||
};
|
||||
|
||||
///////////
|
||||
|
||||
#include "ui_widget_rasterfill.h"
|
||||
|
||||
class QgsRasterFillSymbolLayer;
|
||||
|
508
src/ui/symbollayer/widget_rastermarker.ui
Normal file
508
src/ui/symbollayer/widget_rastermarker.ui
Normal file
@ -0,0 +1,508 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WidgetRasterMarker</class>
|
||||
<widget class="QWidget" name="WidgetRasterMarker">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>338</width>
|
||||
<height>419</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="4" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mRotationDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<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 row="2" column="3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QgsPropertyOverrideButton" name="mWidthDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsPropertyOverrideButton" name="mHeightDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>x</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QgsDoubleSpinBox" 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>-99999999.989999994635582</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99999999.989999994635582</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mOffsetUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>y</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsDoubleSpinBox" 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>-99999999.989999994635582</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99999999.989999994635582</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOffsetDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="mTextureWidthLabel">
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>mSizeSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="4">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<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>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QgsDoubleSpinBox" name="mRotationSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="wrapping">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> °</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-360.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>360.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>mSpinOffsetX</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QgsDoubleSpinBox" name="mWidthSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>100000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="showClearButton" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsDoubleSpinBox" name="mHeightSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>100000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="showClearButton" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QgsRatioLockButton" name="mLockAspectRatio" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Lock aspect ratio</string>
|
||||
</property>
|
||||
<property name="leftMargin" stdset="0">
|
||||
<number>13</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="3" rowspan="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mSizeUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="mRotationLabel">
|
||||
<property name="text">
|
||||
<string>Rotation</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>mRotationSpinBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mFilenameDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Opacity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QgsOpacityWidget" name="mOpacityWidget" native="true">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOpacityDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="mAnchorPointLabel">
|
||||
<property name="text">
|
||||
<string>Anchor point</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="2">
|
||||
<widget class="QComboBox" name="mVerticalAnchorComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Top</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VCenter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bottom</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mVerticalAnchorDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="2">
|
||||
<widget class="QComboBox" name="mHorizontalAnchorComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Left</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>HCenter</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Right</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mHorizontalAnchorDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsPropertyOverrideButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>qgspropertyoverridebutton.h</header>
|
||||
</customwidget>
|
||||
<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>
|
||||
<customwidget>
|
||||
<class>QgsOpacityWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsopacitywidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsRatioLockButton</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsratiolockbutton.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mImageLineEdit</tabstop>
|
||||
<tabstop>mBrowseToolButton</tabstop>
|
||||
<tabstop>mFilenameDDBtn</tabstop>
|
||||
<tabstop>mWidgthSpinBox</tabstop>
|
||||
<tabstop>mHeightSpinBox</tabstop>
|
||||
<tabstop>mSizeUnitWidget</tabstop>
|
||||
<tabstop>mWidthDDBtn</tabstop>
|
||||
<tabstop>mHeightDDBtn</tabstop>
|
||||
<tabstop>cboCoordinateMode</tabstop>
|
||||
<tabstop>mOpacityWidget</tabstop>
|
||||
<tabstop>mOpacityDDBtn</tabstop>
|
||||
<tabstop>mRotationSpinBox</tabstop>
|
||||
<tabstop>mRotationDDBtn</tabstop>
|
||||
<tabstop>mSpinOffsetX</tabstop>
|
||||
<tabstop>mSpinOffsetY</tabstop>
|
||||
<tabstop>mOffsetUnitWidget</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -175,6 +175,7 @@ SET(TESTS
|
||||
testqgis.cpp
|
||||
testqgsrasterfilewriter.cpp
|
||||
testqgsrasterfill.cpp
|
||||
testqgsrastermarker.cpp
|
||||
testqgsrasteriterator.cpp
|
||||
testqgsrasterblock.cpp
|
||||
testqgsrasterlayer.cpp
|
||||
|
199
tests/src/core/testqgsrastermarker.cpp
Normal file
199
tests/src/core/testqgsrastermarker.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
/***************************************************************************
|
||||
testqgsrastermarker.cpp
|
||||
---------------------
|
||||
Date : December 2018
|
||||
Copyright : (C) 2014 Mathieu Pellerin
|
||||
Email : nirvn dot asia 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 "qgstest.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <qgsmapsettings.h>
|
||||
#include <qgsmaplayer.h>
|
||||
#include <qgsvectorlayer.h>
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsproviderregistry.h>
|
||||
#include <qgsproject.h>
|
||||
#include <qgssymbol.h>
|
||||
#include <qgssinglesymbolrenderer.h>
|
||||
#include <qgsmarkersymbollayer.h>
|
||||
|
||||
#include "qgsmultirenderchecker.h"
|
||||
|
||||
/**
|
||||
* \ingroup UnitTests
|
||||
* This is a unit test for raster marker types.
|
||||
*/
|
||||
class TestQgsRasterMarker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestQgsRasterMarker() = default;
|
||||
|
||||
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 rasterMarkerSymbol();
|
||||
void anchor();
|
||||
void alpha();
|
||||
void rotation();
|
||||
void fixedAspectRatio();
|
||||
|
||||
private:
|
||||
bool mTestHasError = false ;
|
||||
bool imageCheck( const QString &type );
|
||||
|
||||
QgsMapSettings mMapSettings;
|
||||
QgsVectorLayer *mPointLayer = nullptr;
|
||||
QgsRasterMarkerSymbolLayer *mRasterMarker = nullptr;
|
||||
QgsMarkerSymbol *mMarkerSymbol = nullptr;
|
||||
QgsSingleSymbolRenderer *mSymbolRenderer = nullptr;
|
||||
QString mTestDataDir;
|
||||
QString mReport;
|
||||
};
|
||||
|
||||
|
||||
void TestQgsRasterMarker::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 + '/';
|
||||
|
||||
//create a marker layer that will be used in all tests
|
||||
QString pointFileName = mTestDataDir + "points.shp";
|
||||
QFileInfo pointFileInfo( pointFileName );
|
||||
mPointLayer = new QgsVectorLayer( pointFileInfo.filePath(),
|
||||
pointFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
|
||||
|
||||
// Register the layer with the registry
|
||||
QgsProject::instance()->addMapLayers(
|
||||
QList<QgsMapLayer *>() << mPointLayer );
|
||||
|
||||
//setup the raster marker symbol
|
||||
mRasterMarker = new QgsRasterMarkerSymbolLayer();
|
||||
mMarkerSymbol = new QgsMarkerSymbol();
|
||||
mMarkerSymbol->changeSymbolLayer( 0, mRasterMarker );
|
||||
mSymbolRenderer = new QgsSingleSymbolRenderer( mMarkerSymbol );
|
||||
mPointLayer->setRenderer( mSymbolRenderer );
|
||||
|
||||
// We only need maprender instead of mapcanvas
|
||||
// since maprender does not require a qui
|
||||
// and is more light weight
|
||||
mMapSettings.setLayers( QList<QgsMapLayer *>() << mPointLayer );
|
||||
mReport += QLatin1String( "<h1>Raster Marker Renderer Tests</h1>\n" );
|
||||
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::cleanupTestCase()
|
||||
{
|
||||
QString myReportFile = QDir::tempPath() + "/qgistest.html";
|
||||
QFile myFile( myReportFile );
|
||||
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
|
||||
{
|
||||
QTextStream myQTextStream( &myFile );
|
||||
myQTextStream << mReport;
|
||||
myFile.close();
|
||||
}
|
||||
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::init()
|
||||
{
|
||||
mRasterMarker->setPath( mTestDataDir + QStringLiteral( "sample_image.png" ) );
|
||||
mRasterMarker->setSize( 30.0 );
|
||||
mRasterMarker->setSizeUnit( QgsUnitTypes::RenderPixels );
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::rasterMarkerSymbol()
|
||||
{
|
||||
mReport += QLatin1String( "<h2>Raster marker symbol renderer test</h2>\n" );
|
||||
bool result = imageCheck( QStringLiteral( "rastermarker" ) );
|
||||
QVERIFY( result );
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::anchor()
|
||||
{
|
||||
mRasterMarker->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( 2 ) );
|
||||
mRasterMarker->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( 2 ) );
|
||||
bool result = imageCheck( QStringLiteral( "rastermarker_anchor" ) );
|
||||
QVERIFY( result );
|
||||
mRasterMarker->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( 1 ) );
|
||||
mRasterMarker->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( 1 ) );
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::alpha()
|
||||
{
|
||||
mReport += QLatin1String( "<h2>Raster marker alpha</h2>\n" );
|
||||
mRasterMarker->setOpacity( 0.5 );
|
||||
bool result = imageCheck( QStringLiteral( "rastermarker_alpha" ) );
|
||||
QVERIFY( result );
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::rotation()
|
||||
{
|
||||
mReport += QLatin1String( "<h2>Raster marker rotation</h2>\n" );
|
||||
mRasterMarker->setAngle( 45.0 );
|
||||
bool result = imageCheck( QStringLiteral( "rastermarker_rotation" ) );
|
||||
QVERIFY( result );
|
||||
}
|
||||
|
||||
void TestQgsRasterMarker::fixedAspectRatio()
|
||||
{
|
||||
mReport += QLatin1String( "<h2>Raster marker fixed aspect ratio</h2>\n" );
|
||||
mRasterMarker->setFixedAspectRatio( 0.2 );
|
||||
bool result = imageCheck( QStringLiteral( "rastermarker_fixedaspectratio" ) );
|
||||
QVERIFY( result );
|
||||
}
|
||||
|
||||
//
|
||||
// Private helper functions not called directly by CTest
|
||||
//
|
||||
|
||||
bool TestQgsRasterMarker::imageCheck( const QString &testType )
|
||||
{
|
||||
//use the QgsRenderChecker test utility class to
|
||||
//ensure the rendered output matches our control image
|
||||
mMapSettings.setExtent( mPointLayer->extent() );
|
||||
mMapSettings.setOutputDpi( 96 );
|
||||
QgsMultiRenderChecker myChecker;
|
||||
myChecker.setControlPathPrefix( QStringLiteral( "symbol_rastermarker" ) );
|
||||
myChecker.setControlName( "expected_" + testType );
|
||||
myChecker.setMapSettings( mMapSettings );
|
||||
myChecker.setColorTolerance( 20 );
|
||||
bool myResultFlag = myChecker.runTest( testType, 500 );
|
||||
mReport += myChecker.report();
|
||||
return myResultFlag;
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsRasterMarker )
|
||||
#include "testqgsrastermarker.moc"
|
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Binary file not shown.
After Width: | Height: | Size: 626 KiB |
Loading…
x
Reference in New Issue
Block a user