481 changes: 481 additions & 0 deletions src/app/qgsmaptoolfreezelabels.cpp

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions src/app/qgsmaptoolfreezelabels.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/***************************************************************************
qgsmaptoolfreezelabels.h
--------------------
begin : 2012-07-12
copyright : (C) 2012 by Larry Shaffer
email : larrys at dakotacarto 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. *
* *
***************************************************************************/

#ifndef QGSMAPTOOLFREEZELABELS_H
#define QGSMAPTOOLFREEZELABELS_H

#include "qgsmaptoollabel.h"
#include "qgsrectangle.h"
#include "qgslegend.h"
#include "qgscoordinatetransform.h"

class QgsHighlight;
class QgsLabelPosition;

/**A map tool for freezing (writing to attribute table) and thawing label positions and rotation*/
class QgsMapToolFreezeLabels: public QgsMapToolLabel
{
Q_OBJECT

public:
QgsMapToolFreezeLabels( QgsMapCanvas *canvas );
~QgsMapToolFreezeLabels();

//! Overridden mouse move event
virtual void canvasMoveEvent( QMouseEvent * e );

//! Overridden mouse press event
virtual void canvasPressEvent( QMouseEvent * e );

//! Overridden mouse release event
virtual void canvasReleaseEvent( QMouseEvent * e );

bool isShowingFrozen() const { return mShowFrozen; }
void setShowingFrozen( bool showing ) { mShowFrozen = showing; }

//! Called when Show Frozen Labels tool is toggled, via its qgisapp.cpp slot
void showFrozenLabels( bool show );

//! Remove rectangles from around frozen labels
void removeFrozenHighlights();

public slots:

//! Update frozen label highlights on layer edit mode change
void updateFrozenLabels();

//! Render highlight rectangles around frozen labels
void highlightFrozenLabels();

protected:

//! Mapping of feature ids of layers that have been highlighted
QMap<QString, QgsHighlight*> mHighlights;

//! Flag to indicate a map canvas drag operation is taking place
bool mDragging;
//! Flag to indicate whether to draw the highlight for frozen labels
bool mShowFrozen;

//! Stores actual select rect
QRect mSelectRect;

//! Stores selection marquee
QgsRubberBand* mRubberBand;

private:

//! Pointer to map renderer
QgsMapRenderer* mRender;

//! Highlights a given label relative to whether its frozen and editable
void highlightLabel( QgsVectorLayer* vlayer,
const QgsLabelPosition& labelpos,
const QString& id,
const QColor& color );

//! Select valid labels to freeze or thaw
void freezeThawLabels( const QgsRectangle& ext, QMouseEvent * e );

//! Freeze or thaw label relative to whether its editable
bool freezeThawLabel( QgsVectorLayer* vlayer,
const QgsLabelPosition& labelpos,
bool freeze );
};

#endif // QGSMAPTOOLFREEZELABELS_H
35 changes: 35 additions & 0 deletions src/app/qgsmaptoollabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,38 @@ bool QgsMapToolLabel::labelMoveable( const QgsMapLayer* ml, int& xCol, int& yCol

return true;
}

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

bool xColOk, yColOk;

QVariant xColumn = ml->customProperty( "labeling/dataDefinedProperty9" );
if ( !xColumn.isValid() )
{
return false;
}
xCol = xColumn.toInt( &xColOk );
if ( !xColOk )
{
return false;
}

QVariant yColumn = ml->customProperty( "labeling/dataDefinedProperty10" );
if ( !yColumn.isValid() )
{
return false;
}
yCol = yColumn.toInt( &yColOk );
if ( !yColOk )
{
return false;
}

return true;
}
5 changes: 5 additions & 0 deletions src/app/qgsmaptoollabel.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class QgsMapToolLabel: public QgsMapTool
@param yCol out: index of the attribute for data defined y coordinate
@return true if labels of layer can be moved*/
bool diagramMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const;
/**Returns true if layer has attribute fields set up
@param xCol out: index of the attribute for data defined x coordinate
@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;

protected:
QgsRubberBand* mLabelRubberBand;
Expand Down
20 changes: 18 additions & 2 deletions src/core/qgslabelsearchtree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,23 @@ void QgsLabelSearchTree::label( const QgsPoint& p, QList<QgsLabelPosition*>& pos
}
}

bool QgsLabelSearchTree::insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName, bool diagram )
void QgsLabelSearchTree::labelsInRect( const QgsRectangle& r, QList<QgsLabelPosition*>& posList )
{
double c_min[2]; c_min[0] = r.xMinimum(); c_min[1] = r.yMinimum();
double c_max[2]; c_max[0] = r.xMaximum(); c_max[1] = r.yMaximum();

QList<QgsLabelPosition*> searchResults;
mSpatialIndex.Search( c_min, c_max, searchCallback, &searchResults );

posList.clear();
QList<QgsLabelPosition*>::const_iterator resultIt = searchResults.constBegin();
for ( ; resultIt != searchResults.constEnd(); ++resultIt )
{
posList.push_back( *resultIt );
}
}

bool QgsLabelSearchTree::insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName, bool diagram, bool frozen )
{
if ( !labelPos )
{
Expand All @@ -68,7 +84,7 @@ bool QgsLabelSearchTree::insertLabel( LabelPosition* labelPos, int featureId, co
cornerPoints.push_back( QgsPoint( labelPos->getX( i ), labelPos->getY( i ) ) );
}
QgsLabelPosition* newEntry = new QgsLabelPosition( featureId, labelPos->getAlpha(), cornerPoints, QgsRectangle( c_min[0], c_min[1], c_max[0], c_max[1] ),
labelPos->getWidth(), labelPos->getHeight(), layerName, labelPos->getUpsideDown(), diagram );
labelPos->getWidth(), labelPos->getHeight(), layerName, labelPos->getUpsideDown(), diagram, frozen );
mSpatialIndex.Insert( c_min, c_max, newEntry );
return true;
}
Expand Down
5 changes: 4 additions & 1 deletion src/core/qgslabelsearchtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ class CORE_EXPORT QgsLabelSearchTree
/**Returns label position(s) at a given point. QgsLabelSearchTree keeps ownership, don't delete the LabelPositions*/
void label( const QgsPoint& p, QList<QgsLabelPosition*>& posList );

/**Returns label position(s) in given rectangle. QgsLabelSearchTree keeps ownership, don't delete the LabelPositions*/
void labelsInRect( const QgsRectangle& r, QList<QgsLabelPosition*>& posList );

/**Inserts label position. Does not take ownership of labelPos
@return true in case of success*/
bool insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName, bool diagram = false );
bool insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName, bool diagram = false, bool frozen = false );

private:
RTree<QgsLabelPosition*, double, 2, double> mSpatialIndex;
Expand Down
7 changes: 4 additions & 3 deletions src/core/qgsmaprenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ struct QgsDiagramLayerSettings;

struct CORE_EXPORT QgsLabelPosition
{
QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down, bool diagram = false ):
featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), upsideDown( upside_down ), isDiagram( diagram ) {}
QgsLabelPosition(): featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), upsideDown( false ), isDiagram( false ) {}
QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down, bool diagram = false, bool frozen = false ):
featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), upsideDown( upside_down ), isDiagram( diagram ), isFrozen( frozen ) {}
QgsLabelPosition(): featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), upsideDown( false ), isDiagram( false ), isFrozen( false ) {}
int featureId;
double rotation;
QVector< QgsPoint > cornerPoints;
Expand All @@ -55,6 +55,7 @@ struct CORE_EXPORT QgsLabelPosition
QString layerID;
bool upsideDown;
bool isDiagram;
bool isFrozen;
};

/** Labeling engine interface.
Expand Down
30 changes: 28 additions & 2 deletions src/core/qgspallabeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class QgsPalGeometry : public PalGeometry
, mId( id )
, mInfo( NULL )
, mIsDiagram( false )
, mIsFrozen( false )
{
mStrId = FID_TO_STRING( id ).toAscii();
}
Expand Down Expand Up @@ -112,6 +113,9 @@ class QgsPalGeometry : public PalGeometry
void setIsDiagram( bool d ) { mIsDiagram = d; }
bool isDiagram() const { return mIsDiagram; }

void setIsFrozen( bool f ) { mIsFrozen = f; }
bool isFrozen() const { return mIsFrozen; }

void addDiagramAttribute( int index, QVariant value ) { mDiagramAttributes.insert( index, value ); }
const QgsAttributeMap& diagramAttributes() { return mDiagramAttributes; }

Expand All @@ -122,6 +126,7 @@ class QgsPalGeometry : public PalGeometry
QgsFeatureId mId;
LabelInfo* mInfo;
bool mIsDiagram;
bool mIsFrozen;
/**Stores attribute values for data defined properties*/
QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > mDataDefinedValues;

Expand Down Expand Up @@ -706,6 +711,9 @@ void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer, QgsFeature& f
{
lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] );
}

// set geometry's frozen property
lbl->setIsFrozen( dataDefinedPosition );
}

int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c ) const
Expand Down Expand Up @@ -1141,7 +1149,7 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
//for diagrams, remove the additional 'd' at the end of the layer id
QString layerId = layerNameUtf8;
layerId.chop( 1 );
mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, true );
mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, true, false );
}
continue;
}
Expand Down Expand Up @@ -1224,7 +1232,7 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )

if ( mLabelSearchTree )
{
mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName() );
mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName(), false, palGeometry->isFrozen() );
}
}

Expand Down Expand Up @@ -1274,6 +1282,24 @@ QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
return positions;
}

QList<QgsLabelPosition> QgsPalLabeling::labelsWithinRect( const QgsRectangle& r )
{
QList<QgsLabelPosition> positions;

QList<QgsLabelPosition*> positionPointers;
if ( mLabelSearchTree )
{
mLabelSearchTree->labelsInRect( r, positionPointers );
QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
{
positions.push_back( QgsLabelPosition( **pointerIt ) );
}
}

return positions;
}

void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
{
candPoint = mCandPoint;
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgspallabeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class QgsMapToPixel;
class QgsFeature;

#include "qgspoint.h"
#include "qgsrectangle.h"
#include "qgsmaprenderer.h" // definition of QgsLabelingEngineInterface
#include "qgsexpression.h"

Expand Down Expand Up @@ -224,6 +225,8 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
virtual void exit();
//! return infos about labels at a given (map) position
virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p );
//! return infos about labels within a given (map) rectangle
virtual QList<QgsLabelPosition> labelsWithinRect( const QgsRectangle& r );

//! called when passing engine among map renderers
virtual QgsLabelingEngineInterface* clone();
Expand Down
37 changes: 37 additions & 0 deletions src/ui/qgisapp.ui
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@
<bool>false</bool>
</attribute>
<addaction name="mActionLabeling"/>
<addaction name="mActionShowFrozenLabels"/>
<addaction name="mActionFreezeLabels"/>
<addaction name="mActionMoveLabel"/>
<addaction name="mActionRotateLabel"/>
<addaction name="mActionChangeLabelProperties"/>
Expand Down Expand Up @@ -1698,6 +1700,41 @@
<string>Creates a scale bar that is displayed on the map canvas</string>
</property>
</action>
<action name="mActionFreezeLabels">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionFreezeLabels.png</normaloff>:/images/themes/default/mActionFreezeLabels.png</iconset>
</property>
<property name="text">
<string>Freeze or Thaw Labels</string>
</property>
<property name="whatsThis">
<string>Freeze (write label location and optionally rotation) to attribute table.

Click on individual labels, or draw a marquee (labels touching will be included). Actions on in-memory attribute table fields are immediate.

Hold Shift key down to Thaw (write NULLs to attribute table), reverting label to dynamic.
Hold Alt key down to toggle selected labels between Frozen and Thawed states.</string>
</property>
</action>
<action name="mActionShowFrozenLabels">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionShowFrozenLabels.png</normaloff>:/images/themes/default/mActionShowFrozenLabels.png</iconset>
</property>
<property name="text">
<string>Show Frozen Labels</string>
</property>
<property name="toolTip">
<string>Show Frozen Labels</string>
</property>
</action>
<action name="mActionNewBlankProject">
<property name="icon">
<iconset resource="../../images/images.qrc">
Expand Down