diff --git a/python/core/auto_generated/symbology/qgsgeometrygeneratorsymbollayer.sip.in b/python/core/auto_generated/symbology/qgsgeometrygeneratorsymbollayer.sip.in index 8fcbfa1efc78..9de6e8960e58 100644 --- a/python/core/auto_generated/symbology/qgsgeometrygeneratorsymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgsgeometrygeneratorsymbollayer.sip.in @@ -70,6 +70,30 @@ Set the expression to generate this geometry. QString geometryExpression() const; %Docstring Gets the expression to generate this geometry. +%End + + QgsUnitTypes::RenderUnit units() const; +%Docstring +Returns the unit for the geometry expression. + +By default this is :py:class:`QgsUnitTypes`.MapUnits, which means that the :py:func:`~QgsGeometryGeneratorSymbolLayer.geometryExpression` +will return geometries in the associated layer's CRS. + +.. seealso:: :py:func:`setUnits` + +.. versionadded:: 3.22 +%End + + void setUnits( QgsUnitTypes::RenderUnit units ); +%Docstring +Sets the ``units`` for the geometry expression. + +By default this is :py:class:`QgsUnitTypes`.MapUnits, which means that the :py:func:`~QgsGeometryGeneratorSymbolLayer.geometryExpression` +will return geometries in the associated layer's CRS. + +.. seealso:: :py:func:`units` + +.. versionadded:: 3.22 %End virtual QgsSymbol *subSymbol(); @@ -105,6 +129,7 @@ context which is available to the evaluated expression. virtual void setColor( const QColor &color ); + private: QgsGeometryGeneratorSymbolLayer( const QgsGeometryGeneratorSymbolLayer © ); }; diff --git a/python/gui/auto_generated/qgsunitselectionwidget.sip.in b/python/gui/auto_generated/qgsunitselectionwidget.sip.in index 2ef85c3591c6..991b7beaa05f 100644 --- a/python/gui/auto_generated/qgsunitselectionwidget.sip.in +++ b/python/gui/auto_generated/qgsunitselectionwidget.sip.in @@ -222,6 +222,24 @@ map scale from the canvas. :param canvas: map canvas .. versionadded:: 2.12 +%End + + bool showMapScaleButton() const; +%Docstring +Returns ``True`` if the widget can show the map scale button when the Map Units option is selected. + +.. seealso:: :py:func:`setShowMapScaleButton` + +.. versionadded:: 3.22 +%End + + void setShowMapScaleButton( bool show ); +%Docstring +Sets whether the widget can show the map scale button when the Map Units option is selected. + +.. seealso:: :py:func:`showMapScaleButton` + +.. versionadded:: 3.22 %End signals: diff --git a/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp b/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp index bd42d7d62095..f2d6eefef066 100644 --- a/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp +++ b/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp @@ -42,6 +42,8 @@ QgsSymbolLayer *QgsGeometryGeneratorSymbolLayer::create( const QVariantMap &prop { symbolLayer->setSubSymbol( QgsFillSymbol::createSimple( properties ) ); } + symbolLayer->setUnits( QgsUnitTypes::decodeRenderUnit( properties.value( QStringLiteral( "units" ), QStringLiteral( "mapunits" ) ).toString() ) ); + symbolLayer->restoreOldDataDefinedProperties( properties ); return symbolLayer; @@ -133,6 +135,7 @@ QgsSymbolLayer *QgsGeometryGeneratorSymbolLayer::clone() const clone->mMarkerSymbol.reset( mMarkerSymbol->clone() ); clone->setSymbolType( mSymbolType ); + clone->setUnits( mUnits ); copyDataDefinedProperties( clone ); copyPaintEffect( clone ); @@ -156,6 +159,8 @@ QVariantMap QgsGeometryGeneratorSymbolLayer::properties() const props.insert( QStringLiteral( "SymbolType" ), QStringLiteral( "Fill" ) ); break; } + props.insert( QStringLiteral( "units" ), QgsUnitTypes::encodeUnit( mUnits ) ); + return props; } @@ -222,11 +227,55 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context ) if ( context.feature() ) { - const QgsExpressionContext &expressionContext = context.renderContext().expressionContext(); + QgsExpressionContext &expressionContext = context.renderContext().expressionContext(); QgsFeature f = expressionContext.feature(); - const QgsGeometry geom = mExpression->evaluate( &expressionContext ).value(); - f.setGeometry( geom ); + + switch ( mUnits ) + { + case QgsUnitTypes::RenderMapUnits: + case QgsUnitTypes::RenderUnknownUnit: // unsupported, not exposed as an option + case QgsUnitTypes::RenderMetersInMapUnits: // unsupported, not exposed as an option + case QgsUnitTypes::RenderPercentage: // unsupported, not exposed as an option + { + QgsGeometry geom = mExpression->evaluate( &expressionContext ).value(); + f.setGeometry( geom ); + break; + } + + case QgsUnitTypes::RenderMillimeters: + case QgsUnitTypes::RenderPixels: + case QgsUnitTypes::RenderPoints: + case QgsUnitTypes::RenderInches: + { + QgsExpressionContextScope *generatorScope = new QgsExpressionContextScope(); + expressionContext.appendScope( generatorScope ); + + QgsGeometry transformed = f.geometry(); + transformed.transform( context.renderContext().coordinateTransform() ); + QTransform mapToPixel = context.renderContext().mapToPixel().transform(); + + // scale transform to target units + const double scale = 1 / context.renderContext().convertToPainterUnits( 1, mUnits ); + mapToPixel.scale( scale, scale ); + + if ( mExpression->referencedVariables().contains( QStringLiteral( "map_geometry" ) ) ) + { + transformed.transform( mapToPixel ); + generatorScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "map_geometry" ), transformed ) ); + } + + QgsGeometry geom = mExpression->evaluate( &expressionContext ).value(); + + geom.transform( mapToPixel.inverted() ); + geom.transform( context.renderContext().coordinateTransform(), QgsCoordinateTransform::ReverseTransform ); + + f.setGeometry( geom ); + + delete expressionContext.popScope(); + break; + } + } QgsExpressionContextScope *subSymbolExpressionContextScope = mSymbol->symbolRenderContext()->expressionContextScope(); diff --git a/src/core/symbology/qgsgeometrygeneratorsymbollayer.h b/src/core/symbology/qgsgeometrygeneratorsymbollayer.h index 80f4e67932a4..5cc3174999ac 100644 --- a/src/core/symbology/qgsgeometrygeneratorsymbollayer.h +++ b/src/core/symbology/qgsgeometrygeneratorsymbollayer.h @@ -76,6 +76,28 @@ class CORE_EXPORT QgsGeometryGeneratorSymbolLayer : public QgsSymbolLayer */ QString geometryExpression() const { return mExpression->expression(); } + /** + * Returns the unit for the geometry expression. + * + * By default this is QgsUnitTypes::MapUnits, which means that the geometryExpression() + * will return geometries in the associated layer's CRS. + * + * \see setUnits() + * \since QGIS 3.22 + */ + QgsUnitTypes::RenderUnit units() const { return mUnits; } + + /** + * Sets the \a units for the geometry expression. + * + * By default this is QgsUnitTypes::MapUnits, which means that the geometryExpression() + * will return geometries in the associated layer's CRS. + * + * \see units() + * \since QGIS 3.22 + */ + void setUnits( QgsUnitTypes::RenderUnit units ) { mUnits = units;} + QgsSymbol *subSymbol() override { return mSymbol; } bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override; @@ -104,6 +126,7 @@ class CORE_EXPORT QgsGeometryGeneratorSymbolLayer : public QgsSymbolLayer void setColor( const QColor &color ) override; + private: QgsGeometryGeneratorSymbolLayer( const QString &expression ); @@ -122,6 +145,8 @@ class CORE_EXPORT QgsGeometryGeneratorSymbolLayer : public QgsSymbolLayer */ Qgis::SymbolType mSymbolType; + QgsUnitTypes::RenderUnit mUnits = QgsUnitTypes::RenderMapUnits; + bool mRenderingFeature = false; bool mHasRenderedFeature = false; }; diff --git a/src/gui/qgsunitselectionwidget.cpp b/src/gui/qgsunitselectionwidget.cpp index f439d46b8ce1..4c077dafb72a 100644 --- a/src/gui/qgsunitselectionwidget.cpp +++ b/src/gui/qgsunitselectionwidget.cpp @@ -252,11 +252,11 @@ void QgsUnitSelectionWidget::toggleUnitRangeButton() { if ( unit() != QgsUnitTypes::RenderUnknownUnit ) { - mMapScaleButton->setVisible( unit() == QgsUnitTypes::RenderMapUnits ); + mMapScaleButton->setVisible( mShowMapScaleButton && unit() == QgsUnitTypes::RenderMapUnits ); } else { - mMapScaleButton->setVisible( mMapUnitIdx != -1 && mUnitCombo->currentIndex() == mMapUnitIdx ); + mMapScaleButton->setVisible( mShowMapScaleButton && mMapUnitIdx != -1 && mUnitCombo->currentIndex() == mMapUnitIdx ); } } @@ -266,6 +266,18 @@ void QgsUnitSelectionWidget::widgetChanged( const QgsMapUnitScale &scale ) emit changed(); } +bool QgsUnitSelectionWidget::showMapScaleButton() const +{ + return mShowMapScaleButton; +} + +void QgsUnitSelectionWidget::setShowMapScaleButton( bool show ) +{ + mShowMapScaleButton = show; + if ( !show ) + mMapScaleButton->hide(); +} + QgsMapUnitScaleDialog::QgsMapUnitScaleDialog( QWidget *parent ) : QDialog( parent ) diff --git a/src/gui/qgsunitselectionwidget.h b/src/gui/qgsunitselectionwidget.h index a00f1f92960c..1d8dfabe9402 100644 --- a/src/gui/qgsunitselectionwidget.h +++ b/src/gui/qgsunitselectionwidget.h @@ -225,6 +225,22 @@ class GUI_EXPORT QgsUnitSelectionWidget : public QWidget, private Ui::QgsUnitSel */ void setMapCanvas( QgsMapCanvas *canvas ); + /** + * Returns TRUE if the widget can show the map scale button when the Map Units option is selected. + * + * \see setShowMapScaleButton() + * \since QGIS 3.22 + */ + bool showMapScaleButton() const; + + /** + * Sets whether the widget can show the map scale button when the Map Units option is selected. + * + * \see showMapScaleButton() + * \since QGIS 3.22 + */ + void setShowMapScaleButton( bool show ); + signals: void changed(); @@ -237,6 +253,7 @@ class GUI_EXPORT QgsUnitSelectionWidget : public QWidget, private Ui::QgsUnitSel QgsMapUnitScale mMapUnitScale; int mMapUnitIdx; QgsMapCanvas *mCanvas = nullptr; + bool mShowMapScaleButton = true; }; diff --git a/src/gui/symbology/qgssymbollayerwidget.cpp b/src/gui/symbology/qgssymbollayerwidget.cpp index bbe4a5d7b115..e36798016ead 100644 --- a/src/gui/symbology/qgssymbollayerwidget.cpp +++ b/src/gui/symbology/qgssymbollayerwidget.cpp @@ -4288,15 +4288,35 @@ QgsGeometryGeneratorSymbolLayerWidget::QgsGeometryGeneratorSymbolLayerWidget( Qg cbxGeometryType->addItem( QgsIconUtils::iconPolygon(), tr( "Polygon / MultiPolygon" ), static_cast< int >( Qgis::SymbolType::Fill ) ); cbxGeometryType->addItem( QgsIconUtils::iconLine(), tr( "LineString / MultiLineString" ), static_cast< int >( Qgis::SymbolType::Line ) ); cbxGeometryType->addItem( QgsIconUtils::iconPoint(), tr( "Point / MultiPoint" ), static_cast< int >( Qgis::SymbolType::Marker ) ); + + mUnitWidget->setUnits( {QgsUnitTypes::RenderMillimeters, + QgsUnitTypes::RenderPoints, + QgsUnitTypes::RenderPixels, + QgsUnitTypes::RenderInches, + QgsUnitTypes::RenderMapUnits + } ); + mUnitWidget->setShowMapScaleButton( false ); + connect( modificationExpressionSelector, &QgsExpressionLineEdit::expressionChanged, this, &QgsGeometryGeneratorSymbolLayerWidget::updateExpression ); connect( cbxGeometryType, static_cast( &QComboBox::currentIndexChanged ), this, &QgsGeometryGeneratorSymbolLayerWidget::updateSymbolType ); + connect( mUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ] + { + if ( !mBlockSignals ) + { + mLayer->setUnits( mUnitWidget->unit() ); + emit symbolChanged(); + } + } ); } void QgsGeometryGeneratorSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *l ) { + mBlockSignals++; mLayer = static_cast( l ); modificationExpressionSelector->setExpression( mLayer->geometryExpression() ); cbxGeometryType->setCurrentIndex( cbxGeometryType->findData( static_cast< int >( mLayer->symbolType() ) ) ); + mUnitWidget->setUnit( mLayer->units() ); + mBlockSignals--; } QgsSymbolLayer *QgsGeometryGeneratorSymbolLayerWidget::symbolLayer() diff --git a/src/gui/symbology/qgssymbollayerwidget.h b/src/gui/symbology/qgssymbollayerwidget.h index 270d046f9d61..cfb97d70a909 100644 --- a/src/gui/symbology/qgssymbollayerwidget.h +++ b/src/gui/symbology/qgssymbollayerwidget.h @@ -1138,6 +1138,7 @@ class GUI_EXPORT QgsGeometryGeneratorSymbolLayerWidget : public QgsSymbolLayerWi private: QgsGeometryGeneratorSymbolLayer *mLayer = nullptr; + int mBlockSignals = 0; private slots: void updateExpression( const QString &string ); diff --git a/src/ui/symbollayer/qgsgeometrygeneratorwidgetbase.ui b/src/ui/symbollayer/qgsgeometrygeneratorwidgetbase.ui index c64b81dc8054..ef82faae108d 100644 --- a/src/ui/symbollayer/qgsgeometrygeneratorwidgetbase.ui +++ b/src/ui/symbollayer/qgsgeometrygeneratorwidgetbase.ui @@ -13,12 +13,16 @@ Form - - + + - - + + + + Units + + @@ -33,6 +37,22 @@ + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + @@ -42,6 +62,12 @@
qgsexpressionlineedit.h
1 + + QgsUnitSelectionWidget + QWidget +
qgsunitselectionwidget.h
+ 1 +