Navigation Menu

Skip to content

Commit

Permalink
[WFS] Fix support of attribute names in upper-case
Browse files Browse the repository at this point in the history
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 a72fcb8
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 43 deletions.
94 changes: 55 additions & 39 deletions src/providers/wfs/qgswfsshareddata.cpp
Expand Up @@ -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 )
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down
13 changes: 9 additions & 4 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -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"/>
Expand All @@ -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))
Expand All @@ -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])

Expand Down

0 comments on commit a72fcb8

Please sign in to comment.