Skip to content

Commit

Permalink
Improve delete part feature to allow deletion by clicking inside a po…
Browse files Browse the repository at this point in the history
…lygon part
  • Loading branch information
leyan committed Apr 29, 2014
1 parent dd739ef commit 717ef84
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 64 deletions.
181 changes: 124 additions & 57 deletions src/app/qgsmaptooldeletepart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,23 @@

#include "qgsmaptooldeletepart.h"

#include "qgisapp.h"
#include "qgsmapcanvas.h"
#include "qgsvertexmarker.h"
#include "qgsvectorlayer.h"
#include "qgsmessagebar.h"
#include "qgsgeometry.h"

#include <QMouseEvent>
#include <QMessageBox>

QgsMapToolDeletePart::QgsMapToolDeletePart( QgsMapCanvas* canvas )
: QgsMapToolVertexEdit( canvas ), mCross( 0 )
: QgsMapToolEdit( canvas ), mCross( 0 )
{
}

QgsMapToolDeletePart::~QgsMapToolDeletePart()
{
delete mCross;
}

void QgsMapToolDeletePart::canvasMoveEvent( QMouseEvent *e )
Expand All @@ -40,92 +42,144 @@ void QgsMapToolDeletePart::canvasMoveEvent( QMouseEvent *e )

void QgsMapToolDeletePart::canvasPressEvent( QMouseEvent *e )
{
delete mCross;
mCross = 0;

mRecentSnappingResults.clear();
//do snap -> new recent snapping results
if ( mSnapper.snapToCurrentLayer( e->pos(), mRecentSnappingResults, QgsSnapper::SnapToVertexAndSegment ) != 0 )
{
//error
}

if ( mRecentSnappingResults.size() > 0 )
{
// remove previous warning
emit messageDiscarded();

QgsPoint markerPoint = mRecentSnappingResults.begin()->snappedVertex;

//show vertex marker
mCross = new QgsVertexMarker( mCanvas );
mCross->setIconType( QgsVertexMarker::ICON_X );
mCross->setCenter( markerPoint );
}
else
{
emit messageEmitted( tr( "could not snap to a part on the current layer." ) );
}
}

void QgsMapToolDeletePart::canvasReleaseEvent( QMouseEvent *e )
{
Q_UNUSED( e );
delete mCross;
mCross = 0;

QgsMapLayer* currentLayer = mCanvas->currentLayer();
if ( !currentLayer )
return;

QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( !vlayer )
{
notifyNotVectorLayer();
return;
}

if ( mRecentSnappingResults.size() > 0 )
if ( !vlayer->isEditable() )
{
QList<QgsSnappingResult>::iterator sr_it = mRecentSnappingResults.begin();
for ( ; sr_it != mRecentSnappingResults.end(); ++sr_it )
{
if ( sr_it->snappedVertexNr != -1 )
deletePart( sr_it->snappedAtGeometry, sr_it->snappedVertexNr, vlayer );
else if ( sr_it->beforeVertexNr != -1 )
deletePart( sr_it->snappedAtGeometry, sr_it->beforeVertexNr, vlayer );
else if ( sr_it->afterVertexNr != -1 )
deletePart( sr_it->snappedAtGeometry, sr_it->afterVertexNr, vlayer );
}
notifyNotEditableLayer();
return;
}

}


void QgsMapToolDeletePart::deletePart( QgsFeatureId fId, int beforeVertexNr, QgsVectorLayer* vlayer )
{
QgsGeometry* g;
QgsFeature f;
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( fId ) ).nextFeature( f );

// find out the part number
QgsGeometry* g = f.geometry();
if ( !g->isMultipart() )
int partNum;
switch( vlayer->geometryType() )
{
QMessageBox::information( mCanvas, tr( "Delete part" ), tr( "This isn't a multipart geometry." ) );
return;
case QGis::Point:
case QGis::Line:
{
if ( mRecentSnappingResults.size() == 0 )
{
return;
}
QgsSnappingResult sr = mRecentSnappingResults.first();
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( sr.snappedAtGeometry ) ).nextFeature( f );
g = f.geometry();
if ( !g )
return;
if ( ( g->type() == QGis::Point && g->asMultiPoint().size() == 1) ||
( g->type() == QGis::Line && g->asMultiPolyline().size() == 1 ) )
{
notifySinglePart();
return;
}
int vertex = sr.snappedVertexNr;
if ( vertex == -1 )
{
vertex = sr.beforeVertexNr;
}
partNum = partNumberOfVertex( g, vertex );
break;
}
case QGis::Polygon:
{
QgsPoint p = mCanvas->getCoordinateTransform()->toMapCoordinates( e->x(),e->y());
p = toLayerCoordinates(vlayer, p);
f = featureUnderPoint(p);
g = f.geometry();
if ( !g )
return;
if ( g->asMultiPolygon().size() == 1 )
{
notifySinglePart();
return;
}
partNum = partNumberOfPoint( g, p );
if ( partNum < 0 )
return;
break;
}
default:
{
QgsDebugMsg("Unknown geometry type");
return;
}
}

int partNum = partNumberOfVertex( g, beforeVertexNr );

if ( g->deletePart( partNum ) )
{
vlayer->beginEditCommand( tr( "Part of multipart feature deleted" ) );
vlayer->changeGeometry( fId, g );
vlayer->changeGeometry( f.id(), g );
vlayer->endEditCommand();
mCanvas->refresh();
}
else
{
QMessageBox::information( mCanvas, tr( "Delete part" ), tr( "Couldn't remove the selected part." ) );
QgisApp::instance()->messageBar()->pushMessage(
tr( "Delete part" ),
tr( "Couldn't remove the selected part." ),
QgsMessageBar::WARNING,
QgisApp::instance()->messageTimeout() );
}
return;
}

QgsFeature QgsMapToolDeletePart::featureUnderPoint(QgsPoint p)
{
QgsRectangle r;
double searchRadius = mCanvas->extent().width()/100;
r.setXMinimum( p.x() - searchRadius );
r.setXMaximum( p.x() + searchRadius );
r.setYMinimum( p.y() - searchRadius );
r.setYMaximum( p.y() + searchRadius );
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( r ) );
QgsFeature f;
fit.nextFeature( f );
return f;
}


int QgsMapToolDeletePart::partNumberOfPoint(QgsGeometry *g, QgsPoint point)
{
int part;
switch ( g->wkbType() )
{
case QGis::WKBMultiPolygon25D:
case QGis::WKBMultiPolygon:
{
QgsMultiPolygon mpolygon = g->asMultiPolygon();
for ( part = 0; part < mpolygon.count(); part++ ) // go through the polygons
{
const QgsPolygon& polygon = mpolygon[part];
QgsGeometry* partGeo = QgsGeometry::fromPolygon(polygon);
if ( partGeo->contains( &point ) )
return part;
}
return -1; // not found
}
default:
return -1;
}
}

int QgsMapToolDeletePart::partNumberOfVertex( QgsGeometry* g, int beforeVertexNr )
Expand All @@ -134,6 +188,14 @@ int QgsMapToolDeletePart::partNumberOfVertex( QgsGeometry* g, int beforeVertexNr

switch ( g->wkbType() )
{
case QGis::WKBLineString25D:
case QGis::WKBLineString:
case QGis::WKBPoint25D:
case QGis::WKBPoint:
case QGis::WKBPolygon25D:
case QGis::WKBPolygon:
return 1;

case QGis::WKBMultiPoint25D:
case QGis::WKBMultiPoint:
if ( beforeVertexNr < g->asMultiPoint().count() )
Expand Down Expand Up @@ -178,12 +240,17 @@ int QgsMapToolDeletePart::partNumberOfVertex( QgsGeometry* g, int beforeVertexNr
}
}


void QgsMapToolDeletePart::deactivate()
{
delete mCross;
mCross = 0;

QgsMapTool::deactivate();
}

void QgsMapToolDeletePart::notifySinglePart()
{
QgisApp::instance()->messageBar()->pushMessage(
tr( "Cannot use delete part" ),
tr( "The Delete part tool cannot be used on single part features." ),
QgsMessageBar::INFO,
QgisApp::instance()->messageTimeout() );
return;
}
20 changes: 13 additions & 7 deletions src/app/qgsmaptooldeletepart.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
#ifndef QGSMAPTOOLDELETEPART_H
#define QGSMAPTOOLDELETEPART_H

#include "qgsmaptoolvertexedit.h"
#include "qgsmaptooledit.h"

class QgsVertexMarker;

/**Map tool to delete vertices from line/polygon features*/
class APP_EXPORT QgsMapToolDeletePart: public QgsMapToolVertexEdit
class APP_EXPORT QgsMapToolDeletePart: public QgsMapToolEdit
{
Q_OBJECT

Expand All @@ -39,14 +39,20 @@ class APP_EXPORT QgsMapToolDeletePart: public QgsMapToolVertexEdit
void deactivate();

private:
QgsVertexMarker* mCross;
QgsVectorLayer* vlayer;
QList<QgsSnappingResult> mRecentSnappingResults;

//! delete part of a geometry
void deletePart( QgsFeatureId fId, int beforeVertexNr, QgsVectorLayer* vlayer );

//! find out part number of geometry given the snapped vertex number
//! find out the part number of geometry given the vertex number
int partNumberOfVertex( QgsGeometry* g, int beforeVertexNr );

//! find out the part number of geometry including the point
int partNumberOfPoint( QgsGeometry* g, QgsPoint point );

//! find which feature is under the point position (different from snapping as we allow the whole polygon surface)
QgsFeature featureUnderPoint(QgsPoint p);

void notifySinglePart();

};

#endif

0 comments on commit 717ef84

Please sign in to comment.