Skip to content
Permalink
Browse files
Mesh editing: Select mesh element by polygon (#44739)
[feature] [mesh] Select mesh elements by polygon

Default behavior: all touched (partially included element) will be selected (green rubber band) (default)
Alt Modifier: only totally included elements will be selected (blue rubber band) (alt + drag)
  • Loading branch information
vcloarec committed Aug 18, 2021
1 parent ad8991f commit 781f4849081fb577937df73f75806deeb3e46885
@@ -923,6 +923,7 @@
<file>themes/default/mIconFolderHomeParams.svg</file>
<file>themes/default/mActionMeasureBearing.svg</file>
<file>themes/default/mActionMeshDigitizing.svg</file>
<file>themes/default/mActionMeshSelectPolygon.svg</file>
<file>themes/default/mActionNewMeshLayer.svg</file>
<file>themes/default/mIconGeometryCollectionLayer.svg</file>
<file>themes/default/mIconGps.svg</file>
@@ -0,0 +1 @@
<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><g stroke-linecap="round"><g fill="none" stroke="#6d7281" stroke-width="1.003672" transform="matrix(1.013697 0 0 .98287877 -.369842 -.425299)"><path d="m13 2-11 11m0-11 11 11m-11.5-11.5h12v12h-12z"/><path d="m13.5 1.5h9v12h-9zm-12 12h12v9h-12zm20.5-11.5-8 11m-6.5 1v8"/><path d="m13 18h-11"/></g><path d="m1.1507031 1.0490189 5.9317031 5.9446938-5.9317031 5.8498513z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m1.1507031 1.0490189h12.1643639l-6.2326608 5.9446938z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m1.1507031 12.843564 5.9317031-5.8498513 6.2326608 5.8498513z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m13.315067 12.843564-6.2326608-5.8498513 6.2326608-5.9446938z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m13.315067 12.843564v-11.7945451h9.123273z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m13.315067 12.843564 9.123273-11.7945451v11.7945451z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/><path d="m7.2 12.831149 6.115067.01242v4.344658l-6.0958115.03813z" fill="#fce94f" stroke="#c4a000" stroke-linejoin="round" stroke-width="1.077"/></g><g transform="translate(-1.136095 .227219)"><path d="m2.2867978.8218 7.66378 13.309544 7.3009022 1.30165 3.02866-4.572794 4.04061-9.7228-10.829921 4.0172z" fill="#e5e5e5" fill-opacity=".629771"/><path d="m2.2867978.8218 7.66378 13.309544 7.3009022 1.30165 3.369489-4.686413 4.04061-9.7227179-10.695993 4.1064706z" fill="none" stroke="#424242" stroke-dasharray="4 1"/></g><path d="m22.652743 15.6955-12.756464-6.9791 5.477553 13.6014 2.180887-3.5312 4.312159 4.7134 1.633122-1.5913-4.408599-4.6683 3.561343-1.5449z" style="fill:#fff;fill-rule:evenodd;stroke:#000;stroke-width:.7;stroke-linecap:round;stroke-linejoin:round"/></svg>
@@ -92,6 +92,8 @@ QgsMapToolEditMeshFrame::QgsMapToolEditMeshFrame( QgsMapCanvas *canvas )
{
mActionDigitizing = new QAction( QgsApplication::getThemePixmap( QStringLiteral( "/mActionMeshDigitizing.svg" ) ), tr( "Digitize mesh elements" ) );
mActionDigitizing->setCheckable( true );
mActionSelectByPolygon = new QAction( QgsApplication::getThemePixmap( QStringLiteral( "/mActionMeshSelectPolygon.svg" ) ), tr( "Select mesh element by polygon" ) );
mActionSelectByPolygon->setCheckable( true );

mActionRemoveVerticesFillingHole = new QAction( this );
mActionDelaunayTriangulation = new QAction( tr( "Delaunay triangulation with selected vertices" ), this );
@@ -111,6 +113,14 @@ QgsMapToolEditMeshFrame::QgsMapToolEditMeshFrame( QgsMapCanvas *canvas )
activateWithState( Digitizing );
} );

connect( mActionSelectByPolygon, &QAction::toggled, this, [this]( bool checked )
{
if ( checked )
activateWithState( SelectingByPolygon );
else
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
} );

connect( mActionDelaunayTriangulation, &QAction::triggered, this, [this]
{
if ( mCurrentEditor && mSelectedVertices.count() > 3 )
@@ -152,6 +162,12 @@ void QgsMapToolEditMeshFrame::activateWithState( State state )
mCurrentState = state;
}

void QgsMapToolEditMeshFrame::backToDigitizing()
{
activateWithState( Digitizing );
mActionDigitizing->setChecked( true );
}

QgsMapToolEditMeshFrame::~QgsMapToolEditMeshFrame()
{
deleteZvalueWidget();
@@ -160,13 +176,15 @@ QgsMapToolEditMeshFrame::~QgsMapToolEditMeshFrame()
QList<QAction *> QgsMapToolEditMeshFrame::actions() const
{
return QList<QAction *>()
<< mActionDigitizing;
<< mActionDigitizing
<< mActionSelectByPolygon;
}

QList<QAction *> QgsMapToolEditMeshFrame::mapToolActions()
{
return QList<QAction *>()
<< mActionDigitizing;
<< mActionDigitizing
<< mActionSelectByPolygon;
}

void QgsMapToolEditMeshFrame::initialize()
@@ -215,12 +233,8 @@ void QgsMapToolEditMeshFrame::initialize()

if ( !mSelectionBand )
mSelectionBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
mSelectionBandPartiallyFillColor = QColor( 0, 215, 120, 63 );
mSelectionBandPartiallyStrokeColor = QColor( 0, 204, 102, 100 );
mSelectionBandTotalFillColor = QColor( 0, 120, 215, 63 );
mSelectionBandTotalStrokeColor = QColor( 0, 102, 204, 100 );
mSelectionBand->setFillColor( mSelectionBandTotalFillColor );
mSelectionBand->setStrokeColor( mSelectionBandTotalStrokeColor );
mSelectionBand->setFillColor( QColor( 254, 178, 76, 63 ) );
mSelectionBand->setStrokeColor( QColor( 254, 58, 29, 100 ) );
mSelectionBand->setZValue( 10 );

if ( !mSelectedFacesRubberband )
@@ -407,6 +421,7 @@ bool QgsMapToolEditMeshFrame::populateContextMenuWithEvent( QMenu *menu, QgsMapM
case AddingNewFace:
case Selecting:
case MovingVertex:
case SelectingByPolygon:
return false;
}

@@ -422,6 +437,7 @@ QgsMapTool::Flags QgsMapToolEditMeshFrame::flags() const
case AddingNewFace:
case Selecting:
case MovingVertex:
case SelectingByPolygon:
return QgsMapTool::Flags();
}

@@ -442,7 +458,8 @@ void QgsMapToolEditMeshFrame::cadCanvasPressEvent( QgsMapMouseEvent *e )
if ( e->button() == Qt::LeftButton )
{
mStartSelectionPos = e->pos();
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
if ( mCurrentState != SelectingByPolygon )
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
switch ( mCurrentState )
{
case Digitizing:
@@ -476,11 +493,11 @@ void QgsMapToolEditMeshFrame::cadCanvasPressEvent( QgsMapMouseEvent *e )
mStartMovingPoint = mapVertexXY( mCurrentVertexIndex );
mCanMovingStart = true;
}
mPreviousState = mCurrentState;
break;
case AddingNewFace:
case Selecting:
case MovingVertex:
case SelectingByPolygon:
break;
}
}
@@ -499,7 +516,8 @@ void QgsMapToolEditMeshFrame::cadCanvasMoveEvent( QgsMapMouseEvent *e )

if ( mLeftButtonPressed &&
mCurrentState != MovingVertex &&
mCurrentState != AddingNewFace )
mCurrentState != AddingNewFace &&
mCurrentState != SelectingByPolygon )
{
if ( mCanMovingStart )
{
@@ -526,18 +544,6 @@ void QgsMapToolEditMeshFrame::cadCanvasMoveEvent( QgsMapMouseEvent *e )
case Selecting:
{
const QRect &rect = QRect( e->pos(), mStartSelectionPos );
mSelectPartiallyContainedFace = e->pos().x() < mStartSelectionPos.x();
if ( mSelectPartiallyContainedFace )
{
mSelectionBand->setFillColor( mSelectionBandPartiallyFillColor );
mSelectionBand->setColor( mSelectionBandPartiallyStrokeColor );
}
else
{
mSelectionBand->setFillColor( mSelectionBandTotalFillColor );
mSelectionBand->setColor( mSelectionBandTotalStrokeColor );
}

mSelectionBand->setToCanvasRectangle( rect );
}
break;
@@ -616,6 +622,9 @@ void QgsMapToolEditMeshFrame::cadCanvasMoveEvent( QgsMapMouseEvent *e )
}
}
break;
case SelectingByPolygon:
mSelectionBand->movePoint( mapPoint );
break;
}

QgsMapToolAdvancedDigitizing::cadCanvasMoveEvent( e );
@@ -729,10 +738,10 @@ void QgsMapToolEditMeshFrame::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
break;
case Selecting:
{
mCurrentState = mPreviousState;
QgsGeometry selectionGeom = mSelectionBand->asGeometry();
selectInGeometry( selectionGeom, e->modifiers() );
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
mCurrentState = Digitizing;
}
break;
case MovingVertex:
@@ -757,6 +766,19 @@ void QgsMapToolEditMeshFrame::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
mMovingVerticesRubberband->reset();
mCurrentState = Digitizing;
break;
case SelectingByPolygon:
if ( e->button() == Qt::LeftButton )
{
mSelectionBand->movePoint( mapPoint );
mSelectionBand->addPoint( mapPoint );
}
else if ( e->button() == Qt::RightButton )
{
QgsGeometry selectionGeom = mSelectionBand->asGeometry();
selectInGeometry( selectionGeom, e->modifiers() );
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
}
break;
}
mDoubleClicks = false;

@@ -878,8 +900,6 @@ bool QgsMapToolEditMeshFrame::testBorderMovingFace( const QgsMeshFace &borderMov
return true;
}



void QgsMapToolEditMeshFrame::keyPressEvent( QKeyEvent *e )
{
bool consumned = false;
@@ -932,6 +952,20 @@ void QgsMapToolEditMeshFrame::keyPressEvent( QKeyEvent *e )
}
}
break;
case SelectingByPolygon:
if ( e->key() == Qt::Key_Escape )
{
mSelectionBand->reset( QgsWkbTypes::PolygonGeometry );
backToDigitizing();
consumned = true;
}

if ( e->key() == Qt::Key_Backspace )
{
mSelectionBand->removePoint( -2, true );
consumned = true;
}
break;
case Selecting:
case MovingVertex:
break;
@@ -956,6 +990,7 @@ void QgsMapToolEditMeshFrame::keyReleaseEvent( QKeyEvent *e )
case Digitizing:
break;
case AddingNewFace:
case SelectingByPolygon:
if ( e->key() == Qt::Key_Backspace )
consumned = true; //to avoid removing the value of the ZvalueWidget
break;
@@ -1270,7 +1305,7 @@ void QgsMapToolEditMeshFrame::selectInGeometry( const QgsGeometry &geometry, Qt:
for ( const int faceIndex : nativeFaceIndexes )
{
const QgsMeshFace &face = nativeFace( faceIndex );
if ( mSelectPartiallyContainedFace )
if ( !( modifiers & Qt::AltModifier ) )
{
std::unique_ptr<QgsPolygon> faceGeom( new QgsPolygon( new QgsLineString( nativeFaceGeometry( faceIndex ) ) ) );
if ( engine->intersects( faceGeom.get() ) )
@@ -102,14 +102,16 @@ class APP_EXPORT QgsMapToolEditMeshFrame : public QgsMapToolAdvancedDigitizing
Digitizing, //!< Digitizing action can be start (add/remove vertices, selection, add/remove faces, move vertices)
AddingNewFace, //!< Adding a face has been start and the user have to choose or digitize vertices
Selecting, //!< Selection is in process
MovingVertex //!< Moving vertex or vertices is processing
MovingVertex, //!< Moving vertex or vertices is processing
SelectingByPolygon, //!< Selection elements by polygon is in progress
};

typedef QPair<int, int> Edge; //first face index, second the vertex index corresponding to the end extremity (ccw)

// methods
void initialize();
void activateWithState( State state );
void backToDigitizing();
const QgsMeshVertex mapVertex( int index ) const;
const QgsPointXY mapVertexXY( int index ) const;
const QgsMeshFace nativeFace( int index ) const;
@@ -166,7 +168,6 @@ class APP_EXPORT QgsMapToolEditMeshFrame : public QgsMapToolAdvancedDigitizing

bool mIsInitialized = false;
State mCurrentState = Digitizing;
State mPreviousState = Digitizing; //used to store a state and restore it after a particular action as selecting
bool mLeftButtonPressed = false;
bool mKeepSelectionOnEdit = false;

@@ -216,7 +217,6 @@ class APP_EXPORT QgsMapToolEditMeshFrame : public QgsMapToolAdvancedDigitizing
QColor mSelectionBandTotalStrokeColor = QColor( 0, 102, 204, 100 );
QgsRubberBand *mSelectedFacesRubberband = nullptr; //own by map canvas
QMap< int, QgsVertexMarker * > mSelectedVerticesMarker;
bool mSelectPartiallyContainedFace = false;

//! members for moving vertices
QgsPointXY mStartMovingPoint;
@@ -246,6 +246,7 @@ class APP_EXPORT QgsMapToolEditMeshFrame : public QgsMapToolAdvancedDigitizing
QAction *mActionFacesRefinement = nullptr;

QAction *mActionDigitizing = nullptr;
QAction *mActionSelectByPolygon = nullptr;

friend class TestQgsMapToolEditMesh;
};
@@ -191,15 +191,15 @@ void TestQgsMapToolEditMesh::editMesh()
QCOMPARE( meshLayerQuadFlower->datasetValue( QgsMeshDatasetIndex( 0, 0 ), QgsPointXY( 2500, 3250 ) ).x(), 1500 );

//Selection
// from left to right
// completely included
tool.mouseMove( 1200, 3600 );
tool.mousePress( 1200, 3600, Qt::LeftButton );
tool.mouseMove( 2700, 2250 );
tool.mouseRelease( 2700, 2250, Qt::LeftButton );
tool.mouseRelease( 2700, 2250, Qt::LeftButton, Qt::AltModifier );
QCOMPARE( editMeshMapTool->mSelectedVertices.count(), 5 );
QCOMPARE( editMeshMapTool->mSelectedFaces.count(), 1 );

// from left to right
// touched
tool.mouseMove( 2700, 2250 );
tool.mousePress( 2700, 2250, Qt::LeftButton );
tool.mouseMove( 1200, 3600 );
@@ -280,9 +280,43 @@ void TestQgsMapToolEditMesh::editMesh()
tool.mouseClick( 1500, 3324, Qt::LeftButton );
centroid = meshLayerQuadFlower->snapOnElement( QgsMesh::Face, QgsPointXY( 1100, 3050 ), 10 );
QVERIFY( centroid.compare( QgsPointXY( 1500, 3100 ), 1e-2 ) );
}

tool.keyClick( Qt::Key_Escape );

QCOMPARE( editMeshMapTool->mSelectedVertices.count(), 0 );
QCOMPARE( editMeshMapTool->mSelectedFaces.count(), 0 );

// Selection by polygon
editMeshMapTool->mActionSelectByPolygon->trigger();

// touched
tool.mouseClick( 3500, 3250, Qt::LeftButton );
tool.mouseClick( 2750, 3250, Qt::LeftButton );
tool.mouseClick( 1750, 2500, Qt::LeftButton );
tool.mouseClick( 2500, 2000, Qt::LeftButton );
tool.mouseClick( 3000, 2000, Qt::LeftButton );
tool.mouseClick( 3000, 2000, Qt::RightButton );

QCOMPARE( editMeshMapTool->mSelectedVertices.count(), 5 );
QCOMPARE( editMeshMapTool->mSelectedFaces.count(), 3 );

// completely included
tool.mouseClick( 2750, 3250, Qt::LeftButton );
tool.mouseClick( 3500, 3250, Qt::LeftButton );
tool.mouseClick( 3000, 2000, Qt::LeftButton );
tool.mouseClick( 2500, 2000, Qt::LeftButton );
tool.mouseClick( 1750, 2500, Qt::LeftButton );
tool.mouseClick( 1750, 2500, Qt::RightButton, Qt::AltModifier );

QCOMPARE( editMeshMapTool->mSelectedVertices.count(), 1 );
QCOMPARE( editMeshMapTool->mSelectedFaces.count(), 0 );

tool.keyClick( Qt::Key_Escape );
tool.keyClick( Qt::Key_Escape );

QCOMPARE( editMeshMapTool->mSelectedVertices.count(), 0 );
QCOMPARE( editMeshMapTool->mSelectedFaces.count(), 0 );
}

QGSTEST_MAIN( TestQgsMapToolEditMesh )
#include "testqgsmaptooleditmesh.moc"

0 comments on commit 781f484

Please sign in to comment.