Curved offset first attempt

This commit is contained in:
Alessandro Pasotti 2022-03-10 16:25:27 +01:00 committed by Nyall Dawson
parent 28bb05b0d5
commit 66322f4c7f
9 changed files with 441 additions and 193 deletions

View File

@ -219,6 +219,7 @@ Contains settings for how a map layer will be labeled.
PositionX,
PositionY,
PositionPoint,
CurvedOffset,
Hali,
Vali,
Rotation,

View File

@ -53,6 +53,7 @@ QgsMapToolLabel::~QgsMapToolLabel()
delete mCalloutOtherPointsRubberBand;
delete mFeatureRubberBand;
delete mFixPointRubberBand;
delete mOffsetFromLineStartRubberBand;
}
void QgsMapToolLabel::deactivate()
@ -216,6 +217,7 @@ void QgsMapToolLabel::createRubberBands()
{
delete mLabelRubberBand;
delete mFeatureRubberBand;
delete mOffsetFromLineStartRubberBand;
//label rubber band
mLabelRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::LineGeometry );
@ -238,6 +240,12 @@ void QgsMapToolLabel::createRubberBands()
QgsGeometry geom = f.geometry();
if ( !geom.isNull() )
{
const int r = QgsSettingsRegistryCore::settingsDigitizingLineColorRed.value();
const int g = QgsSettingsRegistryCore::settingsDigitizingLineColorGreen.value();
const int b = QgsSettingsRegistryCore::settingsDigitizingLineColorBlue.value();
const int a = QgsSettingsRegistryCore::settingsDigitizingLineColorAlpha.value();
if ( geom.type() == QgsWkbTypes::PolygonGeometry )
{
// for polygons, we don't want to fill the whole polygon itself with the rubber band
@ -246,10 +254,13 @@ void QgsMapToolLabel::createRubberBands()
// instead, just use the boundary of the polygon for the rubber band
geom = QgsGeometry( geom.constGet()->boundary() );
}
int r = QgsSettingsRegistryCore::settingsDigitizingLineColorRed.value();
int g = QgsSettingsRegistryCore::settingsDigitizingLineColorGreen.value();
int b = QgsSettingsRegistryCore::settingsDigitizingLineColorBlue.value();
int a = QgsSettingsRegistryCore::settingsDigitizingLineColorAlpha.value();
else if ( geom.type() == QgsWkbTypes::LineGeometry )
{
mOffsetFromLineStartRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PointGeometry );
mOffsetFromLineStartRubberBand->setColor( QColor( r, g, b, a ) );
mOffsetFromLineStartRubberBand->hide();
}
mFeatureRubberBand = new QgsRubberBand( mCanvas, geom.type() );
mFeatureRubberBand->setColor( QColor( r, g, b, a ) );
mFeatureRubberBand->setToGeometry( geom, vlayer );
@ -284,6 +295,8 @@ void QgsMapToolLabel::deleteRubberBands()
mFeatureRubberBand = nullptr;
delete mFixPointRubberBand;
mFixPointRubberBand = nullptr;
delete mOffsetFromLineStartRubberBand;
mOffsetFromLineStartRubberBand = nullptr;
cadDockWidget()->clear();
cadDockWidget()->clearPoints();
}
@ -845,6 +858,39 @@ bool QgsMapToolLabel::currentLabelDataDefinedPosition( double &x, bool &xSuccess
return true;
}
bool QgsMapToolLabel::currentLabelDataDefinedCurvedOffset( double &offset, bool &offsetSuccess, int &curvedOffsetCol ) const
{
offsetSuccess = false;
QgsVectorLayer *vlayer = mCurrentLabel.layer;
QgsFeatureId featureId = mCurrentLabel.pos.featureId;
if ( ! vlayer )
{
return false;
}
if ( !labelOffsettable( vlayer, mCurrentLabel.settings, curvedOffsetCol ) )
{
return false;
}
QgsFeature f;
if ( !vlayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setFlags( QgsFeatureRequest::NoGeometry ) ).nextFeature( f ) )
{
return false;
}
if ( mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::CurvedOffset ) )
{
QgsAttributes attributes = f.attributes();
if ( !attributes.at( curvedOffsetCol ).isNull() )
offset = attributes.at( curvedOffsetCol ).toDouble( &offsetSuccess );
}
return true;
}
QgsMapToolLabel::PropertyStatus QgsMapToolLabel::labelRotatableStatus( QgsVectorLayer *layer, const QgsPalLayerSettings &settings, int &rotationCol ) const
{
PropertyStatus status = PropertyStatus::DoesNotExist;
@ -921,6 +967,30 @@ bool QgsMapToolLabel::changeCurrentLabelDataDefinedPosition( const QVariant &x,
return true;
}
bool QgsMapToolLabel::changeCurrentLabelDataDefinedCurvedOffset( const QVariant &offset )
{
if ( mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::CurvedOffset ) )
{
PropertyStatus status = PropertyStatus::DoesNotExist;
const QString curvedOffsetColName = dataDefinedColumnName( QgsPalLayerSettings::CurvedOffset, mCurrentLabel.settings, mCurrentLabel.layer, status );
const int curvedOffsetCol = mCurrentLabel.layer->fields().lookupField( curvedOffsetColName );
if ( !mCurrentLabel.layer->changeAttributeValue( mCurrentLabel.pos.featureId, curvedOffsetCol, offset ) )
return false;
}
else
{
PropertyStatus status = PropertyStatus::DoesNotExist;
const QString curvedOffsetColName = dataDefinedColumnName( QgsPalLayerSettings::PositionX, mCurrentLabel.settings, mCurrentLabel.layer, status );
const int curvedOffsetCol = mCurrentLabel.layer->fields().lookupField( curvedOffsetColName );
if ( !mCurrentLabel.layer->changeAttributeValue( mCurrentLabel.pos.featureId, curvedOffsetCol, offset ) )
return false;
}
return true;
}
bool QgsMapToolLabel::dataDefinedShowHide( QgsVectorLayer *vlayer, QgsFeatureId featureId, int &show, bool &showSuccess, int &showCol ) const
{
showSuccess = false;
@ -1017,7 +1087,7 @@ bool QgsMapToolLabel::isPinned()
double x, y;
bool xSuccess, ySuccess;
if ( currentLabelDataDefinedPosition( x, xSuccess, y, ySuccess, xCol, yCol, pointCol ) && xSuccess && ySuccess )
if ( currentLabelDataDefinedPosition( x, xSuccess, y, ySuccess, xCol, yCol, pointCol ) )
rc = true;
}
@ -1054,6 +1124,20 @@ bool QgsMapToolLabel::labelMoveable( QgsVectorLayer *vlayer, const QgsPalLayerSe
return false;
}
bool QgsMapToolLabel::labelOffsettable( QgsVectorLayer *vlayer, const QgsPalLayerSettings &settings, int &curvedOffsetCol ) const
{
curvedOffsetCol = -1;
if ( settings.dataDefinedProperties().isActive( QgsPalLayerSettings::CurvedOffset ) )
{
PropertyStatus status = PropertyStatus::DoesNotExist;
QString pointColName = dataDefinedColumnName( QgsPalLayerSettings::CurvedOffset, settings, vlayer, status );
curvedOffsetCol = vlayer->fields().lookupField( pointColName );
if ( curvedOffsetCol >= 0 )
return true;
}
return false;
}
bool QgsMapToolLabel::diagramCanShowHide( QgsVectorLayer *vlayer, int &showCol ) const
{
showCol = -1;

View File

@ -86,6 +86,7 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
QgsRubberBand *mFeatureRubberBand = nullptr;
//! Shows label fixpoint (left/bottom by default)
QgsRubberBand *mFixPointRubberBand = nullptr;
QgsRubberBand *mOffsetFromLineStartRubberBand = nullptr;
struct APP_EXPORT LabelDetails
{
@ -201,6 +202,16 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
*/
bool currentLabelDataDefinedPosition( double &x, bool &xSuccess, double &y, bool &ySuccess, int &xCol, int &yCol, int &pointCol ) const;
/**
* Gets data defined curved offset of current label
* \param offset out: data defined curved offset
* \param offsetSuccess out: FALSE if attribute value is NULL
* \param curvedOffsetCol out: index of the curved offset column
* \returns FALSE if layer does not have data defined label curved offset enabled
* \since QGIS 3.26
*/
bool currentLabelDataDefinedCurvedOffset( double &offset, bool &offsetSuccess, int &curvedOffsetCol ) const;
/**
* Returns data defined rotation of current label
* \param rotation out: rotation value
@ -220,6 +231,14 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
*/
bool changeCurrentLabelDataDefinedPosition( const QVariant &x, const QVariant &y );
/**
* Change the data defined curve offset current label
* \param curvedOffsetCol out: index of the curved offset
* \param x data defined offset
* \returns TRUE if data defined curved offset could be changed
*/
bool changeCurrentLabelDataDefinedCurvedOffset( const QVariant &offset );
/**
* Returns data defined show/hide of a feature.
* \param vlayer vector layer
@ -239,6 +258,7 @@ class APP_EXPORT QgsMapToolLabel: public QgsMapToolAdvancedDigitizing
bool isPinned();
bool labelMoveable( QgsVectorLayer *vlayer, const QgsPalLayerSettings &settings, int &xCol, int &yCol, int &pointCol ) const;
bool labelOffsettable( QgsVectorLayer *vlayer, const QgsPalLayerSettings &settings, int &curvedOffsetCol ) const;
bool createAuxiliaryFields( QgsPalIndexes &palIndexes );
bool createAuxiliaryFields( LabelDetails &details, QgsPalIndexes &palIndexes ) const;

View File

@ -34,6 +34,7 @@ QgsMapToolMoveLabel::QgsMapToolMoveLabel( QgsMapCanvas *canvas, QgsAdvancedDigit
mPalProperties << QgsPalLayerSettings::PositionX;
mPalProperties << QgsPalLayerSettings::PositionY;
mPalProperties << QgsPalLayerSettings::CurvedOffset;
mDiagramProperties << QgsDiagramLayerSettings::PositionX;
mDiagramProperties << QgsDiagramLayerSettings::PositionY;
@ -58,17 +59,52 @@ void QgsMapToolMoveLabel::deleteRubberBands()
void QgsMapToolMoveLabel::cadCanvasMoveEvent( QgsMapMouseEvent *e )
{
if ( mLabelRubberBand )
{
const QgsPointXY pointMapCoords = e->mapPoint();
const double offsetX = pointMapCoords.x() - mStartPointMapCoords.x();
const double offsetY = pointMapCoords.y() - mStartPointMapCoords.y();
mLabelRubberBand->setTranslationOffset( offsetX, offsetY );
mLabelRubberBand->updatePosition();
mLabelRubberBand->update();
mFixPointRubberBand->setTranslationOffset( offsetX, offsetY );
mFixPointRubberBand->updatePosition();
mFixPointRubberBand->update();
bool isCurved { mCurrentLabel.settings.placement == QgsPalLayerSettings::Placement::Curved };
if ( isCurved )
{
// Determine the closest point on the feature
const QgsFeatureId featureId = mCurrentLabel.pos.featureId;
if ( mCurrentLabel.layer )
{
const QgsFeature feature { mCurrentLabel.layer->getFeature( featureId ) };
const QgsGeometry pointMapGeometry { QgsGeometry::fromPointXY( pointMapCoords ) };
if ( feature.geometry().distance( pointMapGeometry ) / mCanvas->mapUnitsPerPixel() > 100.0 )
{
mCurrentLabel.settings.placement = QgsPalLayerSettings::Placement::Horizontal;
isCurved = false;
mOffsetFromLineStartRubberBand->hide();
}
else
{
mOffsetFromLineStartRubberBand->setToGeometry( feature.geometry().nearestPoint( pointMapGeometry ) );
mOffsetFromLineStartRubberBand->show();
}
}
}
if ( isCurved )
{
mLabelRubberBand->hide();
mFixPointRubberBand->hide();
}
else
{
const double offsetX = pointMapCoords.x() - mStartPointMapCoords.x();
const double offsetY = pointMapCoords.y() - mStartPointMapCoords.y();
mLabelRubberBand->setTranslationOffset( offsetX, offsetY );
mLabelRubberBand->updatePosition();
mLabelRubberBand->update();
mFixPointRubberBand->setTranslationOffset( offsetX, offsetY );
mFixPointRubberBand->updatePosition();
mFixPointRubberBand->update();
mLabelRubberBand->show();
mFixPointRubberBand->show();
}
}
else if ( mCalloutMoveRubberBand )
{
@ -92,7 +128,7 @@ void QgsMapToolMoveLabel::cadCanvasMoveEvent( QgsMapMouseEvent *e )
void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
{
if ( !mLabelRubberBand && !mCalloutMoveRubberBand )
if ( !mLabelRubberBand && !mCalloutMoveRubberBand && !mOffsetFromLineStartRubberBand )
{
if ( e->button() != Qt::LeftButton )
return;
@ -182,9 +218,30 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
return;
}
int xCol = -1, yCol = -1, pointCol = -1;
int xCol = -1, yCol = -1, pointCol = -1, curvedOffsetCol = -1;
if ( !mCurrentLabel.pos.isDiagram && !labelMoveable( vlayer, mCurrentLabel.settings, xCol, yCol, pointCol ) )
const bool isCurved { mCurrentLabel.settings.placement == QgsPalLayerSettings::Placement::Curved };
if ( isCurved && !mCurrentLabel.pos.isDiagram && !labelOffsettable( vlayer, mCurrentLabel.settings, pointCol ) )
{
QgsPalIndexes indexes;
if ( createAuxiliaryFields( indexes ) )
return;
if ( !labelOffsettable( vlayer, mCurrentLabel.settings, pointCol ) )
{
PropertyStatus status = PropertyStatus::DoesNotExist;
QString offsetColName = dataDefinedColumnName( QgsPalLayerSettings::CurvedOffset, mCurrentLabel.settings, vlayer, status );
if ( pointCol < 0 )
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Label" ), tr( "The label offset column “%1” does not exist in the layer" ).arg( offsetColName ) );
return;
}
curvedOffsetCol = indexes[ QgsPalLayerSettings::CurvedOffset ];
}
else if ( !mCurrentLabel.pos.isDiagram && !labelMoveable( vlayer, mCurrentLabel.settings, xCol, yCol, pointCol ) )
{
if ( mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::PositionPoint ) )
{
@ -228,10 +285,11 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
yCol = indexes[ QgsDiagramLayerSettings::PositionY ];
}
if ( xCol >= 0 && yCol >= 0 )
if ( ( isCurved && curvedOffsetCol >= 0 ) || ( xCol >= 0 && yCol >= 0 ) )
{
const bool usesAuxFields = vlayer->fields().fieldOrigin( xCol ) == QgsFields::OriginJoin
&& vlayer->fields().fieldOrigin( yCol ) == QgsFields::OriginJoin;
const bool usesAuxFields =
( isCurved && curvedOffsetCol >= 0 && vlayer->fields().fieldOrigin( curvedOffsetCol ) == QgsFields::OriginJoin ) ||
( vlayer->fields().fieldOrigin( xCol ) == QgsFields::OriginJoin && vlayer->fields().fieldOrigin( yCol ) == QgsFields::OriginJoin );
if ( !usesAuxFields && !vlayer->isEditable() )
{
if ( vlayer->startEditing() )
@ -258,7 +316,7 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
createRubberBands();
}
}
else
else // Second click
{
switch ( e->button() )
{
@ -295,12 +353,21 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
int xCol = -1;
int yCol = -1;
int pointCol = -1;
int curvedOffsetCol = -1;
double xPosOrig = 0;
double yPosOrig = 0;
double curvedOffsetOrig = 0;
bool xSuccess = false;
bool ySuccess = false;
bool curvedOffsetSuccess = false;
if ( !isCalloutMove && !currentLabelDataDefinedPosition( xPosOrig, xSuccess, yPosOrig, ySuccess, xCol, yCol, pointCol ) )
const bool isCurved { mCurrentLabel.settings.placement == QgsPalLayerSettings::Placement::Curved };
if ( !isCalloutMove && isCurved && !currentLabelDataDefinedCurvedOffset( curvedOffsetOrig, curvedOffsetSuccess, curvedOffsetCol ) )
{
return;
}
else if ( !isCalloutMove && !currentLabelDataDefinedPosition( xPosOrig, xSuccess, yPosOrig, ySuccess, xCol, yCol, pointCol ) )
{
return;
}
@ -309,111 +376,138 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
return;
}
double xPosNew = 0;
double yPosNew = 0;
// Handle curved offset
if ( isCurved )
{
if ( !xSuccess || !ySuccess )
{
xPosNew = releaseCoords.x() - mClickOffsetX;
yPosNew = releaseCoords.y() - mClickOffsetY;
}
else
{
//transform to map crs first, because xdiff,ydiff are in map coordinates
const QgsMapSettings &ms = mCanvas->mapSettings();
const QgsPointXY transformedPoint = ms.layerToMapCoordinates( vlayer, QgsPointXY( xPosOrig, yPosOrig ) );
xPosOrig = transformedPoint.x();
yPosOrig = transformedPoint.y();
xPosNew = xPosOrig + xdiff;
yPosNew = yPosOrig + ydiff;
}
//transform back to layer crs
if ( mCanvas )
{
const QgsMapSettings &s = mCanvas->mapSettings();
const QgsPointXY transformedPoint = s.mapToLayerCoordinates( vlayer, QgsPointXY( xPosNew, yPosNew ) );
xPosNew = transformedPoint.x();
yPosNew = transformedPoint.y();
}
if ( !isCalloutMove )
vlayer->beginEditCommand( tr( "Moved label" ) + QStringLiteral( " '%1'" ).arg( currentLabelText( 24 ) ) );
else
vlayer->beginEditCommand( tr( "Moved callout" ) );
bool success = false;
if ( !isCalloutMove
&& mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::PositionPoint ) )
{
success = changeCurrentLabelDataDefinedPosition( xPosNew, yPosNew );
}
else
{
// Try to convert to the destination field type
QVariant xNewPos( xPosNew );
QVariant yNewPos( yPosNew );
if ( xCol < vlayer->fields().count() )
const QgsFeature feature { mCurrentLabel.layer->getFeature( featureId ) };
const QgsGeometry pointMapGeometry { QgsGeometry::fromPointXY( releaseCoords ) };
const QgsGeometry anchorPoint { feature.geometry().nearestPoint( pointMapGeometry ) };
const double offset { feature.geometry().lineLocatePoint( anchorPoint ) };
vlayer->beginEditCommand( tr( "Moved curved label offset" ) + QStringLiteral( " '%1'" ).arg( currentLabelText( 24 ) ) );
bool success = false;
if ( mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::CurvedOffset ) )
{
if ( ! vlayer->fields().at( xCol ).convertCompatible( xNewPos ) )
{
xNewPos = xPosNew; // revert and hope for the best
}
success = changeCurrentLabelDataDefinedCurvedOffset( offset );
}
if ( yCol < vlayer->fields().count() )
if ( !success )
{
if ( ! vlayer->fields().at( yCol ).convertCompatible( yNewPos ) )
if ( !vlayer->isEditable() )
{
yNewPos = yPosNew; // revert and hope for the best
QgisApp::instance()->messageBar()->pushWarning( tr( "Move curved label offset" ), tr( "Layer “%1” must be editable in order to move labels from it" ).arg( vlayer->name() ) );
vlayer->endEditCommand();
}
}
success = vlayer->changeAttributeValue( featureId, xCol, xNewPos );
success = vlayer->changeAttributeValue( featureId, yCol, yNewPos ) && success;
}
if ( !success )
else
{
// if the edit command fails, it's likely because the label x/y is being stored in a physical field (not a auxiliary one!)
// and the layer isn't in edit mode
if ( !vlayer->isEditable() )
double xPosNew = 0;
double yPosNew = 0;
if ( !xSuccess || !ySuccess )
{
if ( !isCalloutMove )
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Label" ), tr( "Layer “%1” must be editable in order to move labels from it" ).arg( vlayer->name() ) );
else
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Callout" ), tr( "Layer “%1” must be editable in order to move callouts from it" ).arg( vlayer->name() ) );
xPosNew = releaseCoords.x() - mClickOffsetX;
yPosNew = releaseCoords.y() - mClickOffsetY;
}
else
{
if ( !isCalloutMove )
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Label" ), tr( "Error encountered while storing new label position" ) );
//transform to map crs first, because xdiff,ydiff are in map coordinates
const QgsMapSettings &ms = mCanvas->mapSettings();
const QgsPointXY transformedPoint = ms.layerToMapCoordinates( vlayer, QgsPointXY( xPosOrig, yPosOrig ) );
xPosOrig = transformedPoint.x();
yPosOrig = transformedPoint.y();
xPosNew = xPosOrig + xdiff;
yPosNew = yPosOrig + ydiff;
}
//transform back to layer crs
if ( mCanvas )
{
const QgsMapSettings &s = mCanvas->mapSettings();
const QgsPointXY transformedPoint = s.mapToLayerCoordinates( vlayer, QgsPointXY( xPosNew, yPosNew ) );
xPosNew = transformedPoint.x();
yPosNew = transformedPoint.y();
}
if ( !isCalloutMove )
vlayer->beginEditCommand( tr( "Moved label" ) + QStringLiteral( " '%1'" ).arg( currentLabelText( 24 ) ) );
else
vlayer->beginEditCommand( tr( "Moved callout" ) );
bool success = false;
if ( !isCalloutMove
&& mCurrentLabel.settings.dataDefinedProperties().isActive( QgsPalLayerSettings::PositionPoint ) )
{
success = changeCurrentLabelDataDefinedPosition( xPosNew, yPosNew );
}
else
{
// Try to convert to the destination field type
QVariant xNewPos( xPosNew );
QVariant yNewPos( yPosNew );
if ( xCol < vlayer->fields().count() )
{
if ( ! vlayer->fields().at( xCol ).convertCompatible( xNewPos ) )
{
xNewPos = xPosNew; // revert and hope for the best
}
}
if ( yCol < vlayer->fields().count() )
{
if ( ! vlayer->fields().at( yCol ).convertCompatible( yNewPos ) )
{
yNewPos = yPosNew; // revert and hope for the best
}
}
success = vlayer->changeAttributeValue( featureId, xCol, xNewPos );
success = vlayer->changeAttributeValue( featureId, yCol, yNewPos ) && success;
}
if ( !success )
{
// if the edit command fails, it's likely because the label x/y is being stored in a physical field (not a auxiliary one!)
// and the layer isn't in edit mode
if ( !vlayer->isEditable() )
{
if ( !isCalloutMove )
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Label" ), tr( "Layer “%1” must be editable in order to move labels from it" ).arg( vlayer->name() ) );
else
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Callout" ), tr( "Layer “%1” must be editable in order to move callouts from it" ).arg( vlayer->name() ) );
}
else
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Callout" ), tr( "Error encountered while storing new callout position" ) );
{
if ( !isCalloutMove )
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Label" ), tr( "Error encountered while storing new label position" ) );
else
QgisApp::instance()->messageBar()->pushWarning( tr( "Move Callout" ), tr( "Error encountered while storing new callout position" ) );
}
vlayer->endEditCommand();
break;
}
// set rotation to that of label, if data-defined and no rotation set yet
// honor whether to preserve preexisting data on pin
// must come after setting x and y positions
if ( !isCalloutMove && !mCurrentLabel.pos.isDiagram
&& !mCurrentLabel.pos.isPinned
&& !currentLabelPreserveRotation() )
{
double defRot;
bool rSuccess;
int rCol;
if ( currentLabelDataDefinedRotation( defRot, rSuccess, rCol ) )
{
const double labelRot = mCurrentLabel.pos.rotation * 180 / M_PI;
vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, rCol, labelRot );
}
}
vlayer->endEditCommand();
vlayer->triggerRepaint();
break;
}
// set rotation to that of label, if data-defined and no rotation set yet
// honor whether to preserve preexisting data on pin
// must come after setting x and y positions
if ( !isCalloutMove && !mCurrentLabel.pos.isDiagram
&& !mCurrentLabel.pos.isPinned
&& !currentLabelPreserveRotation() )
{
double defRot;
bool rSuccess;
int rCol;
if ( currentLabelDataDefinedRotation( defRot, rSuccess, rCol ) )
{
const double labelRot = mCurrentLabel.pos.rotation * 180 / M_PI;
vlayer->changeAttributeValue( mCurrentLabel.pos.featureId, rCol, labelRot );
}
}
vlayer->endEditCommand();
vlayer->triggerRepaint();
break;
}
default:
break;

View File

@ -248,6 +248,7 @@ void QgsPalLayerSettings::initPropertyDefinitions()
{ QgsPalLayerSettings::PositionX, QgsPropertyDefinition( "PositionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
{ QgsPalLayerSettings::PositionY, QgsPropertyDefinition( "PositionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
{ QgsPalLayerSettings::PositionPoint, QgsPropertyDefinition( "PositionPoint", QgsPropertyDefinition::DataTypeString, QObject::tr( "Position (point)" ), QObject::tr( "A point geometry" ), origin ) },
{ QgsPalLayerSettings::CurvedOffset, QgsPropertyDefinition( "CurvedOffset", QgsPropertyDefinition::DataTypeString, QObject::tr( "Offset from the line start" ), QObject::tr( "double [0.0-line lenght]" ), origin ) },
{ QgsPalLayerSettings::Hali, QgsPropertyDefinition( "Hali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Horizontal alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>]", origin ) },
{
QgsPalLayerSettings::Vali, QgsPropertyDefinition( "Vali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Vertical alignment" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Bottom</b>|<b>Base</b>|<br>"

View File

@ -322,6 +322,7 @@ class CORE_EXPORT QgsPalLayerSettings
PositionX = 9, //!< X-coordinate data defined label position
PositionY = 10, //!< Y-coordinate data defined label position
PositionPoint = 114, //!< Point-coordinate data defined label position
CurvedOffset = 116, //!< Offset from start for curved lines (since QGIS 3.26)
Hali = 11, //!< Horizontal alignment for data defined label position (Left, Center, Right)
Vali = 12, //!< Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
Rotation = 14, //!< Label rotation (deprecated, for old project compatibility only)

View File

@ -92,6 +92,8 @@ void QgsTextFormatWidget::initWidget()
connect( mCoordYDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::mCoordYDDBtn_activated );
connect( mCoordPointDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordPointDDBtn_changed );
connect( mCoordPointDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::mCoordPointDDBtn_activated );
connect( mCurvedLineOffsetDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCurvedLineOffsetDDBtn_changed );
connect( mCurvedLineOffsetDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::mCurvedLineOffsetDDBtn_activated );
connect( mShapeTypeCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged );
connect( mShapeRotationCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeRotationCmbBx_currentIndexChanged );
connect( mShapeSVGParamsBtn, &QPushButton::clicked, this, &QgsTextFormatWidget::mShapeSVGParamsBtn_clicked );
@ -809,6 +811,7 @@ void QgsTextFormatWidget::populateDataDefinedButtons()
registerDataDefinedButton( mCoordXDDBtn, QgsPalLayerSettings::PositionX );
registerDataDefinedButton( mCoordYDDBtn, QgsPalLayerSettings::PositionY );
registerDataDefinedButton( mCoordPointDDBtn, QgsPalLayerSettings::PositionPoint );
registerDataDefinedButton( mCurvedLineOffsetDDBtn, QgsPalLayerSettings::CurvedOffset );
registerDataDefinedButton( mCoordAlignmentHDDBtn, QgsPalLayerSettings::Hali );
registerDataDefinedButton( mCoordAlignmentVDDBtn, QgsPalLayerSettings::Vali );
registerDataDefinedButton( mCoordRotationDDBtn, QgsPalLayerSettings::LabelRotation );
@ -1616,6 +1619,20 @@ void QgsTextFormatWidget::mCoordPointDDBtn_activated( bool isActive )
mCoordYDDBtn->setActive( false );
}
void QgsTextFormatWidget::mCurvedLineOffsetDDBtn_changed()
{
// TODO: update something?
}
void QgsTextFormatWidget::mCurvedLineOffsetDDBtn_activated( bool isActive )
{
if ( !isActive )
return;
mCurvedLineOffsetDDBtn->setActive( false );
mCurvedLineOffsetDDBtn->setActive( false );
}
void QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged( int )
{
// shape background

View File

@ -290,6 +290,8 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte
void mCoordYDDBtn_activated( bool isActive );
void mCoordPointDDBtn_changed();
void mCoordPointDDBtn_activated( bool isActive );
void mCurvedLineOffsetDDBtn_changed();
void mCurvedLineOffsetDDBtn_activated( bool isActive );
void mShapeTypeCmbBx_currentIndexChanged( int index );
void mShapeRotationCmbBx_currentIndexChanged( int index );
void mShapeSVGParamsBtn_clicked();

View File

@ -109,7 +109,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>499</width>
<width>502</width>
<height>300</height>
</rect>
</property>
@ -625,7 +625,7 @@
<item>
<widget class="QStackedWidget" name="mLabelStackedWidget">
<property name="currentIndex">
<number>0</number>
<number>7</number>
</property>
<widget class="QWidget" name="mLabelPage_Text">
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -654,8 +654,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>485</width>
<height>429</height>
<width>486</width>
<height>457</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
@ -1217,8 +1217,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>471</width>
<height>742</height>
<width>281</width>
<height>597</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_42">
@ -2131,8 +2131,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>299</width>
<height>308</height>
<width>223</width>
<height>250</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
@ -2477,8 +2477,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>296</width>
<height>291</height>
<width>223</width>
<height>219</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_121">
@ -2755,8 +2755,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>438</width>
<height>753</height>
<width>357</width>
<height>606</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_21">
@ -3506,8 +3506,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>457</height>
<width>267</width>
<height>372</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
@ -3934,8 +3934,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>159</width>
<height>211</height>
<width>119</width>
<height>166</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_46">
@ -4083,9 +4083,9 @@ font-style: italic;</string>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>472</width>
<height>1690</height>
<y>-936</y>
<width>475</width>
<height>1402</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
@ -5418,41 +5418,12 @@ font-style: italic;</string>
<string notr="true">labelplacementgroup</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>8</number>
</property>
<property name="rightMargin">
<number>8</number>
</property>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_25">
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordRotationDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mCoordRotationUnitComboBox"/>
</item>
<item>
<widget class="QCheckBox" name="chkPreserveRotation">
<property name="toolTip">
<string>Uncheck to write labeling engine derived rotation on pin and NULL on unpin</string>
</property>
<property name="styleSheet">
<string notr="true">margin-left: 12px; margin-top: 3px;</string>
</property>
<property name="text">
<string>Preserve data rotation values</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<item row="0" column="0">
<widget class="QLabel" name="mCoordLabel">
<property name="text">
<string>Coordinate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_22">
@ -5511,14 +5482,76 @@ font-style: italic;</string>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="mCoordLabel">
<item row="1" column="0">
<widget class="QLabel" name="label_44">
<property name="text">
<string>Coordinate</string>
<string>Point</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordPointDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_22">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string>Offset</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<widget class="QgsPropertyOverrideButton" name="mCurvedLineOffsetDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_24">
<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="3" column="0">
<widget class="QLabel" name="mCoordAlignmentLabel">
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QFrame" name="mCoordAlignmentFrame">
<layout class="QHBoxLayout" name="horizontalLayout_27">
<property name="leftMargin">
@ -5589,51 +5622,43 @@ font-style: italic;</string>
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="mCoordRotationLabel">
<property name="text">
<string>Rotation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_25">
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordPointDDBtn">
<widget class="QgsPropertyOverrideButton" name="mCoordRotationDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_22">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QComboBox" name="mCoordRotationUnitComboBox"/>
</item>
<item>
<widget class="QCheckBox" name="chkPreserveRotation">
<property name="toolTip">
<string>Uncheck to write labeling engine derived rotation on pin and NULL on unpin</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
<property name="styleSheet">
<string notr="true">margin-left: 12px; margin-top: 3px;</string>
</property>
</spacer>
<property name="text">
<string>Preserve data rotation values</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mCoordAlignmentLabel">
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_44">
<property name="text">
<string>Point</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -5818,8 +5843,8 @@ font-style: italic;</string>
<rect>
<x>0</x>
<y>0</y>
<width>430</width>
<height>708</height>
<width>304</width>
<height>571</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@ -6953,6 +6978,7 @@ font-style: italic;</string>
<tabstop>mCoordXDDBtn</tabstop>
<tabstop>mCoordYDDBtn</tabstop>
<tabstop>mCoordPointDDBtn</tabstop>
<tabstop>mCurvedLineOffsetDDBtn</tabstop>
<tabstop>mCoordAlignmentHDDBtn</tabstop>
<tabstop>mCoordAlignmentVDDBtn</tabstop>
<tabstop>mCoordRotationDDBtn</tabstop>
@ -6994,6 +7020,8 @@ font-style: italic;</string>
</tabstops>
<resources>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>