bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QSet<QString>& attributeNames ) { QgsDiagramLayerSettings& s2 = mSettings; const QgsMapSettings& mapSettings = mEngine->mapSettings(); if ( mapSettings.hasCrsTransformEnabled() ) { if ( context.coordinateTransform().isValid() ) // this is context for layer rendering - use its CT as it includes correct datum transform s2.setCoordinateTransform( context.coordinateTransform() ); else // otherwise fall back to creating our own CT - this one may not have the correct datum transform! s2.setCoordinateTransform( QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ) ); } else { s2.setCoordinateTransform( QgsCoordinateTransform() ); } s2.setRenderer( mDiagRenderer ); //add attributes needed by the diagram renderer attributeNames.unite( s2.referencedFields( context.expressionContext(), mFields ) ); return true; }
QgsCoordinateTransform QgsMapSettings::layerTransform( const QgsMapLayer *layer ) const { if ( !layer ) return QgsCoordinateTransform(); return QgsCoordinateTransform( layer->crs(), mDestCRS, mTransformContext ); }
QgsCachedFeatureWriterIterator::QgsCachedFeatureWriterIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest ) : QgsAbstractFeatureIterator( featureRequest ) , mVectorLayerCache( vlCache ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() ) { mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } mFeatIt = vlCache->layer()->getFeatures( mRequest ); }
void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QgsLabelingEngineV2* labelingEngine2, QPainter* painter ) { QgsDebugMsg( "Draw labeling start" ); QTime t; t.start(); // Reset the composition mode before rendering the labels painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile renderContext = QgsRenderContext::fromMapSettings( settings ); renderContext.setPainter( painter ); renderContext.setLabelingEngine( labelingEngine ); #if !defined(QGIS_DISABLE_DEPRECATED) // old labeling - to be removed at some point... drawOldLabeling( settings, renderContext ); #endif drawNewLabeling( settings, renderContext, labelingEngine ); if ( labelingEngine2 ) { // set correct extent renderContext.setExtent( settings.visibleExtent() ); renderContext.setCoordinateTransform( QgsCoordinateTransform() ); labelingEngine2->run( renderContext ); } QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) ); }
QgsCoordinateTransform QgsCoordinateTransformCache::transform( const QString& srcAuthId, const QString& destAuthId, int srcDatumTransform, int destDatumTransform ) { QList< QgsCoordinateTransform > values = mTransforms.values( qMakePair( srcAuthId, destAuthId ) ); QList< QgsCoordinateTransform >::const_iterator valIt = values.constBegin(); for ( ; valIt != values.constEnd(); ++valIt ) { if (( *valIt ).isValid() && ( *valIt ).sourceDatumTransform() == srcDatumTransform && ( *valIt ).destinationDatumTransform() == destDatumTransform ) { return *valIt; } } //not found, insert new value QgsCoordinateReferenceSystem srcCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( srcAuthId ); QgsCoordinateReferenceSystem destCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( destAuthId ); QgsCoordinateTransform ct = QgsCoordinateTransform( srcCrs, destCrs ); ct.setSourceDatumTransform( srcDatumTransform ); ct.setDestinationDatumTransform( destDatumTransform ); ct.initialise(); mTransforms.insertMulti( qMakePair( srcAuthId, destAuthId ), ct ); return ct; }
void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsLabelingEngine* labelingEngine2, QPainter* painter ) { QgsDebugMsg( "Draw labeling start" ); QTime t; t.start(); // Reset the composition mode before rendering the labels painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile renderContext = QgsRenderContext::fromMapSettings( settings ); renderContext.setPainter( painter ); if ( labelingEngine2 ) { // set correct extent renderContext.setExtent( settings.visibleExtent() ); renderContext.setCoordinateTransform( QgsCoordinateTransform() ); labelingEngine2->run( renderContext ); } QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) ); }
QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings &mapSettings ) { QgsRenderContext ctx; ctx.setMapToPixel( mapSettings.mapToPixel() ); ctx.setExtent( mapSettings.visibleExtent() ); ctx.setFlag( DrawEditingInfo, mapSettings.testFlag( QgsMapSettings::DrawEditingInfo ) ); ctx.setFlag( ForceVectorOutput, mapSettings.testFlag( QgsMapSettings::ForceVectorOutput ) ); ctx.setFlag( UseAdvancedEffects, mapSettings.testFlag( QgsMapSettings::UseAdvancedEffects ) ); ctx.setFlag( UseRenderingOptimization, mapSettings.testFlag( QgsMapSettings::UseRenderingOptimization ) ); ctx.setCoordinateTransform( QgsCoordinateTransform() ); ctx.setSelectionColor( mapSettings.selectionColor() ); ctx.setFlag( DrawSelection, mapSettings.testFlag( QgsMapSettings::DrawSelection ) ); ctx.setFlag( DrawSymbolBounds, mapSettings.testFlag( QgsMapSettings::DrawSymbolBounds ) ); ctx.setFlag( RenderMapTile, mapSettings.testFlag( QgsMapSettings::RenderMapTile ) ); ctx.setFlag( Antialiasing, mapSettings.testFlag( QgsMapSettings::Antialiasing ) ); ctx.setFlag( RenderPartialOutput, mapSettings.testFlag( QgsMapSettings::RenderPartialOutput ) ); ctx.setScaleFactor( mapSettings.outputDpi() / 25.4 ); // = pixels per mm ctx.setRendererScale( mapSettings.scale() ); ctx.setExpressionContext( mapSettings.expressionContext() ); ctx.setSegmentationTolerance( mapSettings.segmentationTolerance() ); ctx.setSegmentationToleranceType( mapSettings.segmentationToleranceType() ); ctx.mDistanceArea.setSourceCrs( mapSettings.destinationCrs() ); ctx.mDistanceArea.setEllipsoid( mapSettings.ellipsoid() ); //this flag is only for stopping during the current rendering progress, //so must be false at every new render operation ctx.setRenderingStopped( false ); return ctx; }
QgsFeatureList QgsAddXYFieldsAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { if ( mTransformNeedsInitialization ) { mTransform = QgsCoordinateTransform( mSourceCrs, mCrs, context.transformContext() ); mTransformNeedsInitialization = false; } QVariant x; QVariant y; if ( feature.hasGeometry() ) { if ( feature.geometry().isMultipart() ) throw QgsProcessingException( QObject::tr( "Multipoint features are not supported - please convert to single point features first." ) ); const QgsPointXY point = feature.geometry().asPoint(); try { const QgsPointXY transformed = mTransform.transform( point ); x = transformed.x(); y = transformed.y(); } catch ( QgsCsException & ) { feedback->reportError( QObject::tr( "Could not transform point to destination CRS" ) ); } } QgsFeature f = feature; QgsAttributes attributes = f.attributes(); attributes << x << y; f.setAttributes( attributes ); return QgsFeatureList() << f; }
void QgsGeometryValidationDock::updateLayerTransform() { if ( !mMapCanvas->currentLayer() ) return; mLayerTransform = QgsCoordinateTransform( mMapCanvas->currentLayer()->crs(), mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() ); }
QgsRectangle QgsMapSettings::computeExtentForScale( const QgsPointXY &point, double scale, const QgsCoordinateReferenceSystem &sourceCrs ) const { QgsPointXY center = QgsCoordinateTransform( sourceCrs, destinationCrs(), mTransformContext ).transform( point ); // Output width in inches double outWIn = outputSize().width() / double( outputDpi() ); // Desired visible width (honouring scale) double scaledWIn = outWIn * scale; if ( mapUnits() == QgsUnitTypes::DistanceDegrees ) { // Start with an 1x1 extent around the center QgsRectangle ext( center.x() - 0.5, center.y() - 0.5, center.x() + 0.5, center.y() + 0.5 ); // Get scale at extent, and then scale extent to the desired scale double testScale = mScaleCalculator.calculate( ext, outputSize().width() ); ext.scale( scale / testScale ); return ext; } // Conversion from inches to mapUnits double conversionFactor = 12 * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceFeet, mapUnits() ); double delta = 0.5 * scaledWIn * conversionFactor; return QgsRectangle( center.x() - delta, center.y() - delta, center.x() + delta, center.y() + delta ); }
void QgsInvertedPolygonRenderer::startRender( QgsRenderContext &context, const QgsFields &fields ) { QgsFeatureRenderer::startRender( context, fields ); if ( !mSubRenderer ) { return; } // first call start render on the sub renderer mSubRenderer->startRender( context, fields ); mFeaturesCategories.clear(); mSymbolCategories.clear(); mFeatureDecorations.clear(); mFields = fields; // We compute coordinates of the extent which will serve as exterior ring // for the final polygon // It must be computed in the destination CRS if reprojection is enabled. const QgsMapToPixel &mtp( context.mapToPixel() ); if ( !context.painter() ) { return; } // convert viewport to dest CRS QRect e( context.painter()->viewport() ); // add some space to hide borders and tend to infinity e.adjust( -e.width() * 5, -e.height() * 5, e.width() * 5, e.height() * 5 ); QgsPolylineXY exteriorRing; exteriorRing << mtp.toMapCoordinates( e.topLeft() ); exteriorRing << mtp.toMapCoordinates( e.topRight() ); exteriorRing << mtp.toMapCoordinates( e.bottomRight() ); exteriorRing << mtp.toMapCoordinates( e.bottomLeft() ); exteriorRing << mtp.toMapCoordinates( e.topLeft() ); // copy the rendering context mContext = context; // If reprojection is enabled, we must reproject during renderFeature // and act as if there is no reprojection // If we don't do that, there is no need to have a simple rectangular extent // that covers the whole screen // (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general) if ( context.coordinateTransform().isValid() ) { // disable projection mContext.setCoordinateTransform( QgsCoordinateTransform() ); // recompute extent so that polygon clipping is correct QRect v( context.painter()->viewport() ); mContext.setExtent( QgsRectangle( mtp.toMapCoordinates( v.topLeft() ), mtp.toMapCoordinates( v.bottomRight() ) ) ); // do we have to recompute the MapToPixel ? } mExtentPolygon.clear(); mExtentPolygon.append( exteriorRing ); }
void QgsGrassRegionEdit::setRegion( const QgsPoint& ul, const QgsPoint& lr ) { mStartPoint = ul; mEndPoint = lr; calcSrcRegion(); drawRegion( canvas(), mRubberBand, mSrcRectangle, mCoordinateTransform, true ); drawRegion( canvas(), mSrcRubberBand, QgsRectangle( mStartPoint, mEndPoint ), QgsCoordinateTransform(), true ); }
void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine ) { if ( labelingEngine && !renderContext.renderingStopped() ) { // set correct extent renderContext.setExtent( settings.visibleExtent() ); renderContext.setCoordinateTransform( QgsCoordinateTransform() ); labelingEngine->drawLabeling( renderContext ); labelingEngine->exit(); } }
QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsMemoryFeatureSource>( source, ownSource, request ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mSource->mSubsetString.isEmpty() ) { mSubsetExpression = qgis::make_unique< QgsExpression >( mSource->mSubsetString ); mSubsetExpression->prepare( &mSource->mExpressionContext ); } if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { mSelectRectGeom = QgsGeometry::fromRect( mFilterRect ); mSelectRectEngine.reset( QgsGeometry::createGeometryEngine( mSelectRectGeom.constGet() ) ); mSelectRectEngine->prepareGeometry(); } // if there's spatial index, use it! // (but don't use it when selection rect is not specified) if ( !mFilterRect.isNull() && mSource->mSpatialIndex ) { mUsingFeatureIdList = true; mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect ); QgsDebugMsg( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ) ); } else if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) { mUsingFeatureIdList = true; QgsFeatureMap::const_iterator it = mSource->mFeatures.constFind( mRequest.filterFid() ); if ( it != mSource->mFeatures.constEnd() ) mFeatureIdList.append( mRequest.filterFid() ); } else { mUsingFeatureIdList = false; } rewind(); }
QgsCoordinateTransform QgsDatumTransformStore::transformation( QgsMapLayer* layer ) const { if ( !layer ) return QgsCoordinateTransform(); QString srcAuthId = layer->crs().authid(); QString dstAuthId = mDestCRS.authid(); if ( srcAuthId == dstAuthId ) { return QgsCoordinateTransform(); } QHash< QString, Entry >::const_iterator ctIt = mEntries.find( layer->id() ); if ( ctIt != mEntries.constEnd() && ctIt->srcAuthId == srcAuthId && ctIt->destAuthId == dstAuthId ) { return QgsCoordinateTransformCache::instance()->transform( ctIt->srcAuthId, ctIt->destAuthId, ctIt->srcDatumTransform, ctIt->destDatumTransform ); } else { return QgsCoordinateTransformCache::instance()->transform( srcAuthId, dstAuthId ); } }
QgsCachedFeatureIterator::QgsCachedFeatureIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest ) : QgsAbstractFeatureIterator( featureRequest ) , mVectorLayerCache( vlCache ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() ) { mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } switch ( featureRequest.filterType() ) { case QgsFeatureRequest::FilterFids: mFeatureIds = featureRequest.filterFids(); break; case QgsFeatureRequest::FilterFid: mFeatureIds = QgsFeatureIds() << featureRequest.filterFid(); break; default: mFeatureIds = mVectorLayerCache->mCache.keys().toSet(); break; } mFeatureIdIterator = mFeatureIds.constBegin(); if ( mFeatureIdIterator == mFeatureIds.constEnd() ) close(); }
QgsGPXFeatureIterator::QgsGPXFeatureIterator( QgsGPXFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsGPXFeatureSource>( source, ownSource, request ) { 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; } rewind(); }
QgsMssqlFeatureIterator::QgsMssqlFeatureIterator( QgsMssqlFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsMssqlFeatureSource>( source, ownSource, request ) { mClosed = false; mParser.IsGeography = mSource->mIsGeography; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } BuildStatement( request ); // connect to the database mDatabase = QgsMssqlProvider::GetDatabase( mSource->mService, mSource->mHost, mSource->mDatabaseName, mSource->mUserName, mSource->mPassword ); if ( !mDatabase.open() ) { QgsDebugMsg( "Failed to open database" ); QgsDebugMsg( mDatabase.lastError().text() ); return; } // create sql query mQuery.reset( new QSqlQuery( mDatabase ) ); // start selection rewind(); }
QgsDb2FeatureIterator::QgsDb2FeatureIterator( QgsDb2FeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsDb2FeatureSource>( source, ownSource, request ) { mClosed = false; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } BuildStatement( request ); // connect to the database QString errMsg; mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg ); if ( !errMsg.isEmpty() ) { QgsDebugMsg( "Failed to open database: " + errMsg ); return; } // create sql query mQuery.reset( new QSqlQuery( mDatabase ) ); // start selection rewind(); }
QgsFeature QgsTransformAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback * ) { QgsFeature feature = f; if ( !mCreatedTransform ) { mCreatedTransform = true; mTransform = QgsCoordinateTransform( sourceCrs(), mDestCrs, mTransformContext ); } if ( feature.hasGeometry() ) { QgsGeometry g = feature.geometry(); if ( g.transform( mTransform ) == 0 ) { feature.setGeometry( g ); } else { feature.clearGeometry(); } } return feature; }
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 }
QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsVirtualLayerFeatureSource>( source, ownSource, request ) { // NOTE: this is really bad and should be removed. // it's only here to guard mSource->mSqlite - because if the provider is removed // then mSqlite will be meaningless. // this needs to be totally reworked so that mSqlite no longer depends on the provider // and can be fully encapsulated in the source if ( !mSource->mProvider ) { close(); return; } 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; } try { QString tableName = mSource->mTableName; QStringList wheres; QString offset; QString subset = mSource->mSubset; if ( !subset.isEmpty() ) { wheres << subset; } if ( !mSource->mDefinition.uid().isNull() ) { // filters are only available when a column with unique id exists if ( mSource->mDefinition.hasDefinedGeometry() && !mFilterRect.isNull() ) { bool do_exact = request.flags() & QgsFeatureRequest::ExactIntersect; QString mbr = QStringLiteral( "%1,%2,%3,%4" ).arg( mFilterRect.xMinimum() ).arg( mFilterRect.yMinimum() ).arg( mFilterRect.xMaximum() ).arg( mFilterRect.yMaximum() ); wheres << quotedColumn( mSource->mDefinition.geometryField() ) + " is not null"; wheres << QStringLiteral( "%1Intersects(%2,BuildMbr(%3))" ) .arg( do_exact ? "" : "Mbr", quotedColumn( mSource->mDefinition.geometryField() ), mbr ); } else if ( request.filterType() == QgsFeatureRequest::FilterFid ) { wheres << QStringLiteral( "%1=%2" ) .arg( quotedColumn( mSource->mDefinition.uid() ) ) .arg( request.filterFid() ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString values = quotedColumn( mSource->mDefinition.uid() ) + " IN ("; bool first = true; const auto constFilterFids = request.filterFids(); for ( QgsFeatureId v : constFilterFids ) { if ( !first ) { values += QLatin1String( "," ); } first = false; values += QString::number( v ); } values += QLatin1String( ")" ); wheres << values; } } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { if ( request.filterFid() >= 0 ) offset = QStringLiteral( " LIMIT 1 OFFSET %1" ).arg( request.filterFid() ); else // never return a feature if the id is negative offset = QStringLiteral( " LIMIT 0" ); } else if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { // if an exact intersection is requested, prepare the geometry to intersect QgsGeometry rectGeom = QgsGeometry::fromRect( mFilterRect ); mRectEngine.reset( QgsGeometry::createGeometryEngine( rectGeom.constGet() ) ); mRectEngine->prepareGeometry(); } } if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // copy only selected fields const auto subsetOfAttributes = request.subsetOfAttributes(); for ( int idx : subsetOfAttributes ) { mAttributes << idx; } // ensure that all attributes required for expression filter are being fetched if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { const auto constReferencedColumns = request.filterExpression()->referencedColumns(); for ( const QString &field : constReferencedColumns ) { int attrIdx = mSource->mFields.lookupField( field ); if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); for ( int attrIdx : usedAttributeIndices ) { if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } } else { mAttributes = mSource->mFields.allAttributesList(); } QString columns; { // the first column is always the uid (or 0) if ( !mSource->mDefinition.uid().isNull() ) { columns = quotedColumn( mSource->mDefinition.uid() ); } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { columns = QString::number( request.filterFid() ); } else { columns = QStringLiteral( "0" ); } } const auto constMAttributes = mAttributes; for ( int i : constMAttributes ) { columns += QLatin1String( "," ); QString cname = mSource->mFields.at( i ).name().toLower(); columns += quotedColumn( cname ); } } // the last column is the geometry, if any if ( ( !( request.flags() & QgsFeatureRequest::NoGeometry ) || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) ) && !mSource->mDefinition.geometryField().isNull() && mSource->mDefinition.geometryField() != QLatin1String( "*no*" ) ) { columns += "," + quotedColumn( mSource->mDefinition.geometryField() ); } mSqlQuery = "SELECT " + columns + " FROM " + tableName; if ( !wheres.isEmpty() ) { mSqlQuery += " WHERE " + wheres.join( QStringLiteral( " AND " ) ); } if ( !offset.isEmpty() ) { mSqlQuery += offset; } mQuery.reset( new Sqlite::Query( mSource->mSqlite, mSqlQuery ) ); mFid = 0; } catch ( std::runtime_error &e ) { QgsMessageLog::logMessage( e.what(), QObject::tr( "VLayer" ) ); close(); } }
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(); }
void QgsArcGisServiceSourceSelect::addButtonClicked() { if ( treeView->selectionModel()->selectedRows().isEmpty() ) { return; } QgsOwsConnection connection( mServiceName, cmbConnections->currentText() ); QString pCrsString( labelCoordRefSys->text() ); QgsCoordinateReferenceSystem pCrs( pCrsString ); //prepare canvas extent info for layers with "cache features" option not set QgsRectangle extent; QgsCoordinateReferenceSystem canvasCrs; if ( mapCanvas() ) { extent = mapCanvas()->extent(); canvasCrs = mapCanvas()->mapSettings().destinationCrs(); } //does canvas have "on the fly" reprojection set? if ( pCrs.isValid() && canvasCrs.isValid() ) { try { Q_NOWARN_DEPRECATED_PUSH extent = QgsCoordinateTransform( canvasCrs, pCrs ).transform( extent ); Q_NOWARN_DEPRECATED_POP QgsDebugMsg( QStringLiteral( "canvas transform: Canvas CRS=%1, Provider CRS=%2, BBOX=%3" ) .arg( canvasCrs.authid(), pCrs.authid(), extent.asWktCoordinates() ) ); } catch ( const QgsCsException & ) { // Extent is not in range for specified CRS, leave extent empty. } } //create layers that user selected from this feature source QModelIndexList list = treeView->selectionModel()->selectedRows(); for ( int i = 0; i < list.size(); i++ ) { //add a wfs layer to the map QModelIndex idx = mModelProxy->mapToSource( list[i] ); if ( !idx.isValid() ) { continue; } int row = idx.row(); QString layerTitle = mModel->item( row, 0 )->text(); //layer title/id QString layerName = mModel->item( row, 1 )->text(); //layer name bool cacheFeatures = mServiceType == FeatureService ? mModel->item( row, 3 )->checkState() == Qt::Checked : false; QString filter = mServiceType == FeatureService ? mModel->item( row, 4 )->text() : QString(); //optional filter specified by user if ( cbxUseTitleLayerName->isChecked() && !layerTitle.isEmpty() ) { layerName = layerTitle; } QgsRectangle layerExtent; if ( mServiceType == FeatureService && ( cbxFeatureCurrentViewExtent->isChecked() || !cacheFeatures ) ) { layerExtent = extent; } QString uri = getLayerURI( connection, layerTitle, layerName, pCrsString, filter, layerExtent ); QgsDebugMsg( "Layer " + layerName + ", uri: " + uri ); addServiceLayer( uri, layerName ); } accept(); }
QgsAfsProvider::QgsAfsProvider( const QString& uri ) : QgsVectorDataProvider( uri ) , mValid( false ) , mGeometryType( QgsWkbTypes::Unknown ) , mObjectIdFieldIdx( -1 ) { mDataSource = QgsDataSourceUri( uri ); // Set CRS mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mDataSource.param( "crs" ) ); // Get layer info QString errorTitle, errorMessage; QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mDataSource.param( "url" ), errorTitle, errorMessage ); if ( layerData.isEmpty() ) { pushError( errorTitle + ": " + errorMessage ); appendError( QgsErrorMessage( tr( "getLayerInfo failed" ), "AFSProvider" ) ); return; } mLayerName = layerData["name"].toString(); mLayerDescription = layerData["description"].toString(); // Set extent QStringList coords = mDataSource.param( "bbox" ).split( "," ); if ( coords.size() == 4 ) { bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) mExtent = QgsRectangle(); } if ( mExtent.isEmpty() ) { QVariantMap layerExtentMap = layerData["extent"].toMap(); bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; mExtent.setXMinimum( layerExtentMap["xmin"].toDouble( &xminOk ) ); mExtent.setYMinimum( layerExtentMap["ymin"].toDouble( &yminOk ) ); mExtent.setXMaximum( layerExtentMap["xmax"].toDouble( &xmaxOk ) ); mExtent.setYMaximum( layerExtentMap["ymax"].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) { appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), "AFSProvider" ) ); return; } QgsCoordinateReferenceSystem extentCrs = QgsArcGisRestUtils::parseSpatialReference( layerExtentMap["spatialReference"].toMap() ); if ( !extentCrs.isValid() ) { appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), "AFSProvider" ) ); return; } mExtent = QgsCoordinateTransform( extentCrs, mSourceCRS ).transformBoundingBox( mExtent ); } // Read fields foreach ( const QVariant& fieldData, layerData["fields"].toList() ) { QVariantMap fieldDataMap = fieldData.toMap(); QString fieldName = fieldDataMap["name"].toString(); QVariant::Type type = QgsArcGisRestUtils::mapEsriFieldType( fieldDataMap["type"].toString() ); if ( fieldName == "geometry" || type == QVariant::Invalid ) { QgsDebugMsg( QString( "Skipping unsupported (or possibly geometry) field" ).arg( fieldName ) ); continue; } QgsField field( fieldName, type, fieldDataMap["type"].toString(), fieldDataMap["length"].toInt() ); mFields.append( field ); } // Determine geometry type bool hasM = layerData["hasM"].toBool(); bool hasZ = layerData["hasZ"].toBool(); mGeometryType = QgsArcGisRestUtils::mapEsriGeometryType( layerData["geometryType"].toString() ); if ( mGeometryType == QgsWkbTypes::Unknown ) { appendError( QgsErrorMessage( tr( "Failed to determine geometry type" ), "AFSProvider" ) ); return; } mGeometryType = QgsWkbTypes::zmType( mGeometryType, hasZ, hasM ); // Read OBJECTIDs of all features: these may not be a continuous sequence, // and we need to store these to iterate through the features. This query // also returns the name of the ObjectID field. QVariantMap objectIdData = QgsArcGisRestUtils::getObjectIds( mDataSource.param( "url" ), errorTitle, errorMessage ); if ( objectIdData.isEmpty() ) { appendError( QgsErrorMessage( tr( "getObjectIds failed: %1 - %2" ).arg( errorTitle ).arg( errorMessage ), "AFSProvider" ) ); return; } if ( !objectIdData["objectIdFieldName"].isValid() || !objectIdData["objectIds"].isValid() ) { appendError( QgsErrorMessage( tr( "Failed to determine objectIdFieldName and/or objectIds" ), "AFSProvider" ) ); return; } mObjectIdFieldName = objectIdData["objectIdFieldName"].toString(); for ( int idx = 0, nIdx = mFields.count(); idx < nIdx; ++idx ) { if ( mFields.at( idx ).name() == mObjectIdFieldName ) { mObjectIdFieldIdx = idx; break; } } foreach ( const QVariant& objectId, objectIdData["objectIds"].toList() ) { mObjectIds.append( objectId.toInt() ); } mValid = true; }
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request ) , mFetchedFid( false ) { 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; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { mRequest.expressionContext()->setFields( mSource->mFields ); mRequest.filterExpression()->prepare( mRequest.expressionContext() ); if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += mRequest.subsetOfAttributes().toSet(); mRequest.setSubsetOfAttributes( attributeIndexes.toList() ); } } prepareFields(); mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty(); // by default provider's request is the same mProviderRequest = mRequest; // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator, // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect // values if ( mRequest.destinationCrs().isValid() ) { mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() ); } if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // prepare list of attributes to match provider fields QSet<int> providerSubset; QgsAttributeList subset = mProviderRequest.subsetOfAttributes(); int nPendingFields = mSource->mFields.count(); Q_FOREACH ( int attrIndex, subset ) { if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue; if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider ) providerSubset << mSource->mFields.fieldOriginIndex( attrIndex ); } // This is done in order to be prepared to do fallback order bys // and be sure we have the required columns. // TODO: // It would be nicer to first check if we can compile the order by // and only modify the subset if we cannot. if ( !mProviderRequest.orderBy().isEmpty() ) { Q_FOREACH ( const QString &attr, mProviderRequest.orderBy().usedAttributes() ) { providerSubset << mSource->mFields.lookupField( attr ); } }
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsDelimitedTextFeatureSource>( source, ownSource, request ) , mTestSubset( mSource->mSubsetExpression ) { // 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; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } if ( !mFilterRect.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; // If request doesn't overlap extents, then nothing to return if ( ! mFilterRect.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 ( mFilterRect.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( mFilterRect ); // Sort for efficient sequential retrieval std::sort( 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 ( mFilterRect.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(); //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += attrs.toSet(); mRequest.setSubsetOfAttributes( attributeIndexes.toList() ); } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { QgsAttributeList attrs = request.subsetOfAttributes(); Q_FOREACH ( const QString &attr, mRequest.orderBy().usedAttributes() ) { int attrIndex = mSource->mFields.lookupField( attr ); if ( !attrs.contains( attrIndex ) ) attrs << attrIndex; } mRequest.setSubsetOfAttributes( attrs ); }
QgsAfsProvider::QgsAfsProvider( const QString &uri ) : QgsVectorDataProvider( uri ) , mValid( false ) , mObjectIdFieldIdx( -1 ) { mSharedData.reset( new QgsAfsSharedData() ); mSharedData->mGeometryType = QgsWkbTypes::Unknown; mSharedData->mDataSource = QgsDataSourceUri( uri ); // Set CRS mSharedData->mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mSharedData->mDataSource.param( QStringLiteral( "crs" ) ) ); // Get layer info QString errorTitle, errorMessage; const QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); if ( layerData.isEmpty() ) { pushError( errorTitle + ": " + errorMessage ); appendError( QgsErrorMessage( tr( "getLayerInfo failed" ), QStringLiteral( "AFSProvider" ) ) ); return; } mLayerName = layerData[QStringLiteral( "name" )].toString(); mLayerDescription = layerData[QStringLiteral( "description" )].toString(); // Set extent QStringList coords = mSharedData->mDataSource.param( QStringLiteral( "bbox" ) ).split( ',' ); if ( coords.size() == 4 ) { bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; mSharedData->mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); mSharedData->mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); mSharedData->mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); mSharedData->mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) mSharedData->mExtent = QgsRectangle(); } const QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap(); bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; QgsRectangle originalExtent; originalExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) ); originalExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) ); originalExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) ); originalExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) ); if ( mSharedData->mExtent.isEmpty() && ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) ) { appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), QStringLiteral( "AFSProvider" ) ) ); return; } QgsCoordinateReferenceSystem extentCrs = QgsArcGisRestUtils::parseSpatialReference( layerExtentMap[QStringLiteral( "spatialReference" )].toMap() ); if ( mSharedData->mExtent.isEmpty() && !extentCrs.isValid() ) { appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), QStringLiteral( "AFSProvider" ) ) ); return; } if ( xminOk && yminOk && xmaxOk && ymaxOk ) { QgsLayerMetadata::SpatialExtent spatialExtent; spatialExtent.bounds = QgsBox3d( originalExtent ); spatialExtent.extentCrs = extentCrs; QgsLayerMetadata::Extent metadataExtent; metadataExtent.setSpatialExtents( QList< QgsLayerMetadata::SpatialExtent >() << spatialExtent ); mLayerMetadata.setExtent( metadataExtent ); } if ( extentCrs.isValid() ) { mLayerMetadata.setCrs( extentCrs ); } if ( mSharedData->mExtent.isEmpty() ) { mSharedData->mExtent = originalExtent; Q_NOWARN_DEPRECATED_PUSH mSharedData->mExtent = QgsCoordinateTransform( extentCrs, mSharedData->mSourceCRS ).transformBoundingBox( mSharedData->mExtent ); Q_NOWARN_DEPRECATED_POP }
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; Q_FOREACH ( const QString &attr, mRequest.orderBy().usedAttributes() ) { attributeIndexes << mSource->mFields.lookupField( attr ); } attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); }
QgsAfsProvider::QgsAfsProvider( const QString &uri, const ProviderOptions &options ) : QgsVectorDataProvider( uri, options ) { mSharedData.reset( new QgsAfsSharedData() ); mSharedData->mGeometryType = QgsWkbTypes::Unknown; mSharedData->mDataSource = QgsDataSourceUri( uri ); const QString authcfg = mSharedData->mDataSource.authConfigId(); // Set CRS mSharedData->mSourceCRS.createFromString( mSharedData->mDataSource.param( QStringLiteral( "crs" ) ) ); // Get layer info QString errorTitle, errorMessage; const QString referer = mSharedData->mDataSource.param( QStringLiteral( "referer" ) ); if ( !referer.isEmpty() ) mRequestHeaders[ QStringLiteral( "Referer" )] = referer; const QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), authcfg, errorTitle, errorMessage, mRequestHeaders ); if ( layerData.isEmpty() ) { pushError( errorTitle + ": " + errorMessage ); appendError( QgsErrorMessage( tr( "getLayerInfo failed" ), QStringLiteral( "AFSProvider" ) ) ); return; } mLayerName = layerData[QStringLiteral( "name" )].toString(); mLayerDescription = layerData[QStringLiteral( "description" )].toString(); // Set extent QStringList coords = mSharedData->mDataSource.param( QStringLiteral( "bbox" ) ).split( ',' ); bool limitBbox = false; if ( coords.size() == 4 ) { bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; mSharedData->mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); mSharedData->mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); mSharedData->mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); mSharedData->mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) mSharedData->mExtent = QgsRectangle(); else { // user has set a bounding box limit on the layer - so we only EVER fetch features from this extent limitBbox = true; } } const QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap(); bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; QgsRectangle originalExtent; originalExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) ); originalExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) ); originalExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) ); originalExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) ); if ( mSharedData->mExtent.isEmpty() && ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) ) { appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), QStringLiteral( "AFSProvider" ) ) ); return; } QgsCoordinateReferenceSystem extentCrs = QgsArcGisRestUtils::parseSpatialReference( layerExtentMap[QStringLiteral( "spatialReference" )].toMap() ); if ( mSharedData->mExtent.isEmpty() && !extentCrs.isValid() ) { appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), QStringLiteral( "AFSProvider" ) ) ); return; } if ( xminOk && yminOk && xmaxOk && ymaxOk ) { QgsLayerMetadata::SpatialExtent spatialExtent; spatialExtent.bounds = QgsBox3d( originalExtent ); spatialExtent.extentCrs = extentCrs; QgsLayerMetadata::Extent metadataExtent; metadataExtent.setSpatialExtents( QList< QgsLayerMetadata::SpatialExtent >() << spatialExtent ); mLayerMetadata.setExtent( metadataExtent ); } if ( extentCrs.isValid() ) { mLayerMetadata.setCrs( extentCrs ); } if ( mSharedData->mExtent.isEmpty() ) { mSharedData->mExtent = originalExtent; Q_NOWARN_DEPRECATED_PUSH mSharedData->mExtent = QgsCoordinateTransform( extentCrs, mSharedData->mSourceCRS ).transformBoundingBox( mSharedData->mExtent ); Q_NOWARN_DEPRECATED_POP }