[FEATURE]: move/rotate/change label edit tools to interactively change data defined label properties

git-svn-id: http://svn.osgeo.org/qgis/trunk@14697 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
mhugent 2010-11-17 15:01:15 +00:00
parent 2fd7c32a37
commit ac51927071
30 changed files with 1720 additions and 81 deletions

View File

@ -123,6 +123,7 @@
<file>themes/default/mActionCaptureLine.png</file>
<file>themes/default/mActionCapturePoint.png</file>
<file>themes/default/mActionCapturePolygon.png</file>
<file>themes/default/mActionChangeLabelProperties.png</file>
<file>themes/default/mActionCheckQgisVersion.png</file>
<file>themes/default/mActionCollapseTree.png</file>
<file>themes/default/mActionComposerManager.png</file>
@ -168,6 +169,7 @@
<file>themes/default/mActionMergeFeatures.png</file>
<file>themes/default/mActionMergeFeatureAttributes.png</file>
<file>themes/default/mActionMoveFeature.png</file>
<file>themes/default/mActionMoveLabel.png</file>
<file>themes/default/mActionMoveItemContent.png</file>
<file>themes/default/mActionMoveItemsToBottom.png</file>
<file>themes/default/mActionMoveItemsToTop.png</file>
@ -191,6 +193,7 @@
<file>themes/default/mActionRemoveLayer.png</file>
<file>themes/default/mActionRemoveSelectedFeature.png</file>
<file>themes/default/mActionReshape.png</file>
<file>themes/default/mActionRotateLabel.png</file>
<file>themes/default/mActionRotatePointSymbols.png</file>
<file>themes/default/mActionSaveAsPDF.png</file>
<file>themes/default/mActionSaveAsSVG.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -25,11 +25,32 @@ public:
virtual void drawLabeling( QgsRenderContext& context ) = 0;
//! called when we're done with rendering
virtual void exit() = 0;
//! return infos about labels at a given (map) position
//! @note: this method was added in version 1.7
virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p )= 0;
//! called when passing engine among map renderers
virtual QgsLabelingEngineInterface* clone() = 0;
};
struct QgsLabelPosition
{
%TypeHeaderCode
#include <qgsmaprenderer.h>
%End
QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down );
QgsLabelPosition();
int featureId;
double rotation;
QVector< QgsPoint > cornerPoints;
QgsRectangle labelRect;
double width;
double height;
QString layerID;
bool upsideDown;
};
/**
* \class QgsMapRenderer

View File

@ -77,6 +77,10 @@ public:
@note added in QGIS 1.5*/
double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint /Out/) const;
/**Calculates azimut between this point and other one (clockwise in degree, starting from north)
@note: this function has been added in version 1.7*/
double azimuth( const QgsPoint& other );
//! equality operator
bool operator==(const QgsPoint &other);

View File

@ -28,6 +28,7 @@ SET(QGIS_APP_SRCS
qgsidentifyresults.cpp
qgsfeatureaction.cpp
qgslabeldialog.cpp
qgslabelpropertydialog.cpp
qgslabelengineconfigdialog.cpp
qgslabelinggui.cpp
qgslabelpreview.cpp
@ -37,17 +38,21 @@ SET(QGIS_APP_SRCS
qgsmaptooladdring.cpp
qgsmaptoolannotation.cpp
qgsmaptoolcapture.cpp
qgsmaptoolchangelabelproperties.cpp
qgsmaptooldeletering.cpp
qgsmaptooldeletepart.cpp
qgsmaptooldeletevertex.cpp
qgsmaptooledit.cpp
qgsmaptoolformannotation.cpp
qgsmaptoolidentify.cpp
qgsmaptoollabel.cpp
qgsmaptoolmeasureangle.cpp
qgsmaptoolmovefeature.cpp
qgsmaptoolmovelabel.cpp
qgsmaptoolmovevertex.cpp
qgsmaptoolnodetool.cpp
qgsmaptoolreshape.cpp
qgsmaptoolrotatelabel.cpp
qgsmaptoolrotatepointsymbols.cpp
qgsmaptoolselect.cpp
qgsmaptoolselectrectangle.cpp
@ -175,6 +180,7 @@ SET (QGIS_APP_MOC_HDRS
qgsidentifyresults.h
qgsfeatureaction.h
qgslabeldialog.h
qgslabelpropertydialog.h
qgsmanageconnectionsdialog.h
qgsmaptoolidentify.h
qgsmaptoolsplitfeatures.h

View File

@ -212,6 +212,9 @@
#include "qgsmaptoolzoom.h"
#include "qgsmaptoolsimplify.h"
#include "qgsmeasuretool.h"
#include "qgsmaptoolmovelabel.h"
#include "qgsmaptoolrotatelabel.h"
#include "qgsmaptoolchangelabelproperties.h"
//
// Conditional Includes
@ -580,6 +583,8 @@ QgisApp::~QgisApp()
delete mMapTools.mDeletePart;
delete mMapTools.mAddIsland;
delete mMapTools.mNodeTool;
delete mMapTools.mMoveLabel;
delete mMapTools.mChangeLabelProperties;
delete mPythonUtils;
@ -1229,6 +1234,18 @@ void QgisApp::createActions()
mActionAbout->setMenuRole( QAction::AboutRole ); // put in application menu on Mac OS X
connect( mActionAbout, SIGNAL( triggered() ), this, SLOT( about() ) );
mActionMoveLabel = new QAction( getThemeIcon( "mActionMoveLabel.png" ), tr( "Move Label" ), this );
mActionMoveLabel->setStatusTip( tr( "Move labels interactively" ) );
connect( mActionMoveLabel, SIGNAL( triggered() ), this, SLOT( moveLabel() ) );
mActionRotateLabel = new QAction( getThemeIcon( "mActionRotateLabel.png" ), tr( "Rotate Label" ), this );
mActionRotateLabel->setStatusTip( tr( "Rotate labels interactively" ) );
connect( mActionRotateLabel, SIGNAL( triggered() ), this, SLOT( rotateLabel() ) );
mActionChangeLabelProperties = new QAction( getThemeIcon( "mActionChangeLabelProperties.png" ), tr( "Change label" ), this );
mActionChangeLabelProperties->setStatusTip( tr( "Change label properties" ) );
connect( mActionChangeLabelProperties, SIGNAL( triggered() ), this, SLOT( changeLabelProperties() ) );
mActionStyleManagerV2 = new QAction( tr( "Style manager..." ), this );
shortcuts->registerAction( mActionStyleManagerV2 );
mActionStyleManagerV2->setStatusTip( tr( "Show style manager V2" ) );
@ -1349,6 +1366,12 @@ void QgisApp::createActionGroups()
mMapToolGroup->addAction( mActionNodeTool );
mActionRotatePointSymbols->setCheckable( true );
mMapToolGroup->addAction( mActionRotatePointSymbols );
mActionMoveLabel->setCheckable( true );
mMapToolGroup->addAction( mActionMoveLabel );
mActionRotateLabel->setCheckable( true );
mMapToolGroup->addAction( mActionRotateLabel );
mActionChangeLabelProperties->setCheckable( true );
mMapToolGroup->addAction( mActionChangeLabelProperties );
}
void QgisApp::createMenus()
@ -1606,7 +1629,7 @@ void QgisApp::createMenus()
// don't add it yet, wait for a plugin
mDatabaseMenu = new QMenu( tr( "&Database" ) );
// Raster Menu
mRasterMenu = menuBar()->addMenu( tr( "&Raster" ) );
@ -1843,6 +1866,15 @@ void QgisApp::createToolBars()
mHelpToolBar->addAction( mActionHelpContents );
mHelpToolBar->addAction( QWhatsThis::createAction() );
mToolbarMenu->addAction( mHelpToolBar->toggleViewAction() );
//Label Toolbar
mLabelToolBar = addToolBar( tr( "Label" ) );
mLabelToolBar->setIconSize( myIconSize );
mLabelToolBar->setObjectName( "Label" );
mLabelToolBar->addAction( mActionMoveLabel );
mLabelToolBar->addAction( mActionRotateLabel );
mLabelToolBar->addAction( mActionChangeLabelProperties );
mToolbarMenu->addAction( mLabelToolBar->toggleViewAction() );
}
void QgisApp::createStatusBar()
@ -2245,6 +2277,12 @@ void QgisApp::createCanvasTools()
mMapTools.mNodeTool->setAction( mActionNodeTool );
mMapTools.mRotatePointSymbolsTool = new QgsMapToolRotatePointSymbols( mMapCanvas );
mMapTools.mRotatePointSymbolsTool->setAction( mActionRotatePointSymbols );
mMapTools.mMoveLabel = new QgsMapToolMoveLabel( mMapCanvas );
mMapTools.mMoveLabel->setAction( mActionMoveLabel );
mMapTools.mRotateLabel = new QgsMapToolRotateLabel( mMapCanvas );
mMapTools.mRotateLabel->setAction( mActionRotateLabel );
mMapTools.mChangeLabelProperties = new QgsMapToolChangeLabelProperties( mMapCanvas );
mMapTools.mChangeLabelProperties->setAction( mActionChangeLabelProperties );
//ensure that non edit tool is initialised or we will get crashes in some situations
mNonEditMapTool = mMapTools.mPan;
}
@ -4250,6 +4288,21 @@ bool QgisApp::loadAnnotationItemsFromProject( const QDomDocument& doc )
return true;
}
void QgisApp::moveLabel()
{
mMapCanvas->setMapTool( mMapTools.mMoveLabel );
}
void QgisApp::rotateLabel()
{
mMapCanvas->setMapTool( mMapTools.mRotateLabel );
}
void QgisApp::changeLabelProperties()
{
mMapCanvas->setMapTool( mMapTools.mChangeLabelProperties );
}
QList<QgsAnnotationItem*> QgisApp::annotationItems()
{
QList<QgsAnnotationItem*> itemList;

View File

@ -757,6 +757,13 @@ class QgisApp : public QMainWindow
bool loadAnnotationItemsFromProject( const QDomDocument& doc );
//! Activates the move label tool
void moveLabel();
//! Activates rotate label tool
void rotateLabel();
//! Activates label property tool
void changeLabelProperties();
signals:
/** emitted when a key is pressed and we want non widget sublasses to be able
to pick up on this (e.g. maplayer) */
@ -866,6 +873,7 @@ class QgisApp : public QMainWindow
QToolBar *mAttributesToolBar;
QToolBar *mPluginToolBar;
QToolBar *mHelpToolBar;
QToolBar *mLabelToolBar;
// actions for menus and toolbars -----------------
@ -1002,6 +1010,10 @@ class QgisApp : public QMainWindow
QAction *mActionHelpSeparator2;
QAction *mActionAbout;
QAction *mActionMoveLabel;
QAction *mActionRotateLabel;
QAction *mActionChangeLabelProperties;
QAction *mActionUseRendererV2;
QAction *mActionStyleManagerV2;
@ -1069,6 +1081,9 @@ class QgisApp : public QMainWindow
QgsMapTool* mAnnotation;
QgsMapTool* mFormAnnotation;
QgsMapTool* mTextAnnotation;
QgsMapTool* mMoveLabel;
QgsMapTool* mRotateLabel;
QgsMapTool* mChangeLabelProperties;
} mMapTools;
QgsMapTool *mNonEditMapTool;

View File

@ -0,0 +1,76 @@
/***************************************************************************
qgsmaptoolchangelabelproperties.cpp
---------------------------------
begin : 2010-11-11
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsmaptoolchangelabelproperties.h"
#include "qgslabelpropertydialog.h"
#include "qgsmapcanvas.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
QgsMapToolChangeLabelProperties::QgsMapToolChangeLabelProperties( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas )
{
}
QgsMapToolChangeLabelProperties::~QgsMapToolChangeLabelProperties()
{
}
void QgsMapToolChangeLabelProperties::canvasPressEvent( QMouseEvent * e )
{
deleteRubberBands();
if ( !labelAtPosition( e, mCurrentLabelPos ) )
{
return;
}
QgsVectorLayer* vlayer = currentLayer();
if ( !vlayer || !vlayer->isEditable() )
{
return;
}
createRubberBands();
}
void QgsMapToolChangeLabelProperties::canvasReleaseEvent( QMouseEvent * e )
{
QgsVectorLayer* vlayer = currentLayer();
if ( mLabelRubberBand && mCanvas && vlayer )
{
QgsLabelPropertyDialog d( mCurrentLabelPos.layerID, mCurrentLabelPos.featureId, mCanvas->mapRenderer() );
if ( d.exec() == QDialog::Accepted )
{
const QgsAttributeMap& changes = d.changedProperties();
if ( changes.size() > 0 )
{
vlayer->beginEditCommand( tr( "Label properties changed" ) );
QgsAttributeMap::const_iterator changeIt = changes.constBegin();
for ( ; changeIt != changes.constEnd(); ++changeIt )
{
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, changeIt.key(), changeIt.value(), false );
}
vlayer->endEditCommand();
mCanvas->refresh();
}
}
deleteRubberBands();
}
}

View File

@ -0,0 +1,34 @@
/***************************************************************************
qgsmaptoolchangelabelproperties.h
---------------------------------
begin : 2010-11-11
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSMAPTOOLCHANGELABELPROPERTIES_H
#define QGSMAPTOOLCHANGELABELPROPERTIES_H
#include "qgsmaptoollabel.h"
class QgsMapToolChangeLabelProperties: public QgsMapToolLabel
{
public:
QgsMapToolChangeLabelProperties( QgsMapCanvas* canvas );
~QgsMapToolChangeLabelProperties();
virtual void canvasPressEvent( QMouseEvent * e );
virtual void canvasReleaseEvent( QMouseEvent * e );
};
#endif // QGSMAPTOOLCHANGELABEL_H

347
src/app/qgsmaptoollabel.cpp Normal file
View File

@ -0,0 +1,347 @@
/***************************************************************************
qgsmaptoollabel.cpp
--------------------
begin : 2010-11-03
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsmaptoollabel.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerregistry.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>
QgsMapToolLabel::QgsMapToolLabel( QgsMapCanvas* canvas ): QgsMapTool( canvas ), mLabelRubberBand( 0 ), mFeatureRubberBand( 0 ), mFixPointRubberBand( 0 )
{
}
QgsMapToolLabel::~QgsMapToolLabel()
{
delete mLabelRubberBand;
delete mFeatureRubberBand;
delete mFixPointRubberBand;
}
bool QgsMapToolLabel::labelAtPosition( QMouseEvent* e, QgsLabelPosition& p )
{
QgsPoint pt = toMapCoordinates( e->pos() );
QgsLabelingEngineInterface* labelingEngine = mCanvas->mapRenderer()->labelingEngine();
if ( labelingEngine )
{
QList<QgsLabelPosition> labelPosList = labelingEngine->labelsAtPosition( pt );
QList<QgsLabelPosition>::const_iterator posIt = labelPosList.constBegin();
if ( posIt != labelPosList.constEnd() )
{
p = *posIt;
return true;
}
}
return false;
}
void QgsMapToolLabel::createRubberBands( )
{
delete mLabelRubberBand;
delete mFeatureRubberBand;
//label rubber band
QgsRectangle rect = mCurrentLabelPos.labelRect;
mLabelRubberBand = new QgsRubberBand( mCanvas, false );
mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMaximum() ) );
mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMaximum() ) );
mLabelRubberBand->addPoint( QgsPoint( rect.xMaximum(), rect.yMinimum() ) );
mLabelRubberBand->addPoint( QgsPoint( rect.xMinimum(), rect.yMinimum() ) );
mLabelRubberBand->setColor( Qt::green );
mLabelRubberBand->setWidth( 3 );
mLabelRubberBand->show();
//feature rubber band
QgsVectorLayer* vlayer = currentLayer();
if ( vlayer )
{
QgsFeature f;
if ( currentFeature( f, true ) )
{
QgsGeometry* geom = f.geometry();
if ( geom )
{
mFeatureRubberBand = new QgsRubberBand( mCanvas, geom->type() == QGis::Polygon );
mFeatureRubberBand->setColor( Qt::red );
mFeatureRubberBand->setToGeometry( geom, vlayer );
mFeatureRubberBand->show();
}
}
//fixpoint rubber band
QgsPoint fixPoint;
if ( rotationPoint( fixPoint ) )
{
QgsGeometry* pointGeom = QgsGeometry::fromPoint( fixPoint );
mFixPointRubberBand = new QgsRubberBand( mCanvas, false );
mFixPointRubberBand->setColor( Qt::blue );
mFixPointRubberBand->setToGeometry( pointGeom, vlayer );
mFixPointRubberBand->show();
delete pointGeom;
}
}
}
void QgsMapToolLabel::deleteRubberBands()
{
delete mLabelRubberBand; mLabelRubberBand = 0;
delete mFeatureRubberBand; mFeatureRubberBand = 0;
delete mFixPointRubberBand; mFixPointRubberBand = 0;
}
QgsVectorLayer* QgsMapToolLabel::currentLayer()
{
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID ) );
return vlayer;
}
QgsPalLayerSettings& QgsMapToolLabel::currentLabelSettings( bool* ok )
{
QgsVectorLayer* vlayer = currentLayer();
if ( vlayer )
{
QgsPalLabeling* labelEngine = dynamic_cast<QgsPalLabeling*>( mCanvas->mapRenderer()->labelingEngine() );
if ( labelEngine )
{
if ( ok )
{
*ok = true;
}
return labelEngine->layer( mCurrentLabelPos.layerID );
}
}
if ( ok )
{
*ok = false;
}
return mInvalidLabelSettings;
}
QString QgsMapToolLabel::currentLabelText()
{
QgsVectorLayer* vlayer = currentLayer();
if ( !vlayer )
{
return "";
}
QString labelField = vlayer->customProperty( "labeling/fieldName" ).toString();
if ( !labelField.isEmpty() )
{
int labelFieldId = vlayer->fieldNameIndex( labelField );
QgsFeature f;
if ( vlayer->featureAtId( mCurrentLabelPos.featureId, f, false, true ) )
{
return f.attributeMap()[labelFieldId].toString();
}
}
return "";
}
void QgsMapToolLabel::currentAlignment( QString& hali, QString& vali )
{
hali = "Left";
vali = "Bottom";
QgsFeature f;
if ( !currentFeature( f ) )
{
return;
}
const QgsAttributeMap& featureAttributes = f.attributeMap();
bool settingsOk;
QgsPalLayerSettings& labelSettings = currentLabelSettings( &settingsOk );
if ( settingsOk )
{
QMap< QgsPalLayerSettings::DataDefinedProperties, int > ddProperties = labelSettings.dataDefinedProperties;
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator haliIter = ddProperties.find( QgsPalLayerSettings::Hali );
if ( haliIter != ddProperties.constEnd() )
{
hali = featureAttributes[*haliIter].toString();
}
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator valiIter = ddProperties.find( QgsPalLayerSettings::Vali );
if ( valiIter != ddProperties.constEnd() )
{
vali = featureAttributes[*valiIter].toString();
}
}
}
bool QgsMapToolLabel::currentFeature( QgsFeature& f, bool fetchGeom )
{
QgsVectorLayer* vlayer = currentLayer();
if ( !vlayer )
{
return false;
}
return vlayer->featureAtId( mCurrentLabelPos.featureId, f, fetchGeom, true );
}
QFont QgsMapToolLabel::labelFontCurrentFeature()
{
QFont font;
QgsVectorLayer* vlayer = currentLayer();
bool labelSettingsOk;
QgsPalLayerSettings& layerSettings = currentLabelSettings( &labelSettingsOk );
if ( labelSettingsOk && vlayer )
{
font = layerSettings.textFont;
QgsFeature f;
if ( vlayer->featureAtId( mCurrentLabelPos.featureId, f, false, true ) )
{
const QgsAttributeMap& attributes = f.attributeMap();
QMap< QgsPalLayerSettings::DataDefinedProperties, int > ddProperties = layerSettings.dataDefinedProperties;
//size
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator sizeIt = ddProperties.find( QgsPalLayerSettings::Size );
if ( sizeIt != ddProperties.constEnd() )
{
font.setPointSizeF( attributes[*sizeIt].toDouble() );
}
//family
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator familyIt = ddProperties.find( QgsPalLayerSettings::Family );
if ( familyIt != ddProperties.constEnd() )
{
font.setFamily( attributes[*sizeIt].toString() );
}
//underline
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator underlineIt = ddProperties.find( QgsPalLayerSettings::Underline );
if ( familyIt != ddProperties.constEnd() )
{
font.setUnderline( attributes[*underlineIt].toBool() );
}
//strikeout
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator strikeoutIt = ddProperties.find( QgsPalLayerSettings::Strikeout );
if ( strikeoutIt != ddProperties.constEnd() )
{
font.setStrikeOut( attributes[*strikeoutIt].toBool() );
}
//bold
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator boldIt = ddProperties.find( QgsPalLayerSettings::Bold );
if ( boldIt != ddProperties.constEnd() )
{
font.setBold( attributes[*boldIt].toBool() );
}
//italic
QMap< QgsPalLayerSettings::DataDefinedProperties, int >::const_iterator italicIt = ddProperties.find( QgsPalLayerSettings::Italic );
if ( italicIt != ddProperties.constEnd() )
{
font.setItalic( attributes[*italicIt].toBool() );
}
}
}
return font;
}
bool QgsMapToolLabel::rotationPoint( QgsPoint& pos )
{
QVector<QgsPoint> cornerPoints = mCurrentLabelPos.cornerPoints;
if ( cornerPoints.size() < 4 )
{
return false;
}
if ( mCurrentLabelPos.upsideDown )
{
pos = mCurrentLabelPos.cornerPoints.at( 2 );
}
else
{
pos = mCurrentLabelPos.cornerPoints.at( 0 );
}
//adapt pos depending on data defined alignment
QString haliString, valiString;
currentAlignment( haliString, valiString );
QFont labelFont = labelFontCurrentFeature();
QFontMetricsF labelFontMetrics( labelFont );
//label text?
QString labelText = currentLabelText();
bool labelSettingsOk;
QgsPalLayerSettings& labelSettings = currentLabelSettings( &labelSettingsOk );
if ( !labelSettingsOk )
{
return false;
}
double labelSizeX, labelSizeY;
labelSettings.calculateLabelSize( &labelFontMetrics, labelText, labelSizeX, labelSizeY );
double xdiff = 0;
double ydiff = 0;
if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
{
xdiff = labelSizeX / 2.0;
}
else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
{
xdiff = labelSizeX;
}
if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
{
ydiff = labelSizeY;
}
else
{
double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
{
ydiff = labelSizeY * descentRatio;
}
else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
{
ydiff = labelSizeY * descentRatio;
ydiff = labelSizeY * 0.5 * ( 1 - descentRatio );
}
}
double angle = mCurrentLabelPos.rotation;
double xd = xdiff * cos( angle ) - ydiff * sin( angle );
double yd = xdiff * sin( angle ) + ydiff * cos( angle );
if ( mCurrentLabelPos.upsideDown )
{
pos.setX( pos.x() - xd );
pos.setY( pos.y() - yd );
}
else
{
pos.setX( pos.x() + xd );
pos.setY( pos.y() + yd );
}
return true;
}

80
src/app/qgsmaptoollabel.h Normal file
View File

@ -0,0 +1,80 @@
/***************************************************************************
qgsmaptoollabel.h
--------------------
begin : 2010-11-03
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSMAPTOOLLABEL_H
#define QGSMAPTOOLLABEL_H
#include "qgsmaptool.h"
#include "qgsmaprenderer.h"
#include "qgspallabeling.h"
#include "qgspoint.h"
class QgsRubberBand;
/**Base class for map tools that modify label properties*/
class QgsMapToolLabel: public QgsMapTool
{
public:
QgsMapToolLabel( QgsMapCanvas* canvas );
~QgsMapToolLabel();
protected:
QgsRubberBand* mLabelRubberBand;
QgsRubberBand* mFeatureRubberBand;
/**Shows label fixpoint (left/bottom by default)*/
QgsRubberBand* mFixPointRubberBand;
/**Currently dragged label position*/
QgsLabelPosition mCurrentLabelPos;
/**Returns label position for mouse click location
@param e mouse event
@param p out: label position
@return true in case of success, false if no label at this location*/
bool labelAtPosition( QMouseEvent* e, QgsLabelPosition& p );
/**Finds out rotation point of current label position
@return true in case of success*/
bool rotationPoint( QgsPoint& pos );
/**Creates label / feature / fixpoint rubber bands for the current label position*/
void createRubberBands();
/**Removes label / feature / fixpoint rubber bands*/
void deleteRubberBands();
/**Returns vector layer for current label position*/
QgsVectorLayer* currentLayer();
/**Returns layer settings of current label position*/
QgsPalLayerSettings& currentLabelSettings( bool* ok );
QString currentLabelText();
void currentAlignment( QString& hali, QString& vali );
/**Gets vector feature for current label pos
@return true in case of success*/
bool currentFeature( QgsFeature& f, bool fetchGeom = false );
/**Returns the font for the current feature (considering default font and data defined properties*/
QFont labelFontCurrentFeature();
private:
QgsPalLayerSettings mInvalidLabelSettings;
};
#endif // QGSMAPTOOLLABEL_H

View File

@ -0,0 +1,200 @@
/***************************************************************************
qgsmaptoolmovelabel.cpp
-----------------------
begin : 2010-11-03
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsmaptoolmovelabel.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerregistry.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>
QgsMapToolMoveLabel::QgsMapToolMoveLabel( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas )
{
}
QgsMapToolMoveLabel::~QgsMapToolMoveLabel()
{
}
void QgsMapToolMoveLabel::canvasPressEvent( QMouseEvent * e )
{
deleteRubberBands();
if ( !labelAtPosition( e, mCurrentLabelPos ) )
{
return;
}
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
if ( !layer )
{
return;
}
int xCol, yCol;
if ( layerIsMoveable( layer, xCol, yCol ) )
{
mStartPointMapCoords = toMapCoordinates( e->pos() );
mClickOffsetX = mStartPointMapCoords.x() - mCurrentLabelPos.labelRect.xMinimum();
mClickOffsetY = mStartPointMapCoords.y() - mCurrentLabelPos.labelRect.yMinimum();
createRubberBands();
}
}
void QgsMapToolMoveLabel::canvasMoveEvent( QMouseEvent * e )
{
if ( mLabelRubberBand )
{
QgsPoint pointCanvasCoords = toMapCoordinates( e->pos() );
double offsetX = pointCanvasCoords.x() - mStartPointMapCoords.x();
double offsetY = pointCanvasCoords.y() - mStartPointMapCoords.y();
mLabelRubberBand->setTranslationOffset( offsetX, offsetY );
mLabelRubberBand->updatePosition();
mLabelRubberBand->update();
mFixPointRubberBand->setTranslationOffset( offsetX, offsetY );
mFixPointRubberBand->updatePosition();
mFixPointRubberBand->update();
}
}
void QgsMapToolMoveLabel::canvasReleaseEvent( QMouseEvent * e )
{
if ( !mLabelRubberBand )
{
return;
}
deleteRubberBands();
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
if ( !layer )
{
return;
}
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( layer );
if ( !vlayer )
{
return;
}
if ( !vlayer->isEditable() )
{
return;
}
QgsPoint releaseCoords = toMapCoordinates( e->pos() );
double xdiff = releaseCoords.x() - mStartPointMapCoords.x();
double ydiff = releaseCoords.y() - mStartPointMapCoords.y();
int xCol, yCol;
double xPosOrig, yPosOrig;
bool xSuccess, ySuccess;
if ( !dataDefinedPosition( vlayer, mCurrentLabelPos.featureId, xPosOrig, xSuccess, yPosOrig, ySuccess, xCol, yCol ) )
{
return;
}
double xPosNew, yPosNew;
if ( !xSuccess || !ySuccess )
{
xPosNew = releaseCoords.x() - mClickOffsetX;
yPosNew = releaseCoords.y() - mClickOffsetY;
//todo: consider hali/vali if there
}
else
{
xPosNew = xPosOrig + xdiff;
yPosNew = yPosOrig + ydiff;
}
vlayer->beginEditCommand( tr( "Label moved" ) );
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, xCol, xPosNew, false );
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, yCol, yPosNew, false );
vlayer->endEditCommand();
mCanvas->refresh();
}
bool QgsMapToolMoveLabel::dataDefinedPosition( QgsVectorLayer* vlayer, int featureId, double& x, bool& xSuccess, double& y, bool& ySuccess, int& xCol, int& yCol ) const
{
xSuccess = false;
ySuccess = false;
if ( !vlayer )
{
return false;
}
if ( !layerIsMoveable( vlayer, xCol, yCol ) )
{
return false;
}
QgsFeature f;
if ( !vlayer->featureAtId( featureId, f, false, true ) )
{
return false;
}
QgsAttributeMap attributes = f.attributeMap();
x = attributes[xCol].toDouble( &xSuccess );
y = attributes[yCol].toDouble( &ySuccess );
return true;
}
bool QgsMapToolMoveLabel::layerIsMoveable( 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;
}

View File

@ -0,0 +1,61 @@
/***************************************************************************
qgsmaptoolmovelabel.h
--------------------
begin : 2010-11-03
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSMAPTOOLMOVELABEL_H
#define QGSMAPTOOLMOVELABEL_H
#include "qgsmaptoollabel.h"
/**A map tool for dragging label positions*/
class QgsMapToolMoveLabel: public QgsMapToolLabel
{
public:
QgsMapToolMoveLabel( QgsMapCanvas* canvas );
~QgsMapToolMoveLabel();
virtual void canvasPressEvent( QMouseEvent * e );
virtual void canvasMoveEvent( QMouseEvent * e );
virtual void canvasReleaseEvent( QMouseEvent * e );
protected:
/**Get data defined position of a feature
@param layerId layer identification string
@param x out: data defined x-coordinate
@param xSuccess out: false if attribute value is NULL
@param y out: data defined y-coordinate
@param ySuccess out: false if attribute value is NULL
@param xCol out: index of the x position column
@param yCol out: index of the y position column
@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 true if layer move can be applied to a layer
@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 labels of layer can be moved*/
bool layerIsMoveable( const QgsMapLayer* ml, int& xCol, int& yCol ) const;
/**Start point of the move in map coordinates*/
QgsPoint mStartPointMapCoords;
double mClickOffsetX;
double mClickOffsetY;
};
#endif // QGSMAPTOOLMOVELABEL_H

View File

@ -0,0 +1,207 @@
/***************************************************************************
qgsmaptoolrotatelabel.cpp
-------------------------
begin : 2010-11-09
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 "qgsmaptoolrotatelabel.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerregistry.h"
#include "qgspallabeling.h"
#include "qgspointrotationitem.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>
#include "qgisapp.h"
QgsMapToolRotateLabel::QgsMapToolRotateLabel( QgsMapCanvas* canvas ): QgsMapToolLabel( canvas ), mRotationItem( 0 )
{
}
QgsMapToolRotateLabel::~QgsMapToolRotateLabel()
{
delete mRotationItem;
}
void QgsMapToolRotateLabel::canvasPressEvent( QMouseEvent * e )
{
deleteRubberBands();
if ( !labelAtPosition( e, mCurrentLabelPos ) )
{
return;
}
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID ) );
if ( !vlayer )
{
return;
}
if ( !rotationPoint( mRotationPoint ) )
{
return;
}
int rotationCol;
if ( layerIsRotatable( vlayer, rotationCol ) )
{
mCurrentMouseAzimuth = azimuthToCCW( mRotationPoint.azimuth( toMapCoordinates( e->pos() ) ) );
bool hasRotationValue;
if ( dataDefinedRotation( vlayer, mCurrentLabelPos.featureId, mCurrentRotation, hasRotationValue ) )
{
if ( !hasRotationValue )
{
mCurrentRotation = 0;
}
createRubberBands();
mRotationItem = new QgsPointRotationItem( mCanvas );
mRotationItem->setOrientation( QgsPointRotationItem::Counterclockwise );
mRotationItem->setSymbol( QgisApp::instance()->getThemePixmap( "mActionRotatePointSymbols.png" ).toImage() );
mRotationItem->setPointLocation( mRotationPoint );
mRotationItem->setSymbolRotation( mCurrentRotation );
}
}
}
void QgsMapToolRotateLabel::canvasMoveEvent( QMouseEvent * e )
{
if ( mLabelRubberBand )
{
QgsPoint currentPoint = toMapCoordinates( e->pos() );
double azimuth = azimuthToCCW( mRotationPoint.azimuth( currentPoint ) );
double azimuthDiff = azimuth - mCurrentMouseAzimuth;
azimuthDiff = azimuthDiff > 180 ? azimuthDiff - 360 : azimuthDiff;
mCurrentRotation += azimuthDiff;
mCurrentRotation = mCurrentRotation - static_cast<float>( static_cast<int>( mCurrentRotation / 360 ) ) * 360; //mCurrentRotation % 360;
mCurrentRotation = mCurrentRotation < 0 ? 360 - mCurrentRotation : mCurrentRotation;
mCurrentMouseAzimuth = azimuth - static_cast<float>( static_cast<int>( azimuth / 360 ) ) * 360;
//if shift-modifier is pressed, round to 15 degrees
int displayValue;
if ( e->modifiers() & Qt::ControlModifier )
{
displayValue = roundTo15Degrees( mCurrentRotation );
mCtrlPressed = true;
}
else
{
displayValue = ( int )( mCurrentRotation );
mCtrlPressed = false;
}
if ( mRotationItem )
{
mRotationItem->setSymbolRotation( displayValue );
mRotationItem->update();
}
}
}
void QgsMapToolRotateLabel::canvasReleaseEvent( QMouseEvent * e )
{
deleteRubberBands();
delete mRotationItem;
mRotationItem = 0;
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( mCurrentLabelPos.layerID );
if ( !layer )
{
return;
}
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( layer );
if ( !vlayer )
{
return;
}
int rotationCol;
if ( !layerIsRotatable( vlayer, rotationCol ) )
{
return;
}
double rotation = mCtrlPressed ? roundTo15Degrees( mCurrentRotation ) : mCurrentRotation;
vlayer->beginEditCommand( tr( "Label rotated" ) );
vlayer->changeAttributeValue( mCurrentLabelPos.featureId, rotationCol, mCurrentRotation, false );
vlayer->endEditCommand();
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();
rotation = attributes[rotationCol].toDouble( &rotationSuccess );
return true;
}
int QgsMapToolRotateLabel::roundTo15Degrees( double n )
{
int m = ( int )( n / 15.0 + 0.5 );
return ( m * 15 );
}
double QgsMapToolRotateLabel::azimuthToCCW( double a )
{
return ( a > 0 ? 360 - a : -a );
}

View File

@ -0,0 +1,59 @@
/***************************************************************************
qgsmaptoolrotatelabel.h
-----------------------
begin : 2010-11-09
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSMAPTOOLROTATELABEL_H
#define QGSMAPTOOLROTATELABEL_H
#include "qgsmaptoollabel.h"
class QgsPointRotationItem;
class QgsMapToolRotateLabel: public QgsMapToolLabel
{
public:
QgsMapToolRotateLabel( QgsMapCanvas* canvas );
~QgsMapToolRotateLabel();
virtual void canvasPressEvent( QMouseEvent * e );
virtual void canvasMoveEvent( QMouseEvent * e );
virtual void canvasReleaseEvent( QMouseEvent * e );
protected:
/**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;
/**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*/
static double azimuthToCCW( double a );
double mCurrentRotation;
double mCurrentMouseAzimuth;
QgsPoint mRotationPoint;
QgsPointRotationItem* mRotationItem;
/**True if ctrl was pressed during the last mouse move event*/
bool mCtrlPressed;
};
#endif // QGSMAPTOOLROTATELABEL_H

View File

@ -22,7 +22,7 @@
#include <math.h>
#endif
QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas* canvas ): QgsMapCanvasItem( canvas ), mRotation( 0.0 )
QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas* canvas ): QgsMapCanvasItem( canvas ), mOrientation( Clockwise ), mRotation( 0.0 )
{
//setup font
mFont.setPointSize( 12 );
@ -55,12 +55,11 @@ void QgsPointRotationItem::paint( QPainter * painter )
{
h = sqrt(( double ) mPixmap.width() * mPixmap.width() + mPixmap.height() * mPixmap.height() ) / 2; //the half of the item diagonal
dAngel = acos( mPixmap.width() / ( h * 2 ) ) * 180 / M_PI; //the diagonal angel of the original rect
x = h * cos(( mRotation - dAngel ) * M_PI / 180 );
y = h * sin(( mRotation - dAngel ) * M_PI / 180 );
x = h * cos(( painterRotation( mRotation ) - dAngel ) * M_PI / 180 );
y = h * sin(( painterRotation( mRotation ) - dAngel ) * M_PI / 180 );
}
//painter->translate(-mPixmap.width() / 2.0, -mPixmap.width() / 2.0);
painter->rotate( mRotation );
painter->rotate( painterRotation( mRotation ) );
painter->translate( x - mPixmap.width() / 2.0, -y - mPixmap.height() / 2.0 );
painter->drawPixmap( 0, 0, mPixmap );
@ -109,3 +108,13 @@ void QgsPointRotationItem::setSymbol( const QImage& symbolImage )
}
}
int QgsPointRotationItem::painterRotation( int rotation ) const
{
if ( mOrientation == Clockwise )
{
return rotation;
}
return 360 - ( rotation % 360 );
}

View File

@ -24,6 +24,13 @@
class QgsPointRotationItem: public QgsMapCanvasItem
{
public:
enum Orientation
{
Clockwise = 0,
Counterclockwise
};
QgsPointRotationItem( QgsMapCanvas* canvas );
~QgsPointRotationItem();
@ -39,8 +46,15 @@ class QgsPointRotationItem: public QgsMapCanvasItem
/**Sets rotation symbol from image (takes ownership)*/
void setSymbol( const QImage& symbolImage );
void setOrientation( Orientation o ) { mOrientation = o; }
Orientation orientation() const { return mOrientation; }
private:
QgsPointRotationItem();
/**Converts rotation into QPainter rotation considering mOrientation*/
int painterRotation( int rotation ) const;
/**Clockwise (default) or counterclockwise*/
Orientation mOrientation;
/**Font to display the numerical rotation values*/
QFont mFont;
/**Symboll pixmap*/

View File

@ -53,6 +53,7 @@ SET(QGIS_CORE_SRCS
qgshttptransaction.cpp
qgslabel.cpp
qgslabelattributes.cpp
qgslabelsearchtree.cpp
qgslogger.cpp
qgsmaplayer.cpp
qgsmaplayerregistry.cpp

View File

@ -55,7 +55,7 @@
namespace pal
{
LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed )
: id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed )
: id( id ), cost( cost ), feature( feature ), nbOverlap( 0 ), alpha( alpha ), w( w ), h( h ), nextPart( NULL ), partId( -1 ), reversed( isReversed ), upsideDown( false )
{
// alpha take his value bw 0 and 2*pi rad
@ -111,6 +111,8 @@ namespace pal
x[3] = tx;
y[3] = ty;
upsideDown = true;
if ( this->alpha < M_PI )
this->alpha += M_PI;
else
@ -137,6 +139,7 @@ namespace pal
else
nextPart = NULL;
partId = other.partId;
upsideDown = other.upsideDown;
}
bool LabelPosition::isIn( double *bbox )

View File

@ -79,6 +79,8 @@ namespace pal
//if the layer arrangement is P_LINE
bool reversed;
bool upsideDown;
bool isInConflictSinglePart( LabelPosition* lp );
bool isInConflictMultiPart( LabelPosition* lp );
@ -196,6 +198,7 @@ namespace pal
*/
double getAlpha() const;
bool getReversed() const { return reversed; }
bool getUpsideDown() const { return upsideDown; }
void print();

View File

@ -0,0 +1,62 @@
#include "qgslabelsearchtree.h"
#include "labelposition.h"
bool searchCallback( QgsLabelPosition* pos, void* context )
{
QList<QgsLabelPosition*>* list = static_cast< QList<QgsLabelPosition*>* >( context );
list->push_back( pos );
return true;
}
QgsLabelSearchTree::QgsLabelSearchTree()
{
}
QgsLabelSearchTree::~QgsLabelSearchTree()
{
clear();
}
void QgsLabelSearchTree::label( const QgsPoint& p, QList<QgsLabelPosition*>& posList )
{
double c_min[2]; c_min[0] = p.x() - 1; c_min[1] = p.y() - 1;
double c_max[2]; c_max[0] = p.x() + 1; c_max[1] = p.y() + 1;
mSearchResults.clear();
mSpatialIndex.Search( c_min, c_max, searchCallback, &mSearchResults );
posList = mSearchResults;
}
bool QgsLabelSearchTree::insertLabel( LabelPosition* labelPos, int featureId, const QString& layerName )
{
if ( !labelPos )
{
return false;
}
double c_min[2];
double c_max[2];
labelPos->getBoundingBox( c_min, c_max );
QVector<QgsPoint> cornerPoints;
for ( int i = 0; i < 4; ++i )
{
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() );
mSpatialIndex.Insert( c_min, c_max, newEntry );
return true;
}
void QgsLabelSearchTree::clear()
{
RTree<QgsLabelPosition*, double, 2, double>::Iterator indexIt;
mSpatialIndex.GetFirst( indexIt );
while ( !mSpatialIndex.IsNull( indexIt ) )
{
delete mSpatialIndex.GetAt( indexIt );
mSpatialIndex.GetNext( indexIt );
}
mSpatialIndex.RemoveAll();
}

View File

@ -0,0 +1,54 @@
/***************************************************************************
qgslabelsearchtree.h
Node for raster calculator tree
--------------------
begin : 2010-11-02
copyright : (C) 2010 by Marco Hugentobler
email : marco dot hugentobler at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* 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 QGSLABELSEARCHTREE_H
#define QGSLABELSEARCHTREE_H
#include "qgspoint.h"
#include "qgsmaprenderer.h"
#include <QList>
#include <QVector>
#include <pointset.h>
#include <labelposition.h>
#include "qgsrectangle.h"
using namespace pal;
/**A class to query the labeling structure at a given point (small wraper around pal RTree class)*/
class QgsLabelSearchTree
{
public:
QgsLabelSearchTree();
~QgsLabelSearchTree();
/**Removes and deletes all the entries*/
void clear();
/**Returns label position(s) at a given point. QgsLabelSearchTree keeps ownership, don't delete the LabelPositions*/
void label( const QgsPoint& p, 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 );
private:
RTree<QgsLabelPosition*, double, 2, double> mSpatialIndex;
QList<QgsLabelPosition*> mSearchResults;
};
#endif // QGSLABELTREE_H

View File

@ -19,6 +19,7 @@
#include <QSize>
#include <QStringList>
#include <QVector>
#include "qgis.h"
#include "qgsrectangle.h"
@ -38,12 +39,28 @@ class QgsOverlayObjectPositionManager;
class QgsVectorLayer;
class QgsFeature;
struct QgsLabelPosition
{
QgsLabelPosition( int id, double r, const QVector< QgsPoint >& corners, const QgsRectangle& rect, double w, double h, const QString& layer, bool upside_down ):
featureId( id ), rotation( r ), cornerPoints( corners ), labelRect( rect ), width( w ), height( h ), layerID( layer ), upsideDown( upside_down ) {}
QgsLabelPosition(): featureId( -1 ), rotation( 0 ), labelRect( QgsRectangle() ), width( 0 ), height( 0 ), layerID( "" ), upsideDown( false ) {}
int featureId;
double rotation;
QVector< QgsPoint > cornerPoints;
QgsRectangle labelRect;
double width;
double height;
QString layerID;
bool upsideDown;
};
/** Labeling engine interface.
* \note Added in QGIS v1.4
*/
class QgsLabelingEngineInterface
{
public:
virtual ~QgsLabelingEngineInterface() {}
//! called when we're going to start with rendering
@ -59,6 +76,9 @@ class QgsLabelingEngineInterface
virtual void drawLabeling( QgsRenderContext& context ) = 0;
//! called when we're done with rendering
virtual void exit() = 0;
//! return infos about labels at a given (map) position
//! @note: this method was added in version 1.7
virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p ) = 0;
//! called when passing engine among map renderers
virtual QgsLabelingEngineInterface* clone() = 0;

View File

@ -38,6 +38,7 @@
#include <QTime>
#include <QPainter>
#include "qgslabelsearchtree.h"
#include <qgslogger.h>
#include <qgsvectorlayer.h>
#include <qgsmaplayerregistry.h>
@ -453,6 +454,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
bool dataDefinedPosition = false;
bool dataDefinedRotation = false;
double xPos, yPos, angle;
bool ddXPos, ddYPos;
QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX );
if ( dPosXIt != dataDefinedProperties.constEnd() )
@ -460,74 +462,77 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY );
if ( dPosYIt != dataDefinedProperties.constEnd() )
{
//data defined position
dataDefinedPosition = true;
xPos = f.attributeMap().value( *dPosXIt ).toDouble();
yPos = f.attributeMap().value( *dPosYIt ).toDouble();
//data defined position. But field values could be NULL -> positions will be generated by PAL
xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos );
yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos );
//x/y shift in case of alignment
double xdiff = 0;
double ydiff = 0;
//horizontal alignment
QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
if ( haliIt != dataDefinedProperties.end() )
if ( ddXPos && ddYPos )
{
QString haliString = f.attributeMap().value( *haliIt ).toString();
if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
{
xdiff -= labelX / 2.0;
}
else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
{
xdiff -= labelX;
}
}
dataDefinedPosition = true;
//x/y shift in case of alignment
double xdiff = 0;
double ydiff = 0;
//vertical alignment
QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
if ( valiIt != dataDefinedProperties.constEnd() )
{
QString valiString = f.attributeMap().value( *valiIt ).toString();
if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
//horizontal alignment
QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali );
if ( haliIt != dataDefinedProperties.end() )
{
if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
QString haliString = f.attributeMap().value( *haliIt ).toString();
if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY;
xdiff -= labelX / 2.0;
}
else
else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
{
QFontMetrics labelFontMetrics( labelFont );
double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
xdiff -= labelX;
}
}
if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
//vertical alignment
QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali );
if ( valiIt != dataDefinedProperties.constEnd() )
{
QString valiString = f.attributeMap().value( *valiIt ).toString();
if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
{
if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY * descentRatio;
ydiff -= labelY;
}
else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
else
{
ydiff -= labelY * descentRatio;
ydiff -= labelY * 0.5 * ( 1 - descentRatio );
QFontMetrics labelFontMetrics( labelFont );
double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height();
if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY * descentRatio;
}
else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY * descentRatio;
ydiff -= labelY * 0.5 * ( 1 - descentRatio );
}
}
}
}
}
//data defined rotation?
QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
if ( rotIt != dataDefinedProperties.constEnd() )
{
dataDefinedRotation = true;
angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
//adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
double xd = xdiff * cos( angle ) - ydiff * sin( angle );
double yd = xdiff * sin( angle ) + ydiff * cos( angle );
xdiff = xd;
ydiff = yd;
}
//data defined rotation?
QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation );
if ( rotIt != dataDefinedProperties.constEnd() )
{
dataDefinedRotation = true;
angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180;
//adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
double xd = xdiff * cos( angle ) - ydiff * sin( angle );
double yd = xdiff * sin( angle ) + ydiff * cos( angle );
xdiff = xd;
ydiff = yd;
}
yPos += ydiff;
xPos += xdiff;
yPos += ydiff;
xPos += xdiff;
}
}
}
@ -615,6 +620,8 @@ QgsPalLabeling::QgsPalLabeling()
mShowingCandidates = false;
mShowingAllLabels = false;
mLabelSearchTree = new QgsLabelSearchTree();
}
@ -622,6 +629,8 @@ QgsPalLabeling::~QgsPalLabeling()
{
// make sure we've freed everything
exit();
delete mLabelSearchTree;
mLabelSearchTree = NULL;
}
@ -752,6 +761,8 @@ void QgsPalLabeling::init( QgsMapRenderer* mr )
mPal->setPointP( mCandPoint );
mPal->setLineP( mCandLine );
mPal->setPolyP( mCandPolygon );
mActiveLayers.clear();
}
void QgsPalLabeling::exit()
@ -761,14 +772,15 @@ void QgsPalLabeling::exit()
mMapRenderer = NULL;
}
QgsPalLayerSettings& QgsPalLabeling::layer( const char* layerName )
QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
{
QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
{
QgsPalLayerSettings& lyr = lit.value();
if ( lyr.palLayer->getName() == layerName )
return lyr;
if ( lit.key() && lit.key()->getLayerID() == layerName )
{
return lit.value();
}
}
return mInvalidLayerSettings;
}
@ -780,6 +792,11 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
QPainter* painter = context.painter();
QgsRectangle extent = context.extent();
if ( mLabelSearchTree )
{
mLabelSearchTree->clear();
}
QTime t;
t.start();
@ -797,7 +814,7 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
catch ( std::exception& e )
{
QgsDebugMsg( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ) );
mActiveLayers.clear(); // clean up
//mActiveLayers.clear(); // clean up
return;
}
@ -915,6 +932,11 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
drawLabel( *it, painter, fontForLabel, fontColor, xform, bufferSize, bufferColor, true );
drawLabel( *it, painter, fontForLabel, fontColor, xform );
if ( mLabelSearchTree )
{
mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName() );
}
}
QgsDebugMsg( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ) );
@ -932,10 +954,28 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
lyr.geometries.clear();
}
// labeling is done: clear the active layers hashtable
mActiveLayers.clear();
// mActiveLayers.clear();
}
QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
{
QList<QgsLabelPosition> positions;
QList<QgsLabelPosition*> positionPointers;
if ( mLabelSearchTree )
{
mLabelSearchTree->label( p, 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;
@ -1055,14 +1095,6 @@ void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QPainter* painter, co
void QgsPalLabeling::drawLabelBuffer( QPainter* p, QString text, const QFont& font, double size, QColor color )
{
/*
p->setFont( font );
p->setPen( color );
for (int x = -size; x <= size; x++)
for (int y = -size; y <= size; y++)
p->drawText(x,y, text);
*/
QPainterPath path;
path.addText( 0, 0, font, text );
QPen pen( color );

View File

@ -26,6 +26,7 @@ class QPainter;
class QgsMapRenderer;
class QgsRectangle;
class QgsCoordinateTransform;
class QgsLabelSearchTree;
#include <QString>
#include <QFont>
@ -168,7 +169,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
QgsPalLabeling();
~QgsPalLabeling();
QgsPalLayerSettings& layer( const char* layerName );
QgsPalLayerSettings& layer( const QString& layerName );
void numCandidatePositions( int& candPoint, int& candLine, int& candPolygon );
void setNumCandidatePositions( int candPoint, int candLine, int candPolygon );
@ -199,6 +200,8 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
virtual void drawLabeling( QgsRenderContext& context );
//! called when we're done with rendering
virtual void exit();
//! return infos about labels at a given (map) position
virtual QList<QgsLabelPosition> labelsAtPosition( const QgsPoint& p );
//! called when passing engine among map renderers
virtual QgsLabelingEngineInterface* clone();
@ -214,7 +217,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
void initPal();
protected:
// temporary hashtable of layer settings, being filled during labeling, cleared once labeling's done
// hashtable of layer settings, being filled during labeling
QHash<QgsVectorLayer*, QgsPalLayerSettings> mActiveLayers;
QgsPalLayerSettings mInvalidLayerSettings;
@ -229,6 +232,8 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
bool mShowingCandidates;
bool mShowingAllLabels; // whether to avoid collisions or not
QgsLabelSearchTree* mLabelSearchTree;
};
#endif // QGSPALLABELING_H

View File

@ -43,7 +43,7 @@ QgsVector QgsVector::operator-( void ) const
QgsVector QgsVector::operator*( double scalar ) const
{
return QgsVector( m_x*scalar, m_y*scalar );
return QgsVector( m_x * scalar, m_y * scalar );
}
QgsVector QgsVector::operator/( double scalar ) const
@ -53,12 +53,12 @@ QgsVector QgsVector::operator/( double scalar ) const
double QgsVector::operator*( QgsVector v ) const
{
return m_x*v.m_x + m_y*v.m_y;
return m_x * v.m_x + m_y * v.m_y;
}
double QgsVector::length() const
{
return sqrt( m_x*m_x + m_y*m_y );
return sqrt( m_x * m_x + m_y * m_y );
}
double QgsVector::x() const
@ -80,7 +80,7 @@ QgsVector QgsVector::perpVector() const
double QgsVector::angle( void ) const
{
double ang = atan2( m_y, m_x );
return ang < 0.0 ? ang + 2.0*M_PI : ang;
return ang < 0.0 ? ang + 2.0 * M_PI : ang;
}
double QgsVector::angle( QgsVector v ) const
@ -92,7 +92,7 @@ QgsVector QgsVector::rotateBy( double rot ) const
{
double ang = atan2( m_y, m_x ) + rot;
double len = length();
return QgsVector( len*cos( ang ), len*sin( ang ) );
return QgsVector( len * cos( ang ), len * sin( ang ) );
}
QgsVector QgsVector::normal() const
@ -167,7 +167,7 @@ QString QgsPoint::wellKnownText() const
double QgsPoint::sqrDist( double x, double y ) const
{
return ( m_x - x )*( m_x - x ) + ( m_y - y )*( m_y - y );
return ( m_x - x ) * ( m_x - x ) + ( m_y - y ) * ( m_y - y );
}
double QgsPoint::sqrDist( const QgsPoint& other ) const
@ -175,6 +175,13 @@ double QgsPoint::sqrDist( const QgsPoint& other ) const
return sqrDist( other.x(), other.y() );
}
double QgsPoint::azimuth( const QgsPoint& other )
{
double dx = other.x() - m_x;
double dy = other.y() - m_y;
return ( atan2( dx, dy ) * 180.0 / M_PI );
}
// operators
bool QgsPoint::operator==( const QgsPoint & other )
{

View File

@ -148,6 +148,10 @@ class CORE_EXPORT QgsPoint
@note added in QGIS 1.5*/
double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint ) const;
/**Calculates azimut between this point and other one (clockwise in degree, starting from north)
@note: this function has been added in version 1.7*/
double azimuth( const QgsPoint& other );
//! equality operator
bool operator==( const QgsPoint &other );

View File

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsLabelPropertyDialogBase</class>
<widget class="QDialog" name="QgsLabelPropertyDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>284</width>
<height>411</height>
</rect>
</property>
<property name="windowTitle">
<string>Label properties</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="mLabelTextLabel">
<property name="text">
<string>Text</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mLabelTextLineEdit"/>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="mFontGroupBox">
<property name="title">
<string>Font</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="mFontSizeLabel">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="mFontSizeSpinBox">
<property name="maximum">
<double>999999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="mFontPushButton">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsColorButton" name="mFontColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="mBufferGroupBox">
<property name="title">
<string>Buffer</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="mBufferSizeLabel">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="mBufferSizeSpinBox">
<property name="maximum">
<double>999999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="QgsColorButton" name="mBufferColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="mPositionGroupBlox">
<property name="title">
<string>Position</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="mLabelDistanceLabel">
<property name="text">
<string>Label distance</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="mLabelDistanceSpinBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mXCoordLabel">
<property name="text">
<string>X Coordinate</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="mXCoordSpinBox">
<property name="minimum">
<double>-999999999.000000000000000</double>
</property>
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mYCoordLabel">
<property name="text">
<string>Y Coordinate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="mYCoordSpinBox">
<property name="minimum">
<double>-999999999.000000000000000</double>
</property>
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mHaliLabel">
<property name="text">
<string>Horizontal alignment</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="mHaliComboBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mValiLabel">
<property name="text">
<string>Vertical alignment</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="mValiComboBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="mRotationLabel">
<property name="text">
<string>Rotation</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="mRotationSpinBox">
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsColorButton</class>
<extends>QToolButton</extends>
<header>qgscolorbutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsLabelPropertyDialogBase</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsLabelPropertyDialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>