mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
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:
parent
fac6117a7d
commit
cbc57dcb27
BIN
images/themes/default/mActionDeleteHole.png
Normal file
BIN
images/themes/default/mActionDeleteHole.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
BIN
images/themes/default/mActionDeletePart.png
Normal file
BIN
images/themes/default/mActionDeletePart.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
images/themes/default/mActionSimplify.png
Normal file
BIN
images/themes/default/mActionSimplify.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
161
src/app/qgsmaptooldeletehole.cpp
Normal file
161
src/app/qgsmaptooldeletehole.cpp
Normal 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();
|
||||
}
|
53
src/app/qgsmaptooldeletehole.h
Normal file
53
src/app/qgsmaptooldeletehole.h
Normal 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
|
178
src/app/qgsmaptooldeletepart.cpp
Normal file
178
src/app/qgsmaptooldeletepart.cpp
Normal 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();
|
||||
}
|
||||
|
50
src/app/qgsmaptooldeletepart.h
Normal file
50
src/app/qgsmaptooldeletepart.h
Normal 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
|
381
src/app/qgsmaptoolsimplify.cpp
Normal file
381
src/app/qgsmaptoolsimplify.cpp
Normal 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;
|
||||
}
|
111
src/app/qgsmaptoolsimplify.h
Normal file
111
src/app/qgsmaptoolsimplify.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
59
src/ui/qgssimplifytolerancedialog.ui
Normal file
59
src/ui/qgssimplifytolerancedialog.ui
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user