Skip to content

Commit

Permalink
Merge pull request #1332 from leyan/deletePart
Browse files Browse the repository at this point in the history
Improve delete part feature for polygon
  • Loading branch information
3nids committed May 16, 2014
2 parents f9e4e78 + c0fdfcf commit 2d749be
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 138 deletions.
194 changes: 102 additions & 92 deletions src/app/qgsmaptooldeletepart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@
#include "qgsmapcanvas.h"
#include "qgsvertexmarker.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h"
#include "qgstolerance.h"

#include <QMouseEvent>
#include <QMessageBox>

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

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

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

void QgsMapToolDeletePart::canvasPressEvent( QMouseEvent *e )
{
delete mCross;
mCross = 0;
mPressedFid = -1;
mPressedPartNum = -1;
delete mRubberBand;
mRubberBand = 0;

mRecentSnappingResults.clear();
//do snap -> new recent snapping results
if ( mSnapper.snapToCurrentLayer( e->pos(), mRecentSnappingResults, QgsSnapper::SnapToVertexAndSegment ) != 0 )
QgsMapLayer* currentLayer = mCanvas->currentLayer();
if ( !currentLayer )
return;

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

if ( mRecentSnappingResults.size() > 0 )
if ( !vlayer->isEditable() )
{
// remove previous warning
emit messageDiscarded();
notifyNotEditableLayer();
return;
}

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

//show vertex marker
mCross = new QgsVertexMarker( mCanvas );
mCross->setIconType( QgsVertexMarker::ICON_X );
mCross->setCenter( markerPoint );
}
else
geomPart = partUnderPoint( e->pos(), mPressedFid, mPressedPartNum );

if ( mPressedFid != -1 )
{
emit messageEmitted( tr( "could not snap to a part on the current layer." ) );
mRubberBand = createRubberBand( vlayer->geometryType() );

mRubberBand->setToGeometry( geomPart, vlayer );
mRubberBand->show();
}

}

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

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

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

if ( mRecentSnappingResults.size() > 0 )
if ( !vlayer || !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 );
}
return;
}

}

if ( mPressedFid == -1 )
return;

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

// find out the part number
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mPressedFid ) ).nextFeature( f );
QgsGeometry* g = f.geometry();
if ( !g->isMultipart() )
{
QMessageBox::information( mCanvas, tr( "Delete part" ), tr( "This isn't a multipart geometry." ) );
return;
}

int partNum = partNumberOfVertex( g, beforeVertexNr );

if ( g->deletePart( partNum ) )
if ( g->deletePart( mPressedPartNum ) )
{
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." ) );
emit messageEmitted( tr( "Couldn't remove the selected part." ) );
}

return;
}

int QgsMapToolDeletePart::partNumberOfVertex( QgsGeometry* g, int beforeVertexNr )
QgsGeometry* QgsMapToolDeletePart::partUnderPoint( QPoint point, int& fid, int& partNum )
{
int part;
QgsFeature f;
QgsGeometry* geomPart = new QgsGeometry();

switch ( g->wkbType() )
switch ( vlayer->geometryType() )
{
case QGis::WKBMultiPoint25D:
case QGis::WKBMultiPoint:
if ( beforeVertexNr < g->asMultiPoint().count() )
return beforeVertexNr;
else
return -1;

case QGis::WKBMultiLineString25D:
case QGis::WKBMultiLineString:
case QGis::Point:
case QGis::Line:
{
QgsMultiPolyline mline = g->asMultiPolyline();
for ( part = 0; part < mline.count(); part++ )
if ( mSnapper.snapToCurrentLayer( point, mRecentSnappingResults, QgsSnapper::SnapToVertexAndSegment ) == 0 )
{
if ( beforeVertexNr < mline[part].count() )
return part;

beforeVertexNr -= mline[part].count();
if ( mRecentSnappingResults.length() > 0 )
{
QgsSnappingResult sr = mRecentSnappingResults.first();
int snapVertex = sr.snappedVertexNr;
if ( snapVertex == -1 )
snapVertex = sr.beforeVertexNr;
vlayer->getFeatures( QgsFeatureRequest().setFilterFid( sr.snappedAtGeometry ) ).nextFeature( f );
QgsGeometry* g = f.geometry();
if ( !g->isMultipart() )
return geomPart;
if ( g->wkbType() == QGis::WKBMultiPoint || g->wkbType() == QGis::WKBMultiPoint25D )
{
fid = sr.snappedAtGeometry;
partNum = snapVertex;
return QgsGeometry::fromPoint( sr.snappedVertex );
}
if ( g->wkbType() == QGis::WKBMultiLineString || g->wkbType() == QGis::WKBMultiLineString25D )
{
QgsMultiPolyline mline = g->asMultiPolyline();
for ( int part = 0; part < mline.count(); part++ )
{
if ( snapVertex < mline[part].count() )
{
fid = sr.snappedAtGeometry;
partNum = part;
return QgsGeometry::fromPolyline( mline[part] );
}
snapVertex -= mline[part].count();
}
}
}
}
return -1; // not found
break;
}

case QGis::WKBMultiPolygon25D:
case QGis::WKBMultiPolygon:
case QGis::Polygon:
{
QgsPoint layerCoords = toLayerCoordinates( vlayer, point );
double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() );
QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius,
layerCoords.x() + searchRadius, layerCoords.y() + searchRadius );
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ) );
fit.nextFeature( f );
QgsGeometry* g = f.geometry();
if ( !g )
return geomPart;
if ( !g->isMultipart() )
return geomPart;
QgsMultiPolygon mpolygon = g->asMultiPolygon();
for ( part = 0; part < mpolygon.count(); part++ ) // go through the polygons
for ( int part = 0; part < mpolygon.count(); part++ ) // go through the polygons
{
const QgsPolygon& polygon = mpolygon[part];
for ( int ring = 0; ring < polygon.count(); ring++ ) // go through the rings
QgsGeometry* partGeo = QgsGeometry::fromPolygon( polygon );
if ( partGeo->contains( &layerCoords ) )
{
if ( beforeVertexNr < polygon[ring].count() )
return part;

beforeVertexNr -= polygon[ring].count();
fid = f.id();
partNum = part;
return partGeo;
}
}
return -1; // not found
break;
}

default:
return -1;
{
break;
}
}
return geomPart;
}


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

QgsMapTool::deactivate();
}

19 changes: 12 additions & 7 deletions src/app/qgsmaptooldeletepart.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
#ifndef QGSMAPTOOLDELETEPART_H
#define QGSMAPTOOLDELETEPART_H

#include "qgsmaptoolvertexedit.h"
#include "qgsmaptooledit.h"
#include "qgsrubberband.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 +40,18 @@ 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 );
QgsGeometry* partUnderPoint( QPoint p, int &fid, int &partNum );

//! find out part number of geometry given the snapped vertex number
int partNumberOfVertex( QgsGeometry* g, int beforeVertexNr );
/* Rubberband that shows the part being deleted*/
QgsRubberBand* mRubberBand;

//The feature and part where the mouse cursor was pressed
//This is used to check whether we are still in the same part at cursor release
int mPressedFid;
int mPressedPartNum;
};

#endif
Loading

0 comments on commit 2d749be

Please sign in to comment.