Skip to content
Permalink
Browse files

Fix and update to Freeze/Thaw, Move, and Rotate Label tools

Updates
- Move tool now preserves calculated x, y offset for upside-down labels, when initially moved and data-defined fields have no previous data.
- Move tool now stores the label's PAL solution for rotation on initial move, if rotation is data-defined with no previous data.
- Freeze/Thaw and Move tools now support all variations of previously data-defined halign and valign options on initial freeze.
- Freeze/Thaw tool has updated key modifiers that should work across major platforms.
- Tests for whether layer labels support data-defined rotation moved to base QgsMapToolLabel class.
Fixes
- Fix bug where labels defined in map units causing font to show as pointsize of same integer at small map scales.
- Half of a fix for rotation point of labels with data-defined size of map units actually being based off of point size.
  (still references the layer-defined font size somewhere in the rotation point calculation)
- Valign settings of Base and Half now more accurately key off of inverse of font ascent (instead of descent). No discernible shift occurs now on initial freeze.
  • Loading branch information
dakcarto committed Aug 2, 2012
1 parent b4b1c49 commit f7a3af36733891ac84281e562d5505ce32c69f6f
@@ -257,8 +257,8 @@ void QgsMapToolFreezeLabels::freezeThawLabels( const QgsRectangle& ext, QMouseEv
{

bool doThaw = e->modifiers() & Qt::ShiftModifier ? true : false;
bool toggleThawOrFreeze = e->modifiers() & Qt::AltModifier ? true : false;
bool doHide = e->modifiers() & Qt::ControlModifier ? true : false;
bool toggleThawOrFreeze = e->modifiers() & Qt::ControlModifier ? true : false;
bool doHide = ( doThaw && toggleThawOrFreeze );

// get list of all drawn labels from all layers within, or touching, chosen extent
bool labelChanged = false;
@@ -432,16 +432,23 @@ bool QgsMapToolFreezeLabels::freezeThawLabel( QgsVectorLayer* vlayer,
if ( freeze )
{

QgsPoint labelpoint = labelpos.cornerPoints.at( 0 );
// QgsPoint labelpoint = labelpos.cornerPoints.at( 0 );

double labelX = labelpoint.x();
double labelY = labelpoint.y();
QgsPoint referencePoint;
if ( !rotationPoint( referencePoint, true ) )
{
referencePoint.setX( mCurrentLabelPos.labelRect.xMinimum() );
referencePoint.setY( mCurrentLabelPos.labelRect.yMinimum() );
}

double labelX = referencePoint.x();
double labelY = referencePoint.y();
double labelR = labelpos.rotation * 180 / M_PI;

// transform back to layer crs, if on-fly on
if ( mRender->hasCrsTransformEnabled() )
{
QgsPoint transformedPoint = mRender->mapToLayerCoordinates( vlayer, labelpoint );
QgsPoint transformedPoint = mRender->mapToLayerCoordinates( vlayer, referencePoint );
labelX = transformedPoint.x();
labelY = transformedPoint.y();
}
@@ -230,7 +230,14 @@ QFont QgsMapToolLabel::labelFontCurrentFeature()
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator sizeIt = ddProperties.find( QgsPalLayerSettings::Size );
if ( sizeIt != ddProperties.constEnd() )
{
font.setPointSizeF( attributes[*sizeIt].toDouble() );
if ( layerSettings.fontSizeInMapUnits )
{
font.setPixelSize( layerSettings.sizeToPixel( attributes[*sizeIt].toDouble(), QgsRenderContext() ) );
}
else
{
font.setPointSizeF( attributes[*sizeIt].toDouble() );
}
}

//family
@@ -273,15 +280,15 @@ QFont QgsMapToolLabel::labelFontCurrentFeature()
return font;
}

bool QgsMapToolLabel::rotationPoint( QgsPoint& pos )
bool QgsMapToolLabel::rotationPoint( QgsPoint& pos, bool ignoreUpsideDown )
{
QVector<QgsPoint> cornerPoints = mCurrentLabelPos.cornerPoints;
if ( cornerPoints.size() < 4 )
{
return false;
}

if ( mCurrentLabelPos.upsideDown )
if ( mCurrentLabelPos.upsideDown && !ignoreUpsideDown )
{
pos = mCurrentLabelPos.cornerPoints.at( 2 );
}
@@ -336,7 +343,7 @@ bool QgsMapToolLabel::rotationPoint( QgsPoint& pos )
}
else
{
double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
double descentRatio = 1 / labelFontMetrics.ascent() / labelFontMetrics.height();
if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
{
ydiff = labelSizeY * descentRatio;
@@ -351,7 +358,7 @@ bool QgsMapToolLabel::rotationPoint( QgsPoint& pos )
double angle = mCurrentLabelPos.rotation;
double xd = xdiff * cos( angle ) - ydiff * sin( angle );
double yd = xdiff * sin( angle ) + ydiff * cos( angle );
if ( mCurrentLabelPos.upsideDown )
if ( mCurrentLabelPos.upsideDown && !ignoreUpsideDown )
{
pos.setX( pos.x() - xd );
pos.setY( pos.y() - yd );
@@ -399,7 +406,65 @@ bool QgsMapToolLabel::dataDefinedPosition( QgsVectorLayer* vlayer, int featureId
return true;
}

bool QgsMapToolLabel:: diagramMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const
bool QgsMapToolLabel::layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const
{
const QgsVectorLayer* vlayer = dynamic_cast<const QgsVectorLayer*>( layer );
if ( !vlayer || !vlayer->isEditable() )
{
return false;
}

QVariant rotation = layer->customProperty( "labeling/dataDefinedProperty14" );
if ( !rotation.isValid() )
{
return false;
}

bool rotationOk;
rotationCol = rotation.toInt( &rotationOk );
if ( !rotationOk )
{
return false;
}
return true;
}

bool QgsMapToolLabel::dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess )
{
rotationSuccess = false;
if ( !vlayer )
{
return false;
}

int rotationCol;
if ( !layerIsRotatable( vlayer, rotationCol ) )
{
return false;
}

QgsFeature f;
if ( !vlayer->featureAtId( featureId, f, false, true ) )
{
return false;
}

QgsAttributeMap attributes = f.attributeMap();

//test, if data defined x- and y- values are not null. Otherwise, the position is determined by PAL and the rotation cannot be fixed
int xCol, yCol;
double x, y;
bool xSuccess, ySuccess;
if ( !dataDefinedPosition( vlayer, featureId, x, xSuccess, y, ySuccess, xCol, yCol ) || !xSuccess || !ySuccess )
{
return false;
}

rotation = attributes[rotationCol].toDouble( &rotationSuccess );
return true;
}

bool QgsMapToolLabel::diagramMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const
{
const QgsVectorLayer* vlayer = dynamic_cast<const QgsVectorLayer*>( ml );
if ( vlayer && vlayer->diagramRenderer() )
@@ -46,6 +46,9 @@ class QgsMapToolLabel: public QgsMapTool
@param yCol out: index of the attribute for data defined y coordinate
@return true if layer fields set up and exist*/
bool layerCanFreeze( const QgsMapLayer* ml, int& xCol, int& yCol ) const;
/**Checks if labels in a layer can be rotated
@param rotationCol out: attribute column for data defined label rotation*/
bool layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const;

protected:
QgsRubberBand* mLabelRubberBand;
@@ -63,8 +66,9 @@ class QgsMapToolLabel: public QgsMapTool
bool labelAtPosition( QMouseEvent* e, QgsLabelPosition& p );

/**Finds out rotation point of current label position
@param ignoreUpsideDown treat label as right-side-up
@return true in case of success*/
bool rotationPoint( QgsPoint& pos );
bool rotationPoint( QgsPoint& pos, bool ignoreUpsideDown = false );

/**Creates label / feature / fixpoint rubber bands for the current label position*/
void createRubberBands();
@@ -90,7 +94,8 @@ class QgsMapToolLabel: public QgsMapTool
QFont labelFontCurrentFeature();

/**Get data defined position of a feature
@param layerId layer identification string
@param vlayer vector layer
@param featureId feature identification integer
@param x out: data defined x-coordinate
@param xSuccess out: false if attribute value is NULL
@param y out: data defined y-coordinate
@@ -100,6 +105,15 @@ class QgsMapToolLabel: public QgsMapTool
@return false if layer does not have data defined label position enabled*/
bool dataDefinedPosition( QgsVectorLayer* vlayer, int featureId, double& x, bool& xSuccess, double& y, bool& ySuccess, int& xCol, int& yCol ) const;

/**Returns data defined rotation of a feature.
@param vlayer vector layer
@param featureId feature identification integer
@param rotation out: rotation value
@param rotationSuccess out: false if rotation value is NULL
@return true if data defined rotation is enabled on the layer
*/
bool dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess );

private:
QgsPalLayerSettings mInvalidLabelSettings;
};
@@ -50,7 +50,7 @@ void QgsMapToolMoveLabel::canvasPressEvent( QMouseEvent * e )
{
mStartPointMapCoords = toMapCoordinates( e->pos() );
QgsPoint referencePoint;
if ( !rotationPoint( referencePoint ) )
if ( !rotationPoint( referencePoint, true ) )
{
referencePoint.setX( mCurrentLabelPos.labelRect.xMinimum() );
referencePoint.setY( mCurrentLabelPos.labelRect.yMinimum() );
@@ -152,6 +152,26 @@ void QgsMapToolMoveLabel::canvasReleaseEvent( QMouseEvent * e )
vlayer->beginEditCommand( tr( "Label moved" ) );
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, xCol, xPosNew, false );
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, yCol, yPosNew, false );

// set rotation to that of label, if data-defined and no rotation set yet
// handle case of initially set rotation column fields of 0 instead of NULL
// must come after setting x and y positions
int rCol;
if ( !mCurrentLabelPos.isDiagram && !mCurrentLabelPos.isFrozen
&& layerIsRotatable( vlayer, rCol ) )
{
double labelRot = 0;
double defRot;
bool rSuccess;
if ( dataDefinedRotation( vlayer, mCurrentLabelPos.featureId, defRot, rSuccess ) )
{
labelRot = mCurrentLabelPos.rotation * 180 / M_PI;
if ( !rSuccess || ( rSuccess && defRot != labelRot ) )
{
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, rCol, labelRot, false );
}
}
}
vlayer->endEditCommand();

mCanvas->refresh();
@@ -166,64 +166,6 @@ void QgsMapToolRotateLabel::canvasReleaseEvent( QMouseEvent *e )
mCanvas->refresh();
}

bool QgsMapToolRotateLabel::layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const
{
const QgsVectorLayer* vlayer = dynamic_cast<const QgsVectorLayer*>( layer );
if ( !vlayer || !vlayer->isEditable() )
{
return false;
}

QVariant rotation = layer->customProperty( "labeling/dataDefinedProperty14" );
if ( !rotation.isValid() )
{
return false;
}

bool rotationOk;
rotationCol = rotation.toInt( &rotationOk );
if ( !rotationOk )
{
return false;
}
return true;
}

bool QgsMapToolRotateLabel::dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess )
{
rotationSuccess = false;
if ( !vlayer )
{
return false;
}

int rotationCol;
if ( !layerIsRotatable( vlayer, rotationCol ) )
{
return false;
}

QgsFeature f;
if ( !vlayer->featureAtId( featureId, f, false, true ) )
{
return false;
}

QgsAttributeMap attributes = f.attributeMap();

//test, if data defined x- and y- values are not null. Otherwise, the position is determined by PAL and the rotation cannot be fixed
int xCol, yCol;
double x, y;
bool xSuccess, ySuccess;
if ( !dataDefinedPosition( vlayer, featureId, x, xSuccess, y, ySuccess, xCol, yCol ) || !xSuccess || !ySuccess )
{
return false;
}

rotation = attributes[rotationCol].toDouble( &rotationSuccess );
return true;
}

int QgsMapToolRotateLabel::roundTo15Degrees( double n )
{
int m = ( int )( n / 15.0 + 0.5 );
@@ -33,17 +33,7 @@ class QgsMapToolRotateLabel: public QgsMapToolLabel
virtual void canvasMoveEvent( QMouseEvent * e );
virtual void canvasReleaseEvent( QMouseEvent * e );

/**Checks if labels in a layer can be rotated
@param rotationCol out: attribute column for data defined label rotation*/
bool layerIsRotatable( const QgsMapLayer* layer, int& rotationCol ) const;

protected:
/**Returns data defined rotation of a feature.
@param rotation out: rotation value
@param rotationSuccess out: false if rotation value is NULL
@return true if data defined rotation is enabled on the layer
*/
bool dataDefinedRotation( QgsVectorLayer* vlayer, int featureId, double& rotation, bool& rotationSuccess );

static int roundTo15Degrees( double n );
/**Converts azimuth value to counterclockwise 0 - 360*/
@@ -513,7 +513,7 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
if ( size.isValid() )
{
double sizeDouble = size.toDouble();
if ( sizeDouble <= 0 )
if ( sizeDouble <= 0 || sizeToPixel( sizeDouble, context ) < 1 )
{
return;
}
@@ -859,8 +859,18 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices,
// set whether adjacent lines should be merged
l->setMergeConnectedLines( lyr.mergeLines );

lyr.textFont.setPixelSize( lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx ) );
// fix for font size in map units causing font to show pointsize at small map scales
int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx );

if ( pixelFontSize < 1 )
{
lyr.textFont.setPixelSize( 1 );
lyr.textFont.setPointSize( 1 );
}
else
{
lyr.textFont.setPixelSize( pixelFontSize );
}
//raster and vector scale factors
lyr.vectorScaleFactor = ctx.scaleFactor();
lyr.rasterCompressFactor = ctx.rasterScaleFactor();
@@ -1720,7 +1720,7 @@
</property>
<property name="toolTip">
<string>Freeze or Thaw Labels
Shift thaws, Alt toggles, Ctl (Cmd) hides</string>
Shift thaws, Ctl (Cmd) toggles, Shift+Ctl (Cmd) hides</string>
</property>
</action>
<action name="mActionShowFrozenLabels">

0 comments on commit f7a3af3

Please sign in to comment.
You can’t perform that action at this time.