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; }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == NULL ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans( *selectGeometry ); if ( canvas->mapSettings().hasCrsTransformEnabled() ) { try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), QgsMessageBar::WARNING, QgisApp::instance()->messageTimeout() ); return; } } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->fields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->fields() ); 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 ) ) { // make sure to only use features that are visible if ( r && !r->willRenderFeature( f ) ) continue; const QgsGeometry* g = f.constGeometry(); 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 { vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource( source, ownSource, request ) , mRewind( false ) { mConnection = QgsOracleConn::connectDb( mSource->mUri.connectionInfo() ); if ( !mConnection ) { close(); return; } mQry = QSqlQuery( *mConnection ); if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { mAttributeList = mRequest.subsetOfAttributes(); if ( mAttributeList.isEmpty() ) mAttributeList = mSource->mFields.allAttributesList(); } else mAttributeList = mSource->mFields.allAttributesList(); QString whereClause; switch ( request.filterType() ) { case QgsFeatureRequest::FilterExpression: break; case QgsFeatureRequest::FilterRect: if ( !mSource->mGeometryColumn.isNull() ) { QgsRectangle rect( mRequest.filterRect() ); QString bbox = QString( "mdsys.sdo_geometry(2003,%1,NULL," "mdsys.sdo_elem_info_array(1,1003,3)," "mdsys.sdo_ordinate_array(%2,%3,%4,%5)" ")" ) .arg( mSource->mSrid < 1 ? "NULL" : QString::number( mSource->mSrid ) ) .arg( qgsDoubleToString( rect.xMinimum() ) ) .arg( qgsDoubleToString( rect.yMinimum() ) ) .arg( qgsDoubleToString( rect.xMaximum() ) ) .arg( qgsDoubleToString( rect.yMaximum() ) ); if ( !mSource->mSpatialIndex.isNull() ) { whereClause = QString( "sdo_filter(%1,%2)='TRUE'" ).arg( QgsOracleProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( bbox ); #if 0 if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { whereClause += QString( " AND sdo_relate(%1,%2,'mask=ANYINTERACT')='TRUE'" ) .arg( quotedIdentifier( P->mGeometryColumn ) ) .arg( bbox ); } #endif } } break; case QgsFeatureRequest::FilterFid: whereClause = QgsOracleUtils::whereClause( request.filterFid(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); break; case QgsFeatureRequest::FilterFids: whereClause = QgsOracleUtils::whereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); break; case QgsFeatureRequest::FilterNone: break; } if ( mSource->mRequestedGeomType != QGis::WKBUnknown && mSource->mRequestedGeomType != mSource->mDetectedGeomType ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += QgsOracleConn::databaseTypeFilter( "featureRequest", mSource->mGeometryColumn, mSource->mRequestedGeomType ); } if ( !mSource->mSqlWhereClause.isEmpty() ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += "(" + mSource->mSqlWhereClause + ")"; } if ( !openQuery( whereClause ) ) return; }
bool QgsVectorLayerRenderer::render() { if ( mGeometryType == QgsWkbTypes::NullGeometry || mGeometryType == QgsWkbTypes::UnknownGeometry ) return true; if ( !mRenderer ) { mErrors.append( QObject::tr( "No renderer for drawing." ) ); return false; } bool usingEffect = false; if ( mRenderer->paintEffect() && mRenderer->paintEffect()->enabled() ) { usingEffect = true; mRenderer->paintEffect()->begin( mContext ); } // Per feature blending mode if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mContext.painter()->setCompositionMode( mFeatureBlendMode ); } mRenderer->startRender( mContext, mFields ); QString rendererFilter = mRenderer->filter( mFields ); QgsRectangle requestExtent = mContext.extent(); mRenderer->modifyRequestExtent( requestExtent, mContext ); QgsFeatureRequest featureRequest = QgsFeatureRequest() .setFilterRect( requestExtent ) .setSubsetOfAttributes( mAttrNames, mFields ) .setExpressionContext( mContext.expressionContext() ); if ( mRenderer->orderByEnabled() ) { featureRequest.setOrderBy( mRenderer->orderBy() ); } const QgsFeatureFilterProvider *featureFilterProvider = mContext.featureFilterProvider(); if ( featureFilterProvider ) { featureFilterProvider->filterFeatures( mLayer, featureRequest ); } if ( !rendererFilter.isEmpty() && rendererFilter != QLatin1String( "TRUE" ) ) { featureRequest.combineFilterExpression( rendererFilter ); } // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine. if ( mSimplifyGeometry ) { double map2pixelTol = mSimplifyMethod.threshold(); bool validTransform = true; const QgsMapToPixel &mtp = mContext.mapToPixel(); map2pixelTol *= mtp.mapUnitsPerPixel(); QgsCoordinateTransform ct = mContext.coordinateTransform(); // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem if ( ct.isValid() && !ct.isShortCircuited() ) { try { QgsPointXY center = mContext.extent().center(); double rectSize = ct.sourceCrs().isGeographic() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100; QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize ); QgsRectangle targetRect = ct.transform( sourceRect ); QgsDebugMsgLevel( QString( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ), 4 ); QgsDebugMsgLevel( QString( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ), 4 ); if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() ) { QgsPointXY minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() ); QgsPointXY maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() ); QgsPointXY minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPointXY maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); double sourceHypothenuse = std::sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); double targetHypothenuse = std::sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsgLevel( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ), 4 ); QgsDebugMsgLevel( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ), 4 ); if ( !qgsDoubleNear( targetHypothenuse, 0.0 ) ) map2pixelTol *= ( sourceHypothenuse / targetHypothenuse ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); validTransform = false; } } if ( validTransform ) { QgsSimplifyMethod simplifyMethod; simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering ); simplifyMethod.setTolerance( map2pixelTol ); simplifyMethod.setThreshold( mSimplifyMethod.threshold() ); simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() ); featureRequest.setSimplifyMethod( simplifyMethod ); QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod; vectorMethod.setTolerance( map2pixelTol ); mContext.setVectorSimplifyMethod( vectorMethod ); } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } QgsFeatureIterator fit = mSource->getFeatures( featureRequest ); // Attach an interruption checker so that iterators that have potentially // slow fetchFeature() implementations, such as in the WFS provider, can // check it, instead of relying on just the mContext.renderingStopped() check // in drawRenderer() fit.setInterruptionChecker( &mInterruptionChecker ); if ( ( mRenderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) && mRenderer->usingSymbolLevels() ) drawRendererLevels( fit ); else drawRenderer( fit ); if ( usingEffect ) { mRenderer->paintEffect()->end( mContext ); } return true; }
int QgsTransectSample::createSample( QProgressDialog* pd ) { Q_UNUSED( pd ); if ( !mStrataLayer || !mStrataLayer->isValid() ) { return 1; } if ( !mBaselineLayer || !mBaselineLayer->isValid() ) { return 2; } //stratum id is not necessarily an integer QVariant::Type stratumIdType = QVariant::Int; if ( !mStrataIdAttribute.isEmpty() ) { stratumIdType = mStrataLayer->pendingFields().field( mStrataIdAttribute ).type(); } //create vector file writers for output QgsFields outputPointFields; outputPointFields.append( QgsField( "id", stratumIdType ) ); outputPointFields.append( QgsField( "station_id", QVariant::Int ) ); outputPointFields.append( QgsField( "stratum_id", stratumIdType ) ); outputPointFields.append( QgsField( "station_code", QVariant::String ) ); outputPointFields.append( QgsField( "start_lat", QVariant::Double ) ); outputPointFields.append( QgsField( "start_long", QVariant::Double ) ); QgsVectorFileWriter outputPointWriter( mOutputPointLayer, "utf-8", outputPointFields, QGis::WKBPoint, &( mStrataLayer->crs() ) ); if ( outputPointWriter.hasError() != QgsVectorFileWriter::NoError ) { return 3; } outputPointFields.append( QgsField( "bearing", QVariant::Double ) ); //add bearing attribute for lines QgsVectorFileWriter outputLineWriter( mOutputLineLayer, "utf-8", outputPointFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); if ( outputLineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 4; } QgsFields usedBaselineFields; usedBaselineFields.append( QgsField( "stratum_id", stratumIdType ) ); usedBaselineFields.append( QgsField( "ok", QVariant::String ) ); QgsVectorFileWriter usedBaselineWriter( mUsedBaselineLayer, "utf-8", usedBaselineFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); if ( usedBaselineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 5; } //debug: write clipped buffer bounds with stratum id to same directory as out_point QFileInfo outputPointInfo( mOutputPointLayer ); QString bufferClipLineOutput = outputPointInfo.absolutePath() + "/out_buffer_clip_line.shp"; QgsFields bufferClipLineFields; bufferClipLineFields.append( QgsField( "id", stratumIdType ) ); QgsVectorFileWriter bufferClipLineWriter( bufferClipLineOutput, "utf-8", bufferClipLineFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); //configure distanceArea depending on minDistance units and output CRS QgsDistanceArea distanceArea; distanceArea.setSourceCrs( mStrataLayer->crs().srsid() ); if ( mMinDistanceUnits == Meters ) { distanceArea.setEllipsoidalMode( true ); } else { distanceArea.setEllipsoidalMode( false ); } //possibility to transform output points to lat/long QgsCoordinateTransform toLatLongTransform( mStrataLayer->crs(), QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) ); //init random number generator mt_srand( QTime::currentTime().msec() ); QgsFeatureRequest fr; fr.setSubsetOfAttributes( QStringList() << mStrataIdAttribute << mMinDistanceAttribute << mNPointsAttribute, mStrataLayer->pendingFields() ); QgsFeatureIterator strataIt = mStrataLayer->getFeatures( fr ); QgsFeature fet; int nTotalTransects = 0; int nFeatures = 0; if ( pd ) { pd->setMaximum( mStrataLayer->featureCount() ); } while ( strataIt.nextFeature( fet ) ) { if ( pd ) { pd->setValue( nFeatures ); } if ( pd && pd->wasCanceled() ) { break; } QgsGeometry* strataGeom = fet.geometry(); if ( !strataGeom ) { continue; } //find baseline for strata bool strataIdOk = true; QVariant strataId = fet.attribute( mStrataIdAttribute ); QgsGeometry* baselineGeom = findBaselineGeometry( strataIdOk ? strataId : -1 ); if ( !baselineGeom ) { continue; } double minDistance = fet.attribute( mMinDistanceAttribute ).toDouble(); double minDistanceLayerUnits = minDistance; //if minDistance is in meters and the data in degrees, we need to apply a rough conversion for the buffer distance double bufferDist = minDistance; if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QGis::DecimalDegrees ) { bufferDist = minDistance / 111319.9; minDistanceLayerUnits = bufferDist; } QgsGeometry* clippedBaseline = strataGeom->intersection( baselineGeom ); if ( !clippedBaseline || clippedBaseline->wkbType() == QGis::WKBUnknown ) { delete clippedBaseline; continue; } QgsGeometry* bufferLineClipped = clipBufferLine( strataGeom, clippedBaseline, bufferDist ); if ( !bufferLineClipped ) { delete clippedBaseline; continue; } //save clipped baseline to file QgsFeature blFeature; blFeature.setGeometry( *clippedBaseline ); blFeature.setAttribute( "stratum_id", strataId ); blFeature.setAttribute( "ok", "f" ); usedBaselineWriter.addFeature( blFeature ); //start loop to create random points along the baseline int nTransects = fet.attribute( mNPointsAttribute ).toInt(); int nCreatedTransects = 0; int nIterations = 0; int nMaxIterations = nTransects * 50; QgsSpatialIndex sIndex; //to check minimum distance QMap< QgsFeatureId, QgsGeometry* > lineFeatureMap; while ( nCreatedTransects < nTransects && nIterations < nMaxIterations ) { double randomPosition = (( double )mt_rand() / MD_RAND_MAX ) * clippedBaseline->length(); QgsGeometry* samplePoint = clippedBaseline->interpolate( randomPosition ); ++nIterations; if ( !samplePoint ) { continue; } QgsPoint sampleQgsPoint = samplePoint->asPoint(); QgsPoint latLongSamplePoint = toLatLongTransform.transform( sampleQgsPoint ); QgsFeature samplePointFeature; samplePointFeature.setGeometry( samplePoint ); samplePointFeature.setAttribute( "id", nTotalTransects + 1 ); samplePointFeature.setAttribute( "station_id", nCreatedTransects + 1 ); samplePointFeature.setAttribute( "stratum_id", strataId ); samplePointFeature.setAttribute( "station_code", strataId.toString() + "_" + QString::number( nCreatedTransects + 1 ) ); samplePointFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); samplePointFeature.setAttribute( "start_long", latLongSamplePoint.x() ); //find closest point on clipped buffer line QgsPoint minDistPoint; int afterVertex; if ( bufferLineClipped->closestSegmentWithContext( sampleQgsPoint, minDistPoint, afterVertex ) < 0 ) { continue; } //bearing between sample point and min dist point (transect direction) double bearing = distanceArea.bearing( sampleQgsPoint, minDistPoint ) / M_PI * 180.0; QgsPolyline sampleLinePolyline; QgsPoint ptFarAway( sampleQgsPoint.x() + ( minDistPoint.x() - sampleQgsPoint.x() ) * 1000000, sampleQgsPoint.y() + ( minDistPoint.y() - sampleQgsPoint.y() ) * 1000000 ); QgsPolyline lineFarAway; lineFarAway << sampleQgsPoint << ptFarAway; QgsGeometry* lineFarAwayGeom = QgsGeometry::fromPolyline( lineFarAway ); QgsGeometry* lineClipStratum = lineFarAwayGeom->intersection( strataGeom ); if ( !lineClipStratum ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //cancel if distance between sample point and line is too large (line does not start at point if ( lineClipStratum->distance( *samplePoint ) > 0.000001 ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint if ( lineClipStratum->wkbType() == QGis::WKBMultiLineString || lineClipStratum->wkbType() == QGis::WKBMultiLineString25D ) { QgsGeometry* singleLine = closestMultilineElement( sampleQgsPoint, lineClipStratum ); if ( singleLine ) { delete lineClipStratum; lineClipStratum = singleLine; } } //cancel if length of lineClipStratum is too small double transectLength = distanceArea.measure( lineClipStratum ); if ( transectLength < mMinTransectLength ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //search closest existing profile. Cancel if dist < minDist if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } QgsFeatureId fid( nCreatedTransects ); QgsFeature sampleLineFeature( fid ); sampleLineFeature.setGeometry( lineClipStratum ); sampleLineFeature.setAttribute( "id", nTotalTransects + 1 ); sampleLineFeature.setAttribute( "station_id", nCreatedTransects + 1 ); sampleLineFeature.setAttribute( "stratum_id", strataId ); sampleLineFeature.setAttribute( "station_code", strataId.toString() + "_" + QString::number( nCreatedTransects + 1 ) ); sampleLineFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); sampleLineFeature.setAttribute( "start_long", latLongSamplePoint.x() ); sampleLineFeature.setAttribute( "bearing", bearing ); outputLineWriter.addFeature( sampleLineFeature ); //add point to file writer here. //It can only be written if the corresponding transect has been as well outputPointWriter.addFeature( samplePointFeature ); sIndex.insertFeature( sampleLineFeature ); lineFeatureMap.insert( fid, sampleLineFeature.geometryAndOwnership() ); delete lineFarAwayGeom; ++nTotalTransects; ++nCreatedTransects; } delete clippedBaseline; QgsFeature bufferClipFeature; bufferClipFeature.setGeometry( bufferLineClipped ); bufferClipFeature.setAttribute( "id" , strataId ); bufferClipLineWriter.addFeature( bufferClipFeature ); //delete bufferLineClipped; //delete all line geometries in spatial index QMap< QgsFeatureId, QgsGeometry* >::iterator featureMapIt = lineFeatureMap.begin(); for ( ; featureMapIt != lineFeatureMap.end(); ++featureMapIt ) { delete( featureMapIt.value() ); } lineFeatureMap.clear(); delete baselineGeom; ++nFeatures; } if ( pd ) { pd->setValue( mStrataLayer->featureCount() ); } return 0; }
QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWidget *parent, Qt::WindowFlags flags ) : QDialog( parent, flags ) , mDock( nullptr ) , mLayer( theLayer ) , mRubberBand( nullptr ) , mCurrentSearchWidgetWrapper( nullptr ) { setupUi( this ); Q_FOREACH ( const QgsField& field, mLayer->fields() ) { mVisibleFields.append( field.name() ); } // Fix selection color on loosing 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 ); QSettings settings; int size = settings.value( "/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( "/Windows/BetterAttributeTable/geometry" ).toByteArray() ); myDa = new QgsDistanceArea(); myDa->setSourceCrs( mLayer->crs() ); myDa->setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() ); myDa->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); mEditorContext.setDistanceArea( *myDa ); mEditorContext.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() ); QgsFeatureRequest r; if ( mLayer->geometryType() != QGis::NoGeometry && settings.value( "/qgis/attributeTableBehaviour", QgsAttributeTableFilterModel::ShowAll ).toInt() == QgsAttributeTableFilterModel::ShowVisible ) { QgsMapCanvas *mc = QgisApp::instance()->mapCanvas(); QgsRectangle extent( mc->mapSettings().mapToLayerCoordinates( theLayer, mc->extent() ) ); r.setFilterRect( extent ); QgsGeometry *g = QgsGeometry::fromRect( extent ); mRubberBand = new QgsRubberBand( mc, QGis::Polygon ); mRubberBand->setToGeometry( g, theLayer ); delete g; mActionShowAllFilter->setText( tr( "Show All Features In Initial Canvas Extent" ) ); } // Initialize dual view mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), r, mEditorContext ); 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 ); // Connect filter signals connect( mActionAdvancedFilter, SIGNAL( triggered() ), SLOT( filterExpressionBuilder() ) ); connect( mActionShowAllFilter, SIGNAL( triggered() ), SLOT( filterShowAll() ) ); connect( mActionSelectedFilter, SIGNAL( triggered() ), SLOT( filterSelected() ) ); connect( mActionVisibleFilter, SIGNAL( triggered() ), SLOT( filterVisible() ) ); connect( mActionEditedFilter, SIGNAL( triggered() ), SLOT( filterEdited() ) ); connect( mFilterActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( filterColumnChanged( QObject* ) ) ); connect( mFilterQuery, SIGNAL( returnPressed() ), SLOT( filterQueryAccepted() ) ); connect( mActionApplyFilter, SIGNAL( triggered() ), SLOT( filterQueryAccepted() ) ); connect( mActionSetStyles, SIGNAL( triggered() ), SLOT( openConditionalStyles() ) ); // info from layer to table connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) ); connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) ); connect( mLayer, SIGNAL( layerDeleted() ), this, SLOT( close() ) ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateTitle() ) ); connect( mLayer, SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( updateTitle() ) ); connect( mLayer, SIGNAL( featuresDeleted( QgsFeatureIds ) ), this, SLOT( updateTitle() ) ); connect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( columnBoxInit() ) ); connect( mLayer, SIGNAL( attributeDeleted( int ) ), this, SLOT( columnBoxInit() ) ); // connect table info to window connect( mMainView, SIGNAL( filterChanged() ), this, SLOT( updateTitle() ) ); connect( mMainView, SIGNAL( filterExpressionSet( QString, QgsAttributeForm::FilterType ) ), this, SLOT( formFilterSet( QString, QgsAttributeForm::FilterType ) ) ); connect( mMainView, SIGNAL( formModeChanged( QgsAttributeForm::Mode ) ), this, SLOT( viewModeChanged( QgsAttributeForm::Mode ) ) ); // info from table to application connect( this, SIGNAL( saveEdits( QgsMapLayer * ) ), QgisApp::instance(), SLOT( saveEdits( QgsMapLayer * ) ) ); bool myDockFlag = settings.value( "/qgis/dockAttributeTable", false ).toBool(); if ( myDockFlag ) { mDock = new QgsAttributeTableDock( tr( "%1 (%n Feature(s))", "feature count", mMainView->featureCount() ).arg( mLayer->name() ), QgisApp::instance() ); mDock->setWidget( this ); connect( this, SIGNAL( destroyed() ), mDock, SLOT( close() ) ); QgisApp::instance()->addDockWidget( Qt::BottomDockWidgetArea, mDock ); } columnBoxInit(); updateTitle(); mActionRemoveSelection->setIcon( QgsApplication::getThemeIcon( "/mActionDeselectAll.svg" ) ); mActionSelectAll->setIcon( QgsApplication::getThemeIcon( "/mActionSelectAll.svg" ) ); mActionSelectedToTop->setIcon( QgsApplication::getThemeIcon( "/mActionSelectedToTop.svg" ) ); mActionCopySelectedRows->setIcon( QgsApplication::getThemeIcon( "/mActionEditCopy.svg" ) ); mActionPasteFeatures->setIcon( QgsApplication::getThemeIcon( "/mActionEditPaste.svg" ) ); mActionZoomMapToSelectedRows->setIcon( QgsApplication::getThemeIcon( "/mActionZoomToSelected.svg" ) ); mActionPanMapToSelectedRows->setIcon( QgsApplication::getThemeIcon( "/mActionPanToSelected.svg" ) ); mActionInvertSelection->setIcon( QgsApplication::getThemeIcon( "/mActionInvertSelection.svg" ) ); mActionToggleEditing->setIcon( QgsApplication::getThemeIcon( "/mActionToggleEditing.svg" ) ); mActionSaveEdits->setIcon( QgsApplication::getThemeIcon( "/mActionSaveEdits.svg" ) ); mActionDeleteSelected->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteSelected.svg" ) ); mActionOpenFieldCalculator->setIcon( QgsApplication::getThemeIcon( "/mActionCalculateField.svg" ) ); mActionAddAttribute->setIcon( QgsApplication::getThemeIcon( "/mActionNewAttribute.svg" ) ); mActionRemoveAttribute->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteAttribute.svg" ) ); mTableViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.svg" ) ); mAttributeViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionFormView.svg" ) ); mActionExpressionSelect->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionSelect.svg" ) ); mActionAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionNewTableRow.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->setEnabled(( canChangeAttributes || canDeleteFeatures || canAddAttributes || canDeleteAttributes || canAddFeatures ) && !mLayer->readOnly() ); mActionToggleEditing->blockSignals( false ); mActionSaveEdits->setEnabled( mActionToggleEditing->isEnabled() && mLayer->isEditable() ); mActionReload->setEnabled( ! mLayer->isEditable() ); mActionAddAttribute->setEnabled(( canChangeAttributes || canAddAttributes ) && mLayer->isEditable() ); mActionRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() ); mActionDeleteSelected->setEnabled( canDeleteFeatures && mLayer->isEditable() ); if ( !canDeleteFeatures ) mToolbar->removeAction( mActionDeleteSelected ); mActionAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() ); if ( !canAddFeatures ) mToolbar->removeAction( mActionAddFeature ); if ( canDeleteFeatures || canAddFeatures ) mToolbar->insertSeparator( mActionExpressionSelect ); mMainViewButtonGroup->setId( mTableViewButton, QgsDualView::AttributeTable ); mMainViewButtonGroup->setId( mAttributeViewButton, QgsDualView::AttributeEditor ); // Load default attribute table filter QgsAttributeTableFilterModel::FilterMode defaultFilterMode = ( QgsAttributeTableFilterModel::FilterMode ) settings.value( "/qgis/attributeTableBehaviour", QgsAttributeTableFilterModel::ShowAll ).toInt(); switch ( defaultFilterMode ) { case QgsAttributeTableFilterModel::ShowVisible: filterVisible(); break; case QgsAttributeTableFilterModel::ShowSelected: filterSelected(); break; case QgsAttributeTableFilterModel::ShowAll: default: filterShowAll(); break; } mUpdateExpressionText->registerGetExpressionContextCallback( &_getExpressionContext, mLayer ); mFieldCombo->setFilters( QgsFieldProxyModel::All | QgsFieldProxyModel::HideReadOnly ); mFieldCombo->setLayer( mLayer ); connect( mRunFieldCalc, SIGNAL( clicked() ), this, SLOT( updateFieldFromExpression() ) ); connect( mRunFieldCalcSelected, SIGNAL( clicked() ), this, SLOT( 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, SIGNAL( fieldChanged( QString, bool ) ), this, SLOT( updateButtonStatus( QString, bool ) ) ); mUpdateExpressionText->setLayer( mLayer ); mUpdateExpressionText->setLeftHandButtonStyle( true ); int initialView = settings.value( "/qgis/attributeTableView", -1 ).toInt(); if ( initialView < 0 ) { initialView = settings.value( "/qgis/attributeTableLastView", QgsDualView::AttributeTable ).toInt(); } mMainView->setView( static_cast< QgsDualView::ViewMode >( initialView ) ); mMainViewButtonGroup->button( initialView )->setChecked( true ); connect( mActionToggleMultiEdit, SIGNAL( toggled( bool ) ), mMainView, SLOT( setMultiEditEnabled( bool ) ) ); connect( mActionSearchForm, SIGNAL( toggled( bool ) ), mMainView, SLOT( toggleSearchMode( bool ) ) ); 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(); }
QgsFeatureIterator QgsWFSFeatureSource::getFeatures( const QgsFeatureRequest& request ) { if ( !request.filterRect().isNull() ) emit extentRequested( request.filterRect() ); return QgsFeatureIterator( new QgsWFSFeatureIterator( this, false, request ) ); }
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(); } }
QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>( source, ownSource, request ) , mFeatureQueueSize( sFeatureQueueSize ) , mFetched( 0 ) , mFetchGeometry( false ) , mExpressionCompiled( false ) { if ( !source->mTransactionConnection ) { mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo ); mIsTransactionConnection = false; } else { mConn = source->mTransactionConnection; mConn->lock(); mIsTransactionConnection = true; } if ( !mConn ) { mClosed = true; iteratorClosed(); return; } mCursorName = mConn->uniqueCursorName(); QString whereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { whereClause = whereClauseRect(); } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString fidsWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/postgres/compileExpressions", false ).toBool() ) { QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source ); if ( compiler.compile( request.filterExpression() ) == QgsPostgresExpressionCompiler::Complete ) { whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() ); mExpressionCompiled = true; } } if ( !mSource->mSqlWhereClause.isEmpty() ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += "(" + mSource->mSqlWhereClause + ")"; } if ( !declareCursor( whereClause ) ) { mClosed = true; iteratorClosed(); return; } mFetched = 0; }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( nullptr ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; bool useFallbackWhereClause = false; QString fallbackWhereClause; QString whereClause; //beware - limitAtProvider needs to be set to false if the request cannot be completely handled //by the provider (eg utilising QGIS expression filters) bool limitAtProvider = ( mRequest.limit() >= 0 ); if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } //IMPORTANT - this MUST be the last clause added! else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { useFallbackWhereClause = true; fallbackWhereClause = whereClauses.join( " AND " ); whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } if ( result != QgsSqlExpressionCompiler::Complete ) { //can't apply limit at provider side as we need to check all results using QGIS expressions limitAtProvider = false; } } else { limitAtProvider = false; } } whereClause = whereClauses.join( " AND " ); // Setup the order by QStringList orderByParts; mOrderByCompiled = true; if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { Q_FOREACH ( const QgsFeatureRequest::OrderByClause& clause, request.orderBy() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsExpression expression = clause.expression(); if ( compiler.compile( &expression ) == QgsSqlExpressionCompiler::Complete ) { QString part; part = compiler.result(); if ( clause.nullsFirst() ) orderByParts << QString( "%1 IS NOT NULL" ).arg( part ); else orderByParts << QString( "%1 IS NULL" ).arg( part ); part += clause.ascending() ? " COLLATE NOCASE ASC" : " COLLATE NOCASE DESC"; orderByParts << part; } else { // Bail out on first non-complete compilation. // Most important clauses at the beginning of the list // will still be sent and used to pre-sort so the local // CPU can use its cycles for fine-tuning. mOrderByCompiled = false; break; } } }
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 = 0; int shallTransform = false; if ( layer == NULL ) { 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[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 == NULL ) { 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.geometry() ) { 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; }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , mSharedDS( source->mSharedDS ) , mFirstFieldIsFid( source->mFirstFieldIsFid ) , mFieldsWithoutFid( source->mFieldsWithoutFid ) { for ( const auto &id : mRequest.filterFids() ) { mFilterFids.insert( id ); } mFilterFidsIt = mFilterFids.begin(); // Since connection timeout for OGR connections is problematic and can lead to crashes, disable for now. mRequest.setTimeout( -1 ); if ( mSharedDS ) { mOgrLayer = mSharedDS->getLayerFromNameOrIndex( mSource->mLayerName, mSource->mLayerIndex ); if ( !mOgrLayer ) { return; } } else { //QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection"); mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource, mSource->mShareSameDatasetAmongLayers ), mRequest.timeout(), mRequest.requestMayBeNested() ); if ( !mConn || !mConn->ds ) { return; } if ( mSource->mLayerName.isNull() ) { mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex ); } else { mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() ); } if ( !mOgrLayer ) { return; } if ( !mSource->mSubsetString.isEmpty() ) { mOgrLayerOri = mOgrLayer; mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); // If the mSubsetString was a full SELECT ...., then mOgrLayer will be a OGR SQL layer != mOgrLayerOri mFieldsWithoutFid.clear(); for ( int i = ( mFirstFieldIsFid ) ? 1 : 0; i < mSource->mFields.size(); i++ ) mFieldsWithoutFid.append( mSource->mFields.at( i ) ); if ( !mOgrLayer ) { close(); return; } } } QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr ); if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } mFetchGeometry = ( !mFilterRect.isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || ( mSource->mOgrGeometryTypeFilter != wkbUnknown ); QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { QSet<int> attributeIndexes; const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); for ( int attrIdx : usedAttributeIndices ) { attributeIndexes << attrIdx; } attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) { mFetchGeometry = true; } // make sure we fetch just relevant fields // unless it's a VRT data source filtered by geometry as we don't know which // attributes make up the geometry and OGR won't fetch them to evaluate the // filter if we choose to ignore them (fixes #11223) if ( ( mSource->mDriverName != QLatin1String( "VRT" ) && mSource->mDriverName != QLatin1String( "OGR_VRT" ) ) || mFilterRect.isNull() ) { QgsOgrProviderUtils::setRelevantFields( mOgrLayer, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid, mSource->mSubsetString ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) QgsOgrProviderUtils::setRelevantFields( mOgrLayerOri, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid, mSource->mSubsetString ); } // spatial query to select features if ( !mFilterRect.isNull() ) { OGR_L_SetSpatialFilterRect( mOgrLayer, mFilterRect.xMinimum(), mFilterRect.yMinimum(), mFilterRect.xMaximum(), mFilterRect.yMaximum() ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) OGR_L_SetSpatialFilterRect( mOgrLayerOri, mFilterRect.xMinimum(), mFilterRect.yMinimum(), mFilterRect.xMaximum(), mFilterRect.yMaximum() ); } else { OGR_L_SetSpatialFilter( mOgrLayer, nullptr ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) OGR_L_SetSpatialFilter( mOgrLayerOri, nullptr ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && QgsSettings().value( QStringLiteral( "qgis/compileExpressions" ), true ).toBool() ) { QgsSqlExpressionCompiler *compiler = nullptr; if ( source->mDriverName == QLatin1String( "SQLite" ) || source->mDriverName == QLatin1String( "GPKG" ) ) { compiler = new QgsSQLiteExpressionCompiler( source->mFields ); } else { compiler = new QgsOgrExpressionCompiler( source ); } QgsSqlExpressionCompiler::Result result = compiler->compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { QString whereClause = compiler->result(); if ( !mSource->mSubsetString.isEmpty() && mOgrLayer == mOgrLayerOri ) { whereClause = QStringLiteral( "(" ) + mSource->mSubsetString + QStringLiteral( ") AND (" ) + whereClause + QStringLiteral( ")" ); } if ( OGR_L_SetAttributeFilter( mOgrLayer, mSource->mEncoding->fromUnicode( whereClause ).constData() ) == OGRERR_NONE ) { //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); mCompileStatus = ( mExpressionCompiled ? Compiled : PartiallyCompiled ); } } else if ( mSource->mSubsetString.isEmpty() ) { OGR_L_SetAttributeFilter( mOgrLayer, nullptr ); } delete compiler; } else if ( mSource->mSubsetString.isEmpty() ) { OGR_L_SetAttributeFilter( mOgrLayer, nullptr ); } //start with first feature rewind(); }
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; }
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 ( 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::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()[idx] ); } else { // Lets assume it's an expression QgsExpression* expression = new QgsExpression(( *columnIt )->attribute() ); context->lastScope()->setVariable( QString( "_rownum_" ), 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; }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( NULL ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; QString whereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } whereClause = whereClauses.join( " AND " ); // preparing the SQL statement if ( !prepareStatement( whereClause ) ) { // some error occurred sqliteStatement = NULL; close(); return; } }
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextProvider* p, const QgsFeatureRequest& request ) : QgsAbstractFeatureIterator( request ) , P( p ) { P->mActiveIterators << this; // Determine mode to use based on request... QgsDebugMsg( "Setting up QgsDelimitedTextIterator" ); // Does the layer have geometry - will revise later to determine if we actually need to // load it. bool hasGeometry = P->mGeomRep != QgsDelimitedTextProvider::GeomNone; // Does the layer have an explicit or implicit subset (implicit subset is if we have geometry which can // be invalid) mTestSubset = P->mSubsetExpression; mTestGeometry = false; mMode = FileScan; if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QgsDebugMsg( "Configuring for returning single id" ); mFeatureIds.append( request.filterFid() ); mMode = FeatureIds; mTestSubset = false; } // If have geometry and testing geometry then evaluate options... // If we don't have geometry then all records pass geometry filter. // CC: 2013-05-09 // Not sure about intended relationship between filtering on geometry and // requesting no geometry? Have preserved current logic of ignoring spatial filter // if not requesting geometry. else if ( request.filterType() == QgsFeatureRequest::FilterRect && hasGeometry ) { QgsDebugMsg( "Configuring for rectangle select" ); mTestGeometry = true; // Exact intersection test only applies for WKT geometries mTestGeometryExact = mRequest.flags() & QgsFeatureRequest::ExactIntersect && P->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt; QgsRectangle rect = request.filterRect(); // If request doesn't overlap extents, then nothing to return if ( ! rect.intersects( P->extent() ) ) { QgsDebugMsg( "Rectangle outside layer extents - no features to return" ); mMode = FeatureIds; } // If the request extents include the entire layer, then revert to // a file scan else if ( rect.contains( P->extent() ) ) { QgsDebugMsg( "Rectangle contains layer extents - bypass spatial filter" ); mTestGeometry = false; } // If we have a spatial index then use it. The spatial index already accounts // for the subset. Also means we don't have to test geometries unless doing exact // intersection else if ( P->mUseSpatialIndex ) { mFeatureIds = P->mSpatialIndex->intersects( rect ); // Sort for efficient sequential retrieval qSort( mFeatureIds.begin(), mFeatureIds.end() ); QgsDebugMsg( QString( "Layer has spatial index - selected %1 features from index" ).arg( mFeatureIds.size() ) ); mMode = FeatureIds; mTestSubset = false; mTestGeometry = mTestGeometryExact; } } // If we have a subset index then use it.. if ( mMode == FileScan && P->mUseSubsetIndex ) { QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( P->mSubsetIndex.size() ) ); mTestSubset = false; mMode = SubsetIndex; } // Otherwise just have to scan the file if ( mMode == FileScan ) { QgsDebugMsg( "File will be scanned for desired features" ); } // If the layer has geometry, do we really need to load it? // We need it if it is asked for explicitly in the request, // if we are testing geometry (ie spatial filter), or // if testing the subset expression. if ( hasGeometry && ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || mTestGeometry || ( mTestSubset && P->mSubsetExpression->needsGeometry() ) ) ) { mLoadGeometry = true; } else { QgsDebugMsgLevel( "Feature geometries not required", 4 ); mLoadGeometry = false; } QgsDebugMsg( QString( "Iterator is scanning file: " ) + ( scanningFile() ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is loading geometries: " ) + ( loadGeometry() ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is testing geometries: " ) + ( testGeometry() ? "Yes" : "No" ) ); QgsDebugMsg( QString( "Iterator is testing subset: " ) + ( testSubset() ? "Yes" : "No" ) ); rewind(); }
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; }
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(); }
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(); }
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 ); } } }
QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleProvider *p, const QgsFeatureRequest &request ) : QgsAbstractFeatureIterator( request ) , P( p ) , mRewind( false ) { P->mActiveIterators << this; mQry = QSqlQuery( *P->mConnection ); if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { mAttributeList = mRequest.subsetOfAttributes(); if ( mAttributeList.isEmpty() ) mAttributeList = P->attributeIndexes(); } else mAttributeList = P->attributeIndexes(); QString whereClause; switch ( request.filterType() ) { case QgsFeatureRequest::FilterExpression: break; case QgsFeatureRequest::FilterRect: if ( !P->mGeometryColumn.isNull() ) { QgsRectangle rect( mRequest.filterRect() ); QString bbox = QString( "mdsys.sdo_geometry(2003,%1,NULL," "mdsys.sdo_elem_info_array(1,1003,3)," "mdsys.sdo_ordinate_array(%2,%3,%4,%5)" ")" ) .arg( P->mSrid < 1 ? "NULL" : QString::number( P->mSrid ) ) .arg( qgsDoubleToString( rect.xMinimum() ) ) .arg( qgsDoubleToString( rect.yMinimum() ) ) .arg( qgsDoubleToString( rect.xMaximum() ) ) .arg( qgsDoubleToString( rect.yMaximum() ) ); if ( !P->mSpatialIndex.isNull() ) { whereClause = QString( "sdo_filter(%1,%2)='TRUE'" ).arg( P->quotedIdentifier( P->mGeometryColumn ) ).arg( bbox ); #if 0 if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { whereClause += QString( " AND sdo_relate(%1,%2,'mask=ANYINTERACT')='TRUE'" ) .arg( quotedIdentifier( P->mGeometryColumn ) ) .arg( bbox ); } #endif } } break; case QgsFeatureRequest::FilterFid: whereClause = P->whereClause( request.filterFid() ); break; case QgsFeatureRequest::FilterFids: whereClause = P->whereClause( request.filterFids() ); break; case QgsFeatureRequest::FilterNone: break; } if ( P->mRequestedGeomType != QGis::WKBUnknown && P->mRequestedGeomType != P->mDetectedGeomType ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += QgsOracleConn::databaseTypeFilter( "featureRequest", P->mGeometryColumn, P->mRequestedGeomType ); } if ( !P->mSqlWhereClause.isEmpty() ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += "(" + P->mSqlWhereClause + ")"; } if ( !openQuery( whereClause ) ) return; }
QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWidget *parent, Qt::WindowFlags flags ) : QDialog( parent, flags ) , mDock( 0 ) , mLayer( theLayer ) , mRubberBand( 0 ) { setupUi( this ); // Fix selection color on loosing focus (Windows) setStyleSheet( QgisApp::instance()->styleSheet() ); setAttribute( Qt::WA_DeleteOnClose ); QSettings settings; // Initialize the window geometry restoreGeometry( settings.value( "/Windows/BetterAttributeTable/geometry" ).toByteArray() ); QgsAttributeEditorContext context; myDa = new QgsDistanceArea(); myDa->setSourceCrs( mLayer->crs() ); myDa->setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapSettings().hasCrsTransformEnabled() ); myDa->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); context.setDistanceArea( *myDa ); context.setVectorLayerTools( QgisApp::instance()->vectorLayerTools() ); QgsFeatureRequest r; if ( mLayer->geometryType() != QGis::NoGeometry && settings.value( "/qgis/attributeTableBehaviour", QgsAttributeTableFilterModel::ShowAll ).toInt() == QgsAttributeTableFilterModel::ShowVisible ) { QgsMapCanvas *mc = QgisApp::instance()->mapCanvas(); QgsRectangle extent( mc->mapSettings().mapToLayerCoordinates( theLayer, mc->extent() ) ); r.setFilterRect( extent ); QgsGeometry *g = QgsGeometry::fromRect( extent ); mRubberBand = new QgsRubberBand( mc, true ); mRubberBand->setToGeometry( g, theLayer ); delete g; mActionShowAllFilter->setText( tr( "Show All Features In Initial Canvas Extent" ) ); } // Initialize dual view mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), r, context ); // 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( "/mActionFilter.svg" ); mActionShowAllFilter->setIcon( filterIcon ); mActionAdvancedFilter->setIcon( filterIcon ); mActionSelectedFilter->setIcon( filterIcon ); mActionVisibleFilter->setIcon( filterIcon ); mActionEditedFilter->setIcon( filterIcon ); // Connect filter signals connect( mActionAdvancedFilter, SIGNAL( triggered() ), SLOT( filterExpressionBuilder() ) ); connect( mActionShowAllFilter, SIGNAL( triggered() ), SLOT( filterShowAll() ) ); connect( mActionSelectedFilter, SIGNAL( triggered() ), SLOT( filterSelected() ) ); connect( mActionVisibleFilter, SIGNAL( triggered() ), SLOT( filterVisible() ) ); connect( mActionEditedFilter, SIGNAL( triggered() ), SLOT( filterEdited() ) ); connect( mFilterActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( filterColumnChanged( QObject* ) ) ); connect( mFilterQuery, SIGNAL( returnPressed() ), SLOT( filterQueryAccepted() ) ); connect( mActionApplyFilter, SIGNAL( triggered() ), SLOT( filterQueryAccepted() ) ); // info from layer to table connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( editingToggled() ) ); connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( editingToggled() ) ); connect( mLayer, SIGNAL( layerDeleted() ), this, SLOT( close() ) ); connect( mLayer, SIGNAL( selectionChanged() ), this, SLOT( updateTitle() ) ); connect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( columnBoxInit() ) ); connect( mLayer, SIGNAL( attributeDeleted( int ) ), this, SLOT( columnBoxInit() ) ); // connect table info to window connect( mMainView, SIGNAL( filterChanged() ), this, SLOT( updateTitle() ) ); // info from table to application connect( this, SIGNAL( saveEdits( QgsMapLayer * ) ), QgisApp::instance(), SLOT( saveEdits( QgsMapLayer * ) ) ); bool myDockFlag = settings.value( "/qgis/dockAttributeTable", false ).toBool(); if ( myDockFlag ) { mDock = new QgsAttributeTableDock( tr( "Attribute table - %1 (%n Feature(s))", "feature count", mMainView->featureCount() ).arg( mLayer->name() ), QgisApp::instance() ); mDock->setAllowedAreas( Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea ); mDock->setWidget( this ); connect( this, SIGNAL( destroyed() ), mDock, SLOT( close() ) ); QgisApp::instance()->addDockWidget( Qt::BottomDockWidgetArea, mDock ); } columnBoxInit(); updateTitle(); mRemoveSelectionButton->setIcon( QgsApplication::getThemeIcon( "/mActionUnselectAttributes.png" ) ); mSelectedToTopButton->setIcon( QgsApplication::getThemeIcon( "/mActionSelectedToTop.png" ) ); mCopySelectedRowsButton->setIcon( QgsApplication::getThemeIcon( "/mActionCopySelected.png" ) ); mZoomMapToSelectedRowsButton->setIcon( QgsApplication::getThemeIcon( "/mActionZoomToSelected.svg" ) ); mPanMapToSelectedRowsButton->setIcon( QgsApplication::getThemeIcon( "/mActionPanToSelected.svg" ) ); mInvertSelectionButton->setIcon( QgsApplication::getThemeIcon( "/mActionInvertSelection.png" ) ); mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( "/mActionToggleEditing.svg" ) ); mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( "/mActionSaveEdits.svg" ) ); mDeleteSelectedButton->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteSelected.svg" ) ); mOpenFieldCalculator->setIcon( QgsApplication::getThemeIcon( "/mActionCalculateField.png" ) ); mAddAttribute->setIcon( QgsApplication::getThemeIcon( "/mActionNewAttribute.png" ) ); mRemoveAttribute->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteAttribute.png" ) ); mTableViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionOpenTable.png" ) ); mAttributeViewButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) ); mExpressionSelectButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionSelect.svg" ) ); mAddFeature->setIcon( QgsApplication::getThemeIcon( "/mActionNewTableRow.png" ) ); // 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; mToggleEditingButton->blockSignals( true ); mToggleEditingButton->setCheckable( true ); mToggleEditingButton->setChecked( mLayer->isEditable() ); mToggleEditingButton->setEnabled(( canChangeAttributes || canDeleteFeatures || canAddAttributes || canDeleteAttributes || canAddFeatures ) && !mLayer->isReadOnly() ); mToggleEditingButton->blockSignals( false ); mSaveEditsButton->setEnabled( mToggleEditingButton->isEnabled() && mLayer->isEditable() ); mAddAttribute->setEnabled(( canChangeAttributes || canAddAttributes ) && mLayer->isEditable() ); mDeleteSelectedButton->setEnabled( canDeleteFeatures && mLayer->isEditable() ); mAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() && mLayer->geometryType() == QGis::NoGeometry ); mAddFeature->setHidden( !canAddFeatures || mLayer->geometryType() != QGis::NoGeometry ); mMainViewButtonGroup->setId( mTableViewButton, QgsDualView::AttributeTable ); mMainViewButtonGroup->setId( mAttributeViewButton, QgsDualView::AttributeEditor ); // Load default attribute table filter QgsAttributeTableFilterModel::FilterMode defaultFilterMode = ( QgsAttributeTableFilterModel::FilterMode ) settings.value( "/qgis/attributeTableBehaviour", QgsAttributeTableFilterModel::ShowAll ).toInt(); switch ( defaultFilterMode ) { case QgsAttributeTableFilterModel::ShowVisible: filterVisible(); break; case QgsAttributeTableFilterModel::ShowSelected: filterSelected(); break; case QgsAttributeTableFilterModel::ShowAll: default: filterShowAll(); break; } mFieldModel = new QgsFieldModel(); mFieldModel->setLayer( mLayer ); mFieldCombo->setModel( mFieldModel ); connect( mRunFieldCalc, SIGNAL( clicked() ), this, SLOT( updateFieldFromExpression() ) ); connect( mRunFieldCalcSelected, SIGNAL( clicked() ), this, SLOT( 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, SIGNAL( fieldChanged( QString, bool ) ), this, SLOT( updateButtonStatus( QString, bool ) ) ); mUpdateExpressionText->setLayer( mLayer ); mUpdateExpressionText->setLeftHandButtonStyle( true ); editingToggled(); }
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayer* layer, const QgsFeatureRequest& request ) : QgsAbstractFeatureIterator( request ), L( layer ) { QgsVectorLayerJoinBuffer* joinBuffer = L->mJoinBuffer; mChangedFeaturesRequest = mRequest; if ( L->editBuffer() ) { mAddedFeatures = QgsFeatureMap( L->editBuffer()->addedFeatures() ); mChangedGeometries = QgsGeometryMap( L->editBuffer()->changedGeometries() ); mDeletedFeatureIds = QgsFeatureIds( L->editBuffer()->deletedFeatureIds() ); mChangedAttributeValues = QgsChangedAttributesMap( L->editBuffer()->changedAttributeValues() ); mAddedAttributes = QList<QgsField>( L->editBuffer()->addedAttributes() ); mDeletedAttributeIds = QgsAttributeList( L->editBuffer()->deletedAttributeIds() ); mChangedFeaturesRequest.setFilterFids( L->editBuffer()->changedAttributeValues().keys().toSet() ); } // prepare joins: may add more attributes to fetch (in order to allow join) if ( joinBuffer->containsJoins() ) prepareJoins(); // by default provider's request is the same mProviderRequest = mRequest; if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // prepare list of attributes to match provider fields QgsAttributeList providerSubset; QgsAttributeList subset = mProviderRequest.subsetOfAttributes(); const QgsFields &pendingFields = L->pendingFields(); int nPendingFields = pendingFields.count(); for ( int i = 0; i < subset.count(); ++i ) { int attrIndex = subset[i]; if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue; if ( L->pendingFields().fieldOrigin( attrIndex ) == QgsFields::OriginProvider ) providerSubset << L->pendingFields().fieldOriginIndex( attrIndex ); } mProviderRequest.setSubsetOfAttributes( providerSubset ); } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { mFetchedFid = false; } else // no filter or filter by rect { if ( L->editBuffer() ) { mChangedFeaturesIterator = L->dataProvider()->getFeatures( mChangedFeaturesRequest ); } else { mProviderIterator = L->dataProvider()->getFeatures( mProviderRequest ); } rewindEditBuffer(); } if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { mRequest.filterExpression()->prepare( L->pendingFields() ); } }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( nullptr ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; bool useFallbackWhereClause = false; QString fallbackWhereClause; QString whereClause; //beware - limitAtProvider needs to be set to false if the request cannot be completely handled //by the provider (eg utilising QGIS expression filters) bool limitAtProvider = ( mRequest.limit() >= 0 ); if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } //IMPORTANT - this MUST be the last clause added! else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { QgsAttributeList attrs = request.subsetOfAttributes(); Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); } if ( request.filterExpression()->needsGeometry() ) { mFetchGeometry = true; } if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSQLiteExpressionCompiler compiler = QgsSQLiteExpressionCompiler( source->mFields ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { useFallbackWhereClause = true; fallbackWhereClause = whereClauses.join( " AND " ); whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); mCompileStatus = ( mExpressionCompiled ? Compiled : PartiallyCompiled ); } } if ( result != QgsSqlExpressionCompiler::Complete ) { //can't apply limit at provider side as we need to check all results using QGIS expressions limitAtProvider = false; } } else { limitAtProvider = false; } }
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.prepend( "(" ).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( QString( "\"%1\"" ).arg( joinFieldName ) ); if ( joinValue.isNull() ) { subsetString += " IS NULL"; } else { QString v = joinValue.toString(); switch ( joinValue.type() ) { case QVariant::Int: case QVariant::LongLong: case QVariant::Double: break; default: case QVariant::String: v.replace( "'", "''" ); v.prepend( "'" ).append( "'" ); break; } subsetString += "=" + v; } 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 ); }
void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest& request ) { // build sql statement mStatement = QString( "SELECT " ); mStatement += QString( "[%1]" ).arg( mSource->mFidColName ); mFidCol = mSource->mFields.indexFromName( mSource->mFidColName ); mAttributesToFetch.append( mFidCol ); bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; Q_FOREACH ( int i, subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList() ) { QString fieldname = mSource->mFields.at( i ).name(); if ( mSource->mFidColName == fieldname ) continue; mStatement += QString( ",[%1]" ).arg( fieldname ); mAttributesToFetch.append( i ); } // get geometry col if ( !( request.flags() & QgsFeatureRequest::NoGeometry ) && mSource->isSpatial() ) { mStatement += QString( ",[%1]" ).arg( mSource->mGeometryColName ); } mStatement += QString( "FROM [%1].[%2]" ).arg( mSource->mSchemaName, mSource->mTableName ); bool filterAdded = false; // set spatial filter if ( !request.filterRect().isNull() && mSource->isSpatial() && !request.filterRect().isEmpty() ) { // polygons should be CCW for SqlGeography QString r; QTextStream foo( &r ); foo.setRealNumberPrecision( 8 ); foo.setRealNumberNotation( QTextStream::FixedNotation ); foo << qgsDoubleToString( request.filterRect().xMinimum() ) << " " << qgsDoubleToString( request.filterRect().yMinimum() ) << ", " << qgsDoubleToString( request.filterRect().xMaximum() ) << " " << qgsDoubleToString( request.filterRect().yMinimum() ) << ", " << qgsDoubleToString( request.filterRect().xMaximum() ) << " " << qgsDoubleToString( request.filterRect().yMaximum() ) << ", " << qgsDoubleToString( request.filterRect().xMinimum() ) << " " << qgsDoubleToString( request.filterRect().yMaximum() ) << ", " << qgsDoubleToString( request.filterRect().xMinimum() ) << " " << qgsDoubleToString( request.filterRect().yMinimum() ); mStatement += QString( " where [%1].STIntersects([%2]::STGeomFromText('POLYGON((%3))',%4)) = 1" ).arg( mSource->mGeometryColName, mSource->mGeometryColType, r, QString::number( mSource->mSRId ) ); filterAdded = true; } // set fid filter if ( request.filterType() == QgsFeatureRequest::FilterFid && !mSource->mFidColName.isEmpty() ) { QString fidfilter = QString( " [%1] = %2" ).arg( mSource->mFidColName, QString::number( request.filterFid() ) ); // set attribute filter if ( !filterAdded ) mStatement += " WHERE "; else mStatement += " AND "; mStatement += fidfilter; filterAdded = true; } if ( !mSource->mSqlWhereClause.isEmpty() ) { if ( !filterAdded ) mStatement += " WHERE (" + mSource->mSqlWhereClause + ")"; else mStatement += " AND (" + mSource->mSqlWhereClause + ")"; } QgsDebugMsg( mStatement ); #if 0 if ( fieldCount == 0 ) { QgsDebugMsg( "QgsMssqlProvider::select no fields have been requested" ); mStatement.clear(); } #endif }
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsDelimitedTextFeatureSource>( source, ownSource, request ) , mTestGeometryExact( false ) { // Determine mode to use based on request... QgsDebugMsg( "Setting up QgsDelimitedTextIterator" ); // Does the layer have geometry - will revise later to determine if we actually need to // load it. bool hasGeometry = mSource->mGeomRep != QgsDelimitedTextProvider::GeomNone; // Does the layer have an explicit or implicit subset (implicit subset is if we have geometry which can // be invalid) mTestSubset = mSource->mSubsetExpression; mTestGeometry = false; mMode = FileScan; if ( !request.filterRect().isNull() && hasGeometry ) { QgsDebugMsg( "Configuring for rectangle select" ); mTestGeometry = true; // Exact intersection test only applies for WKT geometries mTestGeometryExact = mRequest.flags() & QgsFeatureRequest::ExactIntersect && mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt; QgsRectangle rect = request.filterRect(); // If request doesn't overlap extents, then nothing to return if ( ! rect.intersects( mSource->mExtent ) && !mTestSubset ) { QgsDebugMsg( "Rectangle outside layer extents - no features to return" ); mMode = FeatureIds; } // If the request extents include the entire layer, then revert to // a file scan else if ( rect.contains( mSource->mExtent ) && !mTestSubset ) { QgsDebugMsg( "Rectangle contains layer extents - bypass spatial filter" ); mTestGeometry = false; } // If we have a spatial index then use it. The spatial index already accounts // for the subset. Also means we don't have to test geometries unless doing exact // intersection else if ( mSource->mUseSpatialIndex ) { mFeatureIds = mSource->mSpatialIndex->intersects( rect ); // Sort for efficient sequential retrieval qSort( mFeatureIds.begin(), mFeatureIds.end() ); QgsDebugMsg( QString( "Layer has spatial index - selected %1 features from index" ).arg( mFeatureIds.size() ) ); mMode = FeatureIds; mTestSubset = false; mTestGeometry = mTestGeometryExact; } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QgsDebugMsg( "Configuring for returning single id" ); if ( request.filterRect().isNull() || mFeatureIds.contains( request.filterFid() ) ) { mFeatureIds = QList<QgsFeatureId>() << request.filterFid(); } mMode = FeatureIds; mTestSubset = false; } // If have geometry and testing geometry then evaluate options... // If we don't have geometry then all records pass geometry filter. // CC: 2013-05-09 // Not sure about intended relationship between filtering on geometry and // requesting no geometry? Have preserved current logic of ignoring spatial filter // if not requesting geometry. else // If we have a subset index then use it.. if ( mMode == FileScan && mSource->mUseSubsetIndex ) { QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) ); mTestSubset = false; mMode = SubsetIndex; } // Otherwise just have to scan the file if ( mMode == FileScan ) { QgsDebugMsg( "File will be scanned for desired features" ); } // If the layer has geometry, do we really need to load it? // We need it if it is asked for explicitly in the request, // if we are testing geometry (ie spatial filter), or // if testing the subset expression. if ( hasGeometry && ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || mTestGeometry || ( mTestSubset && mSource->mSubsetExpression->needsGeometry() ) || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) ) ) { mLoadGeometry = true; } else { QgsDebugMsgLevel( "Feature geometries not required", 4 ); mLoadGeometry = false; } // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { QgsAttributeList attrs = request.subsetOfAttributes(); Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); }
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 }
QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>( source, ownSource, request ) , mFeatureQueueSize( sFeatureQueueSize ) , mFetched( 0 ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mOrderByCompiled( false ) , mLastFetch( false ) , mFilterRequiresGeometry( false ) { if ( !source->mTransactionConnection ) { mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo ); mIsTransactionConnection = false; } else { mConn = source->mTransactionConnection; mIsTransactionConnection = true; } if ( !mConn ) { mClosed = true; iteratorClosed(); return; } mCursorName = mConn->uniqueCursorName(); QString whereClause; bool limitAtProvider = ( mRequest.limit() >= 0 ); bool useFallbackWhereClause = false; QString fallbackWhereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { whereClause = whereClauseRect(); } if ( !mSource->mSqlWhereClause.isEmpty() ) { whereClause = QgsPostgresUtils::andWhereClauses( whereClause, '(' + mSource->mSqlWhereClause + ')' ); } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString fidsWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { QgsAttributeList attrs = mRequest.subsetOfAttributes(); Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); } mFilterRequiresGeometry = request.filterExpression()->needsGeometry(); if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { //IMPORTANT - this MUST be the last clause added! QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source ); if ( compiler.compile( request.filterExpression() ) == QgsSqlExpressionCompiler::Complete ) { useFallbackWhereClause = true; fallbackWhereClause = whereClause; whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() ); mExpressionCompiled = true; mCompileStatus = Compiled; } else { limitAtProvider = false; } } else { limitAtProvider = false; } }