Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
OGR provider - virtual sublayers for each geometry type in multi geom…
…etry layers, fixes #7895
  • Loading branch information
blazek committed May 31, 2013
1 parent 4f46379 commit af0f61e
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 31 deletions.
16 changes: 13 additions & 3 deletions src/app/qgisapp.cpp
Expand Up @@ -2847,19 +2847,29 @@ void QgisApp::loadOGRSublayers( QString layertype, QString uri, QStringList list
for ( int i = 0; i < list.size(); i++ )
{
QString composedURI;
QString layerName = list.at( i ).split( ':' ).value( 0 );
QString layerType = list.at( i ).split( ':' ).value( 1 );

if ( layertype != "GRASS" )
{
composedURI = uri + "|layername=" + list.at( i );
composedURI = uri + "|layername=" + layerName;
}
else
{
composedURI = uri + "|layerindex=" + list.at( i );
composedURI = uri + "|layerindex=" + layerName;
}

if ( !layerType.isEmpty() )
{
composedURI += "|geometrytype=" + layerType;
}

// addVectorLayer( composedURI, list.at( i ), "ogr" );

QgsDebugMsg( "Creating new vector layer using " + composedURI );
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, list.at( i ), "ogr" );
QString name = list.at( i );
name.replace( ":", " " );
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, name, "ogr" );
if ( layer && layer->isValid() )
{
myList << layer;
Expand Down
20 changes: 18 additions & 2 deletions src/gui/qgssublayersdialog.cpp
Expand Up @@ -32,7 +32,7 @@ QgsSublayersDialog::QgsSublayersDialog( ProviderType providerType, QString name,
{
setWindowTitle( tr( "Select vector layers to add..." ) );
layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
<< tr( "Nb of features" ) << tr( "Geometry type" ) );
<< tr( "Number of features" ) << tr( "Geometry type" ) );
}
else if ( providerType == QgsSublayersDialog::Gdal )
{
Expand Down Expand Up @@ -69,7 +69,23 @@ QStringList QgsSublayersDialog::selectionNames()
QStringList list;
for ( int i = 0; i < layersTable->selectedItems().size(); i++ )
{
list << layersTable->selectedItems().at( i )->text( 1 );
// If there are more sub layers of the same name (virtual for geometry types),
// add geometry type

QString name = layersTable->selectedItems().at( i )->text( 1 );
int count = 0;
for ( int j = 0; j < layersTable->topLevelItemCount(); j++ )
{
if ( layersTable->topLevelItem( j )->text( 1 ) == name )
{
count++;
}
}
if ( count > 1 )
{
name += ":" + layersTable->selectedItems().at( i )->text( 3 );
}
list << name;
}
return list;
}
Expand Down
2 changes: 2 additions & 0 deletions src/gui/qgssublayersdialog.h
Expand Up @@ -37,6 +37,8 @@ class GUI_EXPORT QgsSublayersDialog : public QDialog, private Ui::QgsSublayersDi
~QgsSublayersDialog();

void populateLayerTable( QStringList theList, QString delim = ":" );
// Returns list of selected layers, if there are more layers with the same name,
// geometry type is appended separated by semicolon, example: <layer>:<geometryType>
QStringList selectionNames();
QList<int> selectionIndexes();

Expand Down
6 changes: 4 additions & 2 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Expand Up @@ -204,7 +204,8 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )

bool fetchGeom = !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
bool useIntersect = mRequest.flags() & QgsFeatureRequest::ExactIntersect;
if ( fetchGeom || useIntersect )
bool geometryTypeFilter = P->mOgrGeometryTypeFilter != wkbUnknown;
if ( fetchGeom || useIntersect || geometryTypeFilter )
{
OGRGeometryH geom = OGR_F_GetGeometryRef( fet );

Expand All @@ -217,7 +218,8 @@ bool QgsOgrFeatureIterator::readFeature( OGRFeatureH fet, QgsFeature& feature )
feature.setGeometryAndOwnership( wkb, OGR_G_WkbSize( geom ) );
}

if ( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
if (( useIntersect && ( !feature.geometry() || !feature.geometry()->intersects( mRequest.filterRect() ) ) )
|| ( geometryTypeFilter && ( !feature.geometry() || feature.geometry()->wkbType() != ( QGis::WkbType )P->mOgrGeometryTypeFilter ) ) )
{
OGR_F_Destroy( fet );
return false;
Expand Down
157 changes: 133 additions & 24 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -205,6 +205,7 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
, extent_( 0 )
, ogrLayer( 0 )
, ogrOrigLayer( 0 )
, mOgrGeometryTypeFilter( wkbUnknown )
, ogrDriver( 0 )
, valid( false )
, featuresCounted( -1 )
Expand Down Expand Up @@ -267,6 +268,11 @@ QgsOgrProvider::QgsOgrProvider( QString const & uri )
{
mSubsetString = value;
}

if ( field == "geometrytype" )
{
mOgrGeometryTypeFilter = ogrWkbGeometryTypeFromName( value );
}
}
}

Expand Down Expand Up @@ -424,6 +430,11 @@ bool QgsOgrProvider::setSubsetString( QString theSQL, bool updateFeatureCount )
uri += QString( "|subset=%1" ).arg( mSubsetString );
}

if ( mOgrGeometryTypeFilter != wkbUnknown )
{
uri += QString( "|geometrytype=%1" ).arg( ogrWkbGeometryTypeName( mOgrGeometryTypeFilter ) );
}

setDataSourceUri( uri );

OGR_L_ResetReading( ogrLayer );
Expand Down Expand Up @@ -454,8 +465,54 @@ QString QgsOgrProvider::subsetString()
return mSubsetString;
}

QString QgsOgrProvider::ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const
{
QString geom;
switch ( type )
{
case wkbUnknown: geom = "Unknown"; break;
case wkbPoint: geom = "Point"; break;
case wkbLineString: geom = "LineString"; break;
case wkbPolygon: geom = "Polygon"; break;
case wkbMultiPoint: geom = "MultiPoint"; break;
case wkbMultiLineString: geom = "MultiLineString"; break;
case wkbMultiPolygon: geom = "MultiPolygon"; break;
case wkbGeometryCollection: geom = "GeometryCollection"; break;
case wkbNone: geom = "None"; break;
case wkbPoint25D: geom = "Point25D"; break;
case wkbLineString25D: geom = "LineString25D"; break;
case wkbPolygon25D: geom = "Polygon25D"; break;
case wkbMultiPoint25D: geom = "MultiPoint25D"; break;
case wkbMultiLineString25D: geom = "MultiLineString25D"; break;
case wkbMultiPolygon25D: geom = "MultiPolygon25D"; break;
default: geom = QString( "Unknown WKB: %1" ).arg( type );

This comment has been minimized.

Copy link
@rouault

rouault May 31, 2013

Contributor

Either there's a missing case wkbGeometryCollection25D: geom = "GeometryCollection25D"; break , or the line 509 is useless

}
return geom;
}

OGRwkbGeometryType QgsOgrProvider::ogrWkbGeometryTypeFromName( QString typeName ) const
{
if ( typeName == "Point" ) return wkbPoint;
else if ( typeName == "LineString" ) return wkbLineString;
else if ( typeName == "Polygon" ) return wkbPolygon;
else if ( typeName == "MultiPoint" ) return wkbMultiPoint;
else if ( typeName == "MultiLineString" ) return wkbMultiLineString;
else if ( typeName == "MultiPolygon" ) return wkbMultiPolygon;
else if ( typeName == "GeometryCollection" ) return wkbGeometryCollection;
else if ( typeName == "None" ) return wkbNone;
else if ( typeName == "Point25D" ) return wkbPoint25D;
else if ( typeName == "LineString25D" ) return wkbLineString25D;
else if ( typeName == "Polygon25D" ) return wkbPolygon25D;
else if ( typeName == "MultiPoint25D" ) return wkbMultiPoint25D;
else if ( typeName == "MultiLineString25D" ) return wkbMultiLineString25D;
else if ( typeName == "MultiPolygon25D" ) return wkbMultiPolygon25D;
else if ( typeName == "GeometryCollection25D" ) return wkbGeometryCollection25D;
return wkbUnknown;
}

QStringList QgsOgrProvider::subLayers() const
{
QgsDebugMsg( "Entered." );
if ( !valid )
{
return QStringList();
Expand All @@ -471,29 +528,47 @@ QStringList QgsOgrProvider::subLayers() const
QString theLayerName = FROM8( OGR_FD_GetName( fdef ) );
OGRwkbGeometryType layerGeomType = OGR_FD_GetGeomType( fdef );

int theLayerFeatureCount = OGR_L_GetFeatureCount( layer, 0 );

QString geom;
switch ( layerGeomType )
if ( layerGeomType != wkbUnknown )
{
case wkbUnknown: geom = "Unknown"; break;
case wkbPoint: geom = "Point"; break;
case wkbLineString: geom = "LineString"; break;
case wkbPolygon: geom = "Polygon"; break;
case wkbMultiPoint: geom = "MultiPoint"; break;
case wkbMultiLineString: geom = "MultiLineString"; break;
case wkbGeometryCollection: geom = "GeometryCollection"; break;
case wkbNone: geom = "None"; break;
case wkbPoint25D: geom = "Point25D"; break;
case wkbLineString25D: geom = "LineString25D"; break;
case wkbPolygon25D: geom = "Polygon25D"; break;
case wkbMultiPoint25D: geom = "MultiPoint25D"; break;
case wkbMultiLineString25D: geom = "MultiLineString25D"; break;
case wkbMultiPolygon25D: geom = "MultiPolygon25D"; break;
default: geom = QString( "Unknown WKB: %1" ).arg( layerGeomType );
int theLayerFeatureCount = OGR_L_GetFeatureCount( layer, 0 );

QString geom = ogrWkbGeometryTypeName( layerGeomType );

mSubLayerList << QString( "%1:%2:%3:%4" ).arg( i ).arg( theLayerName ).arg( theLayerFeatureCount == -1 ? tr( "Unknown" ) : QString::number( theLayerFeatureCount ) ).arg( geom );
QgsDebugMsg( "Unknown geometry type, count features for each geometry type" );
}
else
{
// Add virtual sublayers for supported geometry types if layer type is unknown
// Count features for geometry types
QMap<OGRwkbGeometryType, int> fCount;
// TODO: avoid reading attributes, setRelevantFields cannot be called here because it is not constant
//setRelevantFields( true, QgsAttributeList() );
OGR_L_ResetReading( ogrLayer );
OGRFeatureH fet;
while (( fet = OGR_L_GetNextFeature( ogrLayer ) ) )
{
if ( !fet ) continue;

This comment has been minimized.

Copy link
@rouault

rouault May 31, 2013

Contributor

This line is useless. The while test prevent fet from being null

OGRGeometryH geom = OGR_F_GetGeometryRef( fet );
if ( geom )
{
OGRwkbGeometryType gType = OGR_G_GetGeometryType( geom );
fCount[gType] = fCount.value( gType ) + 1;
}
OGR_F_Destroy( fet );
}
OGR_L_ResetReading( ogrLayer );
int i = 0;
foreach ( OGRwkbGeometryType gType, fCount.keys() )
{
QString geom = ogrWkbGeometryTypeName( gType );

mSubLayerList << QString( "%1:%2:%3:%4" ).arg( i ).arg( theLayerName ).arg( theLayerFeatureCount == -1 ? tr( "Unknown" ) : QString::number( theLayerFeatureCount ) ).arg( geom );
QString sl = QString( "%1:%2:%3:%4" ).arg( i ).arg( theLayerName ).arg( fCount.value( gType ) ).arg( geom );
QgsDebugMsg( "sub layer: " + sl );
mSubLayerList << sl;
i++;
}
}
}

return mSubLayerList;
Expand Down Expand Up @@ -528,7 +603,9 @@ int QgsOgrProvider::getOgrGeomType( OGRLayerH ogrLayer )
geomType = OGR_FD_GetGeomType( fdef );

//Some ogr drivers (e.g. GML) are not able to determine the geometry type of a layer like this.
//In such cases, we examine the first feature
//In such cases, we use virtual sublayers for each geometry (originally the type was
// guessed from the first feature which resulted in loss of other formats)
/*
if ( geomType == wkbUnknown )
{
OGR_L_ResetReading( ogrLayer );
Expand All @@ -544,6 +621,7 @@ int QgsOgrProvider::getOgrGeomType( OGRLayerH ogrLayer )
}
OGR_L_ResetReading( ogrLayer );
}
*/
}
return geomType;
}
Expand All @@ -553,7 +631,14 @@ void QgsOgrProvider::loadFields()
//the attribute fields need to be read again when the encoding changes
mAttributeFields.clear();

geomType = getOgrGeomType( ogrLayer );
if ( mOgrGeometryTypeFilter != wkbUnknown )
{
geomType = mOgrGeometryTypeFilter;
}
else
{
geomType = getOgrGeomType( ogrLayer );
}
OGRFeatureDefnH fdef = OGR_L_GetLayerDefn( ogrLayer );
if ( fdef )
{
Expand Down Expand Up @@ -733,7 +818,7 @@ QGis::WkbType QgsOgrProvider::geometryType() const
}

/**
* Return the feature type
* Return the feature count
*/
long QgsOgrProvider::featureCount() const
{
Expand Down Expand Up @@ -2190,7 +2275,31 @@ void QgsOgrProvider::recalculateFeatureCount()

// feature count returns number of features within current spatial filter
// so we remove it if there's any and then put it back
featuresCounted = OGR_L_GetFeatureCount( ogrLayer, true );
if ( mOgrGeometryTypeFilter == wkbUnknown )
{
featuresCounted = OGR_L_GetFeatureCount( ogrLayer, true );
}
else
{
featuresCounted = 0;
OGR_L_ResetReading( ogrLayer );
setRelevantFields( true, QgsAttributeList() );
OGR_L_ResetReading( ogrLayer );
OGRFeatureH fet;
while (( fet = OGR_L_GetNextFeature( ogrLayer ) ) )
{
if ( !fet ) continue;

This comment has been minimized.

Copy link
@rouault

rouault May 31, 2013

Contributor

This line is useless. The while test prevent fet from being null

OGRGeometryH geom = OGR_F_GetGeometryRef( fet );
if ( geom )
{
OGRwkbGeometryType gType = OGR_G_GetGeometryType( geom );
if ( gType == mOgrGeometryTypeFilter ) featuresCounted++;
}
OGR_F_Destroy( fet );
}
OGR_L_ResetReading( ogrLayer );

}

if ( filter )
{
Expand Down
6 changes: 6 additions & 0 deletions src/providers/ogr/qgsogrprovider.h
Expand Up @@ -262,6 +262,8 @@ class QgsOgrProvider : public QgsVectorDataProvider

private:
unsigned char *getGeometryPointer( OGRFeatureH fet );
QString ogrWkbGeometryTypeName( OGRwkbGeometryType type ) const;
OGRwkbGeometryType ogrWkbGeometryTypeFromName( QString typeName ) const;
QgsFields mAttributeFields;
OGRDataSourceH ogrDataSource;
void *extent_;
Expand All @@ -281,6 +283,10 @@ class QgsOgrProvider : public QgsVectorDataProvider
//! layer index
int mLayerIndex;

/** Optional geometry type for layers with multiple geometries,
* otherwise wkbUnknown */
OGRwkbGeometryType mOgrGeometryTypeFilter;

//! current spatial filter
QgsRectangle mFetchRect;

Expand Down

1 comment on commit af0f61e

@rouault
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added 3 comments on my review of the code. Minor stuff

Please sign in to comment.