[Qgis-developer] API proposal: fetching of features

Martin Dobias wonder.sk at gmail.com
Thu Oct 27 13:07:43 EDT 2011


Hi devs

I believe that a good API for iteration over features of vector layers
is quite crucial for a GIS because it is being used very heavily in
application code and plguins. Recently after reading an article [1]
and looking at the hints for designing qt-style api [2] I have
realized that the select() method (that tells what should be fetched)
is quite ugly. How intuitive is the code for some common scenarios?
Not much :-)
1. fetch everything from a given extent
  layer->select( pendingAllAttributesList(), extent, true );
2. fetch geometries
  layer->select( QgsAttributeList(), extent, true );
3. fetch features that really intersect the given extent (e.g. for
identification)
  layer->select( pendingAllAttributesList(), extent, true, true );

The problems:
- boolean parameter trap - a user may pass two boolean values, but it
is unclear what is their purpose
- fetching all attributes is very common, however it needs a bit weird
call to fetch the list of all indexes
- adding further options to the select may result in too many parameters

How to address the problems:
- use flags instead of booleans: e.g. "FetchGeometry | UseIntesect"
flags makes more sense than "true, true"
- use a temporary object for fetching data that has sensible defaults
(fetch all features=yes, fetch all attributes=yes, fetch
geometries=yes, use intersect=no) and that can be extended later
1. fetch everything from a given extent
  layer->select( QgsFeatureRequest( extent ) );
2. fetch geometries
  QgsFeatureRequest request( extent );
  request.setAttributesToFetch( QgsAttributeList() );
  layer->select( request );
3. fetch features that really intersect the given extent
  QgsFeatureRequest request( extent );
  request.setUseIntersect( true ); -- or -- request.setFlags( UseIntersect );
  layer->select( request );

It seems that in near future we could add few more useful options for
fetching features - adding them as further parameters in select()
sounds like a bad idea:
- filter based on an expression:
  QgsFeatureRequest request;
  request.setExpression( "type = 5 and $area > 100" );
  layer->select( request );
- fetch just some features:
  QgsFeatureRequest request;
  request.setLimit( 10 );
  layer->select( request );

Maybe featureAtId() call also should be integrated into this API:
  layer->select( QgsFeatureRequest( featureId ) );
  // + one call to nextFeature

This should ideally be combined with the feature iterators I have
introduced in the threading branch - that is use a getFeatures()
method instead of select() and use an iterator object for
reading/iterating over the features (the iterator has some advantages
and it is necessary in scenarios with multiple threads to provide
locking):
  QgsFeatureRequest request;
  // modify request if it should be more specific
  QgsFeatureIterator fi = layer->getFeatures( request );
  while ( fi.nextFeature( feat ) )
  { do something }

What do you think? I would like to gather your opinions - is it
good/bad/complex/inefficient/nonintuitive/verbose/...?
This is a piece of API that is used very heavily, so we should polish
it as much as possible. Of course the proposal is directed towards
QGIS 2.0. While trying to port the QgsFeatureIterator changes from
threading branch to the current master branch I thought it will be
better to do these changes at the same time.

Regards
Martin


[1] http://ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html
[2] http://doc.qt.nokia.com/qq/qq13-apis.html


More information about the Qgis-developer mailing list