[QGIS Commit] r10879 - in trunk/qgis: images/themes/default src/app
src/ui
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Wed Jun 3 07:33:59 EDT 2009
Author: mhugent
Date: 2009-06-03 07:33:58 -0400 (Wed, 03 Jun 2009)
New Revision: 10879
Added:
trunk/qgis/images/themes/default/mActionFromSelectedFeature.png
trunk/qgis/images/themes/default/mActionMergeFeatures.png
trunk/qgis/images/themes/default/mActionRemoveSelectedFeature.png
trunk/qgis/src/app/qgsmergeattributesdialog.cpp
trunk/qgis/src/app/qgsmergeattributesdialog.h
trunk/qgis/src/ui/qgsmergeattributesdialogbase.ui
Modified:
trunk/qgis/src/app/CMakeLists.txt
trunk/qgis/src/app/qgisapp.cpp
trunk/qgis/src/app/qgisapp.h
Log:
New functionality for merging features
Added: trunk/qgis/images/themes/default/mActionFromSelectedFeature.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/images/themes/default/mActionFromSelectedFeature.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/qgis/images/themes/default/mActionMergeFeatures.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/images/themes/default/mActionMergeFeatures.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/qgis/images/themes/default/mActionRemoveSelectedFeature.png
===================================================================
(Binary files differ)
Property changes on: trunk/qgis/images/themes/default/mActionRemoveSelectedFeature.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt 2009-06-03 07:06:25 UTC (rev 10878)
+++ trunk/qgis/src/app/CMakeLists.txt 2009-06-03 11:33:58 UTC (rev 10879)
@@ -41,6 +41,7 @@
qgsmaptoolvertexedit.cpp
qgsmeasuredialog.cpp
qgsmeasuretool.cpp
+ qgsmergeattributesdialog.cpp
qgsnewhttpconnection.cpp
qgsnumericsortlistviewitem.cpp
qgsogrsublayersdialog.cpp
@@ -129,6 +130,7 @@
qgsmeasuretool.h
qgsmeasuredialog.h
+ qgsmergeattributesdialog.h
qgsnewhttpconnection.h
qgsoptions.h
qgsogrsublayersdialog.h
Modified: trunk/qgis/src/app/qgisapp.cpp
===================================================================
--- trunk/qgis/src/app/qgisapp.cpp 2009-06-03 07:06:25 UTC (rev 10878)
+++ trunk/qgis/src/app/qgisapp.cpp 2009-06-03 11:33:58 UTC (rev 10879)
@@ -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,7 +704,13 @@
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
mActionPan = new QAction( getThemeIcon( "mActionPan.png" ), tr( "Pan Map" ), this );
@@ -1053,6 +1060,7 @@
mMapToolGroup->addAction( mActionDeleteRing );
mActionDeletePart->setCheckable( true );
mMapToolGroup->addAction( mActionDeletePart );
+ mMapToolGroup->addAction( mActionMergeFeatures);
}
void QgisApp::createMenus()
@@ -1141,6 +1149,7 @@
mEditMenu->addAction( mActionAddIsland );
mEditMenu->addAction( mActionDeleteRing );
mEditMenu->addAction( mActionDeletePart );
+ mEditMenu->addAction( mActionMergeFeatures );
if ( layout == QDialogButtonBox::GnomeLayout || layout == QDialogButtonBox::MacLayout )
{
@@ -1345,6 +1354,7 @@
mAdvancedDigitizeToolBar->addAction( mActionAddIsland );
mAdvancedDigitizeToolBar->addAction( mActionDeleteRing );
mAdvancedDigitizeToolBar->addAction( mActionDeletePart );
+ mAdvancedDigitizeToolBar->addAction( mActionMergeFeatures );
mToolbarMenu->addAction( mAdvancedDigitizeToolBar->toggleViewAction() );
@@ -4085,6 +4095,139 @@
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 @@
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 @@
mActionSplitFeatures->setEnabled( true );
mActionSimplifyFeature->setEnabled( true );
mActionDeletePart->setEnabled( true );
+
}
else
{
Modified: trunk/qgis/src/app/qgisapp.h
===================================================================
--- trunk/qgis/src/app/qgisapp.h 2009-06-03 07:06:25 UTC (rev 10878)
+++ trunk/qgis/src/app/qgisapp.h 2009-06-03 11:33:58 UTC (rev 10879)
@@ -41,6 +41,7 @@
class QgisAppInterface;
class QgsClipboard;
class QgsComposer;
+class QgsGeometry;
class QgsHelpViewer;
class QgsFeature;
@@ -63,6 +64,7 @@
#include <QPointer>
#include "qgsconfig.h"
+#include "qgsfeature.h"
#include "qgspoint.h"
/*! \class QgisApp
@@ -509,6 +511,8 @@
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 @@
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 @@
QAction *mActionSimplifyFeature;
QAction *mActionDeleteRing;
QAction *mActionDeletePart;
+ QAction *mActionMergeFeatures;
QAction *mActionEditSeparator3;
QAction *mActionPan;
Added: trunk/qgis/src/app/qgsmergeattributesdialog.cpp
===================================================================
--- trunk/qgis/src/app/qgsmergeattributesdialog.cpp (rev 0)
+++ trunk/qgis/src/app/qgsmergeattributesdialog.cpp 2009-06-03 11:33:58 UTC (rev 10879)
@@ -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;
+}
+
Added: trunk/qgis/src/app/qgsmergeattributesdialog.h
===================================================================
--- trunk/qgis/src/app/qgsmergeattributesdialog.h (rev 0)
+++ trunk/qgis/src/app/qgsmergeattributesdialog.h 2009-06-03 11:33:58 UTC (rev 10879)
@@ -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
Added: trunk/qgis/src/ui/qgsmergeattributesdialogbase.ui
===================================================================
--- trunk/qgis/src/ui/qgsmergeattributesdialogbase.ui (rev 0)
+++ trunk/qgis/src/ui/qgsmergeattributesdialogbase.ui 2009-06-03 11:33:58 UTC (rev 10879)
@@ -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>
More information about the QGIS-commit
mailing list