New vector editing tools:

- delete part of multipart feature
- delete hole from polygon
- simplify feature

These tools are in the new "advanced" editing toolbar.
Contributed by Richard Kostecky (qgis-mapper project).


git-svn-id: http://svn.osgeo.org/qgis/trunk@10700 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
wonder 2009-05-02 15:37:35 +00:00
parent fac6117a7d
commit cbc57dcb27
16 changed files with 1237 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -289,5 +289,16 @@ not disjoint with existing polygons of the feature*/
// TODO: destruction of created geometries??
QList<QgsGeometry*> asGeometryCollection() /Factory/;
/** delete a hole in polygon or multipolygon.
Ring 0 is outer ring and can't be deleted.
@return TRUE on success
@note added in version 1.2 */
bool deleteHole( int ringNum, int partNum = 0 );
/** delete part identified by the part number
@return TRUE on success
@note added in version 1.2 */
bool deletePart( int partNum );
}; // class QgsGeometry

View File

@ -27,12 +27,15 @@ SET(QGIS_APP_SRCS
qgsmaptooladdisland.cpp
qgsmaptooladdring.cpp
qgsmaptoolcapture.cpp
qgsmaptooldeletehole.cpp
qgsmaptooldeletepart.cpp
qgsmaptooldeletevertex.cpp
qgsmaptooledit.cpp
qgsmaptoolidentify.cpp
qgsmaptoolmovefeature.cpp
qgsmaptoolmovevertex.cpp
qgsmaptoolselect.cpp
qgsmaptoolsimplify.cpp
qgsmaptoolsplitfeatures.cpp
qgsmaptoolvertexedit.cpp
qgsmeasuredialog.cpp
@ -115,6 +118,7 @@ SET (QGIS_APP_MOC_HDRS
qgsmaptooladdring.h
qgsmaptoolmovefeature.h
qgsmaptoolselect.h
qgsmaptoolsimplify.h
qgsmeasuretool.h
qgsmeasuredialog.h

View File

@ -157,6 +157,8 @@
#include "qgsmaptooladdisland.h"
#include "qgsmaptooladdring.h"
#include "qgsmaptooladdvertex.h"
#include "qgsmaptooldeletehole.h"
#include "qgsmaptooldeletepart.h"
#include "qgsmaptooldeletevertex.h"
#include "qgsmaptoolidentify.h"
#include "qgsmaptoolmovefeature.h"
@ -166,6 +168,7 @@
#include "qgsmaptoolsplitfeatures.h"
#include "qgsmaptoolvertexedit.h"
#include "qgsmaptoolzoom.h"
#include "qgsmaptoolsimplify.h"
#include "qgsmeasuretool.h"
//
@ -465,6 +468,9 @@ QgisApp::~QgisApp()
delete mMapTools.mVertexMove;
delete mMapTools.mVertexDelete;
delete mMapTools.mAddRing;
delete mMapTools.mSimplifyFeature;
delete mMapTools.mDeleteHole;
delete mMapTools.mDeletePart;
delete mMapTools.mAddIsland;
delete mPythonConsole;
@ -667,6 +673,22 @@ void QgisApp::createActions()
connect( mActionAddIsland, SIGNAL( triggered() ), this, SLOT( addIsland() ) );
mActionAddIsland->setEnabled( false );
mActionSimplifyFeature = new QAction( getThemeIcon( "mActionSimplify.png" ), tr( "Simplify Feature" ), this );
mActionSimplifyFeature->setStatusTip( tr( "Simplify Feature" ) );
connect( mActionSimplifyFeature, SIGNAL( triggered() ), this, SLOT( simplifyFeature() ) );
mActionSimplifyFeature->setEnabled( false );
mActionDeleteHole = new QAction( getThemeIcon( "mActionDeleteHole.png" ), tr( "Delete Hole" ), this );
mActionDeleteHole->setStatusTip( tr( "Delete Hole" ) );
connect( mActionDeleteHole, SIGNAL( triggered() ), this, SLOT( deleteHole() ) );
mActionDeleteHole->setEnabled( false );
mActionDeletePart = new QAction( getThemeIcon( "mActionDeletePart.png" ), tr( "Delete Part" ), this );
mActionDeletePart->setStatusTip( tr( "Delete Part" ) );
connect( mActionDeletePart, SIGNAL( triggered() ), this, SLOT( deletePart() ) );
mActionDeletePart->setEnabled( false );
// View Menu Items
mActionPan = new QAction( getThemeIcon( "mActionPan.png" ), tr( "Pan Map" ), this );
@ -990,6 +1012,12 @@ void QgisApp::createActionGroups()
mMapToolGroup->addAction( mActionAddRing );
mActionAddIsland->setCheckable( true );
mMapToolGroup->addAction( mActionAddIsland );
mActionSimplifyFeature->setCheckable( true );
mMapToolGroup->addAction( mActionSimplifyFeature );
mActionDeleteHole->setCheckable( true );
mMapToolGroup->addAction( mActionDeleteHole );
mActionDeletePart->setCheckable( true );
mMapToolGroup->addAction( mActionDeletePart );
}
void QgisApp::createMenus()
@ -1073,9 +1101,15 @@ void QgisApp::createMenus()
mEditMenu->addAction( mActionAddRing );
mEditMenu->addAction( mActionAddIsland );
mActionEditSeparator2 = mEditMenu->addSeparator();
mEditMenu->addAction( mActionSimplifyFeature );
mEditMenu->addAction( mActionDeleteHole );
mEditMenu->addAction( mActionDeletePart );
if ( layout == QDialogButtonBox::GnomeLayout || layout == QDialogButtonBox::MacLayout )
{
mActionEditSeparator2 = mEditMenu->addSeparator();
mActionEditSeparator3 = mEditMenu->addSeparator();
mEditMenu->addAction( mActionOptions );
mEditMenu->addAction( mActionCustomProjection );
}
@ -1267,6 +1301,16 @@ void QgisApp::createToolBars()
mDigitizeToolBar->addAction( mActionCopyFeatures );
mDigitizeToolBar->addAction( mActionPasteFeatures );
mToolbarMenu->addAction( mDigitizeToolBar->toggleViewAction() );
mAdvancedDigitizeToolBar = addToolBar( tr( "Advanced Digitizing" ) );
mAdvancedDigitizeToolBar->setIconSize( myIconSize );
mAdvancedDigitizeToolBar->setObjectName( "Advanced Digitizing" );
mAdvancedDigitizeToolBar->addAction( mActionSimplifyFeature );
mAdvancedDigitizeToolBar->addAction( mActionDeleteHole );
mAdvancedDigitizeToolBar->addAction( mActionDeletePart );
mToolbarMenu->addAction( mAdvancedDigitizeToolBar->toggleViewAction() );
//
// Map Navigation Toolbar
mMapNavToolBar = addToolBar( tr( "Map Navigation" ) );
@ -1606,6 +1650,12 @@ void QgisApp::createCanvas()
mMapTools.mAddRing = new QgsMapToolAddRing( mMapCanvas );
mMapTools.mAddRing->setAction( mActionAddRing );
mMapTools.mAddIsland = new QgsMapToolAddIsland( mMapCanvas );
mMapTools.mSimplifyFeature = new QgsMapToolSimplify( mMapCanvas );
mMapTools.mSimplifyFeature->setAction( mActionSimplifyFeature );
mMapTools.mDeleteHole = new QgsMapToolDeleteHole( mMapCanvas );
mMapTools.mDeleteHole->setAction( mActionDeleteHole );
mMapTools.mDeletePart = new QgsMapToolDeletePart( mMapCanvas );
mMapTools.mDeletePart->setAction( mActionDeletePart );
//ensure that non edit tool is initialised or we will get crashes in some situations
mNonEditMapTool = mMapTools.mPan;
}
@ -3979,6 +4029,21 @@ void QgisApp::moveFeature()
mMapCanvas->setMapTool( mMapTools.mMoveFeature );
}
void QgisApp::simplifyFeature()
{
mMapCanvas->setMapTool( mMapTools.mSimplifyFeature );
}
void QgisApp::deleteHole()
{
mMapCanvas->setMapTool( mMapTools.mDeleteHole );
}
void QgisApp::deletePart()
{
mMapCanvas->setMapTool( mMapTools.mDeletePart );
}
void QgisApp::splitFeatures()
{
mMapCanvas->setMapTool( mMapTools.mSplitFeatures );
@ -5215,11 +5280,13 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
{
mActionCapturePoint->setEnabled( true );
mActionCapturePoint->setVisible( true );
mActionDeletePart->setEnabled( true );
}
else
{
mActionCapturePoint->setEnabled( false );
mActionCapturePoint->setVisible( false );
mActionDeletePart->setEnabled( false );
}
mActionCaptureLine->setEnabled( false );
mActionCapturePolygon->setEnabled( false );
@ -5231,6 +5298,9 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionAddRing->setEnabled( false );
mActionAddIsland->setEnabled( false );
mActionSplitFeatures->setEnabled( false );
mActionSimplifyFeature->setEnabled( false );
mActionDeleteHole->setEnabled( false );
if ( vlayer->isEditable() && dprovider->capabilities() & QgsVectorDataProvider::ChangeGeometries )
{
mActionMoveVertex->setEnabled( true );
@ -5244,12 +5314,16 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionCaptureLine->setEnabled( true );
mActionCaptureLine->setVisible( true );
mActionSplitFeatures->setEnabled( true );
mActionSimplifyFeature->setEnabled( true );
mActionDeletePart->setEnabled( true );
}
else
{
mActionCaptureLine->setEnabled( false );
mActionCaptureLine->setVisible( false );
mActionSplitFeatures->setEnabled( false );
mActionSimplifyFeature->setEnabled( false );
mActionDeletePart->setEnabled( false );
}
mActionCapturePoint->setEnabled( false );
mActionCapturePolygon->setEnabled( false );
@ -5257,6 +5331,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionCapturePolygon->setVisible( false );
mActionAddRing->setEnabled( false );
mActionAddIsland->setEnabled( false );
mActionDeleteHole->setEnabled( false );
}
else if ( vlayer->geometryType() == QGis::Polygon )
{
@ -5267,6 +5342,9 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionAddRing->setEnabled( true );
mActionAddIsland->setEnabled( true );
mActionSplitFeatures->setEnabled( true );
mActionSimplifyFeature->setEnabled( true );
mActionDeleteHole->setEnabled( true );
mActionDeletePart->setEnabled( true );
}
else
{
@ -5275,6 +5353,9 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionAddRing->setEnabled( false );
mActionAddIsland->setEnabled( false );
mActionSplitFeatures->setEnabled( false );
mActionSimplifyFeature->setEnabled( false );
mActionDeleteHole->setEnabled( false );
mActionDeletePart->setEnabled( false );
}
mActionCapturePoint->setEnabled( false );
mActionCaptureLine->setEnabled( false );

View File

@ -42,6 +42,8 @@ class QgisAppInterface;
class QgsClipboard;
class QgsComposer;
class QgsHelpViewer;
class QgsFeature;
class QgsLegend;
class QgsMapCanvas;
class QgsMapLayer;
@ -63,7 +65,6 @@ class QgsVectorLayer;
#include "qgsconfig.h"
#include "qgspoint.h"
/*! \class QgisApp
* \brief Main window for the Qgis application
*/
@ -221,6 +222,9 @@ class QgisApp : public QMainWindow
QAction *actionMoveVertex() { return mActionMoveVertex; }
QAction *actionAddRing() { return mActionAddRing; }
QAction *actionAddIsland() { return mActionAddIsland; }
QAction *actionSimplifyFeature() { return mActionSimplifyFeature; }
QAction *actionDeleteHole() { return mActionDeleteHole; }
QAction *actionDeletePart() { return mActionDeletePart; }
QAction *actionEditSeparator2() { return mActionEditSeparator2; }
QAction *actionPan() { return mActionPan; }
@ -318,6 +322,7 @@ class QgisApp : public QMainWindow
QToolBar *layerToolBar() { return mLayerToolBar; }
QToolBar *mapNavToolToolBar() { return mMapNavToolBar; }
QToolBar *digitizeToolBar() { return mDigitizeToolBar; }
QToolBar *advancedDigitizeToolBar() { return mAdvancedDigitizeToolBar; }
QToolBar *attributesToolBar() { return mAttributesToolBar; }
QToolBar *pluginToolBar() { return mPluginToolBar; }
QToolBar *helpToolBar() { return mHelpToolBar; }
@ -495,6 +500,12 @@ class QgisApp : public QMainWindow
void addRing();
//! activates the add island tool
void addIsland();
//! simplifies feature
void simplifyFeature();
//! deletes hole in polygon
void deleteHole();
//! deletes part of polygon
void deletePart();
//! activates the selection tool
void select();
@ -666,6 +677,7 @@ class QgisApp : public QMainWindow
QToolBar *mLayerToolBar;
QToolBar *mMapNavToolBar;
QToolBar *mDigitizeToolBar;
QToolBar *mAdvancedDigitizeToolBar;
QToolBar *mAttributesToolBar;
QToolBar *mPluginToolBar;
QToolBar *mHelpToolBar;
@ -701,6 +713,10 @@ class QgisApp : public QMainWindow
QAction *mActionAddRing;
QAction *mActionAddIsland;
QAction *mActionEditSeparator2;
QAction *mActionSimplifyFeature;
QAction *mActionDeleteHole;
QAction *mActionDeletePart;
QAction *mActionEditSeparator3;
QAction *mActionPan;
QAction *mActionZoomIn;
@ -816,6 +832,9 @@ class QgisApp : public QMainWindow
QgsMapTool* mVertexDelete;
QgsMapTool* mAddRing;
QgsMapTool* mAddIsland;
QgsMapTool* mSimplifyFeature;
QgsMapTool* mDeleteHole;
QgsMapTool* mDeletePart;
} mMapTools;
QgsMapTool *mNonEditMapTool;
@ -921,6 +940,7 @@ class QgisApp : public QMainWindow
QgsPythonUtils* mPythonUtils;
static QgisApp *smInstance;
};
#endif

View File

@ -0,0 +1,161 @@
/***************************************************************************
qgsmaptooldeletehole.h - delete a hole from polygon
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 "qgsmaptooldeletehole.h"
#include "qgsmapcanvas.h"
#include "qgsvertexmarker.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>
#include <QMessageBox>
QgsMapToolDeleteHole::QgsMapToolDeleteHole( QgsMapCanvas* canvas )
: QgsMapToolVertexEdit( canvas ), mCross( 0 )
{
}
QgsMapToolDeleteHole::~QgsMapToolDeleteHole()
{
delete mCross;
}
void QgsMapToolDeleteHole::canvasMoveEvent( QMouseEvent * e )
{
//nothing to do
}
void QgsMapToolDeleteHole::canvasPressEvent( QMouseEvent * e )
{
delete mCross;
mCross = 0;
mRecentSnappingResults.clear();
//do snap -> new recent snapping results
if ( mSnapper.snapToCurrentLayer( e->pos(), mRecentSnappingResults, QgsSnapper::SnapToVertex ) != 0 )
{
//error
}
if ( mRecentSnappingResults.size() > 0 )
{
QgsPoint markerPoint = mRecentSnappingResults.begin()->snappedVertex;
//show vertex marker
mCross = new QgsVertexMarker( mCanvas );
mCross->setIconType( QgsVertexMarker::ICON_X );
mCross->setCenter( markerPoint );
}
else
{
displaySnapToleranceWarning();
}
}
void QgsMapToolDeleteHole::canvasReleaseEvent( QMouseEvent * e )
{
delete mCross;
mCross = 0;
QgsMapLayer* currentLayer = mCanvas->currentLayer();
if ( !currentLayer )
return;
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
if ( !vlayer )
return;
if ( mRecentSnappingResults.size() > 0 )
{
QList<QgsSnappingResult>::iterator sr_it = mRecentSnappingResults.begin();
for ( ; sr_it != mRecentSnappingResults.end(); ++sr_it )
{
deleteHole( sr_it->snappedAtGeometry, sr_it->snappedVertexNr, vlayer);
}
}
}
void QgsMapToolDeleteHole::deleteHole( int fId, int beforeVertexNr, QgsVectorLayer* vlayer)
{
QgsFeature f;
vlayer->featureAtId( fId, f );
QgsGeometry* g = f.geometry();
QGis::WkbType wkbtype = g->wkbType();
int ringNum, partNum = 0;
if (wkbtype == QGis::WKBPolygon || wkbtype == QGis::WKBPolygon25D)
{
ringNum = ringNumInPolygon( g, beforeVertexNr );
}
else if (wkbtype == QGis::WKBMultiPolygon || wkbtype == QGis::WKBMultiPolygon25D)
{
ringNum = ringNumInMultiPolygon( g, beforeVertexNr, partNum );
}
else
return;
if (g->deleteHole( ringNum, partNum ))
{
vlayer->deleteFeature( fId );
vlayer->addFeature(f);
mCanvas->refresh();
}
}
int QgsMapToolDeleteHole::ringNumInPolygon( QgsGeometry* g, int vertexNr )
{
QgsPolygon polygon = g->asPolygon();
for (int ring = 0; ring < polygon.count(); ring++)
{
if (vertexNr < polygon[ring].count())
return ring;
vertexNr -= polygon[ring].count();
}
return -1;
}
int QgsMapToolDeleteHole::ringNumInMultiPolygon( QgsGeometry* g, int vertexNr, int& partNum )
{
QgsMultiPolygon mpolygon = g->asMultiPolygon();
for (int part = 0; part < mpolygon.count(); part++)
{
const QgsPolygon& polygon = mpolygon[part];
for (int ring = 0; ring < polygon.count(); ring++)
{
if (vertexNr < polygon[ring].count())
{
partNum = part;
return ring;
}
vertexNr -= polygon[ring].count();
}
}
return -1;
}
void QgsMapToolDeleteHole::deactivate()
{
delete mCross;
mCross = 0;
QgsMapTool::deactivate();
}

View File

@ -0,0 +1,53 @@
/***************************************************************************
qgsmaptooldeletehole.h - delete a hole from polygon
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 QGSMAPTOOLDELETEHOLE_H
#define QGSMAPTOOLDELETEHOLE_H
#include "qgsmaptoolvertexedit.h"
#include <QUndoCommand>
class QgsVertexMarker;
/**Map tool to delete vertices from line/polygon features*/
class QgsMapToolDeleteHole: public QgsMapToolVertexEdit
{
public:
QgsMapToolDeleteHole( QgsMapCanvas* canvas );
virtual ~QgsMapToolDeleteHole();
void canvasMoveEvent( QMouseEvent * e );
void canvasPressEvent( QMouseEvent * e );
void canvasReleaseEvent( QMouseEvent * e );
//! called when map tool is being deactivated
void deactivate();
private:
QgsVertexMarker* mCross;
//! delete hole from the geometry
void deleteHole( int fId, int beforeVertexNr, QgsVectorLayer* vlayer);
//! return ring number in polygon
int ringNumInPolygon( QgsGeometry* g, int vertexNr );
//! return ring number in multipolygon and set parNum to index of the part
int ringNumInMultiPolygon( QgsGeometry* g, int vertexNr, int& partNum );
};
#endif

View File

@ -0,0 +1,178 @@
/***************************************************************************
qgsmaptooldeletepart.cpp - delete a part from multipart geometry
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 "qgsmaptooldeletepart.h"
#include "qgsmapcanvas.h"
#include "qgsvertexmarker.h"
#include "qgsvectorlayer.h"
#include <QMouseEvent>
#include <QMessageBox>
QgsMapToolDeletePart::QgsMapToolDeletePart( QgsMapCanvas* canvas )
: QgsMapToolVertexEdit( canvas ), mCross( 0 )
{
}
QgsMapToolDeletePart::~QgsMapToolDeletePart()
{
delete mCross;
}
void QgsMapToolDeletePart::canvasMoveEvent( QMouseEvent * e )
{
//nothing to do
}
void QgsMapToolDeletePart::canvasPressEvent( QMouseEvent * e )
{
delete mCross;
mCross = 0;
mRecentSnappingResults.clear();
//do snap -> new recent snapping results
if ( mSnapper.snapToCurrentLayer( e->pos(), mRecentSnappingResults, QgsSnapper::SnapToVertex ) != 0 )
{
//error
}
if ( mRecentSnappingResults.size() > 0 )
{
QgsPoint markerPoint = mRecentSnappingResults.begin()->snappedVertex;
//show vertex marker
mCross = new QgsVertexMarker( mCanvas );
mCross->setIconType( QgsVertexMarker::ICON_X );
mCross->setCenter( markerPoint );
}
else
{
displaySnapToleranceWarning();
}
}
void QgsMapToolDeletePart::canvasReleaseEvent( QMouseEvent * e )
{
delete mCross;
mCross = 0;
QgsMapLayer* currentLayer = mCanvas->currentLayer();
if ( !currentLayer )
return;
QgsVectorLayer* vlayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
if ( !vlayer )
return;
if ( mRecentSnappingResults.size() > 0 )
{
QList<QgsSnappingResult>::iterator sr_it = mRecentSnappingResults.begin();
for ( ; sr_it != mRecentSnappingResults.end(); ++sr_it )
{
deletePart( sr_it->snappedAtGeometry, sr_it->snappedVertexNr, vlayer);
}
}
}
void QgsMapToolDeletePart::deletePart( int fId, int beforeVertexNr, QgsVectorLayer* vlayer)
{
QgsFeature f;
vlayer->featureAtId( fId, f );
// find out the part number
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 ))
{
vlayer->deleteFeature( fId );
vlayer->addFeature(f);
mCanvas->refresh();
}
else
{
QMessageBox::information(mCanvas, tr("Delete part"), tr("Couldn't remove the selected part."));
}
}
int QgsMapToolDeletePart::partNumberOfVertex( QgsGeometry* g, int beforeVertexNr )
{
int part;
switch ( g->wkbType() )
{
case QGis::WKBMultiPoint25D:
case QGis::WKBMultiPoint:
if ( beforeVertexNr < g->asMultiPoint().count() )
return beforeVertexNr;
else
return -1;
case QGis::WKBMultiLineString25D:
case QGis::WKBMultiLineString:
{
QgsMultiPolyline mline = g->asMultiPolyline();
for (part = 0; part < mline.count(); part++)
{
if (beforeVertexNr < mline[part].count())
return part;
beforeVertexNr -= mline[part].count();
}
return -1; // not found
}
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];
for (int ring = 0; ring < polygon.count(); ring++) // go through the rings
{
if (beforeVertexNr < polygon[ring].count())
return part;
beforeVertexNr -= polygon[ring].count();
}
}
return -1; // not found
}
default:
return -1;
}
}
void QgsMapToolDeletePart::deactivate()
{
delete mCross;
mCross = 0;
QgsMapTool::deactivate();
}

View File

@ -0,0 +1,50 @@
/***************************************************************************
qgsmaptooldeletepart.h - delete a part from multipart geometry
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 QGSMAPTOOLDELETEPART_H
#define QGSMAPTOOLDELETEPART_H
#include "qgsmaptoolvertexedit.h"
class QgsVertexMarker;
/**Map tool to delete vertices from line/polygon features*/
class QgsMapToolDeletePart: public QgsMapToolVertexEdit
{
public:
QgsMapToolDeletePart( QgsMapCanvas* canvas );
virtual ~QgsMapToolDeletePart();
void canvasMoveEvent( QMouseEvent * e );
void canvasPressEvent( QMouseEvent * e );
void canvasReleaseEvent( QMouseEvent * e );
//! called when map tool is being deactivated
void deactivate();
private:
QgsVertexMarker* mCross;
//! delete part of a geometry
void deletePart( int fId, int beforeVertexNr, QgsVectorLayer* vlayer);
//! find out part number of geometry given the snapped vertex number
int partNumberOfVertex( QgsGeometry* g, int beforeVertexNr );
};
#endif

View File

@ -0,0 +1,381 @@
/***************************************************************************
qgsmaptoolsimplify.cpp - simplify vector layer features
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 "qgsmaptoolsimplify.h"
#include "qgsgeometry.h"
#include "qgsmapcanvas.h"
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include "qgstolerance.h"
#include <QMouseEvent>
#include <math.h>
QgsSimplifyDialog::QgsSimplifyDialog(QWidget* parent)
: QDialog( parent )
{
setupUi(this);
connect( horizontalSlider, SIGNAL( valueChanged( int ) ),
this, SLOT( valueChanged( int ) ) );
connect( okButton, SIGNAL(clicked()),
this, SLOT(simplify()));
}
void QgsSimplifyDialog::valueChanged(int value)
{
emit toleranceChanged(value);
}
void QgsSimplifyDialog::simplify()
{
emit storeSimplified();
}
void QgsSimplifyDialog::setRange(int minValue, int maxValue)
{
horizontalSlider->setMinimum(minValue);
horizontalSlider->setMaximum(maxValue);
// let's have 20 page steps
horizontalSlider->setPageStep( (maxValue - minValue) / 20 );
}
QgsMapToolSimplify::QgsMapToolSimplify( QgsMapCanvas* canvas )
: QgsMapToolEdit( canvas), mRubberBand( 0 )
{
mSimplifyDialog = new QgsSimplifyDialog( canvas->topLevelWidget() );
connect( mSimplifyDialog, SIGNAL( toleranceChanged( int ) ),
this, SLOT( toleranceChanged( int ) ) );
connect( mSimplifyDialog, SIGNAL( storeSimplified() ),
this, SLOT(storeSimplified()));
connect( mSimplifyDialog, SIGNAL(finished(int)),
this, SLOT(removeRubberBand()) );
}
QgsMapToolSimplify::~QgsMapToolSimplify()
{
removeRubberBand();
delete mSimplifyDialog;
}
void QgsMapToolSimplify::toleranceChanged(int tolerance)
{
mTolerance = double(tolerance)/toleranceDivider;
// create a copy of selected feature and do the simplification
QgsFeature f = mSelectedFeature;
QgsSimplifyFeature::simplifyLine(f, mTolerance);
mRubberBand->setToGeometry(f.geometry(), false);
}
void QgsMapToolSimplify::storeSimplified()
{
QgsVectorLayer * vlayer = currentVectorLayer();
QgsSimplifyFeature::simplifyLine(mSelectedFeature, mTolerance);
// TODO(md): change geometry of feature instead of delete+add
vlayer->deleteFeature( mSelectedFeature.id() );
vlayer->addFeature(mSelectedFeature);
mCanvas->refresh();
}
int QgsMapToolSimplify::calculateDivider(double num)
{
double tmp = num;
int i = 1;
while (tmp < 1)
{
tmp = tmp*10;
i = i *10;
}
return i;
}
void QgsMapToolSimplify::calculateSliderBoudaries()
{
double minTolerance, maxTolerance;
double tol = 0.0000001;
bool found = false;
bool isLine = mSelectedFeature.geometry()->type() == QGis::Line;
QVector<QgsPoint> pts = getPointList(mSelectedFeature);
int size = pts.size();
if (size == 0 || (isLine && size < 2) || (!isLine && size < 3) )
{
return;
}
// calculate min
while (!found)
{
if (QgsSimplifyFeature::simplifyPoints(pts, tol).size() < size)
{
found = true;
minTolerance = tol/2;
} else {
tol = tol * 2;
}
}
found = false;
int requiredCnt = (isLine ? 2 : 3);
// calculate max
while (!found)
{
if (QgsSimplifyFeature::simplifyPoints(pts, tol).size() < requiredCnt + 1)
{
//TODO: fix for polygon
found = true;
maxTolerance = tol;
} else {
tol = tol * 2;
}
}
toleranceDivider = calculateDivider(minTolerance);
// set min and max
mSimplifyDialog->setRange( int(minTolerance * toleranceDivider),
int(maxTolerance * toleranceDivider) );
}
void QgsMapToolSimplify::canvasPressEvent( QMouseEvent * e )
{
QgsVectorLayer * vlayer = currentVectorLayer();
QgsPoint layerCoords = mCanvas->getCoordinateTransform()->toMapPoint( e->pos().x(), e->pos().y() );
double r = QgsTolerance::vertexSearchRadius(vlayer, mCanvas->mapRenderer());
QgsRectangle selectRect = QgsRectangle( layerCoords.x() - r, layerCoords.y() - r,
layerCoords.x() + r, layerCoords.y() + r);
vlayer->select( QgsAttributeList(), selectRect, true );
QgsGeometry* geometry = QgsGeometry::fromPoint( layerCoords );
double minDistance = 10000000;
double currentDistance;
QgsFeature f;
mSelectedFeature.setValid(FALSE);
while (vlayer->nextFeature(f))
{
currentDistance = geometry->distance( *(f.geometry()) );
if ( currentDistance < minDistance )
{
minDistance = currentDistance;
mSelectedFeature = f;
}
}
// delete previous rubberband (if any)
removeRubberBand();
if (mSelectedFeature.isValid())
{
mRubberBand = new QgsRubberBand(mCanvas);
mRubberBand->setToGeometry(mSelectedFeature.geometry(), false);
mRubberBand->setColor(Qt::red);
mRubberBand->setWidth(2);
mRubberBand->show();
//calculate boudaries for slidebar
calculateSliderBoudaries();
// show dialog as a non-modal window
mSimplifyDialog->show();
}
}
void QgsMapToolSimplify::removeRubberBand()
{
delete mRubberBand;
mRubberBand = 0;
}
void QgsMapToolSimplify::deactivate()
{
if (mSimplifyDialog->isVisible())
mSimplifyDialog->close();
removeRubberBand();
QgsMapTool::deactivate();
}
QVector<QgsPoint> QgsMapToolSimplify::getPointList(QgsFeature& f)
{
QgsGeometry* line = f.geometry();
if ((line->type() != QGis::Line && line->type() != QGis::Polygon ) || line->isMultipart())
{
return QVector<QgsPoint>();
}
if ((line->type() == QGis::Line))
{
return line->asPolyline();
}
else
{
if (line->asPolygon().size() > 1)
{
return QVector<QgsPoint>();
}
return line->asPolygon()[0];
}
}
bool QgsSimplifyFeature::simplifyLine(QgsFeature& lineFeature, double tolerance)
{
QgsGeometry* line = lineFeature.geometry();
if (line->type() != QGis::Line)
{
return FALSE;
}
QVector<QgsPoint> resultPoints = simplifyPoints(line->asPolyline(), tolerance);
lineFeature.setGeometry( QgsGeometry::fromPolyline( resultPoints ) );
return TRUE;
}
//TODO: change to correct structure after
bool QgsSimplifyFeature::simplifyPartOfLine(QgsFeature& lineFeature, int fromVertexNr, int toVertexNr, double tolerance)
{
QgsGeometry* line = lineFeature.geometry();
if (line->type() != QGis::Line)
{
return FALSE;
}
QVector<QgsPoint> resultPoints = simplifyPoints(line->asPolyline(), tolerance);
lineFeature.setGeometry( QgsGeometry::fromPolyline( resultPoints ) );
return TRUE;
}
QVector<QgsPoint> QgsSimplifyFeature::simplifyPoints (const QVector<QgsPoint>& pts, double tolerance)
{
// Douglas-Peucker simplification algorithm
int anchor = 0;
int floater = pts.size() - 1;
QList<StackEntry> stack;
StackEntry temporary;
StackEntry entry = {anchor, floater};
stack.append(entry);
QSet<int> keep;
double anchorX;
double anchorY;
double seg_len;
double max_dist;
int farthest;
double dist_to_seg;
double vecX;
double vecY;
while (!stack.empty())
{
temporary = stack.takeLast();
anchor = temporary.anchor;
floater = temporary.floater;
// initialize line segment
if (pts[floater] != pts[anchor])
{
anchorX = pts[floater].x() - pts[anchor].x();
anchorY = pts[floater].y() - pts[anchor].y();
seg_len = sqrt(anchorX * anchorX + anchorY * anchorY);
// get the unit vector
anchorX /= seg_len;
anchorY /= seg_len;
}
else
{
anchorX = anchorY = seg_len = 0.0;
}
// inner loop:
max_dist = 0.0;
farthest = anchor + 1;
for (int i = anchor + 1; i < floater; i++)
{
dist_to_seg = 0.0;
// compare to anchor
vecX = pts[i].x() - pts[anchor].x();
vecY = pts[i].y() - pts[anchor].y();
seg_len = sqrt( vecX * vecX + vecY * vecY );
// dot product:
double proj = vecX * anchorX + vecY * anchorY;
if (proj < 0.0)
{
dist_to_seg = seg_len;
}
else
{
// compare to floater
vecX = pts[i].x() - pts[floater].x();
vecY = pts[i].y() - pts[floater].y();
seg_len = sqrt( vecX * vecX + vecY *vecY );
// dot product:
proj = vecX * (-anchorX) + vecY * (-anchorY);
if (proj < 0.0)
{
dist_to_seg = seg_len;
}
else
{ // calculate perpendicular distance to line (pythagorean theorem):
dist_to_seg = sqrt(fabs(seg_len * seg_len - proj * proj));
}
if (max_dist < dist_to_seg)
{
max_dist = dist_to_seg;
farthest = i;
}
}
}
if (max_dist <= tolerance)
{ // # use line segment
keep.insert(anchor);
keep.insert(floater);
}
else
{
StackEntry s = {anchor, farthest};
stack.append(s);
StackEntry r = {farthest, floater};
stack.append(r);
}
}
QList<int> keep2 = keep.toList();
qSort(keep2);
QVector<QgsPoint> result;
int position;
while (!keep2.empty())
{
position = keep2.takeFirst();
result.append(pts[position]);
}
return result;
}

View File

@ -0,0 +1,111 @@
/***************************************************************************
qgsmaptoolsimplify.h - simplify vector layer features
---------------------
begin : April 2009
copyright : (C) 2009 by Richard Kostecky
email : csf dot kostej at mail dot com
***************************************************************************
* *
* 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 QGSMAPTOOLSIMPLIFY_H
#define QGSMAPTOOLSIMPLIFY_H
#include "qgsmaptooledit.h"
#include "ui_qgssimplifytolerancedialog.h"
#include <QVector>
#include "qgsfeature.h"
class QgsRubberBand;
class QgsSimplifyDialog : public QDialog, private Ui::SimplifyLineDialog
{
Q_OBJECT
public:
QgsSimplifyDialog( QWidget* parent = NULL );
void setRange(int minValue, int maxValue);
signals:
void toleranceChanged( int tol );
void storeSimplified();
private slots:
void valueChanged( int value );
void simplify();
};
/**Map tool to add vertices to line/polygon features*/
class QgsMapToolSimplify: public QgsMapToolEdit
{
Q_OBJECT
public:
QgsMapToolSimplify( QgsMapCanvas* canvas );
virtual ~QgsMapToolSimplify();
void canvasPressEvent( QMouseEvent * e );
//! called when map tool is being deactivated
void deactivate();
public slots:
void removeRubberBand();
private:
int calculateDivider(double num);
void calculateSliderBoudaries();
QVector<QgsPoint> getPointList(QgsFeature& f);
// data
QgsSimplifyDialog* mSimplifyDialog;
QgsRubberBand* mRubberBand;
QgsFeature mSelectedFeature;
int toleranceDivider;
double mTolerance;
private slots:
void toleranceChanged(int tolerance);
void storeSimplified();
};
/**
Implementation of Douglas-Peucker simplification algorithm.
*/
class QgsSimplifyFeature
{
struct StackEntry {
int anchor;
int floater;
};
public:
/** simplify line feature with specified tolerance. Returns TRUE on success */
static bool simplifyLine(QgsFeature &lineFeature, double tolerance);
/** simplify a part of line feature specified by range of vertices with given tolerance. Returns TRUE on success */
static bool simplifyPartOfLine(QgsFeature &lineFeature, int fromVertexNr, int toVertexNr, double tolerance);
/** simplify a line given by a vector of points and tolerance. Returns simplified vector of points */
static QVector<QgsPoint> simplifyPoints (const QVector<QgsPoint>& pts, double tolerance);
};
#endif

View File

@ -5540,3 +5540,117 @@ QList<QgsGeometry*> QgsGeometry::asGeometryCollection()
return geomCollection;
}
bool QgsGeometry::deleteHole( int ringNum, int partNum )
{
if (ringNum <= 0 || partNum < 0)
return FALSE;
switch ( wkbType() )
{
case QGis::WKBPolygon25D:
case QGis::WKBPolygon:
{
if (partNum != 0)
return FALSE;
QgsPolygon polygon = asPolygon();
if ( ringNum >= polygon.count() )
return FALSE;
polygon.remove( ringNum );
QgsGeometry* g2 = QgsGeometry::fromPolygon( polygon );
*this = *g2;
delete g2;
return TRUE;
}
case QGis::WKBMultiPolygon25D:
case QGis::WKBMultiPolygon:
{
QgsMultiPolygon mpolygon = asMultiPolygon();
if (partNum >= mpolygon.count())
return FALSE;
if ( ringNum >= mpolygon[partNum].count() )
return FALSE;
mpolygon[partNum].remove( ringNum );
QgsGeometry* g2 = QgsGeometry::fromMultiPolygon( mpolygon );
*this = *g2;
delete g2;
return TRUE;
}
default:
return FALSE; // only makes sense with polygons and multipolygons
}
}
bool QgsGeometry::deletePart( int partNum )
{
if (partNum < 0)
return FALSE;
switch ( wkbType() )
{
case QGis::WKBMultiPoint25D:
case QGis::WKBMultiPoint:
{
QgsMultiPoint mpoint = asMultiPoint();
if (partNum >= mpoint.size() || mpoint.size() == 1)
return FALSE;
mpoint.remove( partNum );
QgsGeometry* g2 = QgsGeometry::fromMultiPoint( mpoint );
*this = *g2;
delete g2;
break;
}
case QGis::WKBMultiLineString25D:
case QGis::WKBMultiLineString:
{
QgsMultiPolyline mline = asMultiPolyline();
if (partNum >= mline.size() || mline.size() == 1)
return FALSE;
mline.remove( partNum );
QgsGeometry* g2 = QgsGeometry::fromMultiPolyline( mline );
*this = *g2;
delete g2;
break;
}
case QGis::WKBMultiPolygon25D:
case QGis::WKBMultiPolygon:
{
QgsMultiPolygon mpolygon = asMultiPolygon();
if (partNum >= mpolygon.size() || mpolygon.size() == 1)
return FALSE;
mpolygon.remove( partNum );
QgsGeometry* g2 = QgsGeometry::fromMultiPolygon( mpolygon );
*this = *g2;
delete g2;
break;
}
default:
// single part geometries are ignored
return FALSE;
}
return TRUE;
}

View File

@ -332,6 +332,18 @@ class CORE_EXPORT QgsGeometry
@note added in version 1.1 */
QList<QgsGeometry*> asGeometryCollection();
/** delete a hole in polygon or multipolygon.
Ring 0 is outer ring and can't be deleted.
@return TRUE on success
@note added in version 1.2 */
bool deleteHole( int ringNum, int partNum = 0 );
/** delete part identified by the part number
@return TRUE on success
@note added in version 1.2 */
bool deletePart( int partNum );
private:
// Private variables

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimplifyLineDialog</class>
<widget class="QDialog" name="SimplifyLineDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>431</width>
<height>54</height>
</rect>
</property>
<property name="windowTitle">
<string>Simplify line tolerance</string>
</property>
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Set tolerance</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="horizontalSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="okButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>SimplifyLineDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>24</y>
</hint>
<hint type="destinationlabel">
<x>236</x>
<y>30</y>
</hint>
</hints>
</connection>
</connections>
</ui>