mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
New functionality for merging features
git-svn-id: http://svn.osgeo.org/qgis/trunk@10879 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
parent
aa18385d53
commit
dbaf7bd454
BIN
images/themes/default/mActionFromSelectedFeature.png
Normal file
BIN
images/themes/default/mActionFromSelectedFeature.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
images/themes/default/mActionMergeFeatures.png
Normal file
BIN
images/themes/default/mActionMergeFeatures.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
images/themes/default/mActionRemoveSelectedFeature.png
Normal file
BIN
images/themes/default/mActionRemoveSelectedFeature.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -41,6 +41,7 @@ SET(QGIS_APP_SRCS
|
||||
qgsmaptoolvertexedit.cpp
|
||||
qgsmeasuredialog.cpp
|
||||
qgsmeasuretool.cpp
|
||||
qgsmergeattributesdialog.cpp
|
||||
qgsnewhttpconnection.cpp
|
||||
qgsnumericsortlistviewitem.cpp
|
||||
qgsogrsublayersdialog.cpp
|
||||
@ -129,6 +130,7 @@ SET (QGIS_APP_MOC_HDRS
|
||||
|
||||
qgsmeasuretool.h
|
||||
qgsmeasuredialog.h
|
||||
qgsmergeattributesdialog.h
|
||||
qgsnewhttpconnection.h
|
||||
qgsoptions.h
|
||||
qgsogrsublayersdialog.h
|
||||
|
@ -114,6 +114,7 @@
|
||||
#include "qgsmapoverviewcanvas.h"
|
||||
#include "qgsmaprenderer.h"
|
||||
#include "qgsmaptip.h"
|
||||
#include "qgsmergeattributesdialog.h"
|
||||
#include "qgsmessageviewer.h"
|
||||
#include "qgsoptions.h"
|
||||
#include "qgspastetransformations.h"
|
||||
@ -703,6 +704,12 @@ void QgisApp::createActions()
|
||||
connect( mActionDeletePart, SIGNAL( triggered() ), this, SLOT( deletePart() ) );
|
||||
mActionDeletePart->setEnabled( false );
|
||||
|
||||
mActionMergeFeatures = new QAction( getThemeIcon("mActionMergeFeatures.png"), tr("Merge selected features"), this);
|
||||
shortcuts->registerAction(mActionMergeFeatures);
|
||||
mActionMergeFeatures->setStatusTip( tr("Merge selected features"));
|
||||
connect( mActionMergeFeatures, SIGNAL(triggered()), this, SLOT(mergeSelectedFeatures()));
|
||||
mActionMergeFeatures->setEnabled(false);
|
||||
|
||||
|
||||
// View Menu Items
|
||||
|
||||
@ -1053,6 +1060,7 @@ void QgisApp::createActionGroups()
|
||||
mMapToolGroup->addAction( mActionDeleteRing );
|
||||
mActionDeletePart->setCheckable( true );
|
||||
mMapToolGroup->addAction( mActionDeletePart );
|
||||
mMapToolGroup->addAction( mActionMergeFeatures);
|
||||
}
|
||||
|
||||
void QgisApp::createMenus()
|
||||
@ -1141,6 +1149,7 @@ void QgisApp::createMenus()
|
||||
mEditMenu->addAction( mActionAddIsland );
|
||||
mEditMenu->addAction( mActionDeleteRing );
|
||||
mEditMenu->addAction( mActionDeletePart );
|
||||
mEditMenu->addAction( mActionMergeFeatures );
|
||||
|
||||
if ( layout == QDialogButtonBox::GnomeLayout || layout == QDialogButtonBox::MacLayout )
|
||||
{
|
||||
@ -1345,6 +1354,7 @@ void QgisApp::createToolBars()
|
||||
mAdvancedDigitizeToolBar->addAction( mActionAddIsland );
|
||||
mAdvancedDigitizeToolBar->addAction( mActionDeleteRing );
|
||||
mAdvancedDigitizeToolBar->addAction( mActionDeletePart );
|
||||
mAdvancedDigitizeToolBar->addAction( mActionMergeFeatures );
|
||||
mToolbarMenu->addAction( mAdvancedDigitizeToolBar->toggleViewAction() );
|
||||
|
||||
|
||||
@ -4085,6 +4095,139 @@ void QgisApp::deletePart()
|
||||
mMapCanvas->setMapTool( mMapTools.mDeletePart );
|
||||
}
|
||||
|
||||
QgsGeometry* QgisApp::unionGeometries(const QgsVectorLayer* vl, QgsFeatureList& featureList) const
|
||||
{
|
||||
if(!vl || featureList.size() < 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QgsGeometry* unionGeom = featureList[0].geometry();
|
||||
QgsGeometry* backupPtr = 0; //pointer to delete intermediate results
|
||||
if(!unionGeom)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(int i = 1; i < featureList.size(); ++i)
|
||||
{
|
||||
QgsGeometry* currentGeom = featureList[i].geometry();
|
||||
if(currentGeom)
|
||||
{
|
||||
backupPtr = unionGeom;
|
||||
unionGeom = unionGeom->combine(currentGeom);
|
||||
if(i > 1) //delete previous intermediate results
|
||||
{
|
||||
delete backupPtr;
|
||||
backupPtr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return unionGeom;
|
||||
}
|
||||
|
||||
void QgisApp::mergeSelectedFeatures()
|
||||
{
|
||||
//get active layer (hopefully vector)
|
||||
QgsMapLayer* activeMapLayer = activeLayer();
|
||||
if(!activeMapLayer)
|
||||
{
|
||||
QMessageBox::information(0, tr("No active layer"), tr("No active layer found. Please select a layer in the layer list"));
|
||||
return;
|
||||
}
|
||||
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>(activeMapLayer);
|
||||
if(!vl)
|
||||
{
|
||||
QMessageBox::information(0, tr("Active layer is not vector"), tr("The merge features tool only works on vector layers. Please select a vector layer from the layer list"));
|
||||
return;
|
||||
}
|
||||
if(!vl->isEditable())
|
||||
{
|
||||
QMessageBox::information(0, tr("Layer not editable"), tr("Merging features can only be done for layers in editing mode. To use the merge tool, go to Layer->Toggle editing"));
|
||||
return;
|
||||
}
|
||||
|
||||
//get selected feature ids (as a QSet<int> )
|
||||
const QgsFeatureIds& featureIdSet = vl->selectedFeaturesIds();
|
||||
if(featureIdSet.size() < 2)
|
||||
{
|
||||
QMessageBox::information(0, "Not enough features selected", tr("The merge tool requires at least two selected features"));
|
||||
return;
|
||||
}
|
||||
|
||||
//get initial selection (may be altered by attribute merge dialog later)
|
||||
QgsFeatureList featureList = vl->selectedFeatures(); //get QList<QgsFeature>
|
||||
QgsGeometry* unionGeom = unionGeometries(vl, featureList);
|
||||
if(!unionGeom)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//make a first geometry union and notify the user straight away if the union geometry type does not match the layer one
|
||||
QGis::WkbType originalType = vl->wkbType();
|
||||
QGis::WkbType newType = unionGeom->wkbType();
|
||||
if(unionGeom->wkbType() != vl->wkbType())
|
||||
{
|
||||
QMessageBox::critical(0, "Union operation canceled", tr("The union operation would result in a geometry type that is not compatible with the current layer and therefore is canceled"));
|
||||
delete unionGeom;
|
||||
return;
|
||||
}
|
||||
|
||||
//merge the attributes together
|
||||
QgsMergeAttributesDialog d(featureList, vl, mapCanvas());
|
||||
if(d.exec() == QDialog::Rejected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QgsFeatureList featureListAfter = vl->selectedFeatures();
|
||||
|
||||
if(featureListAfter.size() < 2)
|
||||
{
|
||||
QMessageBox::information(0, "Not enough features selected", tr("The merge tool requires at least two selected features"));
|
||||
delete unionGeom;
|
||||
return;
|
||||
}
|
||||
|
||||
//if the user changed the feature selection in the merge dialog, we need to repead the union and check the type
|
||||
if(featureList.size() != featureListAfter.size())
|
||||
{
|
||||
delete unionGeom;
|
||||
unionGeom = unionGeometries(vl, featureListAfter);
|
||||
if(!unionGeom)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
originalType = vl->wkbType();
|
||||
newType = unionGeom->wkbType();
|
||||
if(unionGeom->wkbType() != vl->wkbType())
|
||||
{
|
||||
QMessageBox::critical(0, "Union operation canceled", tr("The union operation would result in a geometry type that is not compatible with the current layer and therefore is canceled"));
|
||||
delete unionGeom;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//create new feature
|
||||
QgsFeature newFeature;
|
||||
newFeature.setGeometry(unionGeom);
|
||||
newFeature.setAttributeMap(d.mergedAttributesMap());
|
||||
|
||||
QgsFeatureList::const_iterator feature_it = featureListAfter.constBegin();
|
||||
for(; feature_it != featureListAfter.constEnd(); ++feature_it)
|
||||
{
|
||||
vl->deleteFeature(feature_it->id());
|
||||
}
|
||||
|
||||
vl->addFeature(newFeature, false);
|
||||
|
||||
if(mapCanvas())
|
||||
{
|
||||
mapCanvas()->refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void QgisApp::splitFeatures()
|
||||
{
|
||||
mMapCanvas->setMapTool( mMapTools.mSplitFeatures );
|
||||
@ -5314,6 +5457,17 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
|
||||
mActionCutFeatures->setEnabled( false );
|
||||
}
|
||||
|
||||
//merge tool needs editable layer and provider with the capability of adding and deleting features
|
||||
if ( vlayer->isEditable() && (dprovider->capabilities() & QgsVectorDataProvider::DeleteFeatures) \
|
||||
&& QgsVectorDataProvider::AddFeatures)
|
||||
{
|
||||
mActionMergeFeatures->setEnabled(layerHasSelection);
|
||||
}
|
||||
else
|
||||
{
|
||||
mActionMergeFeatures->setEnabled( false );
|
||||
}
|
||||
|
||||
// moving enabled if geometry changes are supported
|
||||
if ( vlayer->isEditable() && dprovider->capabilities() & QgsVectorDataProvider::ChangeGeometries )
|
||||
{
|
||||
@ -5366,6 +5520,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
|
||||
mActionSplitFeatures->setEnabled( true );
|
||||
mActionSimplifyFeature->setEnabled( true );
|
||||
mActionDeletePart->setEnabled( true );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ class QValidator;
|
||||
class QgisAppInterface;
|
||||
class QgsClipboard;
|
||||
class QgsComposer;
|
||||
class QgsGeometry;
|
||||
class QgsHelpViewer;
|
||||
class QgsFeature;
|
||||
|
||||
@ -63,6 +64,7 @@ class QgsVectorLayer;
|
||||
#include <QPointer>
|
||||
|
||||
#include "qgsconfig.h"
|
||||
#include "qgsfeature.h"
|
||||
#include "qgspoint.h"
|
||||
|
||||
/*! \class QgisApp
|
||||
@ -509,6 +511,8 @@ class QgisApp : public QMainWindow
|
||||
void deleteRing();
|
||||
//! deletes part of polygon
|
||||
void deletePart();
|
||||
//! merges the selected features together
|
||||
void mergeSelectedFeatures();
|
||||
|
||||
//! activates the selection tool
|
||||
void select();
|
||||
@ -655,6 +659,9 @@ class QgisApp : public QMainWindow
|
||||
void pasteTransformations();
|
||||
//! check to see if file is dirty and if so, prompt the user th save it
|
||||
bool saveDirty();
|
||||
/** Helper function to union several geometries together (used in function mergeSelectedFeatures)
|
||||
@return 0 in case of error*/
|
||||
QgsGeometry* unionGeometries(const QgsVectorLayer* vl, QgsFeatureList& featureList) const;
|
||||
|
||||
/// QgisApp aren't copyable
|
||||
QgisApp( QgisApp const & );
|
||||
@ -719,6 +726,7 @@ class QgisApp : public QMainWindow
|
||||
QAction *mActionSimplifyFeature;
|
||||
QAction *mActionDeleteRing;
|
||||
QAction *mActionDeletePart;
|
||||
QAction *mActionMergeFeatures;
|
||||
QAction *mActionEditSeparator3;
|
||||
|
||||
QAction *mActionPan;
|
||||
|
532
src/app/qgsmergeattributesdialog.cpp
Normal file
532
src/app/qgsmergeattributesdialog.cpp
Normal file
@ -0,0 +1,532 @@
|
||||
/***************************************************************************
|
||||
qgsmergeattributesdialog.cpp
|
||||
----------------------------
|
||||
begin : May 2009
|
||||
copyright : (C) 2009 by Marco Hugentobler
|
||||
email : marco dot hugentobler at karto dot baug dot ethz dot ch
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* 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 "qgsmergeattributesdialog.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsfield.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsrubberband.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include <limits>
|
||||
#include <QComboBox>
|
||||
|
||||
QgsMergeAttributesDialog::QgsMergeAttributesDialog(const QgsFeatureList& features, QgsVectorLayer* vl, QgsMapCanvas* canvas, QWidget * parent, Qt::WindowFlags f): QDialog(parent, f), mFeatureList(features), mVectorLayer(vl), mMapCanvas(canvas), mSelectionRubberBand(0)
|
||||
{
|
||||
setupUi(this);
|
||||
createTableWidgetContents();
|
||||
|
||||
QHeaderView* verticalHeader = mTableWidget->verticalHeader();
|
||||
if(verticalHeader)
|
||||
{
|
||||
QObject::connect(mTableWidget, SIGNAL(itemSelectionChanged ()), this, SLOT(selectedRowChanged()));
|
||||
}
|
||||
mTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
mTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
mFromSelectedPushButton->setIcon(QgisApp::getThemeIcon("mActionFromSelectedFeature.png"));
|
||||
mRemoveFeatureFromSelectionButton->setIcon(QgisApp::getThemeIcon("mActionRemoveSelectedFeature.png"));
|
||||
}
|
||||
|
||||
QgsMergeAttributesDialog::QgsMergeAttributesDialog(): QDialog()
|
||||
{
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
QgsMergeAttributesDialog::~QgsMergeAttributesDialog()
|
||||
{
|
||||
delete mSelectionRubberBand;
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::createTableWidgetContents()
|
||||
{
|
||||
//get information about attributes from vector layer
|
||||
if(!mVectorLayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const QgsFieldMap& fieldMap = mVectorLayer->pendingFields();
|
||||
|
||||
//combo box row, attributes titles, feature values and current merge results
|
||||
mTableWidget->setRowCount(mFeatureList.size() + 2);
|
||||
mTableWidget->setColumnCount(fieldMap.size());
|
||||
|
||||
//create combo boxes
|
||||
for(int i = 0; i < fieldMap.size(); ++i)
|
||||
{
|
||||
mTableWidget->setCellWidget(0, i, createMergeComboBox(fieldMap[i].type()));
|
||||
}
|
||||
|
||||
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
|
||||
|
||||
//insert attribute names
|
||||
QStringList horizontalHeaderLabels;
|
||||
for(; fieldIt != fieldMap.constEnd(); ++fieldIt)
|
||||
{
|
||||
horizontalHeaderLabels << fieldIt.value().name();
|
||||
}
|
||||
mTableWidget->setHorizontalHeaderLabels(horizontalHeaderLabels);
|
||||
|
||||
//insert the attribute values
|
||||
int currentRow = 1;
|
||||
QStringList verticalHeaderLabels; //the id column is in the
|
||||
verticalHeaderLabels << tr("Id");
|
||||
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
verticalHeaderLabels << QString::number(mFeatureList[i].id());
|
||||
QgsAttributeMap currentAttributeMap = mFeatureList[i].attributeMap();
|
||||
QgsAttributeMap::const_iterator currentMapIt = currentAttributeMap.constBegin();
|
||||
int col = 0;
|
||||
for(; currentMapIt != currentAttributeMap.constEnd(); ++currentMapIt)
|
||||
{
|
||||
QTableWidgetItem* attributeValItem = new QTableWidgetItem(currentMapIt.value().toString());
|
||||
attributeValItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
mTableWidget->setItem(currentRow, col, attributeValItem);
|
||||
++col;
|
||||
}
|
||||
++currentRow;
|
||||
}
|
||||
|
||||
//merge
|
||||
verticalHeaderLabels << tr("Merge");
|
||||
mTableWidget->setVerticalHeaderLabels(verticalHeaderLabels);
|
||||
|
||||
|
||||
//insert currently merged values
|
||||
for(int i = 0; i < fieldMap.size(); ++i)
|
||||
{
|
||||
refreshMergedValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
QComboBox* QgsMergeAttributesDialog::createMergeComboBox(QVariant::Type columnType) const
|
||||
{
|
||||
QComboBox* newComboBox = new QComboBox();
|
||||
//add items for feature
|
||||
QgsFeatureList::const_iterator f_it = mFeatureList.constBegin();
|
||||
for(; f_it != mFeatureList.constEnd(); ++f_it)
|
||||
{
|
||||
newComboBox->addItem(tr("feature %1").arg(f_it->id()));
|
||||
}
|
||||
|
||||
if(columnType == QVariant::Double || columnType == QVariant::Int)
|
||||
{
|
||||
newComboBox->addItem(tr("Minimum"));
|
||||
newComboBox->addItem(tr("Maximum"));
|
||||
newComboBox->addItem(tr("Median"));
|
||||
}
|
||||
else if(columnType == QVariant::String)
|
||||
{
|
||||
newComboBox->addItem(tr("Concatenation"));
|
||||
}
|
||||
if(columnType == QVariant::Double)
|
||||
{
|
||||
newComboBox->addItem(tr("Mean"));
|
||||
}
|
||||
|
||||
QObject::connect(newComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(comboValueChanged(const QString&)));
|
||||
return newComboBox;
|
||||
}
|
||||
|
||||
int QgsMergeAttributesDialog::findComboColumn(QComboBox* c) const
|
||||
{
|
||||
for(int i = 0; i < mTableWidget->columnCount(); ++i)
|
||||
{
|
||||
if(mTableWidget->cellWidget(0, i) == c)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::comboValueChanged(const QString & text)
|
||||
{
|
||||
QComboBox* senderComboBox = dynamic_cast<QComboBox*>(sender());
|
||||
if(!senderComboBox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int column = findComboColumn(senderComboBox);
|
||||
refreshMergedValue(column);
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::selectedRowChanged()
|
||||
{
|
||||
//find out selected row
|
||||
QList<QTableWidgetItem *> selectionList = mTableWidget->selectedItems();
|
||||
if(selectionList.size() < 1)
|
||||
{
|
||||
delete mSelectionRubberBand;
|
||||
mSelectionRubberBand = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int row = selectionList[0]->row();
|
||||
|
||||
if(!mTableWidget || !mMapCanvas || !mVectorLayer || row < 1 || row >= (mTableWidget->rowCount()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//read the feature id
|
||||
QTableWidgetItem* idItem = mTableWidget->verticalHeaderItem(row);
|
||||
if(!idItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool conversionSuccess = false;
|
||||
int featureIdToSelect = idItem->text().toInt(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
//the merge result row was selected
|
||||
delete mSelectionRubberBand;
|
||||
mSelectionRubberBand = 0;
|
||||
return;
|
||||
}
|
||||
createRubberBandForFeature(featureIdToSelect);
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::refreshMergedValue(int col)
|
||||
{
|
||||
//get QComboBox
|
||||
QWidget* cellWidget = mTableWidget->cellWidget(0, col);
|
||||
if(!cellWidget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
QComboBox* comboBox = dynamic_cast<QComboBox*>(cellWidget);
|
||||
if(!comboBox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//evaluate behaviour (feature value or min / max / mean )
|
||||
QString mergeBehaviourString = comboBox->currentText();
|
||||
QString evalText; //text that has to be inserted into merge result field
|
||||
if(mergeBehaviourString == tr("Minimum"))
|
||||
{
|
||||
evalText = minimumAttributeString(col);
|
||||
}
|
||||
else if(mergeBehaviourString == tr("Maximum"))
|
||||
{
|
||||
evalText = maximumAttributeString(col);
|
||||
}
|
||||
else if(mergeBehaviourString == tr("Mean"))
|
||||
{
|
||||
evalText = meanAttributeString(col);
|
||||
}
|
||||
else if(mergeBehaviourString == tr("Median"))
|
||||
{
|
||||
evalText = medianAttributeString(col);
|
||||
}
|
||||
else if(mergeBehaviourString == tr("Concatenation"))
|
||||
{
|
||||
evalText = concatenationAttributeString(col);
|
||||
}
|
||||
else //an existing feature value
|
||||
{
|
||||
int featureId = mergeBehaviourString.split(" ").at(1).toInt(); //probably not very robust for translations...
|
||||
evalText = featureAttributeString(featureId, col);
|
||||
}
|
||||
|
||||
//insert string into table widget
|
||||
QTableWidgetItem* newTotalItem = new QTableWidgetItem(evalText);
|
||||
newTotalItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
||||
mTableWidget->setItem(mTableWidget->rowCount() - 1, col, newTotalItem);
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::featureAttributeString(int featureId, int col)
|
||||
{
|
||||
QString resultText;
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
int currentFid = mFeatureList[i].id();
|
||||
if(currentFid == featureId)
|
||||
{
|
||||
QTableWidgetItem* currentItem = mTableWidget->item(i+1, col);
|
||||
if(!currentItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
resultText = currentItem->text();
|
||||
}
|
||||
}
|
||||
return resultText;
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::minimumAttributeString(int col)
|
||||
{
|
||||
double minimumValue = std::numeric_limits<double>::max();
|
||||
double currentValue;
|
||||
bool conversion = false;
|
||||
int numberOfConsideredFeatures = 0;
|
||||
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
currentValue = mTableWidget->item(i+1, col)->text().toDouble(&conversion);
|
||||
if(conversion)
|
||||
{
|
||||
if(currentValue < minimumValue)
|
||||
{
|
||||
minimumValue = currentValue;
|
||||
++numberOfConsideredFeatures;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(numberOfConsideredFeatures < 1)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QString::number(minimumValue, 'f');
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::maximumAttributeString(int col)
|
||||
{
|
||||
double maximumValue = -std::numeric_limits<double>::max();
|
||||
double currentValue;
|
||||
bool conversion = false;
|
||||
int numberOfConsideredFeatures = 0;
|
||||
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
currentValue = mTableWidget->item(i+1, col)->text().toDouble(&conversion);
|
||||
if(conversion)
|
||||
{
|
||||
if(currentValue > maximumValue)
|
||||
{
|
||||
maximumValue = currentValue;
|
||||
++numberOfConsideredFeatures;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(numberOfConsideredFeatures < 1)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QString::number(maximumValue, 'f');
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::meanAttributeString(int col)
|
||||
{
|
||||
int numberOfConsideredFeatures = 0;
|
||||
double currentValue;
|
||||
double sum = 0;
|
||||
bool conversion = false;
|
||||
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
currentValue = mTableWidget->item(i+1, col)->text().toDouble(&conversion);
|
||||
if(conversion)
|
||||
{
|
||||
sum += currentValue;
|
||||
++numberOfConsideredFeatures;
|
||||
}
|
||||
}
|
||||
double mean = sum / numberOfConsideredFeatures;
|
||||
return QString::number(mean, 'f');
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::medianAttributeString(int col)
|
||||
{
|
||||
//bring all values into a list and sort
|
||||
QList<double> valueList;
|
||||
double currentValue;
|
||||
bool conversionSuccess;
|
||||
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
currentValue = mTableWidget->item(i+1, col)->text().toDouble(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
valueList.push_back(currentValue);
|
||||
}
|
||||
qSort(valueList);
|
||||
|
||||
double medianValue;
|
||||
int size = valueList.size();
|
||||
bool even = (size % 2) < 1;
|
||||
if(even)
|
||||
{
|
||||
medianValue = (valueList[size / 2 - 1] + valueList[size / 2]) / 2;
|
||||
}
|
||||
else //odd
|
||||
{
|
||||
medianValue = valueList[(size + 1) / 2 - 1];
|
||||
}
|
||||
return QString::number(medianValue, 'f');
|
||||
}
|
||||
|
||||
QString QgsMergeAttributesDialog::concatenationAttributeString(int col)
|
||||
{
|
||||
QStringList concatString;
|
||||
for(int i = 0; i < mFeatureList.size(); ++i)
|
||||
{
|
||||
concatString << mTableWidget->item(i+1, col)->text();
|
||||
}
|
||||
return concatString.join(","); //todo: make separator user configurable
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::on_mFromSelectedPushButton_clicked()
|
||||
{
|
||||
//find the selected feature
|
||||
if(!mVectorLayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//find out feature id of selected row
|
||||
QList<QTableWidgetItem *> selectionList = mTableWidget->selectedItems();
|
||||
if(selectionList.size() < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//assume all selected items to be in the same row
|
||||
QTableWidgetItem* selectedItem = selectionList[0];
|
||||
int selectedRow = selectedItem->row();
|
||||
QTableWidgetItem* selectedHeaderItem = mTableWidget->verticalHeaderItem(selectedRow);
|
||||
if(!selectedHeaderItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool conversionSuccess;
|
||||
int featureId = selectedHeaderItem->text().toInt(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < mTableWidget->columnCount(); ++i)
|
||||
{
|
||||
QComboBox* currentComboBox = dynamic_cast<QComboBox*>(mTableWidget->cellWidget(0, i));
|
||||
if(currentComboBox)
|
||||
{
|
||||
currentComboBox->setCurrentIndex(currentComboBox->findText(tr("feature %1").arg(featureId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::on_mRemoveFeatureFromSelectionButton_clicked()
|
||||
{
|
||||
if(!mVectorLayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//find out feature id of selected row
|
||||
QList<QTableWidgetItem *> selectionList = mTableWidget->selectedItems();
|
||||
if(selectionList.size() < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//assume all selected items to be in the same row
|
||||
QTableWidgetItem* selectedItem = selectionList[0];
|
||||
int selectedRow = selectedItem->row();
|
||||
QTableWidgetItem* selectedHeaderItem = mTableWidget->verticalHeaderItem(selectedRow);
|
||||
if(!selectedHeaderItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool conversionSuccess;
|
||||
int featureId = selectedHeaderItem->text().toInt(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
selectedRowChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
mTableWidget->removeRow(selectedRow);
|
||||
selectedRowChanged();
|
||||
|
||||
//remove feature from the vector layer selection
|
||||
QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds();
|
||||
selectedIds.remove(featureId);
|
||||
mVectorLayer->setSelectedFeatures(selectedIds);
|
||||
mMapCanvas->repaint();
|
||||
|
||||
//remove feature option from the combo box (without altering the current merge values)
|
||||
for(int i = 0; i < mTableWidget->columnCount(); ++i)
|
||||
{
|
||||
QComboBox* currentComboBox = dynamic_cast<QComboBox*>(mTableWidget->cellWidget(0, i));
|
||||
if(currentComboBox)
|
||||
{
|
||||
currentComboBox->blockSignals(true);
|
||||
currentComboBox->removeItem(currentComboBox->findText(tr("feature %1").arg(featureId)));
|
||||
currentComboBox->blockSignals(false);
|
||||
}
|
||||
}
|
||||
|
||||
//finally remove the feature from mFeatureList
|
||||
QgsFeatureList::iterator f_it = mFeatureList.begin();
|
||||
for(; f_it != mFeatureList.end(); ++f_it)
|
||||
{
|
||||
if(f_it->id() == featureId)
|
||||
{
|
||||
mFeatureList.erase(f_it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMergeAttributesDialog::createRubberBandForFeature(int featureId)
|
||||
{
|
||||
//create rubber band to highlight the feature
|
||||
delete mSelectionRubberBand;
|
||||
mSelectionRubberBand = new QgsRubberBand(mMapCanvas, mVectorLayer->geometryType() == QGis::Polygon);
|
||||
mSelectionRubberBand->setColor(QColor(255, 0, 0));
|
||||
QgsFeature featureToSelect;
|
||||
mVectorLayer->featureAtId(featureId, featureToSelect, true, false);
|
||||
mSelectionRubberBand->setToGeometry( featureToSelect.geometry(), mVectorLayer );
|
||||
}
|
||||
|
||||
QgsAttributeMap QgsMergeAttributesDialog::mergedAttributesMap() const
|
||||
{
|
||||
QgsAttributeMap resultMap;
|
||||
if(mFeatureList.size() < 1)
|
||||
{
|
||||
return resultMap; //return empty map
|
||||
}
|
||||
|
||||
resultMap = mFeatureList[0].attributeMap();
|
||||
//go through all the items and replace the values in the attribute map
|
||||
for(int i = 0; i < resultMap.size(); ++i)
|
||||
{
|
||||
QTableWidgetItem* currentItem = mTableWidget->item(mFeatureList.size() + 1, i);
|
||||
if(!currentItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
QString mergedString = currentItem->text();
|
||||
QVariant newValue(mergedString);
|
||||
resultMap.insert(i, newValue);
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
78
src/app/qgsmergeattributesdialog.h
Normal file
78
src/app/qgsmergeattributesdialog.h
Normal file
@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
qgsmergeattributesdialog.h
|
||||
--------------------------
|
||||
begin : May 2009
|
||||
copyright : (C) 2009 by Marco Hugentobler
|
||||
email : marco dot hugentobler at karto dot baug dot ethz dot ch
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* 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 QGSMERGEATTRIBUTESDIALOG_H
|
||||
#define QGSMERGEATTRIBUTESDIALOG_H
|
||||
|
||||
#include "ui_qgsmergeattributesdialogbase.h"
|
||||
#include "qgsfeature.h"
|
||||
|
||||
class QgsMapCanvas;
|
||||
class QgsRubberBand;
|
||||
class QgsVectorLayer;
|
||||
class QComboBox;
|
||||
|
||||
|
||||
/**A dialog to insert the merge behaviour for attributes (e.g. for the union features editing tool)*/
|
||||
class QgsMergeAttributesDialog: public QDialog, private Ui::QgsMergeAttributesDialogBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QgsMergeAttributesDialog(const QgsFeatureList& features, QgsVectorLayer* vl, QgsMapCanvas* canvas, QWidget * parent = 0, Qt::WindowFlags f = 0);
|
||||
~QgsMergeAttributesDialog();
|
||||
QgsAttributeMap mergedAttributesMap() const;
|
||||
|
||||
private slots:
|
||||
void comboValueChanged(const QString & text);
|
||||
void selectedRowChanged();
|
||||
void on_mFromSelectedPushButton_clicked();
|
||||
void on_mRemoveFeatureFromSelectionButton_clicked();
|
||||
|
||||
private:
|
||||
QgsMergeAttributesDialog(); //default constructor forbidden
|
||||
void createTableWidgetContents();
|
||||
/**Create new combo box with the options for featureXX / mean / min / max */
|
||||
QComboBox* createMergeComboBox(QVariant::Type columnType) const;
|
||||
/**Returns the table widget column index of a combo box
|
||||
@return the column index or -1 in case of error*/
|
||||
int findComboColumn(QComboBox* c) const;
|
||||
/**Calculates the merged value of a column (depending on the selected merge behaviour) and inserts the value in the corresponding cell*/
|
||||
void refreshMergedValue(int col);
|
||||
/**Inserts the attribute value of a specific feature into the row of merged attributes*/
|
||||
QString featureAttributeString(int featureId, int col);
|
||||
/**Calculates and inserts the minimum attribute value of a column*/
|
||||
QString minimumAttributeString(int col);
|
||||
/**Calculates and inserts the maximum value of a column*/
|
||||
QString maximumAttributeString(int col);
|
||||
/**Calculates and inserts the mean value of a column*/
|
||||
QString meanAttributeString(int col);
|
||||
/**Calculates and inserts the median value of a column*/
|
||||
QString medianAttributeString(int col);
|
||||
/**Appends the values of the features for the final value*/
|
||||
QString concatenationAttributeString(int col);
|
||||
/**Sets mSelectionRubberBand to a new feature*/
|
||||
void createRubberBandForFeature(int featureId);
|
||||
|
||||
QgsFeatureList mFeatureList;
|
||||
QgsVectorLayer* mVectorLayer;
|
||||
QgsMapCanvas* mMapCanvas;
|
||||
/**Item that highlights the selected feature in the merge table*/
|
||||
QgsRubberBand* mSelectionRubberBand;
|
||||
};
|
||||
|
||||
#endif // QGSMERGEATTRIBUTESDIALOG_H
|
134
src/ui/qgsmergeattributesdialogbase.ui
Normal file
134
src/ui/qgsmergeattributesdialogbase.ui
Normal file
@ -0,0 +1,134 @@
|
||||
<ui version="4.0" >
|
||||
<class>QgsMergeAttributesDialogBase</class>
|
||||
<widget class="QDialog" name="QgsMergeAttributesDialogBase" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>382</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Merge feature attributes</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QTableWidget" name="mTableWidget" />
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="mFromSelectedPushButton" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mTakeSelectedAttributesLabel" >
|
||||
<property name="text" >
|
||||
<string>Take attributes from selected feature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>58</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" >
|
||||
<item>
|
||||
<widget class="QPushButton" name="mRemoveFeatureFromSelectionButton" >
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mRemoveFeatureFromSelectionLabel" >
|
||||
<property name="text" >
|
||||
<string>Remove feature from selection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2" >
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0" >
|
||||
<size>
|
||||
<width>98</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<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>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>QgsMergeAttributesDialogBase</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>QgsMergeAttributesDialogBase</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user