[FEATURE]: add tool to graphically rotate points (by changing the value of the rotation field). Needs some more beautifying, perhaps a nicer arrow

git-svn-id: http://svn.osgeo.org/qgis/trunk@11671 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
mhugent 2009-09-16 19:02:26 +00:00
parent 1669b15f6a
commit 94e76d8535
8 changed files with 501 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -40,6 +40,7 @@ SET(QGIS_APP_SRCS
qgsmaptoolmovevertex.cpp
qgsmaptoolnodetool.cpp
qgsmaptoolreshape.cpp
qgsmaptoolrotatepointsymbols.cpp
qgsmaptoolselect.cpp
qgsmaptoolsimplify.cpp
qgsmaptoolsplitfeatures.cpp
@ -52,6 +53,7 @@ SET(QGIS_APP_SRCS
qgsogrsublayersdialog.cpp
qgsoptions.cpp
qgspastetransformations.cpp
qgspointrotationitem.cpp
qgspluginitem.cpp
qgspluginmanager.cpp
qgspluginmetadata.cpp

View File

@ -172,6 +172,7 @@
#include "qgsmaptoolpan.h"
#include "qgsmaptoolselect.h"
#include "qgsmaptoolreshape.h"
#include "qgsmaptoolrotatepointsymbols.h"
#include "qgsmaptoolsplitfeatures.h"
#include "qgsmaptoolvertexedit.h"
#include "qgsmaptoolzoom.h"
@ -740,6 +741,12 @@ void QgisApp::createActions()
connect( mActionNodeTool, SIGNAL( triggered() ), this, SLOT( nodeTool() ) );
mActionNodeTool->setEnabled( false );
mActionRotatePointSymbols = new QAction( getThemeIcon( "mActionRotatePointSymbols.png" ), tr( "Rotate Point Symbols" ), this );
shortcuts->registerAction( mActionRotatePointSymbols );
mActionRotatePointSymbols->setStatusTip( tr( "Rotate Point Symbols" ) );
connect( mActionRotatePointSymbols, SIGNAL( triggered() ), this, SLOT( rotatePointSymbols() ) );
mActionRotatePointSymbols->setEnabled( false );
// View Menu Items
mActionPan = new QAction( getThemeIcon( "mActionPan.png" ), tr( "Pan Map" ), this );
@ -1094,7 +1101,8 @@ void QgisApp::createActionGroups()
mMapToolGroup->addAction( mActionMergeFeatures );
mActionNodeTool->setCheckable( true );
mMapToolGroup->addAction( mActionNodeTool );
mActionRotatePointSymbols->setCheckable( true );
mMapToolGroup->addAction( mActionRotatePointSymbols );
}
void QgisApp::createMenus()
@ -1189,6 +1197,7 @@ void QgisApp::createMenus()
mEditMenu->addAction( mActionSplitFeatures );
mEditMenu->addAction( mActionMergeFeatures );
mEditMenu->addAction( mActionNodeTool );
mEditMenu->addAction( mActionRotatePointSymbols );
if ( layout == QDialogButtonBox::GnomeLayout || layout == QDialogButtonBox::MacLayout )
{
@ -1398,6 +1407,7 @@ void QgisApp::createToolBars()
mAdvancedDigitizeToolBar->addAction( mActionSplitFeatures );
mAdvancedDigitizeToolBar->addAction( mActionMergeFeatures );
mAdvancedDigitizeToolBar->addAction( mActionNodeTool );
mAdvancedDigitizeToolBar->addAction( mActionRotatePointSymbols );
mToolbarMenu->addAction( mAdvancedDigitizeToolBar->toggleViewAction() );
@ -1788,6 +1798,8 @@ void QgisApp::createCanvas()
mMapTools.mDeletePart->setAction( mActionDeletePart );
mMapTools.mNodeTool = new QgsMapToolNodeTool( mMapCanvas );
mMapTools.mNodeTool->setAction( mActionNodeTool );
mMapTools.mRotatePointSymbolsTool = new QgsMapToolRotatePointSymbols( mMapCanvas );
mMapTools.mRotatePointSymbolsTool->setAction( mActionRotatePointSymbols );
//ensure that non edit tool is initialised or we will get crashes in some situations
mNonEditMapTool = mMapTools.mPan;
}
@ -4298,6 +4310,11 @@ void QgisApp::nodeTool()
mMapCanvas->setMapTool( mMapTools.mNodeTool );
}
void QgisApp::rotatePointSymbols()
{
mMapCanvas->setMapTool( mMapTools.mRotatePointSymbolsTool );
}
void QgisApp::splitFeatures()
{
mMapCanvas->setMapTool( mMapTools.mSplitFeatures );
@ -5639,11 +5656,19 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionSplitFeatures->setEnabled( false );
mActionSimplifyFeature->setEnabled( false );
mActionDeleteRing->setEnabled( false );
mActionRotatePointSymbols->setEnabled( false );
if ( vlayer->isEditable() && dprovider->capabilities() & QgsVectorDataProvider::ChangeGeometries )
{
mActionMoveVertex->setEnabled( true );
}
if ( vlayer->isEditable() && dprovider->capabilities() & QgsVectorDataProvider::ChangeAttributeValues )
{
if ( QgsMapToolRotatePointSymbols::layerIsRotatable( vlayer ) )
{
mActionRotatePointSymbols->setEnabled( true );
}
}
return;
}
else if ( vlayer->geometryType() == QGis::Line )
@ -5752,6 +5777,9 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionCopyFeatures->setEnabled( false );
mActionCutFeatures->setEnabled( false );
mActionPasteFeatures->setEnabled( false );
mActionRotatePointSymbols->setEnabled( false );
mActionNodeTool->setEnabled( false );
mActionDeletePart->setEnabled( false );
//NOTE: This check does not really add any protection, as it is called on load not on layer select/activate
//If you load a layer with a provider and idenitfy ability then load another without, the tool would be disabled for both

View File

@ -523,6 +523,8 @@ class QgisApp : public QMainWindow
void mergeSelectedFeatures();
//! provides operations with nodes
void nodeTool();
//! activates the rotate points tool
void rotatePointSymbols();
//! activates the selection tool
void select();
@ -742,6 +744,7 @@ class QgisApp : public QMainWindow
QAction *mActionDeletePart;
QAction *mActionMergeFeatures;
QAction *mActionNodeTool;
QAction *mActionRotatePointSymbols;
QAction *mActionEditSeparator3;
QAction *mActionPan;
@ -864,6 +867,7 @@ class QgisApp : public QMainWindow
QgsMapTool* mDeleteRing;
QgsMapTool* mDeletePart;
QgsMapTool* mNodeTool;
QgsMapTool* mRotatePointSymbolsTool;
} mMapTools;
QgsMapTool *mNonEditMapTool;

View File

@ -0,0 +1,250 @@
/***************************************************************************
qgsmaptoolrotatepointsymbols.cpp
--------------------------------
begin : September 2009
copyright : (C) 2009 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************
* *
* 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 "qgsmaptoolrotatepointsymbols.h"
#include "qgsapplication.h"
#include "qgsmapcanvas.h"
#include "qgspointrotationitem.h"
#include "qgsrenderer.h"
#include "qgssymbol.h"
#include "qgsvectorlayer.h"
#include <QGraphicsPixmapItem>
#include <QMessageBox>
#include <QMouseEvent>
QgsMapToolRotatePointSymbols::QgsMapToolRotatePointSymbols( QgsMapCanvas* canvas ): QgsMapToolEdit( canvas ), \
mActiveLayer( 0 ), mFeatureNumber( 0 ), mCurrentMouseAzimut( 0.0 ), mCurrentRotationFeature( 0.0 ), mRotating( false ), mRotationItem( 0 )
{
}
QgsMapToolRotatePointSymbols::~QgsMapToolRotatePointSymbols()
{
delete mRotationItem;
}
bool QgsMapToolRotatePointSymbols::layerIsRotatable( QgsMapLayer* ml )
{
if ( !ml )
{
return false;
}
//a vector layer
QgsVectorLayer* vLayer = dynamic_cast<QgsVectorLayer*>( ml );
if ( !vLayer )
{
return false;
}
//does it have point or multipoint type?
if ( vLayer->geometryType() != QGis::Point )
{
return false;
}
//does it have a least one rotation attribute?
QList<int> rotationAttributes;
layerRotationAttributes( vLayer, rotationAttributes );
if ( rotationAttributes.size() < 1 )
{
return false;
}
return true;
}
void QgsMapToolRotatePointSymbols::canvasPressEvent( QMouseEvent * e )
{
if ( !mCanvas )
{
return;
}
mActiveLayer = currentVectorLayer();
if ( !mActiveLayer )
{
return;
}
if ( mActiveLayer->geometryType() != QGis::Point || !mActiveLayer->isEditable() )
{
return;
}
//find the closest feature to the pressed position
QgsMapCanvasSnapper canvasSnapper( mCanvas );
QList<QgsSnappingResult> snapResults;
if ( canvasSnapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToVertex, -1 ) != 0 || snapResults.size() < 1 )
{
QMessageBox::critical( 0, tr( "No point feature" ), tr( "No point feature was detected at the clicked position. Please click closer to the feature or enhance the search tolerance under Settings->Options->Digitizing->Serch radius for vertex edits" ) );
return; //error during snapping
}
mFeatureNumber = snapResults.at( 0 ).snappedAtGeometry;
//get list with renderer rotation attributes
if ( layerRotationAttributes( mActiveLayer, mCurrentRotationAttributes ) != 0 )
{
return;
}
if ( mCurrentRotationAttributes.size() < 1 )
{
QMessageBox::critical( 0, tr( "No rotation Attributes" ), tr( "The active point layer does not have a rotation attribute" ) );
return;
}
mSnappedPoint = toCanvasCoordinates( snapResults.at( 0 ).snappedVertex );
//find out initial arrow direction
QgsFeature pointFeature;
if ( !mActiveLayer->featureAtId( mFeatureNumber, pointFeature, false, true ) )
{
return;
}
const QgsAttributeMap pointFeatureAttributes = pointFeature.attributeMap();
const QgsAttributeMap::const_iterator attIt = pointFeatureAttributes.find( mCurrentRotationAttributes.at( 0 ) );
if ( attIt == pointFeatureAttributes.constEnd() )
{
return;
}
mCurrentRotationFeature = attIt.value().toDouble();
createPixmapItem();
if ( mRotationItem )
{
mRotationItem->setPointLocation( snapResults.at( 0 ).snappedVertex );
}
mCurrentMouseAzimut = calculateAzimut( e->pos() );
setPixmapItemRotation( mCurrentMouseAzimut );
mRotating = true;
}
void QgsMapToolRotatePointSymbols::canvasMoveEvent( QMouseEvent * e )
{
if ( !mRotating )
{
return;
}
double azimut = calculateAzimut( e->pos() );
double azimutDiff = azimut - mCurrentMouseAzimut;
//assign new feature rotation, making sure to respect the 0 - 360 degree range
mCurrentRotationFeature += azimutDiff;
if ( mCurrentRotationFeature < 0 )
{
mCurrentRotationFeature = 360 - mCurrentRotationFeature;
}
else if ( mCurrentRotationFeature >= 360 )
{
mCurrentRotationFeature -= 360;
}
mCurrentMouseAzimut = azimut;
if ( mCurrentMouseAzimut < 0 )
{
mCurrentMouseAzimut = 360 - mCurrentMouseAzimut;
}
else if ( mCurrentMouseAzimut >= 360 )
{
mCurrentMouseAzimut -= 360;
}
setPixmapItemRotation( mCurrentRotationFeature );
}
void QgsMapToolRotatePointSymbols::canvasReleaseEvent( QMouseEvent * e )
{
if ( mRotating && mActiveLayer )
{
mActiveLayer->beginEditCommand( tr( "Rotate symbol" ) );
bool rotateSuccess = true;
//write mCurrentRotationFeature to all rotation attributes of feature (mFeatureNumber)
QList<int>::const_iterator it = mCurrentRotationAttributes.constBegin();
for ( ; it != mCurrentRotationAttributes.constEnd(); ++it )
{
if ( !mActiveLayer->changeAttributeValue( mFeatureNumber, *it, mCurrentRotationFeature, true ) )
{
rotateSuccess = false;
}
}
if ( rotateSuccess )
{
mActiveLayer->endEditCommand();
}
else
{
mActiveLayer->destroyEditCommand();
}
}
mRotating = false;
delete mRotationItem;
mRotationItem = 0;
mCanvas->refresh();
}
int QgsMapToolRotatePointSymbols::layerRotationAttributes( const QgsVectorLayer* vl, QList<int>& attList )
{
attList.clear();
if ( !vl )
{
return 1;
}
//get renderer
const QgsRenderer* layerRenderer = vl->renderer();
if ( !layerRenderer )
{
return 2;
}
//get renderer symbols
const QList<QgsSymbol*> rendererSymbols = layerRenderer->symbols();
int currentRotationAttribute;
QList<QgsSymbol*>::const_iterator symbolIt = rendererSymbols.constBegin();
for ( ; symbolIt != rendererSymbols.constEnd(); ++symbolIt )
{
currentRotationAttribute = ( *symbolIt )->rotationClassificationField();
if ( currentRotationAttribute >= 0 )
{
attList.push_back( currentRotationAttribute );
}
}
return 0;
}
double QgsMapToolRotatePointSymbols::calculateAzimut( const QPoint& mousePos )
{
int dx = mousePos.x() - mSnappedPoint.x();
int dy = mousePos.y() - mSnappedPoint.y();
return 180 - atan2( dx, dy ) * 180.0 / M_PI;
}
void QgsMapToolRotatePointSymbols::createPixmapItem()
{
delete mRotationItem;
mRotationItem = new QgsPointRotationItem( mCanvas );
mRotationItem->setSymbol( QgsApplication::defaultThemePath() + "mActionArrowUp.png" );
mCanvas->scene()->addItem( mRotationItem );
}
void QgsMapToolRotatePointSymbols::setPixmapItemRotation( double rotation )
{
mRotationItem->setSymbolRotation( rotation );
mRotationItem->update();
}

View File

@ -0,0 +1,68 @@
/***************************************************************************
qgsmaptoolrotatepointsymbols.h
---------------------
begin : September 2009
copyright : (C) 2009 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************
* *
* 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 QGSMAPTOOLROTATEPOINTSYMBOLS_H
#define QGSMAPTOOLROTATEPOINTSYMBOLS_H
#include "qgsmaptooledit.h"
class QgsPointRotationItem;
/**A class that allows to interactively manipulate the value of the rotation field(s) for point layers*/
class QgsMapToolRotatePointSymbols: public QgsMapToolEdit
{
public:
QgsMapToolRotatePointSymbols( QgsMapCanvas* canvas );
~QgsMapToolRotatePointSymbols();
void canvasPressEvent( QMouseEvent * e );
void canvasMoveEvent( QMouseEvent * e );
void canvasReleaseEvent( QMouseEvent * e );
bool isEditTool() {return true;}
/**Returns true if the symbols of a maplayer can be rotated. This means the layer \
is a vector layer, has type point or multipoint and has at least one rotation attribute in the renderer*/
static bool layerIsRotatable( QgsMapLayer* ml );
private:
QgsVectorLayer* mActiveLayer;
int mFeatureNumber;
/**Last azimut between mouse and edited point*/
double mCurrentMouseAzimut;
/**Last feature rotation*/
double mCurrentRotationFeature;
bool mRotating;
QList<int> mCurrentRotationAttributes;
/**Screen coordinate of the snaped feature*/
QPoint mSnappedPoint;
/**Item that displays rotation during mouse move*/
QgsPointRotationItem* mRotationItem;
/**Finds out the rotation attributes of mActiveLayers
@param vl the point vector layer
@param attList out: the list containing the rotation indices
@return 0 in case of success*/
static int layerRotationAttributes( const QgsVectorLayer* vl, QList<int>& attList );
void drawArrow( double azimut ) const;
/**Calculates the azimut between mousePos and mSnappedPoint*/
double calculateAzimut( const QPoint& mousePos );
/**Create item that shows rotation to the user*/
void createPixmapItem();
/**Sets the rotation of the pixmap item*/
void setPixmapItemRotation( double rotation );
};
#endif // QGSMAPTOOLROTATEPOINTSYMBOLS_H

View File

@ -0,0 +1,97 @@
/***************************************************************************
qgspointrotationitem.cpp
------------------------
begin : September 2009
copyright : (C) 2009 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************
* *
* 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 "qgspointrotationitem.h"
#include <QPainter>
#ifndef Q_OS_MACX
#include <cmath>
#else
#include <math.h>
#endif
QgsPointRotationItem::QgsPointRotationItem( QgsMapCanvas* canvas ): QgsMapCanvasItem( canvas ), mRotation( 0.0 )
{
//setup font
mFont.setPointSize( 12 );
mFont.setBold( true );
}
QgsPointRotationItem::QgsPointRotationItem(): QgsMapCanvasItem( 0 ), mRotation( 0.0 )
{
}
QgsPointRotationItem::~QgsPointRotationItem()
{
}
void QgsPointRotationItem::paint( QPainter * painter )
{
if ( !painter )
{
return;
}
painter->save();
//do a bit of trigonometry to find out how to transform a rotated item such that the center point is at the point feature
double x = 0;
double y = 0;
double h, dAngel;
if ( mPixmap.width() > 0 && mPixmap.height() > 0 )
{
h = sqrt( 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 );
}
//painter->translate(-mPixmap.width() / 2.0, -mPixmap.width() / 2.0);
painter->rotate( mRotation );
painter->translate( x - mPixmap.width() / 2.0, -y - mPixmap.height() / 2.0 );
painter->drawPixmap( 0, 0, mPixmap );
//draw numeric value beside the symbol
painter->restore();
QFontMetricsF fm( mFont );
painter->fillRect( mPixmap.width(), 0, mItemSize.width() - mPixmap.width(), mItemSize.height(), QColor( Qt::white ) );
painter->setFont( mFont );
painter->drawText( mPixmap.width(), mPixmap.height() / 2.0 + fm.height() / 2.0, QString::number( mRotation, 'f', 2 ) );
}
void QgsPointRotationItem::setPointLocation( const QgsPoint& p )
{
QPointF transformedPoint = toCanvasCoordinates( p );
setPos( transformedPoint.x() - mPixmap.width() / 2.0, transformedPoint.y() - mPixmap.height() / 2.0 );
}
void QgsPointRotationItem::setSymbol( const QString& symbolPath )
{
mPixmap = QPixmap( symbolPath );
QFontMetricsF fm( mFont );
mItemSize.setWidth( mPixmap.width() + fm.width( "360.99" ) );
double pixmapHeight = mPixmap.height();
double fontHeight = fm.height();
if ( pixmapHeight >= fontHeight )
{
mItemSize.setHeight( mPixmap.height() );
}
else
{
mItemSize.setHeight( fm.height() );
}
}

View File

@ -0,0 +1,51 @@
/***************************************************************************
qgspointrotationitem.h
----------------------
begin : September 2009
copyright : (C) 2009 by Marco Hugentobler
email : marco at hugis dot net
***************************************************************************
* *
* 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 QGSPOINTROTATIONITEM_H
#define QGSPOINTROTATIONITEM_H
#include "qgsmapcanvasitem.h"
#include <QFontMetricsF>
#include <QPixmap>
/**An item that shows a rotated point symbol (e.g. arrow) centered to a map location together with a text displaying the rotation value*/
class QgsPointRotationItem: public QgsMapCanvasItem
{
public:
QgsPointRotationItem( QgsMapCanvas* canvas );
~QgsPointRotationItem();
void paint( QPainter * painter );
/**Sets the center point of the rotation symbol (in map coordinates)*/
void setPointLocation( const QgsPoint& p );
/**Sets the rotation of the symbol and displays the new rotation number. \
Units are degrees, starting from north direction, clockwise direction*/
void setSymbolRotation( double r ) {mRotation = r;}
/**Sets a symbol from image file*/
void setSymbol( const QString& symbolPath );
private:
QgsPointRotationItem();
/**Font to display the numerical rotation values*/
QFont mFont;
/**Symboll pixmap*/
QPixmap mPixmap;
double mRotation;
};
#endif // QGSPOINTROTATIONITEM_H