void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context ) { if ( f.geometry().isNull() ) return; LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); // TODO: configurable const int nSegments = 4; const QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound; const QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound; const double mitreLimit = 0; QgsGeos engine( g ); QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon ) { QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered ); Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } delete buffered; } }
void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context ) { if ( f.geometry().isNull() ) return; LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal; QgsPoint centroid; if ( mSymbol.altitudeBinding() == Qgs3DTypes::AltBindCentroid ) centroid = QgsPoint( f.geometry().centroid().asPoint() ); QgsGeometry geom = f.geometry(); const QgsAbstractGeometry *g = geom.constGet(); if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) ) { for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() ); out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) ); out.indexes << out.vertices.count() - 1; } } else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) ) { for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom ) { const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) ); for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() ); out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) ); out.indexes << out.vertices.count() - 1; } out.indexes << 0; // add primitive restart } } out.indexes << 0; // add primitive restart }
void QgsMapToolSelectUtils::selectSingleFeature( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, Qt::KeyboardModifiers modifiers ) { QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return; QApplication::setOverrideCursor( Qt::WaitCursor ); QgsFeatureIds selectedFeatures = getMatchingFeatures( canvas, selectGeometry, false, true ); if ( selectedFeatures.isEmpty() ) { if ( !( modifiers & Qt::ShiftModifier || modifiers & Qt::ControlModifier ) ) { // if no modifiers then clicking outside features clears the selection // but if there's a shift or ctrl modifier, then it's likely the user was trying // to modify an existing selection by adding or subtracting features and just // missed the feature vlayer->removeSelection(); } QApplication::restoreOverrideCursor(); return; } QgsVectorLayer::SelectBehavior behavior = QgsVectorLayer::SetSelection; //either shift or control modifier switches to "toggle" selection mode if ( modifiers & Qt::ShiftModifier || modifiers & Qt::ControlModifier ) { QgsFeatureId selectId = *selectedFeatures.constBegin(); QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeatureIds(); if ( layerSelectedFeatures.contains( selectId ) ) behavior = QgsVectorLayer::RemoveFromSelection; else behavior = QgsVectorLayer::AddToSelection; } vlayer->selectByIds( selectedFeatures, behavior ); QApplication::restoreOverrideCursor(); }
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !featureSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) ); if ( !maskSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "OVERLAY" ) ) ); QString dest; QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() ); std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // first build up a list of clip geometries QVector< QgsGeometry > clipGeoms; QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( f.hasGeometry() ) clipGeoms << f.geometry(); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); if ( clipGeoms.isEmpty() ) return outputs; // are we clipping against a single feature? if so, we can show finer progress reports bool singleClipFeature = false; QgsGeometry combinedClipGeom; if ( clipGeoms.length() > 1 ) { combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms ); if ( combinedClipGeom.isEmpty() ) { throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) ); } singleClipFeature = false; } else { combinedClipGeom = clipGeoms.at( 0 ); singleClipFeature = true; } // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureIds testedFeatureIds; int i = -1; Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms ) { i++; if ( feedback->isCanceled() ) { break; } QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) ); QgsFeatureList inputFeatures; QgsFeature f; while ( inputIt.nextFeature( f ) ) inputFeatures << f; if ( inputFeatures.isEmpty() ) continue; double step = 0; if ( singleClipFeature ) step = 100.0 / inputFeatures.length(); int current = 0; Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures ) { if ( feedback->isCanceled() ) { break; } if ( !inputFeature.hasGeometry() ) continue; if ( testedFeatureIds.contains( inputFeature.id() ) ) { // don't retest a feature we have already checked continue; } testedFeatureIds.insert( inputFeature.id() ); if ( !engine->intersects( inputFeature.geometry().constGet() ) ) continue; QgsGeometry newGeometry; if ( !engine->contains( inputFeature.geometry().constGet() ) ) { QgsGeometry currentGeometry = inputFeature.geometry(); newGeometry = combinedClipGeom.intersection( currentGeometry ); if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection ) { QgsGeometry intCom = inputFeature.geometry().combine( newGeometry ); QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry ); newGeometry = intCom.difference( intSym ); } } else { // clip geometry totally contains feature geometry, so no need to perform intersection newGeometry = inputFeature.geometry(); } if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) ) continue; QgsFeature outputFeature; outputFeature.setGeometry( newGeometry ); outputFeature.setAttributes( inputFeature.attributes() ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); if ( singleClipFeature ) feedback->setProgress( current * step ); } if ( !singleClipFeature ) { // coarse progress report for multiple clip geometries feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() ); } }
void QgsAttributeTableDialog::updateRowSelection( int first, int last, int clickType ) { // clickType= 0:Single click, 1:Shift, 2:Ctrl, 3: Dragged click disconnect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); // Id must be mapped to table/view row QModelIndex index = mFilterModel->mapToSource( mFilterModel->index( first, 0 ) ); int fid = mModel->rowToId( index.row() ); bool wasSelected = mSelectedFeatures.contains( fid ); // new selection should be created if ( clickType == 0 ) // Single click { if ( mSelectedFeatures.size() == 1 && wasSelected ) // One item selected return; // Click over a selected item doesn't do anything mView->setCurrentIndex( mFilterModel->index( first, 0 ) ); mView->selectRow( first ); mSelectedFeatures.clear(); mSelectedFeatures.insert( fid ); mLayer->removeSelection(); mLayer->select( fid ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); return; } else if ( clickType == 1 ) // Shift { QgsFeatureIds newSelection; for ( int i = first; i <= last; ++i ) { if ( i >= first ) { // Id must be mapped to table/view row index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) ); fid = mModel->rowToId( index.row() ); } newSelection.insert( fid ); } // Remove items in mSelectedFeatures if they aren't in mNewSelection QgsFeatureIds::Iterator it = mSelectedFeatures.begin(); while ( it != mSelectedFeatures.end() ) { if ( !newSelection.contains( *it ) ) { it = mSelectedFeatures.erase( it ); } else { ++it; } } // Append the other fids in range first-last to mSelectedFeatures QgsFeatureIds::Iterator itNew = newSelection.begin(); for ( ; itNew != newSelection.end(); ++itNew ) { if ( !mSelectedFeatures.contains( *itNew ) ) { mSelectedFeatures.insert( *itNew ); } } } else if ( clickType == 2 ) // Ctrl { // existing selection should be updated if ( wasSelected ) mSelectedFeatures.remove( fid ); else mSelectedFeatures.insert( fid ); } else if ( clickType == 3 ) // Dragged click { QgsFeatureIds newSelection; for ( int i = first; i <= last; ++i ) { if ( i >= first ) { // Id must be mapped to table/view row index = mFilterModel->mapToSource( mFilterModel->index( i, 0 ) ); fid = mModel->rowToId( index.row() ); } newSelection.insert( fid ); } // Remove items in mSelectedFeatures if they aren't in mNewSelection QgsFeatureIds::Iterator it = mSelectedFeatures.begin(); while ( it != mSelectedFeatures.end() ) { if ( !newSelection.contains( *it ) ) { it = mSelectedFeatures.erase( it ); } else { ++it; } } // Append the other fids in range first-last to mSelectedFeatures QgsFeatureIds::Iterator itNew = newSelection.begin(); for ( ; itNew != newSelection.end(); ++itNew ) { if ( !mSelectedFeatures.contains( *itNew ) ) { mSelectedFeatures.insert( *itNew ); } } } updateSelection(); mLayer->setSelectedFeatures( mSelectedFeatures ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateSelectionFromLayer() ) ); }
QgsAbstractGeometryV2* QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometryV2& geom, QMap<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures ) { QScopedPointer<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) ); if ( geomEngine.isNull() ) { return nullptr; } QgsWKBTypes::Type geomTypeBeforeModification = geom.wkbType(); //check if g has polygon type if ( QgsWKBTypes::geometryType( geomTypeBeforeModification ) != QgsWKBTypes::PolygonGeometry ) { return nullptr; } //read avoid intersections list from project properties bool listReadOk; QStringList avoidIntersectionsList = QgsProject::instance()->readListEntry( "Digitizing", "/AvoidIntersectionsList", QStringList(), &listReadOk ); if ( !listReadOk ) return nullptr; //no intersections stored in project does not mean error QList< QgsAbstractGeometryV2* > nearGeometries; //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each QgsVectorLayer* currentLayer = nullptr; QStringList::const_iterator aIt = avoidIntersectionsList.constBegin(); for ( ; aIt != avoidIntersectionsList.constEnd(); ++aIt ) { currentLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( *aIt ) ); if ( currentLayer ) { QgsFeatureIds ignoreIds; QMap<QgsVectorLayer*, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.find( currentLayer ); if ( ignoreIt != ignoreFeatures.constEnd() ) ignoreIds = ignoreIt.value(); QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fi.nextFeature( f ) ) { if ( ignoreIds.contains( f.id() ) ) continue; if ( !f.hasGeometry() ) continue; nearGeometries << f.geometry().geometry()->clone(); } } } if ( nearGeometries.isEmpty() ) { return nullptr; } QgsAbstractGeometryV2* combinedGeometries = geomEngine.data()->combine( nearGeometries ); qDeleteAll( nearGeometries ); if ( !combinedGeometries ) { return nullptr; } QgsAbstractGeometryV2* diffGeom = geomEngine.data()->difference( *combinedGeometries ); delete combinedGeometries; return diffGeom; }
void QgsFieldCalculator::accept() { // Set up QgsDistanceArea each time we (re-)calculate QgsDistanceArea myDa; myDa.setSourceCrs( mVectorLayer->crs().srsid() ); myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() ); myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); QString calcString = builder->expressionText(); QgsExpression exp( calcString ); exp.setGeomCalculator( myDa ); if ( !mVectorLayer || !mVectorLayer->isEditable() ) return; if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } QApplication::setOverrideCursor( Qt::WaitCursor ); mVectorLayer->beginEditCommand( "Field calculator" ); //update existing field if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() ) { QMap<QString, int>::const_iterator fieldIt = mFieldMap.find( mExistingFieldComboBox->currentText() ); if ( fieldIt != mFieldMap.end() ) { mAttributeId = fieldIt.value(); } } else { //create new field QgsField newField( mOutputFieldNameLineEdit->text(), ( QVariant::Type ) mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole ).toInt(), mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 1 ).toString(), mOutputFieldWidthSpinBox->value(), mOutputFieldPrecisionSpinBox->value() ); if ( !mVectorLayer->addAttribute( newField ) ) { QMessageBox::critical( 0, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) ); mVectorLayer->destroyEditCommand(); return; } //get index of the new field const QgsFields& fields = mVectorLayer->pendingFields(); for ( int idx = 0; idx < fields.count(); ++idx ) { if ( fields[idx].name() == mOutputFieldNameLineEdit->text() ) { mAttributeId = idx; break; } } if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } } if ( mAttributeId == -1 ) { mVectorLayer->destroyEditCommand(); QApplication::restoreOverrideCursor(); return; } //go through all the features and change the new attribute QgsFeature feature; bool calculationSuccess = true; QString error; bool onlySelected = mOnlyUpdateSelectedCheckBox->isChecked(); QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds(); bool useGeometry = exp.needsGeometry(); int rownum = 1; bool newField = !mUpdateExistingGroupBox->isChecked(); QVariant emptyAttribute; if ( newField ) emptyAttribute = QVariant( mVectorLayer->pendingFields()[mAttributeId].type() ); QgsFeatureIterator fit = mVectorLayer->getFeatures( QgsFeatureRequest().setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ); while ( fit.nextFeature( feature ) ) { if ( onlySelected ) { if ( !selectedIds.contains( feature.id() ) ) { continue; } } exp.setCurrentRowNumber( rownum ); QVariant value = exp.evaluate( &feature ); if ( exp.hasEvalError() ) { calculationSuccess = false; error = exp.evalErrorString(); break; } else { mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) ); } rownum++; } QApplication::restoreOverrideCursor(); if ( !calculationSuccess ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) ); mVectorLayer->destroyEditCommand(); return; } mVectorLayer->endEditCommand(); QDialog::accept(); }
std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom, const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures ) { std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) ); if ( !geomEngine ) { return nullptr; } QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType(); //check if g has polygon type if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry ) { return nullptr; } if ( avoidIntersectionsLayers.isEmpty() ) return nullptr; //no intersections stored in project does not mean error QList< QgsGeometry > nearGeometries; //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers ) { QgsFeatureIds ignoreIds; QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer ); if ( ignoreIt != ignoreFeatures.constEnd() ) ignoreIds = ignoreIt.value(); QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fi.nextFeature( f ) ) { if ( ignoreIds.contains( f.id() ) ) continue; if ( !f.hasGeometry() ) continue; nearGeometries << f.geometry(); } } if ( nearGeometries.isEmpty() ) { return nullptr; } std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) ); if ( !combinedGeometries ) { return nullptr; } std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) ); return diffGeom; }
void QgsFieldCalculator::accept() { builder->saveToRecent( "fieldcalc" ); if ( !mVectorLayer ) return; // Set up QgsDistanceArea each time we (re-)calculate QgsDistanceArea myDa; myDa.setSourceCrs( mVectorLayer->crs().srsid() ); myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() ); myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); QString calcString = builder->expressionText(); QgsExpression exp( calcString ); exp.setGeomCalculator( myDa ); QgsExpressionContext expContext; expContext << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( mVectorLayer ); if ( !exp.prepare( &expContext ) ) { QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } // Test for creating expression field based on ! mUpdateExistingGroupBox checked rather // than on mNewFieldGroupBox checked, as if the provider does not support adding attributes // then mUpdateExistingGroupBox is set to not checkable, and hence is not checked. This // is a minimum fix to resolve this - better would be some GUI redesign... if ( ! mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) { mVectorLayer->addExpressionField( calcString, fieldDefinition() ); } else { if ( !mVectorLayer->isEditable() ) mVectorLayer->startEditing(); QApplication::setOverrideCursor( Qt::WaitCursor ); mVectorLayer->beginEditCommand( "Field calculator" ); //update existing field if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() ) { QMap<QString, int>::const_iterator fieldIt = mFieldMap.find( mExistingFieldComboBox->currentText() ); if ( fieldIt != mFieldMap.end() ) { mAttributeId = fieldIt.value(); } } else { //create new field const QgsField newField = fieldDefinition(); if ( !mVectorLayer->addAttribute( newField ) ) { QApplication::restoreOverrideCursor(); QMessageBox::critical( 0, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) ); mVectorLayer->destroyEditCommand(); return; } //get index of the new field const QgsFields& fields = mVectorLayer->fields(); for ( int idx = 0; idx < fields.count(); ++idx ) { if ( fields[idx].name() == mOutputFieldNameLineEdit->text() ) { mAttributeId = idx; break; } } //update expression context with new fields expContext.setFields( mVectorLayer->fields() ); if ( ! exp.prepare( &expContext ) ) { QApplication::restoreOverrideCursor(); QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } } if ( mAttributeId == -1 ) { mVectorLayer->destroyEditCommand(); QApplication::restoreOverrideCursor(); return; } //go through all the features and change the new attribute QgsFeature feature; bool calculationSuccess = true; QString error; bool onlySelected = mOnlyUpdateSelectedCheckBox->isChecked(); QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds(); bool useGeometry = exp.needsGeometry(); int rownum = 1; QgsField field = mVectorLayer->fields()[mAttributeId]; bool newField = !mUpdateExistingGroupBox->isChecked(); QVariant emptyAttribute; if ( newField ) emptyAttribute = QVariant( field.type() ); QgsFeatureIterator fit = mVectorLayer->getFeatures( QgsFeatureRequest().setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ); while ( fit.nextFeature( feature ) ) { if ( onlySelected ) { if ( !selectedIds.contains( feature.id() ) ) { continue; } } expContext.setFeature( feature ); expContext.lastScope()->setVariable( QString( "row_number" ), rownum ); QVariant value = exp.evaluate( &expContext ); field.convertCompatible( value ); if ( exp.hasEvalError() ) { calculationSuccess = false; error = exp.evalErrorString(); break; } else { mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) ); } rownum++; } QApplication::restoreOverrideCursor(); if ( !calculationSuccess ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) ); mVectorLayer->destroyEditCommand(); return; } mVectorLayer->endEditCommand(); } QDialog::accept(); }
void QgsFieldCalculator::accept() { if ( mVectorLayer && mVectorLayer->isEditable() ) { QString calcString = mExpressionTextEdit->toPlainText(); //create QgsExpression QgsExpression exp( calcString ); if ( exp.hasParserError() ) { //expression not valid QMessageBox::critical( 0, tr( "Syntax error" ), tr( QString( "Invalid expression syntax. The error message of the parser is: '" + exp.parserErrorString() + "'" ).toLocal8Bit().data() ) ); return; } if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } mVectorLayer->beginEditCommand( "Field calculator" ); //update existing field if ( mUpdateExistingFieldCheckBox->checkState() == Qt::Checked ) { QMap<QString, int>::const_iterator fieldIt = mFieldMap.find( mExistingFieldComboBox->currentText() ); if ( fieldIt != mFieldMap.end() ) { mAttributeId = fieldIt.value(); } } //create new field else { //create new field QgsField newField( mOutputFieldNameLineEdit->text(), ( QVariant::Type ) mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole ).toInt(), mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 1 ).toString(), mOuputFieldWidthSpinBox->value(), mOutputFieldPrecisionSpinBox->value() ); if ( !mVectorLayer->addAttribute( newField ) ) { QMessageBox::critical( 0, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) ); mVectorLayer->destroyEditCommand(); return; } //get index of the new field const QgsFieldMap fieldList = mVectorLayer->pendingFields(); QgsFieldMap::const_iterator it = fieldList.constBegin(); for ( ; it != fieldList.constEnd(); ++it ) { if ( it.value().name() == mOutputFieldNameLineEdit->text() ) { mAttributeId = it.key(); break; } } } if ( mAttributeId == -1 ) { mVectorLayer->destroyEditCommand(); return; } //go through all the features and change the new attribute QgsFeature feature; bool calculationSuccess = true; QString error; bool onlySelected = ( mOnlyUpdateSelectedCheckBox->checkState() == Qt::Checked ); QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds(); // block layerModified signals (that would trigger table update) mVectorLayer->blockSignals( true ); bool useGeometry = exp.needsGeometry(); int rownum = 1; mVectorLayer->select( mVectorLayer->pendingAllAttributesList(), QgsRectangle(), useGeometry, false ); while ( mVectorLayer->nextFeature( feature ) ) { if ( onlySelected ) { if ( !selectedIds.contains( feature.id() ) ) { continue; } } exp.setCurrentRowNumber( rownum ); QVariant value = exp.evaluate( &feature ); if ( exp.hasEvalError() ) { calculationSuccess = false; error = exp.evalErrorString(); break; } else { mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, false ); } rownum++; } // stop blocking layerModified signals and make sure that one layerModified signal is emitted mVectorLayer->blockSignals( false ); mVectorLayer->setModified( true, false ); if ( !calculationSuccess ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) ); mVectorLayer->destroyEditCommand(); return; } mVectorLayer->endEditCommand(); } QDialog::accept(); }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == NULL ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( canvas->mapSettings().hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() ); return; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectGeomTrans.boundingBox() ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
void QgsFieldCalculator::accept() { // Set up QgsDistanceArea each time we (re-)calculate QgsDistanceArea myDa; myDa.setSourceCrs( mVectorLayer->crs().srsid() ); myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() ); myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); QString calcString = builder->expressionText(); QgsExpression exp( calcString ); if ( !mVectorLayer || !mVectorLayer->isEditable() ) return; if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { QMessageBox::critical( 0, tr( "Evaluation error" ), exp.evalErrorString() ); return; } mVectorLayer->beginEditCommand( "Field calculator" ); //update existing field if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() ) { QMap<QString, int>::const_iterator fieldIt = mFieldMap.find( mExistingFieldComboBox->currentText() ); if ( fieldIt != mFieldMap.end() ) { mAttributeId = fieldIt.value(); } } else { //create new field QgsField newField( mOutputFieldNameLineEdit->text(), ( QVariant::Type ) mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole ).toInt(), mOutputFieldTypeComboBox->itemData( mOutputFieldTypeComboBox->currentIndex(), Qt::UserRole + 1 ).toString(), mOutputFieldWidthSpinBox->value(), mOutputFieldPrecisionSpinBox->value() ); if ( !mVectorLayer->addAttribute( newField ) ) { QMessageBox::critical( 0, tr( "Provider error" ), tr( "Could not add the new field to the provider." ) ); mVectorLayer->destroyEditCommand(); return; } //get index of the new field const QgsFieldMap fieldList = mVectorLayer->pendingFields(); QgsFieldMap::const_iterator it = fieldList.constBegin(); for ( ; it != fieldList.constEnd(); ++it ) { if ( it.value().name() == mOutputFieldNameLineEdit->text() ) { mAttributeId = it.key(); break; } } } if ( mAttributeId == -1 ) { mVectorLayer->destroyEditCommand(); return; } //go through all the features and change the new attribute QgsFeature feature; bool calculationSuccess = true; QString error; bool onlySelected = mOnlyUpdateSelectedCheckBox->isChecked(); QgsFeatureIds selectedIds = mVectorLayer->selectedFeaturesIds(); // block layerModified signals (that would trigger table update) mVectorLayer->blockSignals( true ); bool useGeometry = exp.needsGeometry(); int rownum = 1; mVectorLayer->select( mVectorLayer->pendingAllAttributesList(), QgsRectangle(), useGeometry, false ); while ( mVectorLayer->nextFeature( feature ) ) { if ( onlySelected ) { if ( !selectedIds.contains( feature.id() ) ) { continue; } } exp.setCurrentRowNumber( rownum ); exp.setGeomCalculator( myDa ); QVariant value = exp.evaluate( &feature ); if ( exp.hasEvalError() ) { calculationSuccess = false; error = exp.evalErrorString(); break; } else { mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, false ); } rownum++; } // stop blocking layerModified signals and make sure that one layerModified signal is emitted mVectorLayer->blockSignals( false ); mVectorLayer->setModified( true, false ); if ( !calculationSuccess ) { QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) ); mVectorLayer->destroyEditCommand(); return; } mVectorLayer->endEditCommand(); QDialog::accept(); }
void QgsFeatureListView::ensureEditSelection( bool inSelection ) { if ( !mModel->rowCount() ) return; const QModelIndexList selectedIndexes = mCurrentEditSelectionModel->selectedIndexes(); // We potentially want a new edit selection // If we it should be in the feature selection // but we don't find a matching one we might // still stick to the old edit selection bool editSelectionUpdateRequested = false; // There is a valid selection available which we // could fall back to bool validEditSelectionAvailable = false; if ( selectedIndexes.isEmpty() || mModel->mapFromMaster( selectedIndexes.first() ).row() == -1 ) { validEditSelectionAvailable = false; } else { validEditSelectionAvailable = true; } // If we want to force the edit selection to be within the feature selection // let's do some additional checks if ( inSelection ) { // no valid edit selection, update anyway if ( !validEditSelectionAvailable ) { editSelectionUpdateRequested = true; } else { // valid selection: update only if it's not in the feature selection const QgsFeatureIds selectedFids = layerCache()->layer()->selectedFeatureIds(); if ( !selectedFids.contains( mModel->idxToFid( mModel->mapFromMaster( selectedIndexes.first() ) ) ) ) { editSelectionUpdateRequested = true; } } } else { // we don't care if the edit selection is in the feature selection? // well then, only update if there is no valid edit selection available if ( !validEditSelectionAvailable ) editSelectionUpdateRequested = true; } if ( editSelectionUpdateRequested ) { if ( !mUpdateEditSelectionTimer.isSingleShot() ) { mUpdateEditSelectionTimer.setSingleShot( true ); connect( &mUpdateEditSelectionTimer, &QTimer::timeout, this, [ this, inSelection, validEditSelectionAvailable ]() { // The layer might have been removed between timer start and timer triggered // in this case there is nothing left for us to do. if ( !layerCache() ) return; int rowToSelect = -1; if ( inSelection ) { const QgsFeatureIds selectedFids = layerCache()->layer()->selectedFeatureIds(); const int rowCount = mModel->rowCount(); for ( int i = 0; i < rowCount; i++ ) { if ( selectedFids.contains( mModel->idxToFid( mModel->index( i, 0 ) ) ) ) { rowToSelect = i; break; } if ( rowToSelect == -1 && !validEditSelectionAvailable ) rowToSelect = 0; } } else rowToSelect = 0; if ( rowToSelect != -1 ) { setEditSelection( mModel->mapToMaster( mModel->index( rowToSelect, 0 ) ), QItemSelectionModel::ClearAndSelect ); } } ); mUpdateEditSelectionTimer.setInterval( 0 ); } mUpdateEditSelectionTimer.start(); } }
void QgsRelationReferenceWidget::filterChanged() { QVariant nullValue = QgsApplication::nullRepresentation(); QMap<QString, QString> filters; QgsAttributeList attrs; QComboBox *scb = qobject_cast<QComboBox *>( sender() ); Q_ASSERT( scb ); QgsFeature f; QgsFeatureIds featureIds; QString filterExpression; // comboboxes have to be disabled before building filters if ( mChainFilters ) disableChainedComboBoxes( scb ); // build filters const auto constMFilterComboBoxes = mFilterComboBoxes; for ( QComboBox *cb : constMFilterComboBoxes ) { if ( cb->currentIndex() != 0 ) { const QString fieldName = cb->property( "Field" ).toString(); if ( cb->currentText() == nullValue.toString() ) { filters[fieldName] = QStringLiteral( "\"%1\" IS NULL" ).arg( fieldName ); } else { filters[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, cb->currentText() ); } attrs << mReferencedLayer->fields().lookupField( fieldName ); } } if ( mChainFilters ) { QComboBox *ccb = nullptr; const auto constMFilterComboBoxes = mFilterComboBoxes; for ( QComboBox *cb : constMFilterComboBoxes ) { if ( !ccb ) { if ( cb == scb ) ccb = cb; continue; } if ( ccb->currentIndex() != 0 ) { const QString fieldName = cb->property( "Field" ).toString(); cb->blockSignals( true ); cb->clear(); cb->addItem( cb->property( "FieldAlias" ).toString() ); // ccb = scb // cb = scb + 1 QStringList texts; const auto txts { mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] }; for ( const QString &txt : txts ) { QMap<QString, QString> filtersAttrs = filters; filtersAttrs[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, txt ); QString expression = filtersAttrs.values().join( QStringLiteral( " AND " ) ); QgsAttributeList subset = attrs; subset << mReferencedLayer->fields().lookupField( fieldName ); QgsFeatureIterator it( mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterExpression( expression ).setSubsetOfAttributes( subset ) ) ); bool found = false; while ( it.nextFeature( f ) ) { if ( !featureIds.contains( f.id() ) ) featureIds << f.id(); found = true; } // item is only provided if at least 1 feature exists if ( found ) texts << txt; } texts.sort(); cb->addItems( texts ); cb->setEnabled( true ); cb->blockSignals( false ); ccb = cb; } } } filterExpression = filters.values().join( QStringLiteral( " AND " ) ); mComboBox->setFilterExpression( filterExpression ); }
bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } bool useField = false; if ( uniqueIdField == -1 ) { uniqueIdField = 0; } else { useField = true; } QgsFields fields; fields.append( QgsField( QStringLiteral( "UID" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QStringLiteral( "PERIM" ), QVariant::Double ) ); QgsWkbTypes::Type outputType = QgsWkbTypes::Polygon; QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, crs ); QgsFeature currentFeature; QgsGeometry dissolveGeometry; //dissolve geometry QMultiMap<QString, QgsFeatureId> map; if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); QgsFeatureIds::const_iterator it = selection.constBegin(); for ( ; it != selection.constEnd(); ++it ) { #if 0 if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { // break; // it may be better to do something else here? return false; } #endif if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) { continue; } map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } else { QgsFeatureIterator fit = layer->getFeatures(); while ( fit.nextFeature( currentFeature ) ) { #if 0 if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { // break; // it may be better to do something else here? return false; } #endif map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin(); while ( jt != map.constEnd() ) { QString currentKey = jt.key(); int processedFeatures = 0; //take only selection if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); if ( p ) { p->setMaximum( selection.size() ); } processedFeatures = 0; while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p && p->wasCanceled() ) { break; } if ( selection.contains( jt.value() ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } convexFeature( currentFeature, processedFeatures, dissolveGeometry ); ++processedFeatures; } ++jt; } QList<double> values; if ( dissolveGeometry.isEmpty() ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveGeometry = dissolveGeometry.convexHull(); values = simpleMeasure( dissolveGeometry ); QgsAttributes attributes( 3 ); attributes[0] = QVariant( currentKey ); attributes[1] = values.at( 0 ); attributes[2] = values.at( 1 ); QgsFeature dissolveFeature; dissolveFeature.setAttributes( attributes ); dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } //take all features else { int featureCount = layer->featureCount(); if ( p ) { p->setMaximum( featureCount ); } processedFeatures = 0; while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } convexFeature( currentFeature, processedFeatures, dissolveGeometry ); ++processedFeatures; ++jt; } QList<double> values; if ( dissolveGeometry.isEmpty() ) { QgsDebugMsg( "no dissolved geometry - should not happen" ); return false; } dissolveGeometry = dissolveGeometry.convexHull(); // values = simpleMeasure( tmpGeometry ); values = simpleMeasure( dissolveGeometry ); QgsAttributes attributes; attributes[0] = QVariant( currentKey ); attributes[1] = QVariant( values[ 0 ] ); attributes[2] = QVariant( values[ 1 ] ); QgsFeature dissolveFeature; dissolveFeature.setAttributes( attributes ); dissolveFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( dissolveFeature ); } } return true; }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == nullptr ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. //QgsGeometry selectGeomTrans( *selectGeometry ); //if ( canvas->mapSettings().hasCrsTransformEnabled() ) //{ // try // { // QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); // selectGeomTrans.transform( ct ); // } // catch ( QgsCsException &cse ) // { // Q_UNUSED( cse ); // // catch exception for 'invalid' point and leave existing selection unchanged // QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); // LOG_INFO( "CRS Exception\nSelection extends beyond layer's coordinate system" ); // return; // } //} QgsGeometry selectGeomTrans; try{ selectGeomTrans = toLayerCoordinates( canvas, selectGeometry, vlayer ); } catch ( QgsCsException & ) { return; } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->pendingFields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->pendingFields() ); else request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { #if (VERSION_INT >= 21601) context.expressionContext().setFeature( f ); //taken from QGIS 2.16.1 // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) #else if ( r && !r->willRenderFeature( f ) ) #endif continue; QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { SelectFeatures( vlayer, newSelectedFeatures ); // vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer* layer, const QString& shapefileName, bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p ) { if ( !layer ) { return false; } QgsVectorDataProvider* dp = layer->dataProvider(); if ( !dp ) { return false; } bool useField = false; if ( uniqueIdField == -1 ) { uniqueIdField = 0; } else { useField = true; } QgsWkbTypes::Type outputType = dp->wkbType(); QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, crs ); QgsFeature currentFeature; QMultiMap<QString, QgsFeatureId> map; if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); QgsFeatureIds::const_iterator it = selection.constBegin(); for ( ; it != selection.constEnd(); ++it ) { if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) ) { continue; } map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } else { QgsFeatureIterator fit = layer->getFeatures(); while ( fit.nextFeature( currentFeature ) ) { map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() ); } } QgsGeometry dissolveGeometry; //dissolve geometry QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin(); QgsFeature outputFeature; while ( jt != map.constEnd() ) { QString currentKey = jt.key(); int processedFeatures = 0; bool first = true; //take only selection if ( onlySelectedFeatures ) { //use QgsVectorLayer::featureAtId const QgsFeatureIds selection = layer->selectedFeaturesIds(); if ( p ) { p->setMaximum( selection.size() ); } while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p && p->wasCanceled() ) { break; } if ( selection.contains( jt.value() ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } if ( first ) { outputFeature.setAttributes( currentFeature.attributes() ); first = false; } dissolveGeometry = dissolveFeature( currentFeature, dissolveGeometry ); ++processedFeatures; } ++jt; } } //take all features else { int featureCount = layer->featureCount(); if ( p ) { p->setMaximum( featureCount ); } while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) ) { continue; } { outputFeature.setAttributes( currentFeature.attributes() ); first = false; } dissolveGeometry = dissolveFeature( currentFeature, dissolveGeometry ); ++processedFeatures; ++jt; } } outputFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( outputFeature ); } return true; }
void QgsLocationBasedAlgorithm::process( QgsFeatureSource *targetSource, QgsFeatureSource *intersectSource, const QList< int > &selectedPredicates, const std::function < void( const QgsFeature & ) > &handleFeatureFunction, bool onlyRequireTargetIds, QgsFeedback *feedback ) { // build a list of 'reversed' predicates, because in this function // we actually test the reverse of what the user wants (allowing us // to prepare geometries and optimise the algorithm) QList< Predicate > predicates; for ( int i : selectedPredicates ) { predicates << reversePredicate( static_cast< Predicate >( i ) ); } QgsFeatureIds disjointSet; if ( predicates.contains( Disjoint ) ) disjointSet = targetSource->allFeatureIds(); QgsFeatureIds foundSet; QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( targetSource->sourceCrs() ); QgsFeatureIterator fIt = intersectSource->getFeatures( request ); double step = intersectSource->featureCount() > 0 ? 100.0 / intersectSource->featureCount() : 1; int current = 0; QgsFeature f; std::unique_ptr< QgsGeometryEngine > engine; while ( fIt.nextFeature( f ) ) { if ( feedback->isCanceled() ) break; if ( !f.hasGeometry() ) continue; engine.reset(); QgsRectangle bbox = f.geometry().boundingBox(); request = QgsFeatureRequest().setFilterRect( bbox ); if ( onlyRequireTargetIds ) request.setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator testFeatureIt = targetSource->getFeatures( request ); QgsFeature testFeature; while ( testFeatureIt.nextFeature( testFeature ) ) { if ( feedback->isCanceled() ) break; if ( foundSet.contains( testFeature.id() ) ) { // already added this one, no need for further tests continue; } if ( predicates.count() == 1 && predicates.at( 0 ) == Disjoint && !disjointSet.contains( testFeature.id() ) ) { // calculating only the disjoint set, and we've already eliminated this feature so no need for further tests continue; } if ( !engine ) { engine.reset( QgsGeometry::createGeometryEngine( f.geometry().geometry() ) ); engine->prepareGeometry(); } for ( Predicate predicate : qgis::as_const( predicates ) ) { bool isMatch = false; switch ( predicate ) { case Intersects: isMatch = engine->intersects( testFeature.geometry().geometry() ); break; case Contains: isMatch = engine->contains( testFeature.geometry().geometry() ); break; case Disjoint: if ( engine->intersects( testFeature.geometry().geometry() ) ) { disjointSet.remove( testFeature.id() ); } break; case IsEqual: isMatch = engine->isEqual( testFeature.geometry().geometry() ); break; case Touches: isMatch = engine->touches( testFeature.geometry().geometry() ); break; case Overlaps: isMatch = engine->overlaps( testFeature.geometry().geometry() ); break; case Within: isMatch = engine->within( testFeature.geometry().geometry() ); break; case Crosses: isMatch = engine->crosses( testFeature.geometry().geometry() ); break; } if ( isMatch ) { foundSet.insert( testFeature.id() ); handleFeatureFunction( testFeature ); } } } current += 1; feedback->setProgress( current * step ); } if ( predicates.contains( Disjoint ) ) { disjointSet = disjointSet.subtract( foundSet ); QgsFeatureRequest disjointReq = QgsFeatureRequest().setFilterFids( disjointSet ); if ( onlyRequireTargetIds ) disjointReq.setSubsetOfAttributes( QgsAttributeList() ).setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureIterator disjointIt = targetSource->getFeatures( disjointReq ); QgsFeature f; while ( disjointIt.nextFeature( f ) ) { handleFeatureFunction( f ); } } }