Skip to content
Permalink
Browse files

[WFS] Fix support of attribute names in upper-case

Bugfix of issue introduced in commit 9040ec1

When doing the spatialite cache initialization with OGR, the attribute
names get 'laundered' in lower case, which makes their matching afterwards
fail. Fix this. And also handle the situation where an attribute would
be named 'geometry'
  • Loading branch information
rouault committed Apr 9, 2016
1 parent fa3aba3 commit a72fcb8fe1d934359067e75f48fece538c870b54
Showing with 64 additions and 43 deletions.
  1. +55 −39 src/providers/wfs/qgswfsshareddata.cpp
  2. +9 −4 tests/src/python/test_provider_wfs.py
@@ -166,49 +166,65 @@ bool QgsWFSSharedData::createCache()
cacheFields.append( QgsField( QgsWFSConstants::FIELD_HEXWKB_GEOM, QVariant::String, "string" ) );

bool ogrWaySuccessfull = false;
QString geometryFieldname( "__spatialite_geometry" );
#ifdef USE_OGR_FOR_DB_CREATION
// Creating a spatialite database can be quite slow on some file systems
// so we create a GDAL in-memory file, and then copy it on
// the file system.
QString vsimemFilename;
QStringList datasourceOptions;
datasourceOptions.push_back( "INIT_WITH_EPSG=NO" );
vsimemFilename.sprintf( "/vsimem/qgis_wfs_cache_template_%p/features.sqlite", this );
mCacheTablename = CPLGetBasename( vsimemFilename.toStdString().c_str() );
VSIUnlink( vsimemFilename.toStdString().c_str() );
QgsVectorFileWriter* writer = new QgsVectorFileWriter( vsimemFilename, "",
cacheFields, QGis::WKBPolygon, nullptr, "SpatiaLite", datasourceOptions );
if ( writer->hasError() == QgsVectorFileWriter::NoError )
// Only GDAL >= 2.0 can use an alternate geometry field name
#if GDAL_VERSION_MAJOR < 2
const bool hasGeometryField = cacheFields.fieldNameIndex( "geometry" ) >= 0;
if ( !hasGeometryField )
#endif
{
delete writer;

// Copy the temporary database back to disk
vsi_l_offset nLength = 0;
GByte* pabyData = VSIGetMemFileBuffer( vsimemFilename.toStdString().c_str(), &nLength, TRUE );
VSILFILE* fp = VSIFOpenL( mCacheDbname.toStdString().c_str(), "wb " );
if ( fp != nullptr )
#if GDAL_VERSION_MAJOR < 2
geometryFieldname = "GEOMETRY";
#endif
// Creating a spatialite database can be quite slow on some file systems
// so we create a GDAL in-memory file, and then copy it on
// the file system.
QString vsimemFilename;
QStringList datasourceOptions;
QStringList layerOptions;
datasourceOptions.push_back( "INIT_WITH_EPSG=NO" );
layerOptions.push_back( "LAUNDER=NO" ); // to get exact matches for field names, especially regarding case
#if GDAL_VERSION_MAJOR >= 2
layerOptions.push_back( "GEOMETRY_NAME=__spatialite_geometry" );
#endif
vsimemFilename.sprintf( "/vsimem/qgis_wfs_cache_template_%p/features.sqlite", this );
mCacheTablename = CPLGetBasename( vsimemFilename.toStdString().c_str() );
VSIUnlink( vsimemFilename.toStdString().c_str() );
QgsVectorFileWriter* writer = new QgsVectorFileWriter( vsimemFilename, "",
cacheFields, QGis::WKBPolygon, nullptr, "SpatiaLite", datasourceOptions, layerOptions );
if ( writer->hasError() == QgsVectorFileWriter::NoError )
{
VSIFWriteL( pabyData, 1, nLength, fp );
VSIFCloseL( fp );
CPLFree( pabyData );
delete writer;

// Copy the temporary database back to disk
vsi_l_offset nLength = 0;
GByte* pabyData = VSIGetMemFileBuffer( vsimemFilename.toStdString().c_str(), &nLength, TRUE );
VSILFILE* fp = VSIFOpenL( mCacheDbname.toStdString().c_str(), "wb " );
if ( fp != nullptr )
{
VSIFWriteL( pabyData, 1, nLength, fp );
VSIFCloseL( fp );
CPLFree( pabyData );
}
else
{
CPLFree( pabyData );
QgsMessageLog::logMessage( tr( "Cannot create temporary SpatiaLite cache" ), tr( "WFS" ) );
return false;
}

ogrWaySuccessfull = true;
}
else
{
CPLFree( pabyData );
QgsMessageLog::logMessage( tr( "Cannot create temporary SpatiaLite cache" ), tr( "WFS" ) );
return false;
// Be tolerant on failures. Some (Windows) GDAL >= 1.11 builds may
// not define SPATIALITE_412_OR_LATER, and thus the call to
// spatialite_init() may cause failures, which will require using the
// slower method
delete writer;
VSIUnlink( vsimemFilename.toStdString().c_str() );
}

ogrWaySuccessfull = true;
}
else
{
// Be tolerant on failures. Some (Windows) GDAL >= 1.11 builds may
// not define SPATIALITE_412_OR_LATER, and thus the call to
// spatialite_init() may cause failures, which will require using the
// slower method
delete writer;
VSIUnlink( vsimemFilename.toStdString().c_str() );
}
#endif
if ( !ogrWaySuccessfull )
@@ -300,12 +316,12 @@ bool QgsWFSSharedData::createCache()
if ( rc != SQLITE_OK )
ret = false;

sql = QString( "SELECT AddGeometryColumn('%1','geometry',0,'POLYGON',2)" ).arg( mCacheTablename );
sql = QString( "SELECT AddGeometryColumn('%1','%2',0,'POLYGON',2)" ).arg( mCacheTablename ).arg( geometryFieldname );
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
if ( rc != SQLITE_OK )
ret = false;

sql = QString( "SELECT CreateSpatialIndex('%1','geometry')" ).arg( mCacheTablename );
sql = QString( "SELECT CreateSpatialIndex('%1','%2')" ).arg( mCacheTablename ).arg( geometryFieldname );
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
if ( rc != SQLITE_OK )
ret = false;
@@ -336,7 +352,7 @@ bool QgsWFSSharedData::createCache()
// regarding crashes, since this is a temporary DB
QgsDataSourceURI dsURI;
dsURI.setDatabase( mCacheDbname );
dsURI.setDataSource( "", mCacheTablename, "geometry", "", "ogc_fid" );
dsURI.setDataSource( "", mCacheTablename, geometryFieldname, "", "ogc_fid" );
QStringList pragmas;
pragmas << "synchronous=OFF";
pragmas << "journal_mode=MEMORY";
@@ -276,7 +276,8 @@ def testWFS10(self):
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="intfield" nillable="true" type="xsd:int"/>
<xsd:element maxOccurs="1" minOccurs="0" name="INTFIELD" nillable="true" type="xsd:int"/>
<xsd:element maxOccurs="1" minOccurs="0" name="GEOMETRY" nillable="true" type="xsd:int"/>
<xsd:element maxOccurs="1" minOccurs="0" name="longfield" nillable="true" type="xsd:long"/>
<xsd:element maxOccurs="1" minOccurs="0" name="stringfield" nillable="true" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
@@ -291,7 +292,7 @@ def testWFS10(self):
vl = QgsVectorLayer(u"url='http://" + endpoint + u"' typename='my:typename' version='1.0.0'", u'test', u'WFS')
assert vl.isValid()
self.assertEquals(vl.wkbType(), QgsWKBTypes.Point)
self.assertEquals(len(vl.fields()), 3)
self.assertEquals(len(vl.fields()), 4)
self.assertEquals(vl.featureCount(), 0)
reference = QgsGeometry.fromRect(
QgsRectangle(-71.123, 66.33, -65.32, 78.3))
@@ -309,16 +310,20 @@ def testWFS10(self):
<my:typename fid="typename.0">
<my:geometryProperty>
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">2,49</gml:coordinates></gml:Point></my:geometryProperty>
<my:intfield>1</my:intfield>
<my:INTFIELD>1</my:INTFIELD>
<my:GEOMETRY>2</my:GEOMETRY>
<my:longfield>1234567890123</my:longfield>
<my:stringfield>foo</my:stringfield>
</my:typename>
</gml:featureMember>
</wfs:FeatureCollection>""")

values = [f['intfield'] for f in vl.getFeatures()]
values = [f['INTFIELD'] for f in vl.getFeatures()]
self.assertEquals(values, [1])

values = [f['GEOMETRY'] for f in vl.getFeatures()]
self.assertEquals(values, [2])

values = [f['longfield'] for f in vl.getFeatures()]
self.assertEquals(values, [1234567890123])

0 comments on commit a72fcb8

Please sign in to comment.
You can’t perform that action at this time.