void QgsFieldCalculator::accept() { 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 ); 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 QgsGeometryCheckerSetupTab::runChecks() { // Get selected layer const QList<QgsVectorLayer *> layers = getSelectedLayers(); if ( layers.isEmpty() ) return; if ( ui.radioButtonOutputNew->isChecked() ) { for ( QgsVectorLayer *layer : layers ) { if ( layer->dataProvider()->dataSourceUri().startsWith( ui.lineEditOutputDirectory->text() ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The chosen output directory contains one or more input layers." ) ); return; } } } QgsVectorLayer *lineLayerCheckLayer = ui.comboLineLayerIntersection->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboLineLayerIntersection->currentData().toString() ) ) : nullptr; QgsVectorLayer *followBoundaryCheckLayer = ui.comboBoxFollowBoundaries->isEnabled() ? dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboBoxFollowBoundaries->currentData().toString() ) ) : nullptr; if ( layers.contains( lineLayerCheckLayer ) || layers.contains( followBoundaryCheckLayer ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The selected input layers cannot contain a layer also selected for a topology check." ) ); return; } for ( QgsVectorLayer *layer : layers ) { if ( layer->isEditable() ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Input layer '%1' is not allowed to be in editing mode." ).arg( layer->name() ) ); return; } } bool selectedOnly = ui.checkBoxInputSelectedOnly->isChecked(); // Set window busy setCursor( Qt::WaitCursor ); mRunButton->setEnabled( false ); ui.labelStatus->setText( tr( "<b>Preparing output...</b>" ) ); ui.labelStatus->show(); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); QList<QgsVectorLayer *> processLayers; if ( ui.radioButtonOutputNew->isChecked() ) { // Get output directory and file extension QDir outputDir = QDir( ui.lineEditOutputDirectory->text() ); QString outputDriverName = ui.comboBoxOutputFormat->currentData().toString(); QgsVectorFileWriter::MetaData metadata; if ( !QgsVectorFileWriter::driverMetadata( outputDriverName, metadata ) ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "The specified output format cannot be recognized." ) ); mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); return; } QString outputExtension = metadata.ext; // List over input layers, check which existing project layers need to be removed and create output layers QString filenamePrefix = ui.lineEditFilenamePrefix->text(); QSettings().setValue( "/geometry_checker/previous_values/filename_prefix", filenamePrefix ); QStringList toRemove; QStringList createErrors; for ( QgsVectorLayer *layer : layers ) { QString outputPath = outputDir.absoluteFilePath( filenamePrefix + layer->name() + "." + outputExtension ); // Remove existing layer with same uri from project for ( QgsVectorLayer *projectLayer : QgsProject::instance()->layers<QgsVectorLayer *>() ) { if ( projectLayer->dataProvider()->dataSourceUri().startsWith( outputPath ) ) { toRemove.append( projectLayer->id() ); } } // Create output layer QString errMsg; QgsVectorFileWriter::WriterError err = QgsVectorFileWriter::writeAsVectorFormat( layer, outputPath, layer->dataProvider()->encoding(), layer->crs(), outputDriverName, selectedOnly, &errMsg ); if ( err != QgsVectorFileWriter::NoError ) { createErrors.append( errMsg ); continue; } QgsVectorLayer *newlayer = new QgsVectorLayer( outputPath, QFileInfo( outputPath ).completeBaseName(), QStringLiteral( "ogr" ) ); if ( selectedOnly ) { QgsFeature feature; // Get features to select (only selected features were written up to this point) QgsFeatureIds selectedFeatures = newlayer->allFeatureIds(); // Write non-selected feature ids QgsFeatureList features; QgsFeatureIterator it = layer->getFeatures(); while ( it.nextFeature( feature ) ) { if ( !layer->selectedFeatureIds().contains( feature.id() ) ) { features.append( feature ); } } newlayer->dataProvider()->addFeatures( features ); // Set selected features newlayer->selectByIds( selectedFeatures ); } processLayers.append( newlayer ); } // Remove layers from project if ( !toRemove.isEmpty() ) { QgsProject::instance()->removeMapLayers( toRemove ); } // Error if an output layer could not be created if ( !createErrors.isEmpty() ) { QMessageBox::critical( this, tr( "Check Geometries" ), tr( "Failed to create one or more output layers:\n%1" ).arg( createErrors.join( "\n" ) ) ); mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); return; } } else { processLayers = layers; } // Check if output layers are editable QList<QgsVectorLayer *> nonEditableLayers; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { if ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) { nonEditableLayers.append( layer ); } } if ( !nonEditableLayers.isEmpty() ) { QStringList nonEditableLayerNames; for ( QgsVectorLayer *layer : nonEditableLayers ) { nonEditableLayerNames.append( layer->name() ); } if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Check Geometries" ), tr( "The following output layers are in a format that does not support editing features:\n%1\n\nThe geometry check can be performed, but it will not be possible to fix any errors. Do you want to continue?" ).arg( nonEditableLayerNames.join( "\n" ) ), QMessageBox::Yes, QMessageBox::No ) ) { if ( ui.radioButtonOutputNew->isChecked() ) { for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { QString layerPath = layer->dataProvider()->dataSourceUri(); delete layer; if ( ui.comboBoxOutputFormat->currentText() == QLatin1String( "ESRI Shapefile" ) ) { QgsVectorFileWriter::deleteShapeFile( layerPath ); } else { QFile( layerPath ).remove(); } } mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); } return; } } // Setup checker ui.labelStatus->setText( tr( "<b>Building spatial index...</b>" ) ); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); QMap<QString, QgsFeaturePool *> featurePools; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) ); } // LineLayerIntersection check is enabled, make sure there is also a feature pool for that layer if ( ui.checkLineLayerIntersection->isChecked() && !featurePools.keys().contains( ui.comboLineLayerIntersection->currentData().toString() ) ) { QgsVectorLayer *layer = dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( ui.comboLineLayerIntersection->currentData().toString() ) ); Q_ASSERT( layer ); featurePools.insert( layer->id(), new QgsVectorDataProviderFeaturePool( layer, selectedOnly ) ); } QgsGeometryCheckerContext *context = new QgsGeometryCheckerContext( ui.spinBoxTolerance->value(), QgsProject::instance()->crs(), featurePools, QgsProject::instance()->transformContext() ); QList<QgsGeometryCheck *> checks; for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) { QgsGeometryCheck *check = factory->createInstance( context, ui ); if ( check ) { checks.append( check ); } } QgsGeometryChecker *checker = new QgsGeometryChecker( checks, context ); emit checkerStarted( checker ); if ( ui.radioButtonOutputNew->isChecked() ) { QList<QgsMapLayer *> addLayers; for ( QgsVectorLayer *layer : qgis::as_const( processLayers ) ) { addLayers.append( layer ); } QgsProject::instance()->addMapLayers( addLayers ); } // Run ui.buttonBox->addButton( mAbortButton, QDialogButtonBox::ActionRole ); mRunButton->hide(); ui.progressBar->setRange( 0, 0 ); ui.labelStatus->hide(); ui.progressBar->show(); ui.widgetInputs->setEnabled( false ); QEventLoop evLoop; QFutureWatcher<void> futureWatcher; connect( checker, &QgsGeometryChecker::progressValue, ui.progressBar, &QProgressBar::setValue ); connect( &futureWatcher, &QFutureWatcherBase::finished, &evLoop, &QEventLoop::quit ); connect( mAbortButton, &QAbstractButton::clicked, &futureWatcher, &QFutureWatcherBase::cancel ); connect( mAbortButton, &QAbstractButton::clicked, this, &QgsGeometryCheckerSetupTab::showCancelFeedback ); mIsRunningInBackground = true; int maxSteps = 0; futureWatcher.setFuture( checker->execute( &maxSteps ) ); ui.progressBar->setRange( 0, maxSteps ); evLoop.exec(); mIsRunningInBackground = false; // Restore window unsetCursor(); mAbortButton->setEnabled( true ); ui.buttonBox->removeButton( mAbortButton ); mRunButton->setEnabled( true ); mRunButton->show(); ui.progressBar->hide(); ui.labelStatus->hide(); ui.widgetInputs->setEnabled( true ); // Show result emit checkerFinished( !futureWatcher.isCanceled() ); }
QgsFeature QgsCentroidAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback *feedback ) { QgsFeature feature = f; if ( feature.hasGeometry() ) { feature.setGeometry( feature.geometry().centroid() ); if ( !feature.geometry() ) { feedback->pushInfo( QObject::tr( "Error calculating centroid for feature %1" ).arg( feature.id() ) ); } } return feature; }
QgsFeatureList QgsSubdivideAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback *feedback ) { QgsFeature feature = f; if ( feature.hasGeometry() ) { feature.setGeometry( feature.geometry().subdivide( mMaxNodes ) ); if ( !feature.geometry() ) { feedback->reportError( QObject::tr( "Error calculating subdivision for feature %1" ).arg( feature.id() ) ); } } return QgsFeatureList() << feature; }
void QgsVectorLayerRenderer::drawRendererLevels( QgsFeatureIterator &fit ) { QHash< QgsSymbol *, QList<QgsFeature> > features; // key = symbol, value = array of features QgsSingleSymbolRenderer *selRenderer = nullptr; if ( !mSelectedFeatureIds.isEmpty() ) { selRenderer = new QgsSingleSymbolRenderer( QgsSymbol::defaultSymbol( mGeometryType ) ); selRenderer->symbol()->setColor( mContext.selectionColor() ); selRenderer->setVertexMarkerAppearance( mVertexMarkerStyle, mVertexMarkerSize ); selRenderer->startRender( mContext, mFields ); } QgsExpressionContextScope *symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr, new QgsExpressionContextScope() ); mContext.expressionContext().appendScope( symbolScope ); // 1. fetch features QgsFeature fet; while ( fit.nextFeature( fet ) ) { if ( mContext.renderingStopped() ) { qDebug( "rendering stop!" ); stopRenderer( selRenderer ); delete mContext.expressionContext().popScope(); return; } if ( !fet.hasGeometry() ) continue; // skip features without geometry mContext.expressionContext().setFeature( fet ); QgsSymbol *sym = mRenderer->symbolForFeature( fet, mContext ); if ( !sym ) { continue; } if ( !features.contains( sym ) ) { features.insert( sym, QList<QgsFeature>() ); } features[sym].append( fet ); // new labeling engine if ( mContext.labelingEngine() ) { QgsGeometry obstacleGeometry; QgsSymbolList symbols = mRenderer->originalSymbolsForFeature( fet, mContext ); if ( !symbols.isEmpty() && fet.geometry().type() == QgsWkbTypes::PointGeometry ) { obstacleGeometry = QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, symbols ); } if ( !symbols.isEmpty() ) { QgsExpressionContextUtils::updateSymbolScope( symbols.at( 0 ), symbolScope ); } if ( mLabelProvider ) { mLabelProvider->registerFeature( fet, mContext, obstacleGeometry ); } if ( mDiagramProvider ) { mDiagramProvider->registerFeature( fet, mContext, obstacleGeometry ); } } } delete mContext.expressionContext().popScope(); // find out the order QgsSymbolLevelOrder levels; QgsSymbolList symbols = mRenderer->symbols( mContext ); for ( int i = 0; i < symbols.count(); i++ ) { QgsSymbol *sym = symbols[i]; for ( int j = 0; j < sym->symbolLayerCount(); j++ ) { int level = sym->symbolLayer( j )->renderingPass(); if ( level < 0 || level >= 1000 ) // ignore invalid levels continue; QgsSymbolLevelItem item( sym, j ); while ( level >= levels.count() ) // append new empty levels levels.append( QgsSymbolLevel() ); levels[level].append( item ); } } // 2. draw features in correct order for ( int l = 0; l < levels.count(); l++ ) { QgsSymbolLevel &level = levels[l]; for ( int i = 0; i < level.count(); i++ ) { QgsSymbolLevelItem &item = level[i]; if ( !features.contains( item.symbol() ) ) { QgsDebugMsg( "level item's symbol not found!" ); continue; } int layer = item.layer(); QList<QgsFeature> &lst = features[item.symbol()]; QList<QgsFeature>::iterator fit; for ( fit = lst.begin(); fit != lst.end(); ++fit ) { if ( mContext.renderingStopped() ) { stopRenderer( selRenderer ); return; } bool sel = mContext.showSelection() && mSelectedFeatureIds.contains( fit->id() ); // maybe vertex markers should be drawn only during the last pass... bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) ); mContext.expressionContext().setFeature( *fit ); try { mRenderer->renderFeature( *fit, mContext, layer, sel, drawMarker ); } catch ( const QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" ) .arg( fet.id() ).arg( cse.what() ) ); } } } } stopRenderer( selRenderer ); }
bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer, const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale, bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p ) { if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() ) { return false; } //create line field / id map for line layer QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer) QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet ); } //create output datasource or attributes in memory provider QgsVectorFileWriter* fileWriter = 0; QgsFeatureList memoryProviderFeatures; if ( !memoryProvider ) { QGis::WkbType memoryProviderType = QGis::WKBMultiLineString; if ( locationField2 == -1 ) { memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint; } else { memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString; } fileWriter = new QgsVectorFileWriter( outputLayer, eventLayer->dataProvider()->encoding(), eventLayer->pendingFields(), memoryProviderType, &( lineLayer->crs() ), outputFormat ); } else { memoryProvider->addAttributes( eventLayer->pendingFields().toList() ); } //iterate over eventLayer and write new features to output file or layer fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ); QgsGeometry* lrsGeom = 0; double measure1, measure2 = 0.0; int nEventFeatures = eventLayer->pendingFeatureCount(); int featureCounter = 0; int nOutputFeatures = 0; //number of output features for the current event feature if ( p ) { p->setWindowModality( Qt::WindowModal ); p->setMinimum( 0 ); p->setMaximum( nEventFeatures ); p->show(); } while ( fit.nextFeature( fet ) ) { nOutputFeatures = 0; //update progress dialog if ( p ) { if ( p->wasCanceled() ) { break; } p->setValue( featureCounter ); ++featureCounter; } measure1 = fet.attribute( locationField1 ).toDouble(); if ( locationField2 != -1 ) { measure2 = fet.attribute( locationField2 ).toDouble(); } QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() ); QList<QgsFeature>::const_iterator featureIdIt = featureIdList.constBegin(); for ( ; featureIdIt != featureIdList.constEnd(); ++featureIdIt ) { if ( locationField2 == -1 ) { lrsGeom = locateAlongMeasure( measure1, featureIdIt->geometry() ); } else { lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->geometry() ); } if ( lrsGeom ) { ++nOutputFeatures; addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry ); } } if ( nOutputFeatures < 1 ) { unlocatedFeatureIds.insert( fet.id() ); } } if ( p ) { p->setValue( nEventFeatures ); } if ( memoryProvider ) { memoryProvider->addFeatures( memoryProviderFeatures ); } delete fileWriter; 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 ); } } }
QVariantMap QgsDbscanClusteringAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); const std::size_t minSize = static_cast< std::size_t>( parameterAsInt( parameters, QStringLiteral( "MIN_SIZE" ), context ) ); const double eps = parameterAsDouble( parameters, QStringLiteral( "EPS" ), context ); const bool borderPointsAreNoise = parameterAsBoolean( parameters, QStringLiteral( "DBSCAN*" ), context ); QgsFields outputFields = source->fields(); const QString clusterFieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context ); QgsFields newFields; newFields.append( QgsField( clusterFieldName, QVariant::Int ) ); outputFields = QgsProcessingUtils::combineFields( outputFields, newFields ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, source->wkbType(), source->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // build spatial index feedback->pushInfo( QObject::tr( "Building spatial index" ) ); QgsSpatialIndexKDBush index( *source, feedback ); if ( feedback->isCanceled() ) return QVariantMap(); // dbscan! feedback->pushInfo( QObject::tr( "Analysing clusters" ) ); std::unordered_map< QgsFeatureId, int> idToCluster; idToCluster.reserve( index.size() ); QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest().setNoAttributes() ); const long featureCount = source->featureCount(); int clusterCount = 0; dbscan( minSize, eps, borderPointsAreNoise, featureCount, features, index, idToCluster, clusterCount, feedback ); // write clusters const double writeStep = featureCount > 0 ? 10.0 / featureCount : 1; features = source->getFeatures(); int i = 0; QgsFeature feat; while ( features.nextFeature( feat ) ) { i++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( 90 + i * writeStep ); QgsAttributes attr = feat.attributes(); auto cluster = idToCluster.find( feat.id() ); if ( cluster != idToCluster.end() ) { attr << cluster->second; } else { attr << QVariant(); } feat.setAttributes( attr ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); outputs.insert( QStringLiteral( "NUM_CLUSTERS" ), clusterCount ); return outputs; }
void QgsDbscanClusteringAlgorithm::dbscan( const std::size_t minSize, const double eps, const bool borderPointsAreNoise, const long featureCount, QgsFeatureIterator features, QgsSpatialIndexKDBush &index, std::unordered_map< QgsFeatureId, int> &idToCluster, int &clusterCount, QgsProcessingFeedback *feedback ) { const double step = featureCount > 0 ? 90.0 / featureCount : 1; std::unordered_set< QgsFeatureId > visited; visited.reserve( index.size() ); QgsFeature feat; int i = 0; clusterCount = 0; while ( features.nextFeature( feat ) ) { if ( feedback->isCanceled() ) { break; } if ( !feat.hasGeometry() ) { feedback->setProgress( ++i * step ); continue; } if ( visited.find( feat.id() ) != visited.end() ) { // already visited! continue; } QgsPointXY point; if ( QgsWkbTypes::flatType( feat.geometry().wkbType() ) == QgsWkbTypes::Point ) point = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( feat.geometry().constGet() ) ); else { // not a point geometry feedback->reportError( QObject::tr( "Feature %1 is a %2 feature, not a point." ).arg( feat.id() ).arg( QgsWkbTypes::displayString( feat.geometry().wkbType() ) ) ); feedback->setProgress( ++i * step ); continue; } std::unordered_set< QgsSpatialIndexKDBushData, KDBushDataHashById, KDBushDataEqualById> within; if ( minSize > 1 ) { index.within( point, eps, [ &within]( const QgsSpatialIndexKDBushData & data ) { within.insert( data ); } ); if ( within.size() < minSize ) continue; visited.insert( feat.id() ); } else { // optimised case for minSize == 1, we can skip the initial check within.insert( QgsSpatialIndexKDBushData( feat.id(), point.x(), point.y() ) ); } // start new cluster clusterCount++; idToCluster[ feat.id() ] = clusterCount; feedback->setProgress( ++i * step ); while ( !within.empty() ) { if ( feedback->isCanceled() ) { break; } QgsSpatialIndexKDBushData j = *within.begin(); within.erase( within.begin() ); if ( visited.find( j.id ) != visited.end() ) { // already visited! continue; } visited.insert( j.id ); feedback->setProgress( ++i * step ); // check from this point QgsPointXY point2 = j.point(); std::unordered_set< QgsSpatialIndexKDBushData, KDBushDataHashById, KDBushDataEqualById > within2; index.within( point2, eps, [&within2]( const QgsSpatialIndexKDBushData & data ) { within2.insert( data ); } ); if ( within2.size() >= minSize ) { // expand neighbourhood std::copy_if( within2.begin(), within2.end(), std::inserter( within, within.end() ), [&visited]( const QgsSpatialIndexKDBushData & needle ) { return visited.find( needle.id ) == visited.end(); } ); } if ( !borderPointsAreNoise || within2.size() >= minSize ) { idToCluster[ j.id ] = clusterCount; } } } }
void QgsAttributeTableDialog::doSearch( QString searchString ) { // parse search string and build parsed tree QgsExpression search( searchString ); if ( search.hasParserError() ) { QMessageBox::critical( this, tr( "Parsing error" ), search.parserErrorString() ); return; } if ( ! search.prepare( mLayer->pendingFields() ) ) { QMessageBox::critical( this, tr( "Evaluation error" ), search.evalErrorString() ); } // TODO: fetch only necessary columns //QStringList columns = search.referencedColumns(); bool fetchGeom = search.needsGeometry(); QApplication::setOverrideCursor( Qt::WaitCursor ); mSelectedFeatures.clear(); if ( cbxSearchSelectedOnly->isChecked() ) { QgsFeatureList selectedFeatures = mLayer->selectedFeatures(); for ( QgsFeatureList::Iterator it = selectedFeatures.begin(); it != selectedFeatures.end(); ++it ) { QgsFeature& feat = *it; if ( search.evaluate( &feat ).toInt() != 0 ) mSelectedFeatures << feat.id(); // check if there were errors during evaluating if ( search.hasEvalError() ) break; } } else { mLayer->select( mLayer->pendingAllAttributesList(), QgsRectangle(), fetchGeom ); QgsFeature f; while ( mLayer->nextFeature( f ) ) { if ( search.evaluate( &f ).toInt() != 0 ) mSelectedFeatures << f.id(); // check if there were errors during evaluating if ( search.hasEvalError() ) break; } } QApplication::restoreOverrideCursor(); if ( search.hasEvalError() ) { QMessageBox::critical( this, tr( "Error during search" ), search.evalErrorString() ); return; } mLayer->setSelectedFeatures( mSelectedFeatures ); QString str; QWidget *w = mDock ? qobject_cast<QWidget*>( mDock ) : qobject_cast<QWidget*>( this ); if ( mSelectedFeatures.size() ) { w->setWindowTitle( tr( "Attribute table - %1 (%n matching features)", "matching features", mSelectedFeatures.size() ).arg( mLayer->name() ) ); } else { w->setWindowTitle( tr( "Attribute table - %1 (No matching features)" ).arg( mLayer->name() ) ); } mView->setFocus(); }
void QgsDelimitedTextProvider::rescanFile() { mRescanRequired = false; resetIndexes(); bool buildSpatialIndex = mSpatialIndex != 0; bool buildSubsetIndex = mBuildSubsetIndex && ( mSubsetExpression || mGeomRep != GeomNone ); // In case file has been rewritten check that it is still valid mValid = mLayerValid && mFile->isValid(); if ( ! mValid ) return; // Open the file and get number of rows, etc. We assume that the // file has a header row and process accordingly. Caller should make // sure that the delimited file is properly formed. QStringList messages; if ( mGeomRep == GeomAsWkt ) { mWktFieldIndex = mFile->fieldIndex( mWktFieldName ); if ( mWktFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Wkt" ).arg( mWktFieldName ) ); } } else if ( mGeomRep == GeomAsXy ) { mXFieldIndex = mFile->fieldIndex( mXFieldName ); mYFieldIndex = mFile->fieldIndex( mYFieldName ); if ( mXFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "X" ).arg( mWktFieldName ) ); } if ( mYFieldIndex < 0 ) { messages.append( tr( "%0 field %1 is not defined in delimited text file" ).arg( "Y" ).arg( mWktFieldName ) ); } } if ( messages.size() > 0 ) { reportErrors( messages, false ); QgsDebugMsg( "Delimited text source invalid on rescan - missing geometry fields" ); mValid = false; return; } // Reset the field columns for ( int i = 0; i < attributeFields.size(); i++ ) { attributeColumns[i] = mFile->fieldIndex( attributeFields[i].name() ); } // Scan through the features in the file mSubsetIndex.clear(); mUseSubsetIndex = false; QgsFeatureIterator fi = getFeatures( QgsFeatureRequest() ); mNumberFeatures = 0; mExtent = QgsRectangle(); QgsFeature f; while ( fi.nextFeature( f ) ) { if ( mGeometryType != QGis::NoGeometry ) { if ( mNumberFeatures == 0 ) { mExtent = f.constGeometry()->boundingBox(); } else { QgsRectangle bbox( f.constGeometry()->boundingBox() ); mExtent.combineExtentWith( &bbox ); } if ( buildSpatialIndex ) mSpatialIndex->insertFeature( f ); } if ( buildSubsetIndex ) mSubsetIndex.append(( quintptr ) f.id() ); mNumberFeatures++; } if ( buildSubsetIndex ) { long recordCount = mFile->recordCount(); recordCount -= recordCount / SUBSET_ID_THRESHOLD_FACTOR; mUseSubsetIndex = recordCount < mSubsetIndex.size(); if ( ! mUseSubsetIndex ) mSubsetIndex.clear(); } mUseSpatialIndex = buildSpatialIndex; }
void QgsFieldCalculator::accept() { builder->saveToRecent( QStringLiteral( "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()->ellipsoid() ); QString calcString = builder->expressionText(); QgsExpression exp( calcString ); exp.setGeomCalculator( &myDa ); exp.setDistanceUnits( QgsProject::instance()->distanceUnits() ); exp.setAreaUnits( QgsProject::instance()->areaUnits() ); QgsExpressionContext expContext; expContext << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( mVectorLayer ); if ( !exp.prepare( &expContext ) ) { QMessageBox::critical( nullptr, tr( "Evaluation error" ), exp.evalErrorString() ); return; } bool updatingGeom = false; // 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( QStringLiteral( "Field calculator" ) ); //update existing field if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() ) { if ( mExistingFieldComboBox->currentData().toString() == QLatin1String( "geom" ) ) { //update geometry mAttributeId = -1; updatingGeom = true; } else { QMap<QString, int>::const_iterator fieldIt = mFieldMap.constFind( mExistingFieldComboBox->currentText() ); if ( fieldIt != mFieldMap.constEnd() ) { mAttributeId = fieldIt.value(); } } } else { //create new field const QgsField newField = fieldDefinition(); if ( !mVectorLayer->addAttribute( newField ) ) { QApplication::restoreOverrideCursor(); QMessageBox::critical( nullptr, 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.at( 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( nullptr, tr( "Evaluation error" ), exp.evalErrorString() ); return; } } if ( mAttributeId == -1 && !updatingGeom ) { mVectorLayer->destroyEditCommand(); QApplication::restoreOverrideCursor(); return; } //go through all the features and change the new attribute QgsFeature feature; bool calculationSuccess = true; QString error; bool useGeometry = exp.needsGeometry(); int rownum = 1; QgsField field = !updatingGeom ? mVectorLayer->fields().at( mAttributeId ) : QgsField(); bool newField = !mUpdateExistingGroupBox->isChecked(); QVariant emptyAttribute; if ( newField ) emptyAttribute = QVariant( field.type() ); QgsFeatureRequest req = QgsFeatureRequest().setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ); if ( mOnlyUpdateSelectedCheckBox->isChecked() ) { req.setFilterFids( mVectorLayer->selectedFeatureIds() ); } QgsFeatureIterator fit = mVectorLayer->getFeatures( req ); while ( fit.nextFeature( feature ) ) { expContext.setFeature( feature ); expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), rownum, true ) ); QVariant value = exp.evaluate( &expContext ); if ( exp.hasEvalError() ) { calculationSuccess = false; error = exp.evalErrorString(); break; } else if ( updatingGeom ) { if ( value.canConvert< QgsGeometry >() ) { QgsGeometry geom = value.value< QgsGeometry >(); mVectorLayer->changeGeometry( feature.id(), geom ); } } else { field.convertCompatible( value ); mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) ); } rownum++; } QApplication::restoreOverrideCursor(); if ( !calculationSuccess ) { QMessageBox::critical( nullptr, tr( "Error" ), tr( "An error occurred while evaluating the calculation string:\n%1" ).arg( error ) ); mVectorLayer->destroyEditCommand(); return; } mVectorLayer->endEditCommand(); } QDialog::accept(); }
QVariantMap QgsZonalHistogramAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > zones( parameterAsSource( parameters, QStringLiteral( "INPUT_VECTOR" ), context ) ); if ( !zones ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_VECTOR" ) ) ); long count = zones->featureCount(); double step = count > 0 ? 100.0 / count : 1; long current = 0; QList< double > uniqueValues; QMap< QgsFeatureId, QHash< double, qgssize > > featuresUniqueValues; // First loop through the zones to build up a list of unique values across all zones to determine sink fields list QgsFeatureRequest request; request.setNoAttributes(); if ( zones->sourceCrs() != mCrs ) { request.setDestinationCrs( mCrs, context.transformContext() ); } QgsFeatureIterator it = zones->getFeatures( request ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( feedback && feedback->isCanceled() ) { break; } feedback->setProgress( current * step ); if ( !f.hasGeometry() ) { current++; continue; } QgsGeometry featureGeometry = f.geometry(); QgsRectangle featureRect = featureGeometry.boundingBox().intersect( mRasterExtent ); if ( featureRect.isEmpty() ) { current++; continue; } int nCellsX, nCellsY; QgsRectangle rasterBlockExtent; QgsRasterAnalysisUtils::cellInfoForBBox( mRasterExtent, featureRect, mCellSizeX, mCellSizeY, nCellsX, nCellsY, mNbCellsXProvider, mNbCellsYProvider, rasterBlockExtent ); QHash< double, qgssize > fUniqueValues; QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY, rasterBlockExtent, [ &fUniqueValues]( double value ) { fUniqueValues[value]++; }, false ); if ( fUniqueValues.count() < 1 ) { // The cell resolution is probably larger than the polygon area. We switch to slower precise pixel - polygon intersection in this case // TODO: eventually deal with weight if needed QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( mRasterInterface.get(), mRasterBand, featureGeometry, nCellsX, nCellsY, mCellSizeX, mCellSizeY, rasterBlockExtent, [ &fUniqueValues]( double value, double ) { fUniqueValues[value]++; }, false ); } for ( auto it = fUniqueValues.constBegin(); it != fUniqueValues.constEnd(); ++it ) { if ( uniqueValues.indexOf( it.key() ) == -1 ) { uniqueValues << it.key(); } featuresUniqueValues[f.id()][it.key()] += it.value(); } current++; } std::sort( uniqueValues.begin(), uniqueValues.end() ); QString fieldPrefix = parameterAsString( parameters, QStringLiteral( "COLUMN_PREFIX" ), context ); QgsFields newFields; for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it ) { newFields.append( QgsField( QStringLiteral( "%1%2" ).arg( fieldPrefix, mHasNoDataValue && *it == mNodataValue ? QStringLiteral( "NODATA" ) : QString::number( *it ) ), QVariant::LongLong, QString(), -1, 0 ) ); } QgsFields fields = QgsProcessingUtils::combineFields( zones->fields(), newFields ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, zones->wkbType(), zones->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); it = zones->getFeatures( QgsFeatureRequest() ); while ( it.nextFeature( f ) ) { QgsAttributes attributes = f.attributes(); QHash< double, qgssize > fUniqueValues = featuresUniqueValues.value( f.id() ); for ( auto it = uniqueValues.constBegin(); it != uniqueValues.constEnd(); ++it ) { attributes += fUniqueValues.value( *it, 0 ); } QgsFeature outputFeature; outputFeature.setGeometry( f.geometry() ); outputFeature.setAttributes( attributes ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
void QgsFeatureRendererV2::renderFeatureWithSymbol( QgsFeature& feature, QgsSymbolV2* symbol, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) { QgsSymbolV2::SymbolType symbolType = symbol->type(); const QgsGeometry* geom = feature.constGeometry(); if ( !geom || !geom->geometry() ) { return; } const QgsGeometry* segmentizedGeometry = geom; bool deleteSegmentizedGeometry = false; context.setGeometry( geom->geometry() ); //convert curve types to normal point/line/polygon ones switch ( QgsWKBTypes::flatType( geom->geometry()->wkbType() ) ) { case QgsWKBTypes::CurvePolygon: case QgsWKBTypes::CircularString: case QgsWKBTypes::CompoundCurve: case QgsWKBTypes::MultiSurface: case QgsWKBTypes::MultiCurve: { QgsAbstractGeometryV2* g = geom->geometry()->segmentize(); if ( !g ) { return; } segmentizedGeometry = new QgsGeometry( g ); deleteSegmentizedGeometry = true; break; } default: break; } switch ( QgsWKBTypes::flatType( segmentizedGeometry->geometry()->wkbType() ) ) { case QgsWKBTypes::Point: { if ( symbolType != QgsSymbolV2::Marker ) { QgsDebugMsg( "point can be drawn only with marker symbol!" ); break; } QPointF pt; _getPoint( pt, context, segmentizedGeometry->asWkb() ); (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected ); if ( context.testFlag( QgsRenderContext::DrawSymbolBounds ) ) { //draw debugging rect context.painter()->setPen( Qt::red ); context.painter()->setBrush( QColor( 255, 0, 0, 100 ) ); context.painter()->drawRect((( QgsMarkerSymbolV2* )symbol )->bounds( pt, context ) ); } } break; case QgsWKBTypes::LineString: { if ( symbolType != QgsSymbolV2::Line ) { QgsDebugMsg( "linestring can be drawn only with line symbol!" ); break; } QPolygonF pts; _getLineString( pts, context, segmentizedGeometry->asWkb(), symbol->clipFeaturesToExtent() ); (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected ); } break; case QgsWKBTypes::Polygon: { if ( symbolType != QgsSymbolV2::Fill ) { QgsDebugMsg( "polygon can be drawn only with fill symbol!" ); break; } QPolygonF pts; QList<QPolygonF> holes; _getPolygon( pts, holes, context, segmentizedGeometry->asWkb(), symbol->clipFeaturesToExtent() ); (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected ); } break; case QgsWKBTypes::MultiPoint: { if ( symbolType != QgsSymbolV2::Marker ) { QgsDebugMsg( "multi-point can be drawn only with marker symbol!" ); break; } QgsConstWkbPtr wkbPtr( segmentizedGeometry->asWkb() + 1 + sizeof( int ) ); unsigned int num; wkbPtr >> num; const unsigned char* ptr = wkbPtr; QPointF pt; for ( unsigned int i = 0; i < num; ++i ) { ptr = QgsConstWkbPtr( _getPoint( pt, context, ptr ) ); (( QgsMarkerSymbolV2* )symbol )->renderPoint( pt, &feature, context, layer, selected ); } } break; case QgsWKBTypes::MultiCurve: case QgsWKBTypes::MultiLineString: { if ( symbolType != QgsSymbolV2::Line ) { QgsDebugMsg( "multi-linestring can be drawn only with line symbol!" ); break; } QgsConstWkbPtr wkbPtr( segmentizedGeometry->asWkb() + 1 + sizeof( int ) ); unsigned int num; wkbPtr >> num; const unsigned char* ptr = wkbPtr; QPolygonF pts; const QgsGeometryCollectionV2* geomCollection = dynamic_cast<const QgsGeometryCollectionV2*>( geom->geometry() ); for ( unsigned int i = 0; i < num; ++i ) { if ( geomCollection ) { context.setGeometry( geomCollection->geometryN( i ) ); } ptr = QgsConstWkbPtr( _getLineString( pts, context, ptr, symbol->clipFeaturesToExtent() ) ); (( QgsLineSymbolV2* )symbol )->renderPolyline( pts, &feature, context, layer, selected ); } } break; case QgsWKBTypes::MultiSurface: case QgsWKBTypes::MultiPolygon: { if ( symbolType != QgsSymbolV2::Fill ) { QgsDebugMsg( "multi-polygon can be drawn only with fill symbol!" ); break; } QgsConstWkbPtr wkbPtr( segmentizedGeometry->asWkb() + 1 + sizeof( int ) ); unsigned int num; wkbPtr >> num; const unsigned char* ptr = wkbPtr; QPolygonF pts; QList<QPolygonF> holes; const QgsGeometryCollectionV2* geomCollection = dynamic_cast<const QgsGeometryCollectionV2*>( geom->geometry() ); for ( unsigned int i = 0; i < num; ++i ) { if ( geomCollection ) { context.setGeometry( geomCollection->geometryN( i ) ); } ptr = _getPolygon( pts, holes, context, ptr, symbol->clipFeaturesToExtent() ); (( QgsFillSymbolV2* )symbol )->renderPolygon( pts, ( holes.count() ? &holes : NULL ), &feature, context, layer, selected ); } break; } default: QgsDebugMsg( QString( "feature %1: unsupported wkb type 0x%2 for rendering" ).arg( feature.id() ).arg( geom->wkbType(), 0, 16 ) ); } if ( drawVertexMarker ) { const QgsCoordinateTransform* ct = context.coordinateTransform(); const QgsMapToPixel& mtp = context.mapToPixel(); QgsPointV2 vertexPoint; QgsVertexId vertexId; double x, y, z; QPointF mapPoint; while ( geom->geometry()->nextVertex( vertexId, vertexPoint ) ) { //transform x = vertexPoint.x(); y = vertexPoint.y(); z = vertexPoint.z(); if ( ct ) { ct->transformInPlace( x, y, z ); } mapPoint.setX( x ); mapPoint.setY( y ); mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() ); renderVertexMarker( mapPoint, context ); } } if ( deleteSegmentizedGeometry ) { delete segmentizedGeometry; } }
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( QString( "UID" ), QVariant::String ) ); fields.append( QgsField( QString( "AREA" ), QVariant::Double ) ); fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) ); QGis::WkbType outputType = QGis::WKBPolygon; const QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs ); QgsFeature currentFeature; QgsGeometry* dissolveGeometry = 0; //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 ) { 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[ 0 ]; attributes[2] = values[ 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; // QgsGeometry* tmpGeometry = 0; if ( !dissolveGeometry ) { 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 QgsMapToolRotateFeature::canvasReleaseEvent( QgsMapMouseEvent *e ) { deleteRotationWidget(); if ( !mCanvas ) { return; } QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { deleteRubberband(); notifyNotVectorLayer(); return; } if ( e->button() == Qt::RightButton ) { deleteRubberband(); mRotationActive = false; return; } // place anchor point on CTRL + click if ( e->modifiers() & Qt::ControlModifier ) { if ( !mAnchorPoint ) { return; } mAnchorPoint->setCenter( toMapCoordinates( e->pos() ) ); mStartPointMapCoords = toMapCoordinates( e->pos() ); mStPoint = e->pos(); return; } // Initialize rotation if not yet active if ( !mRotationActive ) { mRotation = 0; mRotationOffset = 0; deleteRubberband(); mInitialPos = e->pos(); if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } QgsPointXY layerCoords = toLayerCoordinates( vlayer, e->pos() ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) ); //find the closest feature QgsGeometry pointGeometry = QgsGeometry::fromPoint( layerCoords ); if ( pointGeometry.isNull() ) { return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.hasGeometry() ) { double currentDistance = pointGeometry.distance( f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } if ( minDistance == std::numeric_limits<double>::max() ) { emit messageEmitted( tr( "Could not find a nearby feature in the current layer." ) ); return; } QgsRectangle bound = cf.geometry().boundingBox(); mStartPointMapCoords = toMapCoordinates( vlayer, bound.center() ); if ( !mAnchorPoint ) { mAnchorPoint = new QgsVertexMarker( mCanvas ); } mAnchorPoint->setIconType( QgsVertexMarker::ICON_CROSS ); mAnchorPoint->setCenter( mStartPointMapCoords ); mStPoint = toCanvasCoordinates( mStartPointMapCoords ); mRotatedFeatures.clear(); mRotatedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mRotatedFeatures = vlayer->selectedFeatureIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); QgsFeature feat; QgsFeatureIterator it = vlayer->getSelectedFeatures(); while ( it.nextFeature( feat ) ) { mRubberBand->addGeometry( feat.geometry(), vlayer ); } } mRubberBand->setColor( QColor( 255, 0, 0, 65 ) ); mRubberBand->setWidth( 2 ); mRubberBand->show(); double XDistance = mInitialPos.x() - mAnchorPoint->x(); double YDistance = mInitialPos.y() - mAnchorPoint->y(); mRotationOffset = std::atan2( YDistance, XDistance ) * ( 180 / M_PI ); createRotationWidget(); if ( e->modifiers() & Qt::ShiftModifier ) { if ( mRotationWidget ) { mRotationWidget->setMagnet( 45 ); } } mRotationActive = true; return; } applyRotation( mRotation ); }
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; } QGis::WkbType outputType = dp->geometryType(); const QgsCoordinateReferenceSystem crs = layer->crs(); QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->pendingFields(), 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 = 0; //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; } dissolveFeature( currentFeature, processedFeatures, &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; } dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry ); ++processedFeatures; ++jt; } } outputFeature.setGeometry( dissolveGeometry ); vWriter.addFeature( outputFeature ); } return true; }
bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f ) { f.setValid( false ); if ( mClosed ) return false; if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) { if ( mFetchedFid ) return false; bool res = nextFeatureFid( f ); mFetchedFid = true; return res; } if ( mRequest.filterType() == QgsFeatureRequest::FilterRect ) { if ( fetchNextChangedGeomFeature( f ) ) return true; // no more changed geometries } if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { if ( fetchNextChangedAttributeFeature( f ) ) return true; // no more changed features } while ( fetchNextAddedFeature( f ) ) { return true; } // no more added features if ( mProviderIterator.isClosed() ) { mChangedFeaturesIterator.close(); mProviderIterator = L->dataProvider()->getFeatures( mProviderRequest ); } while ( mProviderIterator.nextFeature( f ) ) { if ( mFetchConsidered.contains( f.id() ) ) continue; // TODO[MD]: just one resize of attributes f.setFields( &L->mUpdatedFields ); // update attributes updateChangedAttributes( f ); if ( !mFetchJoinInfo.isEmpty() ) addJoinedAttributes( f ); // update geometry // TODO[MK]: FilterRect check after updating the geometry if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) ) updateFeatureGeometry( f ); return true; } // no more provider features close(); return false; }
void QgsMapToolMoveFeature::canvasPressEvent( QMouseEvent * e ) { delete mRubberBand; mRubberBand = 0; QgsVectorLayer* vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //find first geometry under mouse cursor and store iterator to it QgsPoint layerCoords = toLayerCoordinates( vlayer, e->pos() ); QSettings settings; double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapRenderer() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); if ( vlayer->selectedFeatureCount() == 0 ) { QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ).setSubsetOfAttributes( QgsAttributeList() ) ); //find the closest feature QgsGeometry* pointGeometry = QgsGeometry::fromPoint( layerCoords ); if ( !pointGeometry ) { return; } double minDistance = std::numeric_limits<double>::max(); QgsFeature cf; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( f.geometry() ) { double currentDistance = pointGeometry->distance( *f.geometry() ); if ( currentDistance < minDistance ) { minDistance = currentDistance; cf = f; } } } delete pointGeometry; if ( minDistance == std::numeric_limits<double>::max() ) { return; } mMovedFeatures.clear(); mMovedFeatures << cf.id(); //todo: take the closest feature, not the first one... mRubberBand = createRubberBand( vlayer->geometryType() ); mRubberBand->setToGeometry( cf.geometry(), vlayer ); } else { mMovedFeatures = vlayer->selectedFeaturesIds(); mRubberBand = createRubberBand( vlayer->geometryType() ); for ( int i = 0; i < vlayer->selectedFeatureCount(); i++ ) { mRubberBand->addGeometry( vlayer->selectedFeatures()[i].geometry(), vlayer ); } } mStartPointMapCoords = toMapCoordinates( e->pos() ); mRubberBand->setColor( QColor( 255, 0, 0, 65 ) ); mRubberBand->setWidth( 2 ); mRubberBand->show(); }
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 ); if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { 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->pendingFields(); for ( int idx = 0; idx < fields.count(); ++idx ) { if ( fields[idx].name() == mOutputFieldNameLineEdit->text() ) { mAttributeId = idx; break; } } if ( ! exp.prepare( mVectorLayer->pendingFields() ) ) { 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; const QgsField& field = mVectorLayer->pendingFields()[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; } } exp.setCurrentRowNumber( rownum ); QVariant value = exp.evaluate( &feature ); 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(); }
int QgsAtlasComposition::updateFeatures() { //needs to be called when layer, filter, sort changes if ( !mCoverageLayer ) { return 0; } updateFilenameExpression(); // select all features with all attributes QgsFeatureIterator fit = mCoverageLayer->getFeatures(); std::auto_ptr<QgsExpression> filterExpression; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) ); if ( filterExpression->hasParserError() ) { throw std::runtime_error( tr( "Feature filter parser error: %1" ).arg( filterExpression->parserErrorString() ).toLocal8Bit().data() ); } } // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process // We thus store the feature ids for future extraction QgsFeature feat; mFeatureIds.clear(); mFeatureKeys.clear(); while ( fit.nextFeature( feat ) ) { if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { QVariant result = filterExpression->evaluate( &feat, mCoverageLayer->pendingFields() ); if ( filterExpression->hasEvalError() ) { throw std::runtime_error( tr( "Feature filter eval error: %1" ).arg( filterExpression->evalErrorString() ).toLocal8Bit().data() ); } // skip this feature if the filter evaluation if false if ( !result.toBool() ) { continue; } } mFeatureIds.push_back( feat.id() ); if ( mSortFeatures ) { mFeatureKeys.insert( feat.id(), feat.attributes()[ mSortKeyAttributeIdx ] ); } } // sort features, if asked for if ( mSortFeatures ) { FieldSorter sorter( mFeatureKeys, mSortAscending ); qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter ); } QgsExpression::setSpecialColumn( "$numfeatures", QVariant(( int )mFeatureIds.size() ) ); //jump to first feature if currently using an atlas preview //need to do this in case filtering/layer change has altered matching features if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas ) { firstFeature(); } return mFeatureIds.size(); }
void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context ) { QString labelText = f.attributeMap()[fieldIndex].toString(); double labelX, labelY; // will receive label size QFont labelFont = textFont; //data defined label size? QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size ); if ( it != dataDefinedProperties.constEnd() ) { //find out size QVariant size = f.attributeMap().value( *it ); if ( size.isValid() ) { double sizeDouble = size.toDouble(); if ( sizeDouble <= 0 ) { return; } labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) ); } QFontMetricsF labelFontMetrics( labelFont ); calculateLabelSize( &labelFontMetrics, labelText, labelX, labelY ); } else { calculateLabelSize( fontMetrics, labelText, labelX, labelY ); } QgsGeometry* geom = f.geometry(); if ( ct ) // reproject the geometry if necessary geom->transform( *ct ); GEOSGeometry* geos_geom = geom->asGeos(); if ( geos_geom == NULL ) return; // invalid geometry if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) ) { return; } //data defined position / alignment / rotation? bool dataDefinedPosition = false; bool dataDefinedRotation = false; double xPos = 0.0, yPos = 0.0, angle = 0.0; bool ddXPos, ddYPos; QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX ); if ( dPosXIt != dataDefinedProperties.constEnd() ) { QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY ); if ( dPosYIt != dataDefinedProperties.constEnd() ) { //data defined position. But field values could be NULL -> positions will be generated by PAL xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos ); yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos ); if ( ddXPos && ddYPos ) { dataDefinedPosition = true; //x/y shift in case of alignment double xdiff = 0; double ydiff = 0; //horizontal alignment QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali ); if ( haliIt != dataDefinedProperties.end() ) { QString haliString = f.attributeMap().value( *haliIt ).toString(); if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX / 2.0; } else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX; } } //vertical alignment QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali ); if ( valiIt != dataDefinedProperties.constEnd() ) { QString valiString = f.attributeMap().value( *valiIt ).toString(); if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 ) { if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY; } else { QFontMetrics labelFontMetrics( labelFont ); double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height(); if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; } else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; ydiff -= labelY * 0.5 * ( 1 - descentRatio ); } } } } //data defined rotation? QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation ); if ( rotIt != dataDefinedProperties.constEnd() ) { dataDefinedRotation = true; angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180; //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center double xd = xdiff * cos( angle ) - ydiff * sin( angle ); double yd = xdiff * sin( angle ) + ydiff * cos( angle ); xdiff = xd; ydiff = yd; } //project xPos and yPos from layer to map CRS double z = 0; if ( ct ) { ct->transformInPlace( xPos, yPos, z ); } yPos += ydiff; xPos += xdiff; } } } QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, GEOSGeom_clone( geos_geom ) ); // record the created geometry - it will be deleted at the end. geometries.append( lbl ); // register feature to the layer try { if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(), xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation ) ) return; } catch ( std::exception &e ) { Q_UNUSED( e ); QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( f.id() ) + QString::fromLatin1( e.what() ) ); return; } // TODO: only for placement which needs character info pal::Feature* feat = palLayer->getFeature( lbl->strId() ); feat->setLabelInfo( lbl->info( fontMetrics, xform, rasterCompressFactor ) ); // TODO: allow layer-wide feature dist in PAL...? //data defined label-feature distance? double distance = dist; QMap< DataDefinedProperties, int >::const_iterator dDistIt = dataDefinedProperties.find( QgsPalLayerSettings::LabelDistance ); if ( dDistIt != dataDefinedProperties.constEnd() ) { distance = f.attributeMap().value( *dDistIt ).toDouble(); } if ( distance != 0 ) { if ( distInMapUnits ) //convert distance from mm/map units to pixels { distance /= context.mapToPixel().mapUnitsPerPixel(); } else //mm { distance *= vectorScaleFactor; } feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance ); } //add parameters for data defined labeling to QgsPalGeometry QMap< DataDefinedProperties, int >::const_iterator dIt = dataDefinedProperties.constBegin(); for ( ; dIt != dataDefinedProperties.constEnd(); ++dIt ) { lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] ); } }
void QgsVectorLayerRenderer::drawRenderer( QgsFeatureIterator &fit ) { QgsExpressionContextScope *symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr, new QgsExpressionContextScope() ); mContext.expressionContext().appendScope( symbolScope ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { try { if ( mContext.renderingStopped() ) { QgsDebugMsg( QString( "Drawing of vector layer %1 canceled." ).arg( layerId() ) ); break; } if ( !fet.hasGeometry() ) continue; // skip features without geometry mContext.expressionContext().setFeature( fet ); bool sel = mContext.showSelection() && mSelectedFeatureIds.contains( fet.id() ); bool drawMarker = ( mDrawVertexMarkers && mContext.drawEditingInformation() && ( !mVertexMarkerOnlyForSelection || sel ) ); // render feature bool rendered = mRenderer->renderFeature( fet, mContext, -1, sel, drawMarker ); // labeling - register feature if ( rendered ) { // new labeling engine if ( mContext.labelingEngine() && ( mLabelProvider || mDiagramProvider ) ) { QgsGeometry obstacleGeometry; QgsSymbolList symbols = mRenderer->originalSymbolsForFeature( fet, mContext ); if ( !symbols.isEmpty() && fet.geometry().type() == QgsWkbTypes::PointGeometry ) { obstacleGeometry = QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, symbols ); } if ( !symbols.isEmpty() ) { QgsExpressionContextUtils::updateSymbolScope( symbols.at( 0 ), symbolScope ); } if ( mLabelProvider ) { mLabelProvider->registerFeature( fet, mContext, obstacleGeometry ); } if ( mDiagramProvider ) { mDiagramProvider->registerFeature( fet, mContext, obstacleGeometry ); } } } } catch ( const QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" ) .arg( fet.id() ).arg( cse.what() ) ); } } delete mContext.expressionContext().popScope(); stopRenderer( nullptr ); }
void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context ) { //get diagram layer settings, diagram renderer QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layer ); if ( layerIt == mActiveDiagramLayers.constEnd() ) { return; } //convert geom to geos QgsGeometry* geom = feat.geometry(); if ( layerIt.value().ct && !willUseLayer( layer ) ) // reproject the geometry if feature not already transformed for labeling { geom->transform( *( layerIt.value().ct ) ); } GEOSGeometry* geos_geom = geom->asGeos(); if ( geos_geom == 0 ) { return; // invalid geometry } //create PALGeometry with diagram = true QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone( geos_geom ) ); lbl->setIsDiagram( true ); // record the created geometry - it will be deleted at the end. layerIt.value().geometries.append( lbl ); double diagramWidth = 0; double diagramHeight = 0; QgsDiagramRendererV2* dr = layerIt.value().renderer; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat.attributeMap(), context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } //append the diagram attributes to lbl QList<int> diagramAttrib = dr->diagramAttributes(); QList<int>::const_iterator diagAttIt = diagramAttrib.constBegin(); for ( ; diagAttIt != diagramAttrib.constEnd(); ++diagAttIt ) { lbl->addDiagramAttribute( *diagAttIt, feat.attributeMap()[*diagAttIt] ); } } // register feature to the layer int ddColX = layerIt.value().xPosColumn; int ddColY = layerIt.value().yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos ) { bool posXOk, posYOk; //data defined diagram position is always centered ddPosX = feat.attributeMap()[ddColX].toDouble( &posXOk ) - diagramWidth / 2.0; ddPosY = feat.attributeMap()[ddColY].toDouble( &posYOk ) - diagramHeight / 2.0; if ( !posXOk || !posYOk ) { ddPos = false; } else { const QgsCoordinateTransform* ct = layerIt.value().ct; if ( ct ) { double z = 0; ct->transformInPlace( ddPosX, ddPosY, z ); } } } try { if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) ) { return; } } catch ( std::exception &e ) { Q_UNUSED( e ); QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( feat.id() ) + QString::fromLatin1( e.what() ) ); return; } pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() ); QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 ); QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 ); palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist ); }
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->mapRenderer()->hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapRenderer()->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__ ) ); QMessageBox::warning( canvas, QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system." ) ); 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" ) ); vlayer->select( QgsAttributeList(), selectGeomTrans.boundingBox(), true, true ); QgsFeatureIds newSelectedFeatures; QgsFeature f; int closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( vlayer->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() ) ); QgsFeatureIds layerSelectedFeatures; if ( doDifference ) { layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { layerSelectedFeatures.remove( *i ); } else { layerSelectedFeatures.insert( *i ); } } } else { layerSelectedFeatures = newSelectedFeatures; } vlayer->setSelectedFeatures( layerSelectedFeatures ); QApplication::restoreOverrideCursor(); }
void QgsVectorLayerEditBuffer::updateFeatureGeometry( QgsFeature &f ) { if ( mChangedGeometries.contains( f.id() ) ) f.setGeometry( mChangedGeometries[f.id()] ); }
QgsVectorLayerImport::ImportError QgsVectorLayerImport::importLayer( QgsVectorLayer* layer, const QString& uri, const QString& providerKey, const QgsCoordinateReferenceSystem *destCRS, bool onlySelected, QString *errorMessage, bool skipAttributeCreation, QMap<QString, QVariant> *options, QProgressDialog *progress ) { const QgsCoordinateReferenceSystem* outputCRS; QgsCoordinateTransform* ct = nullptr; bool shallTransform = false; if ( !layer ) return ErrInvalidLayer; if ( destCRS && destCRS->isValid() ) { // This means we should transform outputCRS = destCRS; shallTransform = true; } else { // This means we shouldn't transform, use source CRS as output (if defined) outputCRS = &layer->crs(); } bool overwrite = false; bool forceSinglePartGeom = false; if ( options ) { overwrite = options->take( "overwrite" ).toBool(); forceSinglePartGeom = options->take( "forceSinglePartGeometryType" ).toBool(); } QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields(); QGis::WkbType wkbType = layer->wkbType(); // Special handling for Shapefiles if ( layer->providerType() == "ogr" && layer->storageType() == "ESRI Shapefile" ) { // convert field names to lowercase for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx ) { fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() ); } if ( !forceSinglePartGeom ) { // convert wkbtype to multipart (see #5547) switch ( wkbType ) { case QGis::WKBPoint: wkbType = QGis::WKBMultiPoint; break; case QGis::WKBLineString: wkbType = QGis::WKBMultiLineString; break; case QGis::WKBPolygon: wkbType = QGis::WKBMultiPolygon; break; case QGis::WKBPoint25D: wkbType = QGis::WKBMultiPoint25D; break; case QGis::WKBLineString25D: wkbType = QGis::WKBMultiLineString25D; break; case QGis::WKBPolygon25D: wkbType = QGis::WKBMultiPolygon25D; break; default: break; } } } QgsVectorLayerImport * writer = new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress ); // check whether file creation was successful ImportError err = writer->hasError(); if ( err != NoError ) { if ( errorMessage ) *errorMessage = writer->errorMessage(); delete writer; return err; } if ( errorMessage ) { errorMessage->clear(); } QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->attributeList(); QgsFeature fet; QgsFeatureRequest req; if ( wkbType == QGis::WKBNoGeometry ) req.setFlags( QgsFeatureRequest::NoGeometry ); if ( skipAttributeCreation ) req.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = layer->getFeatures( req ); const QgsFeatureIds& ids = layer->selectedFeaturesIds(); // Create our transform if ( destCRS ) ct = new QgsCoordinateTransform( layer->crs(), *destCRS ); // Check for failure if ( !ct ) shallTransform = false; int n = 0; if ( errorMessage ) { *errorMessage = QObject::tr( "Feature write errors:" ); } if ( progress ) { progress->setRange( 0, layer->featureCount() ); } bool cancelled = false; // write all features while ( fit.nextFeature( fet ) ) { if ( progress && progress->wasCanceled() ) { cancelled = true; if ( errorMessage ) { *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() ); } break; } if ( writer->errorCount() > 1000 ) { if ( errorMessage ) { *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() ); } break; } if ( onlySelected && !ids.contains( fet.id() ) ) continue; if ( shallTransform ) { try { if ( fet.constGeometry() ) { fet.geometry()->transform( *ct ); } } catch ( QgsCsException &e ) { delete ct; delete writer; QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" ) .arg( fet.id() ).arg( e.what() ); QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) ); if ( errorMessage ) *errorMessage += '\n' + msg; return ErrProjection; } } if ( skipAttributeCreation ) { fet.initAttributes( 0 ); } if ( !writer->addFeature( fet ) ) { if ( writer->hasError() && errorMessage ) { *errorMessage += '\n' + writer->errorMessage(); } } n++; if ( progress ) { progress->setValue( n ); } } // flush the buffer to be sure that all features are written if ( !writer->flushBuffer() ) { if ( writer->hasError() && errorMessage ) { *errorMessage += '\n' + writer->errorMessage(); } } int errors = writer->errorCount(); if ( !writer->createSpatialIndex() ) { if ( writer->hasError() && errorMessage ) { *errorMessage += '\n' + writer->errorMessage(); } } delete writer; if ( shallTransform ) { delete ct; } if ( errorMessage ) { if ( errors > 0 ) { *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n ); } else { errorMessage->clear(); } } if ( cancelled ) return ErrUserCancelled; else if ( errors > 0 ) return ErrFeatureWriteFailed; return NoError; }
int QgsAtlasComposition::updateFeatures() { //needs to be called when layer, filter, sort changes if ( !mCoverageLayer ) { return 0; } updateFilenameExpression(); // select all features with all attributes QgsFeatureRequest req; QScopedPointer<QgsExpression> filterExpression; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression.reset( new QgsExpression( mFeatureFilter ) ); if ( filterExpression->hasParserError() ) { mFilterParserError = filterExpression->parserErrorString(); return 0; } //filter good to go req.setFilterExpression( mFeatureFilter ); } mFilterParserError = QString(); QgsFeatureIterator fit = mCoverageLayer->getFeatures( req ); QScopedPointer<QgsExpression> nameExpression; if ( !mPageNameExpression.isEmpty() ) { nameExpression.reset( new QgsExpression( mPageNameExpression ) ); if ( nameExpression->hasParserError() ) { nameExpression.reset( 0 ); } nameExpression->prepare( mCoverageLayer->pendingFields() ); } // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process // We thus store the feature ids for future extraction QgsFeature feat; mFeatureIds.clear(); mFeatureKeys.clear(); int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName ); while ( fit.nextFeature( feat ) ) { QString pageName; if ( !nameExpression.isNull() ) { QVariant result = nameExpression->evaluate( &feat, mCoverageLayer->fields() ); if ( nameExpression->hasEvalError() ) { QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) ); } pageName = result.toString(); } mFeatureIds.push_back( qMakePair( feat.id(), pageName ) ); if ( mSortFeatures && sortIdx != -1 ) { mFeatureKeys.insert( feat.id(), feat.attributes()[ sortIdx ] ); } } // sort features, if asked for if ( mFeatureKeys.count() ) { FieldSorter sorter( mFeatureKeys, mSortAscending ); qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter ); } QgsExpression::setSpecialColumn( "$numfeatures", QVariant(( int )mFeatureIds.size() ) ); emit numberFeaturesChanged( mFeatureIds.size() ); //jump to first feature if currently using an atlas preview //need to do this in case filtering/layer change has altered matching features if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas ) { firstFeature(); } return mFeatureIds.size(); }
QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const { if ( mInjectNull && index.row() == 0 ) { if ( role == Qt::DisplayRole ) { return QSettings().value( "qgis/nullValue", "NULL" ).toString(); } else if ( role == QgsAttributeTableModel::FeatureIdRole ) { return QVariant( QVariant::Int ); } else { return QVariant( QVariant::Invalid ); } } if ( role == Qt::DisplayRole || role == Qt::EditRole ) { QgsFeature feat; mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat ); QgsExpressionContext context; context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( mFilterModel->layer() ); context.setFeature( feat ); return mExpression->evaluate( &context ); } if ( role == FeatureInfoRole ) { FeatureInfo featInfo; QgsFeature feat; mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat ); QgsVectorLayerEditBuffer* editBuffer = mFilterModel->layer()->editBuffer(); if ( editBuffer ) { const QList<QgsFeatureId> addedFeatures = editBuffer->addedFeatures().keys(); const QList<QgsFeatureId> changedFeatures = editBuffer->changedAttributeValues().keys(); if ( addedFeatures.contains( feat.id() ) ) { featInfo.isNew = true; } if ( changedFeatures.contains( feat.id() ) ) { featInfo.isEdited = true; } } return QVariant::fromValue( featInfo ); } else if ( role == FeatureRole ) { QgsFeature feat; mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat ); return QVariant::fromValue( feat ); } else if ( role == Qt::TextAlignmentRole ) { return Qt::AlignLeft; } return sourceModel()->data( mapToSource( index ), role ); }
void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { //check if we operate on a vector layer //todo: move this to a function in parent class to avoid duplication QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { deleteTempRubberBand(); //find out bounding box of mCaptureList if ( size() < 1 ) { stopCapturing(); return; } QgsPoint firstPoint = points().at( 0 ); QgsRectangle bbox( firstPoint.x(), firstPoint.y(), firstPoint.x(), firstPoint.y() ); for ( int i = 1; i < size(); ++i ) { bbox.combineExtentWith( points().at( i ).x(), points().at( i ).y() ); } //query all the features that intersect bounding box of capture line QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bbox ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; int reshapeReturn; bool reshapeDone = false; vlayer->beginEditCommand( tr( "Reshape" ) ); while ( fit.nextFeature( f ) ) { //query geometry //call geometry->reshape(mCaptureList) //register changed geometry in vector layer QgsGeometry geom = f.geometry(); if ( !geom.isEmpty() ) { reshapeReturn = geom.reshapeGeometry( points() ); if ( reshapeReturn == 0 ) { //avoid intersections on polygon layers if ( vlayer->geometryType() == Qgis::Polygon ) { //ignore all current layer features as they should be reshaped too QMap<QgsVectorLayer*, QSet<QgsFeatureId> > ignoreFeatures; ignoreFeatures.insert( vlayer, vlayer->allFeatureIds() ); if ( geom.avoidIntersections( ignoreFeatures ) != 0 ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } if ( geom.isGeosEmpty() ) //intersection removal might have removed the whole geometry { emit messageEmitted( tr( "The feature cannot be reshaped because the resulting geometry is empty" ), QgsMessageBar::CRITICAL ); vlayer->destroyEditCommand(); stopCapturing(); return; } } vlayer->changeGeometry( f.id(), geom ); reshapeDone = true; } } } if ( reshapeDone ) { vlayer->endEditCommand(); } else { vlayer->destroyEditCommand(); } stopCapturing(); } }