Skip to content

Commit

Permalink
Move feature now benefits from Advanced Digitizing
Browse files Browse the repository at this point in the history
QgsMapToolMoveFeature now inherits QgsMapToolAdvancedDigitizing
this allows to specify distance, angles, complex and multiple moves at once
it is now a click and click operation (similarly to the rotate feature map tool): so it can be cancelled once enabled with the right click
  • Loading branch information
3nids committed Aug 11, 2016
1 parent 7ee55a7 commit af1fee5
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 100 deletions.
6 changes: 6 additions & 0 deletions doc/api_break.dox
Expand Up @@ -261,6 +261,12 @@ variant instead.</li>
<li>expandAction() has been removed. Use QgsExpression::replaceExpressionText() instead.</li>
</ul>

\subsection qgis_api_break_3_0_QgsAdvancedDigitizingDockWidget QgsAdvancedDigitizingDockWidget

<ul>
<li>canvasReleaseEvent takes now QgsAdvancedDigitizingDockWidget::CaptureMode as second argument.</li>
</ul>

\subsection qgis_api_break_3_0_QgsAtlasComposition QgsAtlasComposition

<ul>
Expand Down
14 changes: 12 additions & 2 deletions python/gui/qgsadvanceddigitizingdockwidget.sip
Expand Up @@ -49,6 +49,16 @@ class QgsAdvancedDigitizingDockWidget : QDockWidget
Parallel //!< Parallel
};

/**
* Determines if the dock has to record one, two or many points.
*/
enum CaptureMode
{
SinglePoint, //!< Capture a single point (e.g. for point digitizing)
TwoPoints, //!< Capture two points (e.g. for translation)
ManyPoints //!< Capture many points (e.g. line or polygon digitizing)
};

/**
* @brief The CadConstraint is an abstract class for all basic constraints (angle/distance/x/y).
* It contains all values (locked, value, relative) and pointers to corresponding widgets.
Expand Down Expand Up @@ -169,10 +179,10 @@ class QgsAdvancedDigitizingDockWidget : QDockWidget
* Will react on a canvas release event
*
* @param e A mouse event (may be modified)
* @param captureSegment Capture segments?
* @param mode determines if the dock has to record one, two or many points.
* @return If the event is hidden (construction mode hides events from the maptool)
*/
bool canvasReleaseEvent( QgsMapMouseEvent* e , bool captureSegment );
bool canvasReleaseEvent( QgsMapMouseEvent* e , CaptureMode mode );
/**
* Will react on a canvas move event
*
Expand Down
1 change: 1 addition & 0 deletions python/gui/qgsmaptooladvanceddigitizing.sip
Expand Up @@ -32,6 +32,7 @@ class QgsMapToolAdvancedDigitizing : QgsMapToolEdit
{
CaptureNone, //!< Do not capture
CapturePoint, //!< Capture points
CaptureSegment, //!< Capture a segment (i.e. 2 points)
CaptureLine, //!< Capture lines
CapturePolygon //!< Capture polygons
};
Expand Down
168 changes: 83 additions & 85 deletions src/app/qgsmaptoolmovefeature.cpp
Expand Up @@ -23,28 +23,30 @@
#include "qgsvectorlayer.h"
#include "qgstolerance.h"
#include "qgisapp.h"
#include "qgsadvanceddigitizingdockwidget.h"

#include <QMouseEvent>
#include <QSettings>
#include <limits>

QgsMapToolMoveFeature::QgsMapToolMoveFeature( QgsMapCanvas* canvas )
: QgsMapToolEdit( canvas )
: QgsMapToolAdvancedDigitizing( canvas, QgisApp::instance()->cadDockWidget() )
, mRubberBand( nullptr )
{
mToolName = tr( "Move feature" );
mCaptureMode = QgsMapToolAdvancedDigitizing::CaptureSegment;
}

QgsMapToolMoveFeature::~QgsMapToolMoveFeature()
{
delete mRubberBand;
}

void QgsMapToolMoveFeature::canvasMoveEvent( QgsMapMouseEvent* e )
void QgsMapToolMoveFeature::cadCanvasMoveEvent( QgsMapMouseEvent* e )
{
if ( mRubberBand )
{
QgsPoint pointCanvasCoords = toMapCoordinates( e->pos() );
QgsPoint pointCanvasCoords = e->mapPoint();
double offsetX = pointCanvasCoords.x() - mStartPointMapCoords.x();
double offsetY = pointCanvasCoords.y() - mStartPointMapCoords.y();
mRubberBand->setTranslationOffset( offsetX, offsetY );
Expand All @@ -53,118 +55,114 @@ void QgsMapToolMoveFeature::canvasMoveEvent( QgsMapMouseEvent* e )
}
}

void QgsMapToolMoveFeature::canvasPressEvent( QgsMapMouseEvent* e )
void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e )
{
delete mRubberBand;
mRubberBand = nullptr;

QgsVectorLayer* vlayer = currentVectorLayer();
if ( !vlayer )
{
notifyNotVectorLayer();
return;
}

if ( !vlayer->isEditable() )
if ( !vlayer || !vlayer->isEditable() )
{
delete mRubberBand;
mRubberBand = nullptr;
cadDockWidget()->clear();
notifyNotEditableLayer();
return;
}

//find first geometry under mouse cursor and store iterator to it
QgsPoint layerCoords = toLayerCoordinates( vlayer, e->pos() );
QSettings settings;
double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );

if ( vlayer->selectedFeatureCount() == 0 )
if ( !mRubberBand )
{
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) );

//find the closest feature
QgsGeometry pointGeometry = QgsGeometry::fromPoint( layerCoords );
if ( pointGeometry.isEmpty() )
//find first geometry under mouse cursor and store iterator to it
QgsPoint layerCoords = toLayerCoordinates( vlayer, e->pos() );
double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );

if ( vlayer->selectedFeatureCount() == 0 )
{
return;
}
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) );

double minDistance = std::numeric_limits<double>::max();
//find the closest feature
QgsGeometry pointGeometry = QgsGeometry::fromPoint( layerCoords );
if ( pointGeometry.isEmpty() )
{
cadDockWidget()->clear();
return;
}

QgsFeature cf;
QgsFeature f;
while ( fit.nextFeature( f ) )
{
if ( f.hasGeometry() )
double minDistance = std::numeric_limits<double>::max();

QgsFeature cf;
QgsFeature f;
while ( fit.nextFeature( f ) )
{
double currentDistance = pointGeometry.distance( f.geometry() );
if ( currentDistance < minDistance )
if ( f.hasGeometry() )
{
minDistance = currentDistance;
cf = f;
double currentDistance = pointGeometry.distance( f.geometry() );
if ( currentDistance < minDistance )
{
minDistance = currentDistance;
cf = f;
}
}
}
}

if ( minDistance == std::numeric_limits<double>::max() )
if ( minDistance == std::numeric_limits<double>::max() )
{
cadDockWidget()->clear();
return;
}

mMovedFeatures.clear();
mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one...

mRubberBand = createRubberBand( vlayer->geometryType() );
mRubberBand->setToGeometry( cf.geometry(), vlayer );
}
else
{
return;
mMovedFeatures = vlayer->selectedFeaturesIds();

mRubberBand = createRubberBand( vlayer->geometryType() );
QgsFeature feat;
QgsFeatureIterator it = vlayer->selectedFeaturesIterator( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );

while ( it.nextFeature( feat ) )
{
mRubberBand->addGeometry( feat.geometry(), vlayer );
}
}

mMovedFeatures.clear();
mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one...
mStartPointMapCoords = e->mapPoint();
mRubberBand->setColor( QColor( 255, 0, 0, 65 ) );
mRubberBand->setWidth( 2 );
mRubberBand->show();

mRubberBand = createRubberBand( vlayer->geometryType() );
mRubberBand->setToGeometry( cf.geometry(), vlayer );
}
else
{
mMovedFeatures = vlayer->selectedFeaturesIds();

mRubberBand = createRubberBand( vlayer->geometryType() );
QgsFeature feat;
QgsFeatureIterator it = vlayer->selectedFeaturesIterator( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );
delete mRubberBand;
mRubberBand = nullptr;

while ( it.nextFeature( feat ) )
if ( e->button() != Qt::LeftButton )
{
mRubberBand->addGeometry( feat.geometry(), vlayer );
cadDockWidget()->clear();
return;
}
}

mStartPointMapCoords = toMapCoordinates( e->pos() );
mRubberBand->setColor( QColor( 255, 0, 0, 65 ) );
mRubberBand->setWidth( 2 );
mRubberBand->show();

}

void QgsMapToolMoveFeature::canvasReleaseEvent( QgsMapMouseEvent* e )
{
if ( !mRubberBand )
{
return;
}

QgsVectorLayer* vlayer = currentVectorLayer();
if ( !vlayer )
{
return;
}

QgsPoint startPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, mStartPointMapCoords );
QgsPoint stopPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, e->pos() );
QgsPoint startPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, mStartPointMapCoords );
QgsPoint stopPointLayerCoords = toLayerCoordinates(( QgsMapLayer* )vlayer, e->mapPoint() );

double dx = stopPointLayerCoords.x() - startPointLayerCoords.x();
double dy = stopPointLayerCoords.y() - startPointLayerCoords.y();
vlayer->beginEditCommand( tr( "Feature moved" ) );
Q_FOREACH ( QgsFeatureId id, mMovedFeatures )
{
vlayer->translateFeature( id, dx, dy );
double dx = stopPointLayerCoords.x() - startPointLayerCoords.x();
double dy = stopPointLayerCoords.y() - startPointLayerCoords.y();
vlayer->beginEditCommand( tr( "Feature moved" ) );
Q_FOREACH ( QgsFeatureId id, mMovedFeatures )
{
vlayer->translateFeature( id, dx, dy );
}
delete mRubberBand;
mRubberBand = nullptr;
vlayer->endEditCommand();
vlayer->triggerRepaint();
}
delete mRubberBand;
mRubberBand = nullptr;
vlayer->endEditCommand();
vlayer->triggerRepaint();
}

//! called when map tool is being deactivated
Expand Down
13 changes: 7 additions & 6 deletions src/app/qgsmaptoolmovefeature.h
Expand Up @@ -16,21 +16,19 @@
#ifndef QGSMAPTOOLMOVEFEATURE_H
#define QGSMAPTOOLMOVEFEATURE_H

#include "qgsmaptooledit.h"
#include "qgsmaptooladvanceddigitizing.h"

/** Map tool for translating feature position by mouse drag*/
class APP_EXPORT QgsMapToolMoveFeature: public QgsMapToolEdit
class APP_EXPORT QgsMapToolMoveFeature: public QgsMapToolAdvancedDigitizing
{
Q_OBJECT
public:
QgsMapToolMoveFeature( QgsMapCanvas* canvas );
virtual ~QgsMapToolMoveFeature();

virtual void canvasMoveEvent( QgsMapMouseEvent* e ) override;
virtual void cadCanvasMoveEvent( QgsMapMouseEvent* e ) override;

virtual void canvasPressEvent( QgsMapMouseEvent* e ) override;

virtual void canvasReleaseEvent( QgsMapMouseEvent* e ) override;
virtual void cadCanvasReleaseEvent( QgsMapMouseEvent* e ) override;

//! called when map tool is being deactivated
void deactivate() override;
Expand All @@ -44,6 +42,9 @@ class APP_EXPORT QgsMapToolMoveFeature: public QgsMapToolEdit

/** Id of moved feature*/
QgsFeatureIds mMovedFeatures;

QPoint mPressPos;

};

#endif
6 changes: 3 additions & 3 deletions src/gui/qgsadvanceddigitizingdockwidget.cpp
Expand Up @@ -900,7 +900,7 @@ bool QgsAdvancedDigitizingDockWidget::canvasPressEvent( QgsMapMouseEvent* e )
return mCadEnabled && mConstructionMode;
}

bool QgsAdvancedDigitizingDockWidget::canvasReleaseEvent( QgsMapMouseEvent* e, bool captureSegment )
bool QgsAdvancedDigitizingDockWidget::canvasReleaseEvent( QgsMapMouseEvent* e, CaptureMode mode )
{
if ( !mCadEnabled )
return false;
Expand Down Expand Up @@ -932,8 +932,8 @@ bool QgsAdvancedDigitizingDockWidget::canvasReleaseEvent( QgsMapMouseEvent* e, b

if ( e->button() == Qt::LeftButton )
{
// stop digitizing if not intermediate point and if line or polygon
if ( !mConstructionMode && !captureSegment )
// stop digitizing if not intermediate point and enough points are recorded with respect to the mode
if ( !mConstructionMode && ( mode == SinglePoint || ( mode == TwoPoints && mCadPointList.count() > 2 ) ) )
{
clearPoints();
}
Expand Down
14 changes: 12 additions & 2 deletions src/gui/qgsadvanceddigitizingdockwidget.h
Expand Up @@ -70,6 +70,16 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private
Parallel //!< Parallel
};

/**
* Determines if the dock has to record one, two or many points.
*/
enum CaptureMode
{
SinglePoint, //!< Capture a single point (e.g. for point digitizing)
TwoPoints, //!< Capture two points (e.g. for translation)
ManyPoints //!< Capture many points (e.g. line or polygon digitizing)
};

/** \ingroup gui
* @brief The CadConstraint is an abstract class for all basic constraints (angle/distance/x/y).
* It contains all values (locked, value, relative) and pointers to corresponding widgets.
Expand Down Expand Up @@ -209,10 +219,10 @@ class GUI_EXPORT QgsAdvancedDigitizingDockWidget : public QgsDockWidget, private
* Will react on a canvas release event
*
* @param e A mouse event (may be modified)
* @param captureSegment Capture segments?
* @param mode determines if the dock has to record one, two or many points.
* @return If the event is hidden (construction mode hides events from the maptool)
*/
bool canvasReleaseEvent( QgsMapMouseEvent* e , bool captureSegment );
bool canvasReleaseEvent( QgsMapMouseEvent* e , CaptureMode mode );
/**
* Will react on a canvas move event
*
Expand Down
18 changes: 17 additions & 1 deletion src/gui/qgsmaptooladvanceddigitizing.cpp
Expand Up @@ -43,7 +43,23 @@ void QgsMapToolAdvancedDigitizing::canvasPressEvent( QgsMapMouseEvent* e )
void QgsMapToolAdvancedDigitizing::canvasReleaseEvent( QgsMapMouseEvent* e )
{
snap( e );
if ( !mCadDockWidget->canvasReleaseEvent( e, mCaptureMode == CaptureLine || mCaptureMode == CapturePolygon ) )

QgsAdvancedDigitizingDockWidget::CaptureMode dockMode;
switch ( mCaptureMode )
{
case CaptureLine:
case CapturePolygon:
dockMode = QgsAdvancedDigitizingDockWidget::ManyPoints;
break;
case CaptureSegment:
dockMode = QgsAdvancedDigitizingDockWidget::TwoPoints;
break;
default:
dockMode = QgsAdvancedDigitizingDockWidget::SinglePoint;
break;
}

if ( !mCadDockWidget->canvasReleaseEvent( e, dockMode ) )
cadCanvasReleaseEvent( e );
}

Expand Down

0 comments on commit af1fee5

Please sign in to comment.