bool QgsReclassifyByLayerAlgorithm::_prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * ) { std::unique_ptr< QgsFeatureSource >tableSource( parameterAsSource( parameters, QStringLiteral( "INPUT_TABLE" ), context ) ); if ( !tableSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_TABLE" ) ) ); QString fieldMin = parameterAsString( parameters, QStringLiteral( "MIN_FIELD" ), context ); mMinFieldIdx = tableSource->fields().lookupField( fieldMin ); if ( mMinFieldIdx < 0 ) throw QgsProcessingException( QObject::tr( "Invalid field specified for MIN_FIELD: %1" ).arg( fieldMin ) ); QString fieldMax = parameterAsString( parameters, QStringLiteral( "MAX_FIELD" ), context ); mMaxFieldIdx = tableSource->fields().lookupField( fieldMax ); if ( mMaxFieldIdx < 0 ) throw QgsProcessingException( QObject::tr( "Invalid field specified for MAX_FIELD: %1" ).arg( fieldMax ) ); QString fieldValue = parameterAsString( parameters, QStringLiteral( "VALUE_FIELD" ), context ); mValueFieldIdx = tableSource->fields().lookupField( fieldValue ); if ( mValueFieldIdx < 0 ) throw QgsProcessingException( QObject::tr( "Invalid field specified for VALUE_FIELD: %1" ).arg( fieldValue ) ); QgsFeatureRequest request; request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( QgsAttributeList() << mMinFieldIdx << mMaxFieldIdx << mValueFieldIdx ); mTableIterator = tableSource->getFeatures( request ); return true; }
bool QgsVectorLayerFeatureCounter::run() { QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems(); QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin(); for ( ; symbolIt != symbolList.constEnd(); ++symbolIt ) { mSymbolFeatureCountMap.insert( symbolIt->label(), 0 ); } // If there are no features to be counted, we can spare us the trouble if ( mFeatureCount > 0 ) { int featuresCounted = 0; // Renderer (rule based) may depend on context scale, with scale is ignored if 0 QgsRenderContext renderContext; renderContext.setRendererScale( 0 ); renderContext.setExpressionContext( mExpressionContext ); QgsFeatureRequest request; if ( !mRenderer->filterNeedsGeometry() ) request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() ); QgsFeatureIterator fit = mSource->getFeatures( request ); // TODO: replace QgsInterruptionChecker with QgsFeedback // fit.setInterruptionChecker( mFeedback ); mRenderer->startRender( renderContext, mSource->fields() ); double progress = 0; QgsFeature f; while ( fit.nextFeature( f ) ) { renderContext.expressionContext().setFeature( f ); QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext ); Q_FOREACH ( const QString &key, featureKeyList ) { mSymbolFeatureCountMap[key] += 1; } ++featuresCounted; double p = ( static_cast< double >( featuresCounted ) / mFeatureCount ) * 100; if ( p - progress > 1 ) { progress = p; setProgress( progress ); } if ( isCanceled() ) { mRenderer->stopRender( renderContext ); return false; } } mRenderer->stopRender( renderContext ); }
bool QgsVectorDataProvider::empty() const { QgsFeature f; QgsFeatureRequest request; request.setNoAttributes(); request.setFlags( QgsFeatureRequest::NoGeometry ); request.setLimit( 1 ); if ( getFeatures( request ).nextFeature( f ) ) return false; else return true; }
void QgsVectorLayerFeatureIterator::FetchJoinInfo::addJoinedAttributesDirect( QgsFeature& f, const QVariant& joinValue ) const { // no memory cache, query the joined values by setting substring QString subsetString = joinLayer->dataProvider()->subsetString(); // provider might already have a subset string QString bkSubsetString = subsetString; if ( !subsetString.isEmpty() ) { subsetString.append( " AND " ); } QString joinFieldName; if ( joinInfo->joinFieldName.isEmpty() && joinInfo->joinFieldIndex >= 0 && joinInfo->joinFieldIndex < joinLayer->pendingFields().count() ) joinFieldName = joinLayer->pendingFields().field( joinInfo->joinFieldIndex ).name(); // for compatibility with 1.x else joinFieldName = joinInfo->joinFieldName; subsetString.append( "\"" + joinFieldName + "\"" + " = " + "\"" + joinValue.toString() + "\"" ); joinLayer->dataProvider()->setSubsetString( subsetString, false ); // select (no geometry) QgsFeatureRequest request; request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( attributes ); QgsFeatureIterator fi = joinLayer->getFeatures( request ); // get first feature QgsFeature fet; if ( fi.nextFeature( fet ) ) { int index = indexOffset; const QgsAttributes& attr = fet.attributes(); for ( int i = 0; i < attr.count(); ++i ) { if ( i == joinField ) continue; f.setAttribute( index++, attr[i] ); } } else { // no suitable join feature found, keeping empty (null) attributes } joinLayer->dataProvider()->setSubsetString( bkSubsetString, false ); }
QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const { if ( fieldIndex < 0 || fieldIndex >= fields().count() ) return QSet<QVariant>(); QgsFeatureRequest req; req.setFlags( QgsFeatureRequest::NoGeometry ); req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex ); QSet<QVariant> values; QgsFeatureIterator it = getFeatures( req ); QgsFeature f; while ( it.nextFeature( f ) ) { values.insert( f.attribute( fieldIndex ) ); if ( limit > 0 && values.size() >= limit ) return values; } return values; }
QVariant QgsFeatureSource::maximumValue( int fieldIndex ) const { if ( fieldIndex < 0 || fieldIndex >= fields().count() ) return QVariant(); QgsFeatureRequest req; req.setFlags( QgsFeatureRequest::NoGeometry ); req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex ); QVariant max; QgsFeatureIterator it = getFeatures( req ); QgsFeature f; while ( it.nextFeature( f ) ) { QVariant v = f.attribute( fieldIndex ); if ( !v.isNull() && ( qgsVariantGreaterThan( v, max ) || max.isNull() ) ) { max = v; } } return max; }
QVariant QgsFeatureSource::minimumValue( int fieldIndex ) const { if ( fieldIndex < 0 || fieldIndex >= fields().count() ) return QVariant(); QgsFeatureRequest req; req.setFlags( QgsFeatureRequest::NoGeometry ); req.setSubsetOfAttributes( QgsAttributeList() << fieldIndex ); QVariant min; QgsFeatureIterator it = getFeatures( req ); QgsFeature f; while ( it.nextFeature( f ) ) { QVariant v = f.attribute( fieldIndex ); if ( v.isValid() && qgsVariantLessThan( v, min ) ) { min = v; } } return min; }
void QgsVectorLayerUndoCommandChangeAttribute::undo() { QVariant original = mOldValue; if ( FID_IS_NEW( mFid ) ) { // added feature QgsFeatureMap::iterator it = mBuffer->mAddedFeatures.find( mFid ); Q_ASSERT( it != mBuffer->mAddedFeatures.end() ); it.value().setAttribute( mFieldIndex, mOldValue ); } else if ( mFirstChange ) { // existing feature mBuffer->mChangedAttributeValues[mFid].remove( mFieldIndex ); if ( mBuffer->mChangedAttributeValues[mFid].isEmpty() ) mBuffer->mChangedAttributeValues.remove( mFid ); if ( !mOldValue.isValid() ) { // get old value from provider QgsFeature tmp; QgsFeatureRequest request; request.setFilterFid( mFid ); request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( QgsAttributeList() << mFieldIndex ); QgsFeatureIterator fi = layer()->getFeatures( request ); if ( fi.nextFeature( tmp ) ) original = tmp.attribute( mFieldIndex ); } } else { mBuffer->mChangedAttributeValues[mFid][mFieldIndex] = mOldValue; } emit mBuffer->attributeValueChanged( mFid, mFieldIndex, original ); }
QgsFeatureList QgsQuickIdentifyKit::identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const { QgsFeatureList results; if ( !layer || !layer->isSpatial() ) return results; if ( !layer->isInScaleRange( mMapSettings->mapSettings().scale() ) ) return results; QgsFeatureList featureList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = searchRadiusMU(); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureRequest req; req.setFilterRect( r ); req.setLimit( mFeaturesLimit ); req.setFlags( QgsFeatureRequest::ExactIntersect ); QgsFeatureIterator fit = layer->getFeatures( req ); QgsFeature f; while ( fit.nextFeature( f ) ) featureList << QgsFeature( f ); } catch ( QgsCsException &cse ) { QgsDebugMsg( QStringLiteral( "Invalid point, proceed without a found features." ) ); Q_UNUSED( cse ); } bool filter = false; QgsRenderContext context( QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ) ); context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); QgsFeatureRenderer *renderer = layer->renderer(); if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) { // setup scale for scale dependent visibility (rule based) renderer->startRender( context, layer->fields() ); filter = renderer->capabilities() & QgsFeatureRenderer::Filter; } for ( const QgsFeature &feature : featureList ) { context.expressionContext().setFeature( feature ); if ( filter && !renderer->willRenderFeature( const_cast<QgsFeature &>( feature ), context ) ) continue; results.append( feature ); } if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) { renderer->stopRender( context ); } return results; }
QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *layer, QgsAttributeTableFilterModel::FilterMode initialMode, QWidget *parent, Qt::WindowFlags flags ) : QDialog( parent, flags ) , mLayer( layer ) { setObjectName( QStringLiteral( "QgsAttributeTableDialog/" ) + layer->id() ); setupUi( this ); connect( mActionCutSelectedRows, &QAction::triggered, this, &QgsAttributeTableDialog::mActionCutSelectedRows_triggered ); connect( mActionCopySelectedRows, &QAction::triggered, this, &QgsAttributeTableDialog::mActionCopySelectedRows_triggered ); connect( mActionPasteFeatures, &QAction::triggered, this, &QgsAttributeTableDialog::mActionPasteFeatures_triggered ); connect( mActionToggleEditing, &QAction::toggled, this, &QgsAttributeTableDialog::mActionToggleEditing_toggled ); connect( mActionSaveEdits, &QAction::triggered, this, &QgsAttributeTableDialog::mActionSaveEdits_triggered ); connect( mActionReload, &QAction::triggered, this, &QgsAttributeTableDialog::mActionReload_triggered ); connect( mActionInvertSelection, &QAction::triggered, this, &QgsAttributeTableDialog::mActionInvertSelection_triggered ); connect( mActionRemoveSelection, &QAction::triggered, this, &QgsAttributeTableDialog::mActionRemoveSelection_triggered ); connect( mActionSelectAll, &QAction::triggered, this, &QgsAttributeTableDialog::mActionSelectAll_triggered ); connect( mActionZoomMapToSelectedRows, &QAction::triggered, this, &QgsAttributeTableDialog::mActionZoomMapToSelectedRows_triggered ); connect( mActionPanMapToSelectedRows, &QAction::triggered, this, &QgsAttributeTableDialog::mActionPanMapToSelectedRows_triggered ); connect( mActionSelectedToTop, &QAction::toggled, this, &QgsAttributeTableDialog::mActionSelectedToTop_toggled ); connect( mActionAddAttribute, &QAction::triggered, this, &QgsAttributeTableDialog::mActionAddAttribute_triggered ); connect( mActionRemoveAttribute, &QAction::triggered, this, &QgsAttributeTableDialog::mActionRemoveAttribute_triggered ); connect( mActionOpenFieldCalculator, &QAction::triggered, this, &QgsAttributeTableDialog::mActionOpenFieldCalculator_triggered ); connect( mActionDeleteSelected, &QAction::triggered, this, &QgsAttributeTableDialog::mActionDeleteSelected_triggered ); connect( mMainView, &QgsDualView::currentChanged, this, &QgsAttributeTableDialog::mMainView_currentChanged ); connect( mActionAddFeature, &QAction::triggered, this, &QgsAttributeTableDialog::mActionAddFeature_triggered ); connect( mActionExpressionSelect, &QAction::triggered, this, &QgsAttributeTableDialog::mActionExpressionSelect_triggered ); connect( mMainView, &QgsDualView::showContextMenuExternally, this, &QgsAttributeTableDialog::showContextMenu ); const QgsFields fields = mLayer->fields(); for ( const QgsField &field : fields ) { mVisibleFields.append( field.name() ); } // Fix selection color on losing focus (Windows) setStyleSheet( QgisApp::instance()->styleSheet() ); setAttribute( Qt::WA_DeleteOnClose ); layout()->setMargin( 0 ); layout()->setContentsMargins( 0, 0, 0, 0 ); static_cast< QGridLayout * >( layout() )->setVerticalSpacing( 0 ); QgsSettings settings; int size = settings.value( QStringLiteral( "/qgis/iconSize" ), 16 ).toInt(); if ( size > 32 ) { size -= 16; } else if ( size == 32 ) { size = 24; } else { size = 16; } mToolbar->setIconSize( QSize( size, size ) ); // Initialize the window geometry restoreGeometry( settings.value( QStringLiteral( "Windows/BetterAttributeTable/geometry" ) ).toByteArray() ); myDa = new QgsDistanceArea(); myDa->setSourceCrs( mLayer->crs(), QgsProject::instance()->transformContext() ); myDa->setEllipsoid( QgsProject::instance()->ellipsoid() ); mEditorContext.setDistanceArea( *myDa ); mEditorContext.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() ); mEditorContext.setMapCanvas( QgisApp::instance()->mapCanvas() ); QgsFeatureRequest r; bool needsGeom = false; if ( mLayer->geometryType() != QgsWkbTypes::NullGeometry && initialMode == QgsAttributeTableFilterModel::ShowVisible ) { QgsMapCanvas *mc = QgisApp::instance()->mapCanvas(); QgsRectangle extent( mc->mapSettings().mapToLayerCoordinates( layer, mc->extent() ) ); r.setFilterRect( extent ); needsGeom = true; } else if ( initialMode == QgsAttributeTableFilterModel::ShowSelected ) { r.setFilterFids( layer->selectedFeatureIds() ); } if ( !needsGeom ) r.setFlags( QgsFeatureRequest::NoGeometry ); // Initialize dual view mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), r, mEditorContext, false ); QgsAttributeTableConfig config = mLayer->attributeTableConfig(); mMainView->setAttributeTableConfig( config ); // Initialize filter gui elements mFilterActionMapper = new QSignalMapper( this ); mFilterColumnsMenu = new QMenu( this ); mActionFilterColumnsMenu->setMenu( mFilterColumnsMenu ); mApplyFilterButton->setDefaultAction( mActionApplyFilter ); // Set filter icon in a couple of places QIcon filterIcon = QgsApplication::getThemeIcon( "/mActionFilter2.svg" ); mActionShowAllFilter->setIcon( filterIcon ); mActionAdvancedFilter->setIcon( filterIcon ); mActionSelectedFilter->setIcon( filterIcon ); mActionVisibleFilter->setIcon( filterIcon ); mActionEditedFilter->setIcon( filterIcon ); mActionFeatureActions = new QToolButton(); mActionFeatureActions->setAutoRaise( false ); mActionFeatureActions->setPopupMode( QToolButton::InstantPopup ); mActionFeatureActions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mAction.svg" ) ) ); mActionFeatureActions->setText( tr( "Actions" ) ); mActionFeatureActions->setToolTip( tr( "Actions" ) ); mToolbar->addWidget( mActionFeatureActions ); // Connect filter signals connect( mActionAdvancedFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterExpressionBuilder ); connect( mActionShowAllFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterShowAll ); connect( mActionSelectedFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterSelected ); connect( mActionVisibleFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterVisible ); connect( mActionEditedFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterEdited ); connect( mFilterActionMapper, SIGNAL( mapped( QObject * ) ), SLOT( filterColumnChanged( QObject * ) ) ); connect( mFilterQuery, &QLineEdit::returnPressed, this, &QgsAttributeTableDialog::filterQueryAccepted ); connect( mActionApplyFilter, &QAction::triggered, this, &QgsAttributeTableDialog::filterQueryAccepted ); connect( mActionSetStyles, &QAction::triggered, this, &QgsAttributeTableDialog::openConditionalStyles ); // info from layer to table connect( mLayer, &QgsVectorLayer::editingStarted, this, &QgsAttributeTableDialog::editingToggled ); connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsAttributeTableDialog::editingToggled ); connect( mLayer, &QObject::destroyed, mMainView, &QgsDualView::cancelProgress ); connect( mLayer, &QgsVectorLayer::selectionChanged, this, &QgsAttributeTableDialog::updateTitle ); connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsAttributeTableDialog::updateTitle ); connect( mLayer, &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableDialog::updateTitle ); connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsAttributeTableDialog::updateTitle ); connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsAttributeTableDialog::columnBoxInit ); connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsAttributeTableDialog::columnBoxInit ); connect( mLayer, &QgsVectorLayer::readOnlyChanged, this, &QgsAttributeTableDialog::editingToggled ); // connect table info to window connect( mMainView, &QgsDualView::filterChanged, this, &QgsAttributeTableDialog::updateTitle ); connect( mMainView, &QgsDualView::filterExpressionSet, this, &QgsAttributeTableDialog::formFilterSet ); connect( mMainView, &QgsDualView::formModeChanged, this, &QgsAttributeTableDialog::viewModeChanged ); // info from table to application connect( this, &QgsAttributeTableDialog::saveEdits, this, [ = ] { QgisApp::instance()->saveEdits(); } ); const bool dockTable = settings.value( QStringLiteral( "qgis/dockAttributeTable" ), false ).toBool(); if ( dockTable ) { mDock = new QgsAttributeTableDock( QString(), QgisApp::instance() ); mDock->setWidget( this ); connect( this, &QObject::destroyed, mDock, &QWidget::close ); QgisApp::instance()->addDockWidget( Qt::BottomDockWidgetArea, mDock ); } mActionDockUndock->setChecked( dockTable ); connect( mActionDockUndock, &QAction::toggled, this, &QgsAttributeTableDialog::toggleDockMode ); installEventFilter( this ); columnBoxInit(); updateTitle(); mActionRemoveSelection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeselectAll.svg" ) ) ); mActionSelectAll->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSelectAll.svg" ) ) ); mActionSelectedToTop->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSelectedToTop.svg" ) ) ); mActionCopySelectedRows->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) ); mActionPasteFeatures->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ) ); mActionZoomMapToSelectedRows->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) ); mActionPanMapToSelectedRows->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPanToSelected.svg" ) ) ); mActionInvertSelection->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionInvertSelection.svg" ) ) ); mActionToggleEditing->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) ); mActionSaveEdits->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) ); mActionDeleteSelected->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) ); mActionOpenFieldCalculator->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCalculateField.svg" ) ) ); mActionAddAttribute->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) ); mActionRemoveAttribute->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) ); mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) ); mAttributeViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFormView.svg" ) ) ); mActionExpressionSelect->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionSelect.svg" ) ) ); mActionAddFeature->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) ); mActionFeatureActions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mAction.svg" ) ) ); // toggle editing bool canChangeAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues; bool canDeleteFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteFeatures; bool canAddAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddAttributes; bool canDeleteAttributes = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::DeleteAttributes; bool canAddFeatures = mLayer->dataProvider()->capabilities() & QgsVectorDataProvider::AddFeatures; mActionToggleEditing->blockSignals( true ); mActionToggleEditing->setCheckable( true ); mActionToggleEditing->setChecked( mLayer->isEditable() ); mActionToggleEditing->blockSignals( false ); mActionSaveEdits->setEnabled( mActionToggleEditing->isEnabled() && mLayer->isEditable() ); mActionReload->setEnabled( ! mLayer->isEditable() ); mActionAddAttribute->setEnabled( ( canChangeAttributes || canAddAttributes ) && mLayer->isEditable() ); mActionRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() ); if ( !canDeleteFeatures ) { mToolbar->removeAction( mActionDeleteSelected ); mToolbar->removeAction( mActionCutSelectedRows ); } mActionAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() ); mActionPasteFeatures->setEnabled( canAddFeatures && mLayer->isEditable() ); if ( !canAddFeatures ) { mToolbar->removeAction( mActionAddFeature ); mToolbar->removeAction( mActionPasteFeatures ); } mMainViewButtonGroup->setId( mTableViewButton, QgsDualView::AttributeTable ); mMainViewButtonGroup->setId( mAttributeViewButton, QgsDualView::AttributeEditor ); switch ( initialMode ) { case QgsAttributeTableFilterModel::ShowVisible: filterVisible(); break; case QgsAttributeTableFilterModel::ShowSelected: filterSelected(); break; case QgsAttributeTableFilterModel::ShowAll: default: filterShowAll(); break; } // Layer might have been destroyed while loading! if ( mLayer ) { mUpdateExpressionText->registerExpressionContextGenerator( this ); mFieldCombo->setFilters( QgsFieldProxyModel::AllTypes | QgsFieldProxyModel::HideReadOnly ); mFieldCombo->setLayer( mLayer ); connect( mRunFieldCalc, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpression ); connect( mRunFieldCalcSelected, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpressionSelected ); // NW TODO Fix in 2.6 - Doesn't work with field model for some reason. // connect( mUpdateExpressionText, SIGNAL( returnPressed() ), this, SLOT( updateFieldFromExpression() ) ); connect( mUpdateExpressionText, static_cast < void ( QgsFieldExpressionWidget::* )( const QString &, bool ) > ( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsAttributeTableDialog::updateButtonStatus ); mUpdateExpressionText->setLayer( mLayer ); mUpdateExpressionText->setLeftHandButtonStyle( true ); int initialView = settings.value( QStringLiteral( "qgis/attributeTableView" ), -1 ).toInt(); if ( initialView < 0 ) { initialView = settings.value( QStringLiteral( "qgis/attributeTableLastView" ), QgsDualView::AttributeTable ).toInt(); } mMainView->setView( static_cast< QgsDualView::ViewMode >( initialView ) ); mMainViewButtonGroup->button( initialView )->setChecked( true ); connect( mActionToggleMultiEdit, &QAction::toggled, mMainView, &QgsDualView::setMultiEditEnabled ); connect( mActionSearchForm, &QAction::toggled, mMainView, &QgsDualView::toggleSearchMode ); updateMultiEditButtonState(); if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout ) { //not supported with custom UI mActionToggleMultiEdit->setEnabled( false ); mActionToggleMultiEdit->setToolTip( tr( "Multiedit is not supported when using custom UI forms" ) ); mActionSearchForm->setEnabled( false ); mActionSearchForm->setToolTip( tr( "Search is not supported when using custom UI forms" ) ); } editingToggled(); // Close and delete if the layer has been destroyed connect( mLayer, &QObject::destroyed, this, &QWidget::close ); } else { QWidget::close(); } }
QgsVectorLayerExporter::ExportError QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destCRS, bool onlySelected, QString *errorMessage, const QMap<QString, QVariant> &options, QgsFeedback *feedback ) { QgsCoordinateReferenceSystem outputCRS; QgsCoordinateTransform ct; bool shallTransform = false; if ( !layer ) return ErrInvalidLayer; if ( 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; QMap<QString, QVariant> providerOptions = options; if ( !options.isEmpty() ) { overwrite = providerOptions.take( QStringLiteral( "overwrite" ) ).toBool(); forceSinglePartGeom = providerOptions.take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool(); } QgsFields fields = layer->fields(); QgsWkbTypes::Type wkbType = layer->wkbType(); // Special handling for Shapefiles if ( layer->providerType() == QLatin1String( "ogr" ) && layer->storageType() == QLatin1String( "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 QgsWkbTypes::Point: wkbType = QgsWkbTypes::MultiPoint; break; case QgsWkbTypes::LineString: wkbType = QgsWkbTypes::MultiLineString; break; case QgsWkbTypes::Polygon: wkbType = QgsWkbTypes::MultiPolygon; break; case QgsWkbTypes::Point25D: wkbType = QgsWkbTypes::MultiPoint25D; break; case QgsWkbTypes::LineString25D: wkbType = QgsWkbTypes::MultiLineString25D; break; case QgsWkbTypes::Polygon25D: wkbType = QgsWkbTypes::MultiPolygon25D; break; default: break; } } } QgsVectorLayerExporter *writer = new QgsVectorLayerExporter( uri, providerKey, fields, wkbType, outputCRS, overwrite, providerOptions ); // check whether file creation was successful ExportError err = writer->errorCode(); if ( err != NoError ) { if ( errorMessage ) *errorMessage = writer->errorMessage(); delete writer; return err; } if ( errorMessage ) { errorMessage->clear(); } QgsFeature fet; QgsFeatureRequest req; if ( wkbType == QgsWkbTypes::NoGeometry ) req.setFlags( QgsFeatureRequest::NoGeometry ); if ( onlySelected ) req.setFilterFids( layer->selectedFeatureIds() ); QgsFeatureIterator fit = layer->getFeatures( req ); // Create our transform if ( destCRS.isValid() ) { Q_NOWARN_DEPRECATED_PUSH ct = QgsCoordinateTransform( layer->crs(), destCRS ); Q_NOWARN_DEPRECATED_POP }
bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap> &attributeMaps ) { if ( !mVectorLayer ) { return false; } attributeMaps.clear(); //prepare filter expression std::auto_ptr<QgsExpression> filterExpression; bool activeFilter = false; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) ); if ( !filterExpression->hasParserError() ) { activeFilter = true; } } QgsRectangle selectionRect; if ( mComposerMap && mShowOnlyVisibleFeatures ) { selectionRect = *mComposerMap->currentMapExtent(); if ( mVectorLayer && mComposition->mapSettings().hasCrsTransformEnabled() ) { //transform back to layer CRS QgsCoordinateTransform coordTransform( mVectorLayer->crs(), mComposition->mapSettings().destinationCrs() ); try { selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); return false; } } } QgsFeatureRequest req; if ( !selectionRect.isEmpty() ) req.setFilterRect( selectionRect ); req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoGeometry ); if ( !mDisplayAttributes.isEmpty() ) req.setSubsetOfAttributes( mDisplayAttributes.toList() ); QgsFeature f; int counter = 0; QgsFeatureIterator fit = mVectorLayer->getFeatures( req ); while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures ) { //check feature against filter if ( activeFilter ) { QVariant result = filterExpression->evaluate( &f, mVectorLayer->pendingFields() ); // skip this feature if the filter evaluation if false if ( !result.toBool() ) { continue; } } attributeMaps.push_back( QgsAttributeMap() ); for ( int i = 0; i < f.attributes().size(); i++ ) { if ( !mDisplayAttributes.isEmpty() && !mDisplayAttributes.contains( i ) ) continue; attributeMaps.last().insert( i, f.attributes()[i] ); } ++counter; } //sort the list, starting with the last attribute QgsComposerAttributeTableCompare c; for ( int i = mSortInformation.size() - 1; i >= 0; --i ) { c.setSortColumn( mSortInformation.at( i ).first ); c.setAscending( mSortInformation.at( i ).second ); qStableSort( attributeMaps.begin(), attributeMaps.end(), c ); } return true; }
QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project ) { QgsFeatureRequest request; QDomNodeList fidNodes = filterElem.elementsByTagName( QStringLiteral( "FeatureId" ) ); QDomNodeList goidNodes = filterElem.elementsByTagName( QStringLiteral( "GmlObjectId" ) ); if ( !fidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement fidElem; for ( int f = 0; f < fidNodes.size(); f++ ) { fidElem = fidNodes.at( f ).toElement(); if ( !fidElem.hasAttribute( QStringLiteral( "fid" ) ) ) { throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" ); } QString fid = fidElem.attribute( QStringLiteral( "fid" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No FeatureId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( !goidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement goidElem; for ( int f = 0; f < goidNodes.size(); f++ ) { goidElem = goidNodes.at( f ).toElement(); if ( !goidElem.hasAttribute( QStringLiteral( "id" ) ) && !goidElem.hasAttribute( QStringLiteral( "gml:id" ) ) ) { throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" ); } QString fid = goidElem.attribute( QStringLiteral( "id" ) ); if ( fid.isEmpty() ) fid = goidElem.attribute( QStringLiteral( "gml:id" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No GmlObjectId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( filterElem.firstChildElement().tagName() == QLatin1String( "BBOX" ) ) { QDomElement bboxElem = filterElem.firstChildElement(); QDomElement childElem = bboxElem.firstChildElement(); while ( !childElem.isNull() ) { if ( childElem.tagName() == QLatin1String( "Box" ) ) { request.setFilterRect( QgsOgcUtils::rectangleFromGMLBox( childElem ) ); } else if ( childElem.tagName() != QLatin1String( "PropertyName" ) ) { QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem ); request.setFilterRect( geom.boundingBox() ); } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } // Apply BBOX through filterRect even inside an And to use spatial index else if ( filterElem.firstChildElement().tagName() == QLatin1String( "And" ) && !filterElem.firstChildElement().firstChildElement( QLatin1String( "BBOX" ) ).isNull() ) { QDomElement childElem = filterElem.firstChildElement().firstChildElement(); while ( !childElem.isNull() ) { QDomElement childFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) ); childFilterElement.appendChild( childElem.cloneNode( true ) ); QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement ); if ( childElem.tagName() == QLatin1String( "BBOX" ) ) { if ( request.filterRect().isEmpty() ) { request.setFilterRect( childRequest.filterRect() ); } else { request.setFilterRect( request.filterRect().intersect( childRequest.filterRect() ) ); } } else { if ( !request.filterExpression() ) { request.setFilterExpression( childRequest.filterExpression()->expression() ); } else { QgsExpressionNode *opLeft = request.filterExpression()->rootNode()->clone(); QgsExpressionNode *opRight = childRequest.filterExpression()->rootNode()->clone(); std::unique_ptr<QgsExpressionNodeBinaryOperator> node = qgis::make_unique<QgsExpressionNodeBinaryOperator>( QgsExpressionNodeBinaryOperator::boAnd, opLeft, opRight ); QgsExpression expr( node->dump() ); request.setFilterExpression( expr ); } } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } else { QgsVectorLayer *layer = nullptr; if ( project != nullptr ) { layer = layerByTypeName( project, typeName ); } std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) ); if ( filter ) { if ( filter->hasParserError() ) { throw QgsRequestNotWellFormedException( filter->parserErrorString() ); } if ( filter->needsGeometry() ) { request.setFlags( QgsFeatureRequest::NoFlags ); } request.setFilterExpression( filter->expression() ); return request; } } return request; }
bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap> &attributeMaps ) { if ( !mVectorLayer ) { return false; } QScopedPointer< QgsExpressionContext > context( createExpressionContext() ); context->setFields( mVectorLayer->fields() ); attributeMaps.clear(); //prepare filter expression QScopedPointer<QgsExpression> filterExpression; bool activeFilter = false; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression.reset( new QgsExpression( mFeatureFilter ) ); if ( !filterExpression->hasParserError() ) { activeFilter = true; } } QgsRectangle selectionRect; if ( mComposerMap && mShowOnlyVisibleFeatures ) { selectionRect = *mComposerMap->currentMapExtent(); if ( mComposition->mapSettings().hasCrsTransformEnabled() ) { //transform back to layer CRS QgsCoordinateTransform coordTransform( mVectorLayer->crs(), mComposition->mapSettings().destinationCrs() ); try { selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); return false; } } } QgsFeatureRequest req; if ( !selectionRect.isEmpty() ) req.setFilterRect( selectionRect ); req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags ); QgsFeature f; int counter = 0; QgsFeatureIterator fit = mVectorLayer->getFeatures( req ); while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures ) { context->setFeature( f ); //check feature against filter if ( activeFilter && !filterExpression.isNull() ) { QVariant result = filterExpression->evaluate( context.data() ); // skip this feature if the filter evaluation is false if ( !result.toBool() ) { continue; } } attributeMaps.push_back( QgsAttributeMap() ); QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin(); int i = 0; for ( ; columnIt != mColumns.constEnd(); ++columnIt ) { int idx = mVectorLayer->fieldNameIndex(( *columnIt )->attribute() ); if ( idx != -1 ) { attributeMaps.last().insert( i, f.attributes().at( idx ) ); } else { // Lets assume it's an expression QgsExpression* expression = new QgsExpression(( *columnIt )->attribute() ); context->lastScope()->setVariable( QString( "row_number" ), counter + 1 ); expression->prepare( context.data() ); QVariant value = expression->evaluate( context.data() ); attributeMaps.last().insert( i, value.toString() ); } i++; } ++counter; } //sort the list, starting with the last attribute QgsComposerAttributeTableCompare c; QList< QPair<int, bool> > sortColumns = sortAttributes(); for ( int i = sortColumns.size() - 1; i >= 0; --i ) { c.setSortColumn( sortColumns.at( i ).first ); c.setAscending( sortColumns.at( i ).second ); qStableSort( attributeMaps.begin(), attributeMaps.end(), c ); } adjustFrameToSize(); 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(); }
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback ) { int count = 0; int totalCount = source.featureCount(); if ( totalCount == 0 ) return; // nothing to do here QgsFeatureId newFid = -1; QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( source.wkbType() ) ); QgsFeatureRequest requestOnlyGeoms; requestOnlyGeoms.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureRequest requestOnlyAttrs; requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureRequest requestOnlyIds; requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry ); requestOnlyIds.setSubsetOfAttributes( QgsAttributeList() ); // make a set of used feature IDs so that we do not try to reuse them for newly added features QgsFeature f; QSet<QgsFeatureId> fids; QgsFeatureIterator it = source.getFeatures( requestOnlyIds ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; fids.insert( f.id() ); } QHash<QgsFeatureId, QgsGeometry> geometries; QgsSpatialIndex index; QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds; // which features overlap a particular area // resolve intersections it = source.getFeatures( requestOnlyGeoms ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; QgsFeatureId fid1 = f.id(); QgsGeometry g1 = f.geometry(); std::unique_ptr< QgsGeometryEngine > g1engine; geometries.insert( fid1, g1 ); index.insertFeature( f ); QgsRectangle bbox( f.geometry().boundingBox() ); const QList<QgsFeatureId> ids = index.intersects( bbox ); for ( QgsFeatureId fid2 : ids ) { if ( fid1 == fid2 ) continue; if ( !g1engine ) { // use prepared geometries for faster intersection tests g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) ); g1engine->prepareGeometry(); } QgsGeometry g2 = geometries.value( fid2 ); if ( !g1engine->intersects( g2.constGet() ) ) continue; QgsGeometry geomIntersection = g1.intersection( g2 ); if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) ) continue; // // add intersection geometry // // figure out new fid while ( fids.contains( newFid ) ) --newFid; fids.insert( newFid ); geometries.insert( newFid, geomIntersection ); QgsFeature fx( newFid ); fx.setGeometry( geomIntersection ); index.insertFeature( fx ); // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly // created geometries - in such case we need to retrieve original IDs QList<QgsFeatureId> lst; if ( intersectingIds.contains( fid1 ) ) lst << intersectingIds.value( fid1 ); else lst << fid1; if ( intersectingIds.contains( fid2 ) ) lst << intersectingIds.value( fid2 ); else lst << fid2; intersectingIds.insert( newFid, lst ); // // update f1 // QgsGeometry g12 = g1.difference( g2 ); index.deleteFeature( f ); geometries.remove( fid1 ); if ( sanitizeDifferenceResult( g12 ) ) { geometries.insert( fid1, g12 ); QgsFeature f1x( fid1 ); f1x.setGeometry( g12 ); index.insertFeature( f1x ); } // // update f2 // QgsGeometry g21 = g2.difference( g1 ); QgsFeature f2old( fid2 ); f2old.setGeometry( g2 ); index.deleteFeature( f2old ); geometries.remove( fid2 ); if ( sanitizeDifferenceResult( g21 ) ) { geometries.insert( fid2, g21 ); QgsFeature f2x( fid2 ); f2x.setGeometry( g21 ); index.insertFeature( f2x ); } // update our temporary copy of the geometry to what is left from it g1 = g12; g1engine.reset(); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } // release some memory of structures we don't need anymore fids.clear(); index = QgsSpatialIndex(); // load attributes QHash<QgsFeatureId, QgsAttributes> attributesHash; it = source.getFeatures( requestOnlyAttrs ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; attributesHash.insert( f.id(), f.attributes() ); } // store stuff in the sink for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i ) { if ( feedback->isCanceled() ) return; QgsFeature outFeature( i.key() ); outFeature.setGeometry( i.value() ); if ( intersectingIds.contains( i.key() ) ) { const QList<QgsFeatureId> ids = intersectingIds.value( i.key() ); for ( QgsFeatureId id : ids ) { outFeature.setAttributes( attributesHash.value( id ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } else { outFeature.setAttributes( attributesHash.value( i.key() ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } }
bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &contents ) { contents.clear(); if (( mSource == QgsComposerAttributeTableV2::AtlasFeature || mSource == QgsComposerAttributeTableV2::RelationChildren ) && !mComposition->atlasComposition().enabled() ) { //source mode requires atlas, but atlas disabled return false; } QgsVectorLayer* layer = sourceLayer(); if ( !layer ) { //no source layer return false; } //prepare filter expression std::auto_ptr<QgsExpression> filterExpression; bool activeFilter = false; if ( mFilterFeatures && !mFeatureFilter.isEmpty() ) { filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) ); if ( !filterExpression->hasParserError() ) { activeFilter = true; } } QgsRectangle selectionRect; if ( mComposerMap && mShowOnlyVisibleFeatures ) { selectionRect = *mComposerMap->currentMapExtent(); if ( layer && mComposition->mapSettings().hasCrsTransformEnabled() ) { //transform back to layer CRS QgsCoordinateTransform coordTransform( layer->crs(), mComposition->mapSettings().destinationCrs() ); try { selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); return false; } } } QgsFeatureRequest req; if ( mSource == QgsComposerAttributeTableV2::RelationChildren ) { QgsRelation relation = QgsProject::instance()->relationManager()->relation( mRelationId ); QgsFeature* atlasFeature = mComposition->atlasComposition().currentFeature(); if ( atlasFeature ) { req = relation.getRelatedFeaturesRequest( *atlasFeature ); } else { //no atlas feature, so empty table return true; } } if ( !selectionRect.isEmpty() ) req.setFilterRect( selectionRect ); req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags ); if ( mSource == QgsComposerAttributeTableV2::AtlasFeature && mComposition->atlasComposition().enabled() ) { //source mode is current atlas feature QgsFeature* atlasFeature = mComposition->atlasComposition().currentFeature(); if ( atlasFeature ) { req.setFilterFid( atlasFeature->id() ); } else { //no atlas feature, so empty table return true; } } QgsFeature f; int counter = 0; QgsFeatureIterator fit = layer->getFeatures( req ); while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures ) { //check feature against filter if ( activeFilter ) { QVariant result = filterExpression->evaluate( &f, layer->pendingFields() ); // skip this feature if the filter evaluation is false if ( !result.toBool() ) { continue; } } //check against atlas feature intersection if ( mFilterToAtlasIntersection ) { if ( !f.geometry() || ! mComposition->atlasComposition().enabled() ) { continue; } QgsFeature* atlasFeature = mComposition->atlasComposition().currentFeature(); if ( !atlasFeature || !atlasFeature->geometry() || !f.geometry()->intersects( atlasFeature->geometry() ) ) { //feature falls outside current atlas feature continue; } } QgsComposerTableRow currentRow; QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin(); for ( ; columnIt != mColumns.constEnd(); ++columnIt ) { int idx = layer->fieldNameIndex(( *columnIt )->attribute() ); if ( idx != -1 ) { currentRow << f.attributes()[idx]; } else { // Lets assume it's an expression QgsExpression* expression = new QgsExpression(( *columnIt )->attribute() ); expression->setCurrentRowNumber( counter + 1 ); expression->prepare( layer->pendingFields() ); QVariant value = expression->evaluate( f ) ; currentRow << value; } } if ( !mShowUniqueRowsOnly || !contentsContainsRow( contents, currentRow ) ) { contents << currentRow; ++counter; } } //sort the list, starting with the last attribute QgsComposerAttributeTableCompareV2 c; QList< QPair<int, bool> > sortColumns = sortAttributes(); for ( int i = sortColumns.size() - 1; i >= 0; --i ) { c.setSortColumn( sortColumns.at( i ).first ); c.setAscending( sortColumns.at( i ).second ); qStableSort( contents.begin(), contents.end(), c ); } recalculateTableSize(); return true; }
void QgsDualView::setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode ) { // cleanup any existing connections switch ( mFilterModel->filterMode() ) { case QgsAttributeTableFilterModel::ShowVisible: disconnect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged ); break; case QgsAttributeTableFilterModel::ShowAll: case QgsAttributeTableFilterModel::ShowEdited: case QgsAttributeTableFilterModel::ShowFilteredList: break; case QgsAttributeTableFilterModel::ShowSelected: disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures ); break; } QgsFeatureRequest r = mMasterModel->request(); bool needsGeometry = filterMode == QgsAttributeTableFilterModel::ShowVisible; bool requiresTableReload = ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) // previous request was subset || ( needsGeometry && r.flags() & QgsFeatureRequest::NoGeometry ) // no geometry for last request || ( mMasterModel->rowCount() == 0 ); // no features if ( !needsGeometry ) r.setFlags( r.flags() | QgsFeatureRequest::NoGeometry ); else r.setFlags( r.flags() & ~( QgsFeatureRequest::NoGeometry ) ); r.setFilterFids( QgsFeatureIds() ); r.setFilterRect( QgsRectangle() ); r.disableFilter(); // setup new connections and filter request parameters switch ( filterMode ) { case QgsAttributeTableFilterModel::ShowVisible: connect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged ); if ( mFilterModel->mapCanvas() ) { QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() ); r.setFilterRect( rect ); } break; case QgsAttributeTableFilterModel::ShowAll: case QgsAttributeTableFilterModel::ShowEdited: case QgsAttributeTableFilterModel::ShowFilteredList: break; case QgsAttributeTableFilterModel::ShowSelected: connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures ); r.setFilterFids( masterModel()->layer()->selectedFeatureIds() ); break; } if ( requiresTableReload ) { mMasterModel->setRequest( r ); whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry ); mMasterModel->loadLayer(); } //update filter model mFilterModel->setFilterMode( filterMode ); emit filterChanged(); }
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 ); } } }
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() ); } // write all features while ( fit.nextFeature( fet ) ) { if ( progress && progress->wasCanceled() ) { 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(); } } return errors == 0 ? NoError : ErrFeatureWriteFailed; }
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect ) { QgsFeatureIds newSelectedFeatures; if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry ) return newSelectedFeatures; QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return newSelectedFeatures; // 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; try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() ); if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry ) { // convert add more points to the edges of the rectangle // improve transformation result QgsPolygonXY poly( selectGeomTrans.asPolygon() ); if ( poly.size() == 1 && poly.at( 0 ).size() == 5 ) { const QgsPolylineXY &ringIn = poly.at( 0 ); QgsPolygonXY newpoly( 1 ); newpoly[0].resize( 41 ); QgsPolylineXY &ringOut = newpoly[0]; ringOut[ 0 ] = ringIn.at( 0 ); int i = 1; for ( int j = 1; j < 5; j++ ) { QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 ); for ( int k = 0; k < 9; k++ ) { ringOut[ i ] = ringOut[ i - 1 ] + v; i++; } ringOut[ i++ ] = ringIn.at( j ); } selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly ); } } selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), Qgis::Warning, QgisApp::instance()->messageTimeout() ); return newSelectedFeatures; } QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 ); QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 ); QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer ); std::unique_ptr< QgsFeatureRenderer > r; if ( vlayer->renderer() ) { r.reset( vlayer->renderer()->clone() ); r->startRender( context, vlayer->fields() ); } QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() ); else request.setNoAttributes(); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { context.expressionContext().setFeature( f ); // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) 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() ) ); return newSelectedFeatures; }
void QgsDxfExport::writeEntitiesSymbolLevels( QgsVectorLayer* layer ) { if ( !layer ) { return; } QgsFeatureRendererV2* renderer = layer->rendererV2(); if ( !renderer ) { //return error } QHash< QgsSymbolV2*, QList<QgsFeature> > features; QgsRenderContext ctx = renderContext(); QgsSymbolV2RenderContext sctx( ctx, QgsSymbolV2::MM , 1.0, false, 0, 0 ); renderer->startRender( ctx, layer->pendingFields() ); //get iterator QgsFeatureRequest req; if ( layer->wkbType() == QGis::WKBNoGeometry ) { req.setFlags( QgsFeatureRequest::NoGeometry ); } req.setSubsetOfAttributes( QStringList( renderer->usedAttributes() ), layer->pendingFields() ); if ( !mExtent.isEmpty() ) { req.setFilterRect( mExtent ); } QgsFeatureIterator fit = layer->getFeatures( req ); //fetch features QgsFeature fet; QgsSymbolV2* featureSymbol = 0; while ( fit.nextFeature( fet ) ) { featureSymbol = renderer->symbolForFeature( fet ); if ( !featureSymbol ) { continue; } QHash< QgsSymbolV2*, QList<QgsFeature> >::iterator it = features.find( featureSymbol ); if ( it == features.end() ) { it = features.insert( featureSymbol, QList<QgsFeature>() ); } it.value().append( fet ); } //find out order QgsSymbolV2LevelOrder levels; QgsSymbolV2List symbols = renderer->symbols(); for ( int i = 0; i < symbols.count(); i++ ) { QgsSymbolV2* 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; QgsSymbolV2LevelItem item( sym, j ); while ( level >= levels.count() ) // append new empty levels levels.append( QgsSymbolV2Level() ); levels[level].append( item ); } } //export symbol layers and symbology for ( int l = 0; l < levels.count(); l++ ) { QgsSymbolV2Level& level = levels[l]; for ( int i = 0; i < level.count(); i++ ) { QgsSymbolV2LevelItem& item = level[i]; QHash< QgsSymbolV2*, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() ); int llayer = item.layer(); QList<QgsFeature>& featureList = levelIt.value(); QList<QgsFeature>::iterator featureIt = featureList.begin(); for ( ; featureIt != featureList.end(); ++featureIt ) { addFeature( sctx, layer->name(), levelIt.key()->symbolLayer( llayer ), levelIt.key() ); } } } renderer->stopRender( ctx ); }