Skip to content

Commit

Permalink
Merge pull request #2987 from rouault/wfs_uppercase_attribute
Browse files Browse the repository at this point in the history
[WFS] Fix support of attribute names in upper-case
  • Loading branch information
jef-n committed Apr 9, 2016
2 parents f90f999 + a72fcb8 commit 7d235d2
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 7d235d2

Please sign in to comment.