458 changes: 458 additions & 0 deletions src/plugins/topology/checkDock.cpp

Large diffs are not rendered by default.

146 changes: 146 additions & 0 deletions src/plugins/topology/checkDock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/***************************************************************************
checkDock.h
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 CHECKDOCK_H
#define CHECKDOCK_H

#include <QDockWidget>

#include <qgsvectorlayer.h>
#include <qgsgeometry.h>
//#include <qgsvertexmarker.h>
#include "qgsspatialindex.h"
//#include <spatialindex/qgsspatialindex.h>

#include "ui_checkDock.h"
#include "rulesDialog.h"
#include "topolError.h"
#include "topolTest.h"
#include "dockModel.h"

class QgsMapLayerRegistry;
class QgsRubberBand;
class QgsVertexMarker;
class QgisApp;
class QgisInterface;
class checkDock;

class checkDock : public QDockWidget, public Ui::checkDock
{
Q_OBJECT

public:
/**
* Constructor
* @param qIface pointer to QgisInterface instance that is passed to the rulesDialog
* @param parent parent object
*/
checkDock( QgisInterface* qIface, QWidget *parent = 0 );
~checkDock();

private slots:
/**
* Launches the configuration dialog
*/
void configure();
/**
* Launches fixing routine
*/
void fix();
/**
* Validates the whole layer
*/
void validateAll();
/**
* Validates the current extent
*/
void validateExtent();
/**
* Validates only selected features
*/
void validateSelected();
/**
* toggles the visibility of rubber band error markers
*/
void toggleErrorMarkers();
/**
* Handles error selection
* @param index clicked index in the table
*/
void errorListClicked( const QModelIndex& index );
/**
* Deletes allocated errors' data
*/
void deleteErrors();
/**
* Filters all errors involving features from specified layer
* @param layerId layer ID
*/
void parseErrorListByLayer( QString layerId );
/**
* Clears rubberbands when window is hidden
* @param visible true if the window is visible
*/
void updateRubberBands( bool visible );


private:
rulesDialog* mConfigureDialog;
QgisApp* mQgisApp;

QgsRubberBand* mRBConflict;
QgsRubberBand* mRBFeature1;
QgsRubberBand* mRBFeature2;
QgsVertexMarker* mVMConflict;
QgsVertexMarker* mVMFeature1;
QgsVertexMarker* mVMFeature2;
QList<QgsRubberBand*> mRbErrorMarkers;
bool mMarkersVisible;

ErrorList mErrorList;
DockModel* mErrorListModel;

QgisInterface* qgsInterface;

//pointer to topology tests table
QTableWidget* mTestTable;

topolTest* mTest;
QgsMapLayerRegistry* mLayerRegistry;

/**
* Runs tests from the test table
* @param type validation type - what features to check
*/
void runTests( ValidateType type );
/**
* Validates topology
* @param type validation type - what features to check
*/
void validate( ValidateType type );
/**
* Filters all errors involving specified feature
* @param featureId feature ID
*/
void parseErrorListByFeature( int featureId );
/**
* Deletes vertex markers
*/
void clearVertexMarkers();
};

#endif
89 changes: 89 additions & 0 deletions src/plugins/topology/checkDock.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>checkDock</class>
<widget class="QDockWidget" name="checkDock">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>436</width>
<height>439</height>
</rect>
</property>
<property name="windowTitle">
<string>Topology Checker</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="mValidateAllButton">
<property name="text">
<string>Validate All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mValidateExtentButton">
<property name="text">
<string>Validate Extent</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="mToggleRubberbands">
<property name="text">
<string>Toggle Error Markers</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="mErrorTableView"/>
</item>
<item>
<widget class="QLabel" name="mComment">
<property name="text">
<string>Topology not checked yet</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="mConfigureButton">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mFixBox">
<item>
<property name="text">
<string>Select automatic fix</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="mFixButton">
<property name="text">
<string>Fix!</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
Binary file added src/plugins/topology/configureRules.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
125 changes: 125 additions & 0 deletions src/plugins/topology/dockModel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/***************************************************************************
dockModel.cpp
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 "dockModel.h"
#include "topolError.h"

DockModel::DockModel( ErrorList& theErrorList, QObject *parent = 0 ) : mErrorlist( theErrorList )
{
mHeader << "Error" << "Layer" << "Feature ID";

}

int DockModel::rowCount( const QModelIndex &parent ) const
{
return mErrorlist.count();
}

int DockModel::columnCount( const QModelIndex &parent ) const
{
return 3;
}

QVariant DockModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( role == Qt::DisplayRole )
{
if ( orientation == Qt::Vertical ) //row
{
return QVariant( section );
}
else
{
return mHeader[section];
}
}
else return QVariant();
}

QVariant DockModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() || ( role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::EditRole ) )
return QVariant();

int row = index.row();
// if(!row)
// {
// return QVariant();
// }
int column = index.column();

if ( role == Qt::TextAlignmentRole )
{
if ( column )
return QVariant( Qt::AlignRight );
else
return QVariant( Qt::AlignLeft );
}

QVariant val;
switch ( column )
{
case 0:
val = mErrorlist[row]->name();
break;
case 1:
if ( !mErrorlist[row]->featurePairs().first().layer )
val = QString( "Unkown" );
else
val = mErrorlist[row]->featurePairs().first().layer->name();
break;
case 2:
val = mErrorlist[row]->featurePairs().first().feature.id();
break;
default:
val = QVariant();
}

if ( val.isNull() )
{
return QVariant();
}

// convert to QString from some other representation
// this prevents displaying greater numbers in exponential format
return val.toString();
}

bool DockModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
return false;
}

Qt::ItemFlags DockModel::flags( const QModelIndex &index ) const
{
if ( !index.isValid() )
return Qt::ItemIsEnabled;

Qt::ItemFlags flags = QAbstractItemModel::flags( index );
return flags;
}

void DockModel::resetModel()
{
reset();
}

void DockModel::reload( const QModelIndex &index1, const QModelIndex &index2 )

{
emit dataChanged( index1, index2 );
}
91 changes: 91 additions & 0 deletions src/plugins/topology/dockModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/***************************************************************************
dockModel.h
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 DOCKMODEL_H
#define DOCKMODEL_H

#include <QAbstractTableModel>
#include <QModelIndex>
#include <QObject>

#include "topolError.h"

class DockModel: public QAbstractTableModel
{
Q_OBJECT

public:
/**
* Constructor
* @param theErrorList reference to the ErrorList where errors will be stored
* @param parent parent object
*/
DockModel(ErrorList& theErrorList, QObject *parent);
/**
* Returns header data
* @param section required section
* @param orientation horizontal or vertical orientation
* @param role data role
*/
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
/**
* Returns data on the given index
* @param index model index
* @param role data role
*/
virtual QVariant data(const QModelIndex &index, int role) const;
/**
* Updates data on given index
* @param index model index
* @param value new data value
* @param role data role
*/
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
/**
* Returns item flags for the index
* @param index model index
*/
Qt::ItemFlags flags(const QModelIndex &index) const;

/**
* Returns the number of rows
* @param parent parent index
*/
int rowCount(const QModelIndex &parent) const;
/**
* Returns the number of columns
* @param parent parent index
*/
int columnCount(const QModelIndex &parent) const;

/**
* Reloads the model data between indices
* @param index1 start index
* @param index2 end index
*/
void reload(const QModelIndex &index1, const QModelIndex &index2);
/**
* Resets the model
*/
void resetModel();

private:
ErrorList& mErrorlist;
QList<QString> mHeader;
};

#endif
123 changes: 123 additions & 0 deletions src/plugins/topology/geosFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#ifndef GEOSFUNCTIONS_H
#define GEOSFUNCTIONS_H

#include <geos_c.h>
#include <qgsgeometry.h>

class GEOSException
{
public:
GEOSException( const char *theMsg )
{
if ( strcmp( theMsg, "Unknown exception thrown" ) == 0 && lastMsg )
{
delete [] theMsg;
char *aMsg = new char[strlen( lastMsg )+1];
strcpy( aMsg, lastMsg );
msg = aMsg;
}
else
{
msg = theMsg;
lastMsg = msg;
}
}

// copy constructor
GEOSException( const GEOSException &rhs )
{
*this = rhs;
}

~GEOSException()
{
if ( lastMsg == msg )
lastMsg = NULL;
delete [] msg;
}

const char *what()
{
return msg;
}

private:
const char *msg;
static const char *lastMsg;
};

/**
* Checks whether two geometries touch each other
* @param g1 first geometry
* @param g2 second geometry
*/
bool geosTouches(QgsGeometry* g1, QgsGeometry* g2)
{
try
{
if (1 == GEOSTouches(g1->asGeos(), g2->asGeos()))
return true;
}
catch (GEOSException &e)
{
return false;
}
}

/**
* Checks whether two geometries are identical in all respect(duplicates)
* @param g1 first geometry
* @param g2 second geometry
*/
bool geosEquals(QgsGeometry* g1, QgsGeometry* g2)
{
try
{
if (1 == GEOSEquals(g1->asGeos(), g2->asGeos()))
return true;
}
catch (GEOSException &e)
{
return false;
}
}



/**
* Checks whether two geometries overlap
* @param g1 first geometry
* @param g2 second geometry
*/
bool geosOverlaps(QgsGeometry* g1, QgsGeometry* g2)
{
try
{
if (1 == GEOSOverlaps(g1->asGeos(), g2->asGeos()))
return true;
}
catch (GEOSException &e)
{
return false;
}
}

/**
* Checks whether the first geometry contains the second geometry
* @param g1 first geometry
* @param g2 second geometry
*/
bool geosContains(QgsGeometry* g1, QgsGeometry* g2)
{
try
{
if (1 == GEOSContains(g1->asGeos(), g2->asGeos()))
return true;
}
catch (GEOSException &e)
{
return false;
}
}

#endif
274 changes: 274 additions & 0 deletions src/plugins/topology/rulesDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/***************************************************************************
rulesDialog.cpp
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 <QtGui>

#include <qgsvectordataprovider.h>
#include <qgsvectorlayer.h>
//#include <qgssearchstring.h>
//#include <qgssearchtreenode.h>
#include <qgsmaplayer.h>
#include <qgsmaplayerregistry.h>

#include <qgsproviderregistry.h>
#include <qgslogger.h>
#include <qgisinterface.h>
#include <qgsproject.h>

//#include "../../app/qgisapp.h"

#include "rulesDialog.h"
#include "topolTest.h"

rulesDialog::rulesDialog( QList<QString> layerList, QMap<QString, TopologyRule> testMap, QgisInterface* theQgisIface, QWidget *parent )
: QDialog( parent ), Ui::rulesDialog()
{
setupUi( this );

mQgisIface = theQgisIface;

//setHorizontalHeaderItems();
mTestTable->hideColumn( 4 );
mTestTable->hideColumn( 5 );

mTestConfMap = testMap;
mTestTable->setSelectionBehavior( QAbstractItemView::SelectRows );
mTestBox->addItems( mTestConfMap.keys() );

QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();

for ( int i = 0; i < layerList.size(); ++i )
{
// add layer ID to the layerId list
mLayerIds << layerList[i];

//QgsVectorLayer* v1 = ( QgsVectorLayer* )layerRegistry->mapLayers()[layerList[i]];

// add layer name to the layer combo boxes
mLayer1Box->addItem((( QgsVectorLayer* )layerRegistry->mapLayers()[layerList[i]] )->name() );
mLayer2Box->addItem((( QgsVectorLayer* )layerRegistry->mapLayers()[layerList[i]] )->name() );
}

connect( mAddTestButton, SIGNAL( clicked() ), this, SLOT( addTest() ) );
connect( mAddTestButton, SIGNAL( clicked() ), mTestTable, SLOT( resizeColumnsToContents() ) );
// attempt to add new test when Ok clicked
connect( buttonBox, SIGNAL( accepted() ), this, SLOT( addTest() ) );
connect( mDeleteTestButton, SIGNAL( clicked() ), this, SLOT( deleteTest() ) );
connect( mTestBox, SIGNAL( currentIndexChanged( const QString& ) ), this, SLOT( showControls( const QString& ) ) );
mTestBox->setCurrentIndex( 4 );

//this resets this plugin up if a project is loaded
connect( mQgisIface->mainWindow(), SIGNAL( projectRead() ), this, SLOT( projectRead() ) );
projectRead();
}

rulesDialog::~rulesDialog()
{
}

void rulesDialog::setHorizontalHeaderItems()
{
QStringList labels;
labels << "Test" << "Layer #1" << "Layer #2" << "Tolerance" << "" << "";
mTestTable->setHorizontalHeaderLabels( labels );
}

void rulesDialog::readTest( int index, QgsMapLayerRegistry* layerRegistry )
{
QString testName;
QString layer1Id;
QString layer2Id;
QString tolerance;
QgsProject* project = QgsProject::instance();
QString postfix = QString( "%1" ).arg( index );

testName = project->readEntry( "Topol", "/testname_" + postfix, "" );
tolerance = project->readEntry( "Topol", "/tolerance_" + postfix, "" );
layer1Id = project->readEntry( "Topol", "/layer1_" + postfix, "" );
layer2Id = project->readEntry( "Topol", "/layer2_" + postfix, "" );

QgsVectorLayer* l1;
if ( !( QgsVectorLayer* )layerRegistry->mapLayers().contains( layer1Id ) )
return;

l1 = ( QgsVectorLayer* )layerRegistry->mapLayers()[layer1Id];
if ( !l1 )
return;

QString layer1Name = l1->name();
QString layer2Name;
QgsVectorLayer* l2;

if ( mTestConfMap[testName].useSecondLayer )
{
if ( !( QgsVectorLayer* )layerRegistry->mapLayers().contains( layer2Id ) )
return;
else
{
l2 = ( QgsVectorLayer* )layerRegistry->mapLayers()[layer2Id];
layer2Name = l2->name();
}
}
else
layer2Name = "No layer";

int row = index;
mTestTable->insertRow( row );

QTableWidgetItem* newItem;
newItem = new QTableWidgetItem( testName );
newItem->setFlags( newItem->flags() & ~Qt::ItemIsEditable );
mTestTable->setItem( row, 0, newItem );

newItem = new QTableWidgetItem( layer1Name );
newItem->setFlags( newItem->flags() & ~Qt::ItemIsEditable );
mTestTable->setItem( row, 1, newItem );

newItem = new QTableWidgetItem( layer2Name );
newItem->setFlags( newItem->flags() & ~Qt::ItemIsEditable );
mTestTable->setItem( row, 2, newItem );

if ( mTestConfMap[testName].useTolerance )
newItem = new QTableWidgetItem( tolerance );
else
newItem = new QTableWidgetItem( QString( "No tolerance" ) );

newItem->setFlags( newItem->flags() & ~Qt::ItemIsEditable );
mTestTable->setItem( row, 3, newItem );

// add layer ids to hidden columns
newItem = new QTableWidgetItem( layer1Id );
mTestTable->setItem( row, 4, newItem );
newItem = new QTableWidgetItem( layer2Id );
mTestTable->setItem( row, 5, newItem );
}

void rulesDialog::projectRead()
{
QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();
int testCount = QgsProject::instance()->readNumEntry( "Topol", "/testCount" );
mTestTable->clearContents();

for ( int i = 0; i < testCount; ++i )
readTest( i, layerRegistry );
}

void rulesDialog::showControls( const QString& testName )
{
mLayer2Box->setVisible( mTestConfMap[testName].useSecondLayer );

bool useTolerance = mTestConfMap[testName].useTolerance;
mToleranceBox->setVisible( useTolerance );
mToleranceLabel->setVisible( useTolerance );
}

void rulesDialog::addLayer( QgsMapLayer* layer )
{
mLayerIds << layer->id();

// add layer name to the layer combo boxes
mLayer1Box->addItem( layer->name() );
mLayer2Box->addItem( layer->name() );
}

void rulesDialog::removeLayer( QString layerId )
{
int index = mLayerIds.indexOf( layerId );

mLayerIds.removeAt( index );
// + 1 for "No layer" string
mLayer1Box->removeItem( index + 1 );
mLayer2Box->removeItem( index + 1 );

// TODO: Maybe tell the dock that we have no layers under
//if (mLayer1Box->size() == 1) do something
}

void rulesDialog::addTest()
{
//sanity checks
QString test = mTestBox->currentText();
QString layer1 = mLayer1Box->currentText();
if ( layer1 == "No layer" )
return;

QString layer2 = mLayer2Box->currentText();
if ( layer2 == "No layer" && mTestConfMap[test].useSecondLayer )
return;

int row = mTestTable->rowCount();
mTestTable->insertRow( row );

QTableWidgetItem* newItem;
newItem = new QTableWidgetItem( test );
mTestTable->setItem( row, 0, newItem );
newItem = new QTableWidgetItem( layer1 );
mTestTable->setItem( row, 1, newItem );

if ( mTestConfMap[test].useSecondLayer )
newItem = new QTableWidgetItem( layer2 );
else
newItem = new QTableWidgetItem( "No layer" );

mTestTable->setItem( row, 2, newItem );

if ( mTestConfMap[test].useTolerance )
newItem = new QTableWidgetItem( QString( "%1" ).arg( mToleranceBox->value() ) );
else
newItem = new QTableWidgetItem( QString( "No tolerance" ) );

mTestTable->setItem( row, 3, newItem );

QString layer1ID, layer2ID;
// add layer ids to hidden columns
// -1 for "No layer" string
if ( mTestConfMap[test].useSecondLayer )
layer2ID = mLayerIds[mLayer2Box->currentIndex() - 1];
else
layer2ID = "No layer";

layer1ID = mLayerIds[mLayer1Box->currentIndex() - 1];

//TODO: use setItemData (or something like that) instead of hidden columns
newItem = new QTableWidgetItem( layer1ID );
mTestTable->setItem( row, 4, newItem );
newItem = new QTableWidgetItem( layer2ID );
mTestTable->setItem( row, 5, newItem );

// save state to the project file.....
QString postfix = QString( "%1" ).arg( row );
QgsProject* project = QgsProject::instance();

project->writeEntry( "Topol", "/testCount", row + 1 );
project->writeEntry( "Topol", "/testname_" + postfix, test );
project->writeEntry( "Topol", "/tolerance_" + postfix, QString( "%1" ).arg( mToleranceBox->value() ) );
project->writeEntry( "Topol", "/layer1_" + postfix, layer1ID );
project->writeEntry( "Topol", "/layer2_" + postfix, layer2ID );

// reset controls to default
mTestBox->setCurrentIndex( 0 );
mLayer1Box->setCurrentIndex( 0 );
mLayer2Box->setCurrentIndex( 0 );
mToleranceBox->setValue( 0 );
}

void rulesDialog::deleteTest()
{
int row = mTestTable->currentRow();
if ( 0 <= row && row < mTestTable->rowCount() )
mTestTable->removeRow( row );
}
100 changes: 100 additions & 0 deletions src/plugins/topology/rulesDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/***************************************************************************
rulesDialog.h
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 RULESDIALOG_H_
#define RULESDIALOG_H_

#include <QDialog>

#include <qgsvectorlayer.h>

#include "ui_rulesDialog.h"
#include "topolTest.h"

class QgisInterface;
class QgsMapLayerRegistry;

class rulesDialog : public QDialog, public Ui::rulesDialog
{
Q_OBJECT

public:
/*
* Constructor
* @param layerList List of layer IDs
* @param testMap maps test names to test routines
* @param theQgisIface pointer to a QgisInterface instance
* @param parent parent widget
*/
rulesDialog( QList<QString> layerList, QMap<QString, TopologyRule> testMap, QgisInterface* theQgisIface, QWidget *parent );
~rulesDialog();
/*
* Returns pointer to the test table
*/
QTableWidget* testTable() { return mTestTable; }
/*
* Returns pointer to the test combobox
*/
QComboBox* testBox() { return mTestBox; }

private:
QMap<QString, TopologyRule> mTestConfMap;
QList<QString> mLayerIds;
QgisInterface* mQgisIface;

/*
* Reads a test from the project
* @param index test index
* @param layerRegistry pointer to a QgsMapLayerRegistry instance
*/
void readTest( int index, QgsMapLayerRegistry* layerRegistry );
/*
* Sets the horizontal header for tet table
*/
void setHorizontalHeaderItems();

private slots:
/*
* Shows or hides controls according to test settings
* @param testName name of the test
*/
void showControls( const QString& testName );
/*
* Adds test to the table
*/
void addTest();
/*
* Deletes test from the table
*/
void deleteTest();
/*
* Reads tests from the project
*/
void projectRead();
/*
* Adds layer to layer comboboxes
* @param layer layer pointer
*/
void addLayer( QgsMapLayer* layer );
/*
* Deletes layer to layer comboboxes
* @param layerId layer ID
*/
void removeLayer( QString layerId );
};

#endif
198 changes: 198 additions & 0 deletions src/plugins/topology/rulesDialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>rulesDialog</class>
<widget class="QDialog" name="rulesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>739</width>
<height>545</height>
</rect>
</property>
<property name="windowTitle">
<string>Test settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Current Rules</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="mTestTable">
<column>
<property name="text">
<string>Rule</string>
</property>
</column>
<column>
<property name="text">
<string>Layer #1</string>
</property>
</column>
<column>
<property name="text">
<string>Layer #2</string>
</property>
</column>
<column>
<property name="text">
<string>Tolerance</string>
</property>
</column>
<column>
<property name="text">
<string>Layer1ID</string>
</property>
</column>
<column>
<property name="text">
<string>Layer2ID</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="mTestBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mLayer1Box">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>No layer</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QComboBox" name="mLayer2Box">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>No layer</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="mToleranceLabel">
<property name="text">
<string>Tolerance</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="mToleranceBox"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="mAddTestButton">
<property name="text">
<string>Add New Rule</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mDeleteTestButton">
<property name="text">
<string>Delete Rule</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>198</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>rulesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>506</x>
<y>369</y>
</hint>
<hint type="destinationlabel">
<x>289</x>
<y>194</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>rulesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>506</x>
<y>369</y>
</hint>
<hint type="destinationlabel">
<x>289</x>
<y>194</y>
</hint>
</hints>
</connection>
</connections>
</ui>
178 changes: 178 additions & 0 deletions src/plugins/topology/topol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/***************************************************************************
topol.cpp
TOPOLogy checker
-------------------
begin : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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. *
* *
***************************************************************************/

// QGIS Specific includes
#include <qgsmaplayer.h>
#include <qgsapplication.h>
#include <qgisinterface.h>
#include <qgisgui.h>

// Qt4 Related Includes
#include <QAction>
#include <QToolBar>
#include <QFile>
#include <QMessageBox>

#include "topol.h"
#include "checkDock.h"

static const char * const sIdent = "$Id: plugin.cpp 8053 2008-01-26 13:59:53Z timlinux $";
static const QString sName = QObject::tr( "Topology Checker" );
static const QString sDescription = QObject::tr( "A Plugin for finding topological errors in vector layers" );
static const QString sCategory = QObject::tr( "Vector" );
static const QString sPluginVersion = QObject::tr( "Version 0.1" );
static const QgisPlugin::PLUGINTYPE sPluginType = QgisPlugin::UI;
static const QString sPluginIcon = ":/topology/topol.png";

//////////////////////////////////////////////////////////////////////
//
// THE FOLLOWING METHODS ARE MANDATORY FOR ALL PLUGINS
//
//////////////////////////////////////////////////////////////////////

/**
* Constructor for the plugin. The plugin is passed a pointer
* an interface object that provides access to exposed functions in QGIS.
* @param theQGisInterface - Pointer to the QGIS interface object
*/
Topol::Topol( QgisInterface * theQgisInterface ):
QgisPlugin( sName, sDescription, sCategory, sPluginVersion, sPluginType ),
mQGisIface( theQgisInterface )
{
mDock = 0;
}

Topol::~Topol()
{
}

/*
* Initialize the GUI interface for the plugin - this is only called once when the plugin is
* added to the plugin registry in the QGIS application.
*/
void Topol::initGui()
{
mQActionPointer = new QAction( QIcon( ":/topology/topol.png" ), tr( "TopologyChecker" ), this );
//mQActionPointer = new QAction( QIcon(), tr( "Topology Checker" ), this );
mQActionPointer->setCheckable( true );


// Create the action for tool
//mQActionPointer = new QAction(QIcon(":/topol_c/topol.png"),tr("Topology Checker"), this);
// Set the what's this text
mQActionPointer->setWhatsThis( tr( "Topology Checker for vector layer" ) );
// Connect the action to the run
connect( mQActionPointer, SIGNAL( triggered() ), this, SLOT( showOrHide() ) );
// Add the icon to the toolbar
mQGisIface->addToolBarIcon( mQActionPointer );
mQGisIface->addPluginToMenu( tr( "&Topol" ), mQActionPointer );
//run();
}
//method defined in interface
void Topol::help()
{
//implement me!
}

void Topol::showOrHide()
{
if ( !mDock )
run();
else
if ( mQActionPointer->isChecked() )
mDock->show();
else
mDock->hide();
}

// Slot called when the menu item is triggered
// If you created more menu items / toolbar buttons in initiGui, you should
// create a separate handler for each action - this single run() method will
// not be enough
void Topol::run()
{
mDock = new checkDock( mQGisIface );
mQGisIface->addDockWidget( Qt::RightDockWidgetArea, mDock );
connect( mDock, SIGNAL( visibilityChanged( bool ) ), mQActionPointer, SLOT( setChecked( bool ) ) );
//mDock->show();
}

// Unload the plugin by cleaning up the GUI
void Topol::unload()
{
// remove the GUI
mQGisIface->removePluginMenu( "&Topol", mQActionPointer );
mQGisIface->removeToolBarIcon( mQActionPointer );
delete mQActionPointer;
}

//////////////////////////////////////////////////////////////////////////
//
//
// THE FOLLOWING CODE IS AUTOGENERATED BY THE PLUGIN BUILDER SCRIPT
// YOU WOULD NORMALLY NOT NEED TO MODIFY THIS, AND YOUR PLUGIN
// MAY NOT WORK PROPERLY IF YOU MODIFY THIS INCORRECTLY
//
//
//////////////////////////////////////////////////////////////////////////


/**
* Required extern functions needed for every plugin
* These functions can be called prior to creating an instance
* of the plugin class
*/
// Class factory to return a new instance of the plugin class
QGISEXTERN QgisPlugin * classFactory( QgisInterface * theQgisInterfacePointer )
{
return new Topol( theQgisInterfacePointer );
}
// Return the name of the plugin - note that we do not user class members as
// the class may not yet be insantiated when this method is called.
QGISEXTERN QString name()
{
return sName;
}

// Return the description
QGISEXTERN QString description()
{
return sDescription;
}

// Return the type (either UI or MapLayer plugin)
QGISEXTERN int type()
{
return sPluginType;
}

// Return the version number for the plugin
QGISEXTERN QString version()
{
return sPluginVersion;
}

QGISEXTERN QString icon()
{
return sPluginIcon;
}

// Delete ourself
QGISEXTERN void unload( QgisPlugin * thePluginPointer )
{
delete thePluginPointer;
}
111 changes: 111 additions & 0 deletions src/plugins/topology/topol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/***************************************************************************
topol.h
-------------------
begin : Jan 21, 2004
copyright : (C) 2004 by Tim Sutton
email : tim@linfiniti.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. *
* *
***************************************************************************/
/* $Id: plugin.h 7796 2007-12-16 22:11:38Z homann $ */
/***************************************************************************
* QGIS Programming conventions:
*
* mVariableName - a class level member variable
* sVariableName - a static class level member variable
* variableName() - accessor for a class member (no 'get' in front of name)
* setVariableName() - mutator for a class member (prefix with 'set')
*
* Additional useful conventions:
*
* theVariableName - a method parameter (prefix with 'the')
* myVariableName - a locally declared variable within a method ('my' prefix)
*
* DO: Use mixed case variable names - myVariableName
* DON'T: separate variable names using underscores: my_variable_name (NO!)
*
* **************************************************************************/
#ifndef TOPOL_H
#define TOPOL_H

//QT4 includes
#include <QObject>

//QGIS includes
#include "../qgisplugin.h"

//forward declarations
class QAction;
class QToolBar;

class QgisInterface;
class checkDock;

/**
* \class Plugin
* \brief [name] plugin for QGIS
* [description]
*/
class Topol:public QObject, public QgisPlugin
{
Q_OBJECT;
public:

//////////////////////////////////////////////////////////////////////
//
// MANDATORY PLUGIN METHODS FOLLOW
//
//////////////////////////////////////////////////////////////////////

/**
* Constructor for a plugin. The QgisInterface pointer is passed by
* QGIS when it attempts to instantiate the plugin.
* @param theInterface Pointer to the QgisInterface object.
*/
Topol(QgisInterface * theInterface);
//! Destructor
virtual ~Topol();

public slots:
//! init the gui
virtual void initGui();
//! Create and show the dialog box
void run();
//! Show/hide the dialog box
void showOrHide();
//! unload the plugin
void unload();
//! show the help document
void help();

private:

////////////////////////////////////////////////////////////////////
//
// MANDATORY PLUGIN PROPERTY DECLARATIONS .....
//
////////////////////////////////////////////////////////////////////

int mPluginType;
//! Pointer to the QGIS interface object
QgisInterface *mQGisIface;
//!pointer to the qaction for this plugin
QAction * mQActionPointer;
checkDock* mDock;

////////////////////////////////////////////////////////////////////
//
// ADD YOUR OWN PROPERTY DECLARATIONS AFTER THIS POINT.....
//
////////////////////////////////////////////////////////////////////
};

#endif //Topol_H
Binary file added src/plugins/topology/topol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/plugins/topology/topol.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/topology" >
<file>topol.png</file>
<file>configureRules.png</file>
<file>validateAll.png</file>
<file>validateExtent.png</file>
</qresource>
</RCC>
229 changes: 229 additions & 0 deletions src/plugins/topology/topolError.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/***************************************************************************
topolError.h
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 "topolError.h"

//TODO: tell dock to parse errorlist when feature is deleted
bool TopolError::fix( QString fixName )
{
std::cout << "fix: \"" << fixName.toStdString() << "\"\n";
( this->*mFixMap[fixName] )();
}

bool TopolError::fixMove( FeatureLayer fl1, FeatureLayer fl2 )
{
bool ok;
QgsFeature f1, f2;

ok = fl1.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl1.feature.id() ) ).nextFeature( f1 );
ok = ok && fl2.layer->getFeatures(( QgsFeatureRequest().setFilterFid( fl2.feature.id() ) ) ).nextFeature( f2 );

if ( !ok )
return false;


QgsGeometry* g2, *g1 = f1.geometry();
// 0 means success
if ( !f1.geometry()->makeDifference( f2.geometry() ) )
return fl1.layer->changeGeometry( f1.id(), f1.geometry() );

return false;
}

bool TopolError::fixMoveFirst()
{
return fixMove( mFeaturePairs.first(), mFeaturePairs[1] );
}

bool TopolError::fixMoveSecond()
{
return fixMove( mFeaturePairs[1], mFeaturePairs.first() );
}

bool TopolError::fixUnion( FeatureLayer fl1, FeatureLayer fl2 )
{
bool ok;
QgsFeature f1, f2;

ok = fl1.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl1.feature.id() ) ).nextFeature( f1 );
ok = ok && fl2.layer->getFeatures(( QgsFeatureRequest().setFilterFid( fl2.feature.id() ) ) ).nextFeature( f2 );

if ( !ok )
return false;

QgsGeometry* g = f1.geometry()->combine( f2.geometry() );
if ( !g )
return false;

if ( fl2.layer->deleteFeature( f2.id() ) )
return fl1.layer->changeGeometry( f1.id(), g );

return false;
}

bool TopolError::fixSnap()
{
bool ok;
QgsFeature f1, f2;
FeatureLayer fl = mFeaturePairs[1];
ok = fl.layer->getFeatures(( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ) ).nextFeature( f2 );
fl = mFeaturePairs.first();
ok = ok && fl.layer->getFeatures( QgsFeatureRequest().setFilterFid( fl.feature.id() ) ).nextFeature( f1 );

if ( !ok )
return false;

QgsGeometry* ge = f1.geometry();

QgsPolyline line = ge->asPolyline();
line.last() = mConflict->asPolyline().last();

QgsGeometry* newG = QgsGeometry::fromPolyline( line );
bool ret = fl.layer->changeGeometry( f1.id(), newG );
delete newG;

return ret;
}

bool TopolError::fixUnionFirst()
{
return fixUnion( mFeaturePairs.first(), mFeaturePairs[1] );
}

bool TopolError::fixUnionSecond()
{
return fixUnion( mFeaturePairs[1], mFeaturePairs.first() );
}

bool TopolError::fixDeleteFirst()
{
FeatureLayer fl = mFeaturePairs.first();
return fl.layer->deleteFeature( fl.feature.id() );
}

bool TopolError::fixDeleteSecond()
{
FeatureLayer fl = mFeaturePairs[1];
return fl.layer->deleteFeature( fl.feature.id() );
}

TopolError::TopolError( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ):
mFeaturePairs( theFeaturePairs ),
mBoundingBox( theBoundingBox ),
mConflict( theConflict )
{
mFixMap["Select automatic fix"] = &TopolError::fixDummy;
}

TopolErrorIntersection::TopolErrorIntersection( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "intersecting geometries";

mFixMap["Move blue feature"] = &TopolErrorIntersection::fixMoveFirst;
mFixMap["Move red feature"] = &TopolErrorIntersection::fixMoveSecond;
mFixMap["Delete blue feature"] = &TopolErrorIntersection::fixDeleteFirst;
mFixMap["Delete red feature"] = &TopolErrorIntersection::fixDeleteSecond;

// allow union only when both features have the same geometry type
if ( theFeaturePairs.first().feature.geometry()->type() == theFeaturePairs[1].feature.geometry()->type() )
{
mFixMap["Union to blue feature"] = &TopolErrorIntersection::fixUnionFirst;
mFixMap["Union to red feature"] = &TopolErrorIntersection::fixUnionSecond;
}
}

TopolErrorClose::TopolErrorClose( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "features too close";

mFixMap["Move blue feature"] = &TopolErrorClose::fixMoveFirst;
mFixMap["Move red feature"] = &TopolErrorClose::fixMoveSecond;
mFixMap["Snap to segment"] = &TopolErrorClose::fixSnap;
}

TopolErrorCovered::TopolErrorCovered( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "point not covered by segment";
mFixMap["Delete point"] = &TopolErrorCovered::fixDeleteFirst;
}

TopolErrorShort::TopolErrorShort( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "segment too short";
mFixMap["Delete feature"] = &TopolErrorShort::fixDeleteFirst;
}

TopolErrorValid::TopolErrorValid( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "invalid geometry";
mFixMap["Delete feature"] = &TopolErrorValid::fixDeleteFirst;
}

TopolErrorDangle::TopolErrorDangle( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "dangling end";
mFixMap["Delete feature"] = &TopolErrorDangle::fixDeleteFirst;
}

TopolErrorDuplicates::TopolErrorDuplicates( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "duplicate geometry";
//mFixMap["Delete feature"] = &TopolErrorDuplicates::fixDeleteFirst;
}

TopolErrorPseudos::TopolErrorPseudos( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "pseudo node";
//mFixMap["Delete feature"] = &TopolErrorDuplicates::fixDeleteFirst;
}

TopolErrorOverlaps::TopolErrorOverlaps( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "overlaps";
//mFixMap["Delete feature"] = &TopolErrorDuplicates::fixDeleteFirst;
}

TopolErrorGaps::TopolErrorGaps( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "gaps";
//mFixMap["Delete feature"] = &TopolErrorDuplicates::fixDeleteFirst;
}

TopolErrorPointNotCoveredByLineEnds::TopolErrorPointNotCoveredByLineEnds( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "point not covered";
}

TopolErrorLineEndsNotCoveredByPoints::TopolErrorLineEndsNotCoveredByPoints( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "line ends not covered by point";
}

TopolErrorPointNotInPolygon::TopolErrorPointNotInPolygon( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "point not in polygon";
}

TopolErrorPolygonContainsPoint::TopolErrorPolygonContainsPoint( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "polygon does not contain point";
}

TopolErroMultiPart::TopolErroMultiPart( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs ) : TopolError( theBoundingBox, theConflict, theFeaturePairs )
{
mName = "multipart feature";
}
233 changes: 233 additions & 0 deletions src/plugins/topology/topolError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/***************************************************************************
topolError.h
TOPOLogy checker
-------------------
begin : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 TOPOLERROR_H
#define TOPOLERROR_H

#include <qgsvectorlayer.h>
#include <qgsgeometry.h>
#include <qgsrectangle.h>

class TopolError;
typedef QList<TopolError*> ErrorList;
typedef bool ( TopolError::*fixFunction )();

class FeatureLayer
{
public:
FeatureLayer() :
layer( 0 ), feature( QgsFeature() ) {};
/**
* Constructor
* @param theLayer layer pointer
* @param theFeature QgsFeature
*/
FeatureLayer( QgsVectorLayer* theLayer, QgsFeature theFeature ) :
layer( theLayer ), feature( theFeature ) {};

QgsVectorLayer* layer;
QgsFeature feature;
};

class TopolError
{
protected:
QString mName;
QgsRectangle mBoundingBox;
QgsGeometry* mConflict;
QList<FeatureLayer> mFeaturePairs;
QMap<QString, fixFunction> mFixMap;

/**
* A dummy fix - does nothing
*/
bool fixDummy() { return false; }
/**
* Snaps to a feature
*/
bool fixSnap();
/**
* Moves first feature
*/
bool fixMoveFirst();
/**
* Moves second feature
*/
bool fixMoveSecond();
/**
* Unions features to the first
*/
bool fixUnionFirst();
/**
* Unions features to the first
*/
bool fixUnionSecond();
/**
* Deletes first feature
*/
bool fixDeleteFirst();
/**
* Deletes second feature
*/
bool fixDeleteSecond();

//helper fix functions

/**
* Makes geometry difference
* @param fl1 first FeatureLayer pair
* @param fl2 second FeatureLayer pair
*/
bool fixMove( FeatureLayer fl1, FeatureLayer fl2 );
/**
* Unions features to the first one
* @param fl1 first FeatureLayer pair
* @param fl2 second FeatureLayer pair
*/
bool fixUnion( FeatureLayer fl1, FeatureLayer fl2 );

public:
/**
* Constructor
* @param theBoundingBox bounding box of the two features
* @param theConflict geometry representation of the conflict
* @param theFeaturePairs FeatureLayer pairs of the two features
*/
TopolError( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );

/**
* Destructor
*/
virtual ~TopolError() { delete mConflict; }
/**
* Runs fixing function
* @param fixName name of the fix
*/
virtual bool fix( QString fixName );
/**
* Returns error's name
*/
virtual QString name() { return mName; }
/**
* Returns topology conflict
*/
virtual QgsGeometry* conflict() { return mConflict; }
/**
* Returns bounding box of the error
*/
virtual QgsRectangle boundingBox() { return mBoundingBox; }
/**
* Returns FeatureLayer pairs from the error
*/
virtual QList<FeatureLayer> featurePairs() { return mFeaturePairs; }
/**
* Returns the names of posible fixes
*/
virtual QStringList fixNames() { return mFixMap.keys(); }
};

class TopolErrorIntersection : public TopolError
{
public:
TopolErrorIntersection( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorClose : public TopolError
{
public:
TopolErrorClose( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorCovered : public TopolError
{
public:
TopolErrorCovered( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorShort : public TopolError
{
public:
TopolErrorShort( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorValid : public TopolError
{
public:
TopolErrorValid( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorDangle : public TopolError
{
public:
TopolErrorDangle( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorDuplicates : public TopolError
{
public:
TopolErrorDuplicates( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorPseudos : public TopolError
{
public:
TopolErrorPseudos( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorOverlaps : public TopolError
{
public:
TopolErrorOverlaps( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorGaps : public TopolError
{
public:
TopolErrorGaps( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorPointNotCoveredByLineEnds : public TopolError
{
public:
TopolErrorPointNotCoveredByLineEnds( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorLineEndsNotCoveredByPoints : public TopolError
{
public:
TopolErrorLineEndsNotCoveredByPoints( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorPointNotInPolygon : public TopolError
{
public:
TopolErrorPointNotInPolygon( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErrorPolygonContainsPoint : public TopolError
{
public:
TopolErrorPolygonContainsPoint( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

class TopolErroMultiPart : public TopolError
{
public:
TopolErroMultiPart( QgsRectangle theBoundingBox, QgsGeometry* theConflict, QList<FeatureLayer> theFeaturePairs );
};

#endif
1,700 changes: 1,700 additions & 0 deletions src/plugins/topology/topolTest.cpp

Large diffs are not rendered by default.

280 changes: 280 additions & 0 deletions src/plugins/topology/topolTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/***************************************************************************
topolTest.h
TOPOLogy checker
-------------------
date : May 2009
copyright : Vita Cizek
email : weetya (at) gmail.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 TOPOLTEST_H
#define TOPOLTEST_H

#include <QObject>

#include <qgsvectorlayer.h>
#include <qgsgeometry.h>
#include "qgsspatialindex.h"

#include "topolError.h"

class topolTest;
class QgisInterface;
class WKTReader;

enum ValidateType { ValidateAll, ValidateExtent, ValidateSelected };

typedef ErrorList( topolTest::*testFunction )( double, QgsVectorLayer*, QgsVectorLayer*,bool );

class TopologyRule
{
public:
bool useSecondLayer;
bool useTolerance;
bool useSpatialIndex;
testFunction f;

/**
* Constructor
* initializes the test to use both layers and not to use the tolerance
*/
TopologyRule()
{
useSecondLayer = true;
useTolerance = false;
f = 0;
}
};

/**
helper class to pass as comparator to map,set etc..
*/
class PointComparer
{
public:
bool operator()( QgsPoint p1, QgsPoint p2 )const
{
if ( p1.x() < p2.x() )
{
return true;
}

if ( p1.x() == p2.x() )
{
if ( p1.y() < p2.y() )
{
return true;
}
}

return false;
}
};


class topolTest: public QObject
{
Q_OBJECT

public:
topolTest( QgisInterface* qgsIface );
~topolTest();

/**
* Returns copy of the test map
*/
QMap<QString, TopologyRule> testMap() { return mTopologyRuleMap; }
/**
* Runs the test and returns all found errors
* @param testName name of the test
* @param layer1 pointer to the first layer
* @param layer2 pointer to the second layer
* @param type type what features to validate
* @param tolerance possible tolerance
*/
ErrorList runTest( QString testName, QgsVectorLayer* layer1, QgsVectorLayer* layer2, ValidateType type, double tolerance );

/**
* Checks for intersections of the two layers
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 pointer to the second layer
*/
ErrorList checkOverlapWithLayer( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2 , bool isExtent);
/**
* Checks for self-intersections in the layer
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkSelfIntersections( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );
/**
* Checks for features that are too close
* @param tolerance allowed tolerance
* @param layer1 pointer to the first layer
* @param layer2 pointer to the second layer
*/
ErrorList checkCloseFeature( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );
/**
* Checks for short segments
* @param tolerance tolerance - not used
* @param layer1 pointer to the first layer
* @param layer2 pointer to the second layer
*/
ErrorList checkSegmentLength( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );
/**
* Checks for dangling lines
* @param tolerance allowed tolerance
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkDanglingLines( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2 , bool isExtent);
/**
* Checks for points not covered by any segment
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 pointer to the second layer
*/
ErrorList checkPointCoveredBySegment( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );
/**
* Checks for invalid geometries
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkValid( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for duplicate geometries
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkDuplicates( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for pseudo nodes
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkPseudos( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for overlaps of polygons from same layer
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkOverlaps( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for gaps among polygons from same layer
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkGaps( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for points form one layer that are not covered by line ends form another layer
* @param tolerance not used
* @param layer1 pointer to the first point layer
* @param layer2 pointer to the second line layer
*/
ErrorList checkPointCoveredByLineEnds( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for points that are not inside any polygons from another layer
* @param tolerance not used
* @param layer1 pointer to the first point layer
* @param layer2 pointer to the second polygon layer
*/
ErrorList checkPointInPolygon( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent );

/**
* Checks for polygons that do not contain any points form another layer
* @param tolerance not used
* @param layer1 pointer to the first polygon layer
* @param layer2 pointer to the second point layer
*/
ErrorList checkPolygonContainsPoint( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2,bool isExtent );

/**
* Checks for multipart features
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
ErrorList checkMultipart( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2,bool isExtent );

/**
* Checks for line features that do not have both ends covered by points from another layer
* @param tolerance not used
* @param layer1 pointer to the first linelayer
* @param layer2 pointer to the second point layer
*/
ErrorList checkyLineEndsCoveredByPoints( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2,bool isExtent );


public slots:
/**
* Checks for invalid geometries
* @param tolerance not used
* @param layer1 pointer to the first layer
* @param layer2 not used
*/
void setTestCancelled();

private:
QMap<QString, QgsSpatialIndex*> mLayerIndexes;
QMap<QString, TopologyRule> mTopologyRuleMap;

QList<FeatureLayer> mFeatureList1;
QMap<int, FeatureLayer> mFeatureMap2;

QgisInterface* theQgsInterface;
bool mTestCancelled;

/**
* Builds spatial index for the layer
* @param layer pointer to the layer
*/
QgsSpatialIndex* createIndex( QgsVectorLayer* layer, QgsRectangle extent );

/**
* Fills the feature list with features from the layer
* @param layer pointer to the layer
* @param extent of the layer to add features
*/
void fillFeatureList( QgsVectorLayer* layer, QgsRectangle extent );

/**
* Fills the feature map with features from the layer
* @param layer pointer to the layer
* @param extent of the layer to create index
*/
void fillFeatureMap( QgsVectorLayer* layer, QgsRectangle extent );

/**
* Returns true if the test was cancelled
*/
bool testCancelled();

signals:
/**
* Informs progress dialog about current status
* @param value process status
*/
void progress( int value );
};

#endif
Binary file added src/plugins/topology/validateAll.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/plugins/topology/validateExtent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.