mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
[WFS provider] Handle the case where the layer schema has a ogc_fid field
Fixes #15062
This commit is contained in:
parent
7c30985f80
commit
d7414d7eca
@ -240,15 +240,21 @@ bool QgsWFSSharedData::createCache()
|
||||
cacheFields.append( QgsField( QgsWFSConstants::FIELD_MD5, QVariant::String, "string" ) );
|
||||
|
||||
bool ogrWaySuccessful = false;
|
||||
QString fidName( "__ogc_fid" );
|
||||
QString geometryFieldname( "__spatialite_geometry" );
|
||||
#ifdef USE_OGR_FOR_DB_CREATION
|
||||
// Only GDAL >= 2.0 can use an alternate geometry field name
|
||||
// Only GDAL >= 2.0 can use an alternate geometry or FID field name
|
||||
// but QgsVectorFileWriter will refuse anyway to create a ogc_fid, so we will
|
||||
// do it manually
|
||||
bool useReservedNames = cacheFields.fieldNameIndex( "ogc_fid" ) >= 0;
|
||||
#if GDAL_VERSION_MAJOR < 2
|
||||
const bool hasGeometryField = cacheFields.fieldNameIndex( "geometry" ) >= 0;
|
||||
if ( !hasGeometryField )
|
||||
if ( cacheFields.fieldNameIndex( "geometry" ) >= 0 )
|
||||
useReservedNames = true;
|
||||
#endif
|
||||
if ( !useReservedNames )
|
||||
{
|
||||
#if GDAL_VERSION_MAJOR < 2
|
||||
fidName = "ogc_fid";
|
||||
geometryFieldname = "GEOMETRY";
|
||||
#endif
|
||||
// Creating a spatialite database can be quite slow on some file systems
|
||||
@ -260,6 +266,7 @@ bool QgsWFSSharedData::createCache()
|
||||
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( "FID=__ogc_fid" );
|
||||
layerOptions.push_back( "GEOMETRY_NAME=__spatialite_geometry" );
|
||||
#endif
|
||||
vsimemFilename.sprintf( "/vsimem/qgis_wfs_cache_template_%p/features.sqlite", this );
|
||||
@ -374,7 +381,7 @@ bool QgsWFSSharedData::createCache()
|
||||
if ( !ogrWaySuccessful )
|
||||
{
|
||||
mCacheTablename = "features";
|
||||
sql = QString( "CREATE TABLE %1 (ogc_fid INTEGER PRIMARY KEY" ).arg( mCacheTablename );
|
||||
sql = QString( "CREATE TABLE %1 (%2 INTEGER PRIMARY KEY" ).arg( mCacheTablename ).arg( fidName );
|
||||
Q_FOREACH ( QgsField field, cacheFields )
|
||||
{
|
||||
QString type( "VARCHAR" );
|
||||
@ -389,17 +396,26 @@ bool QgsWFSSharedData::createCache()
|
||||
sql += ")";
|
||||
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QString( "%1 failed" ).arg( sql ) );
|
||||
ret = false;
|
||||
}
|
||||
|
||||
sql = QString( "SELECT AddGeometryColumn('%1','%2',0,'POLYGON',2)" ).arg( mCacheTablename, geometryFieldname );
|
||||
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QString( "%1 failed" ).arg( sql ) );
|
||||
ret = false;
|
||||
}
|
||||
|
||||
sql = QString( "SELECT CreateSpatialIndex('%1','%2')" ).arg( mCacheTablename, geometryFieldname );
|
||||
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QString( "%1 failed" ).arg( sql ) );
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We need an index on the gmlid, since we will check for duplicates, particularly
|
||||
@ -407,14 +423,20 @@ bool QgsWFSSharedData::createCache()
|
||||
sql = QString( "CREATE INDEX idx_%2 ON %1(%2)" ).arg( mCacheTablename ).arg( QgsWFSConstants::FIELD_GMLID );
|
||||
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QString( "%1 failed" ).arg( sql ) );
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if ( mDistinctSelect )
|
||||
{
|
||||
sql = QString( "CREATE INDEX idx_%2 ON %1(%2)" ).arg( mCacheTablename ).arg( QgsWFSConstants::FIELD_MD5 );
|
||||
rc = sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr );
|
||||
if ( rc != SQLITE_OK )
|
||||
{
|
||||
QgsDebugMsg( QString( "%1 failed" ).arg( sql ) );
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
( void )sqlite3_exec( db, "COMMIT", nullptr, nullptr, nullptr );
|
||||
@ -435,7 +457,7 @@ bool QgsWFSSharedData::createCache()
|
||||
// regarding crashes, since this is a temporary DB
|
||||
QgsDataSourceURI dsURI;
|
||||
dsURI.setDatabase( mCacheDbname );
|
||||
dsURI.setDataSource( "", mCacheTablename, geometryFieldname, "", "ogc_fid" );
|
||||
dsURI.setDataSource( "", mCacheTablename, geometryFieldname, "", fidName );
|
||||
QStringList pragmas;
|
||||
pragmas << "synchronous=OFF";
|
||||
pragmas << "journal_mode=WAL"; // WAL is needed to avoid reader to block writers
|
||||
|
@ -389,7 +389,8 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<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="datetimefield" nillable="true" type="xsd:dateTime"/>
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
|
||||
<!-- use geometry that is the default spatialite geometry name -->
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="geometry" nillable="true" type="gml:PointPropertyType"/>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
@ -416,8 +417,9 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:boundedBy><gml:null>unknown</gml:null></gml:boundedBy>
|
||||
<gml:featureMember>
|
||||
<my:typename fid="typename.0">
|
||||
<my:geometryProperty>
|
||||
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">426858,5427937</gml:coordinates></gml:Point></my:geometryProperty>
|
||||
<my:geometry>
|
||||
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">426858,5427937</gml:coordinates></gml:Point>
|
||||
</my:geometry>
|
||||
<my:INTFIELD>1</my:INTFIELD>
|
||||
<my:GEOMETRY>2</my:GEOMETRY>
|
||||
<my:longfield>1234567890123</my:longfield>
|
||||
@ -878,7 +880,8 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="gml:AbstractFeatureType">
|
||||
<xsd:sequence>
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="id" nillable="true" type="xsd:int"/>
|
||||
<!-- use ogc_fid that is the default spatialite FID name -->
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="ogc_fid" nillable="true" type="xsd:int"/>
|
||||
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
@ -903,14 +906,14 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:featureMembers>
|
||||
<my:typename gml:id="typename.200">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>2</my:id>
|
||||
<my:ogc_fid>2</my:ogc_fid>
|
||||
</my:typename>
|
||||
</gml:featureMembers>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
extent = QgsRectangle(-70, 60, -60, 80)
|
||||
request = QgsFeatureRequest().setFilterRect(extent)
|
||||
values = [f['id'] for f in vl.getFeatures(request)]
|
||||
values = [f['ogc_fid'] for f in vl.getFeatures(request)]
|
||||
self.assertEqual(values, [2])
|
||||
|
||||
# To show that if we zoom-in, we won't issue a new request
|
||||
@ -923,14 +926,14 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:featureMembers>
|
||||
<my:typename gml:id="typename.20000">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>200</my:id>
|
||||
<my:ogc_fid>200</my:ogc_fid>
|
||||
</my:typename>
|
||||
</gml:featureMembers>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
extent = QgsRectangle(-66, 62, -62, 78)
|
||||
request = QgsFeatureRequest().setFilterRect(extent)
|
||||
values = [f['id'] for f in vl.getFeatures(request)]
|
||||
values = [f['ogc_fid'] for f in vl.getFeatures(request)]
|
||||
self.assertEqual(values, [2])
|
||||
|
||||
# Move to a neighbouring area, and reach the download limit
|
||||
@ -944,18 +947,18 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:featureMembers>
|
||||
<my:typename gml:id="typename.200">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>2</my:id>
|
||||
<my:ogc_fid>2</my:ogc_fid>
|
||||
</my:typename>
|
||||
<my:typename gml:id="typename.300">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>85 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>3</my:id>
|
||||
<my:ogc_fid>3</my:ogc_fid>
|
||||
</my:typename>
|
||||
</gml:featureMembers>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
extent = QgsRectangle(-70, 65, -60, 90)
|
||||
request = QgsFeatureRequest().setFilterRect(extent)
|
||||
values = [f['id'] for f in vl.getFeatures(request)]
|
||||
values = [f['ogc_fid'] for f in vl.getFeatures(request)]
|
||||
self.assertEqual(values, [2, 3])
|
||||
|
||||
# Zoom-in again, and bring more features
|
||||
@ -969,18 +972,18 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:featureMembers>
|
||||
<my:typename gml:id="typename.200">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>2</my:id>
|
||||
<my:ogc_fid>2</my:ogc_fid>
|
||||
</my:typename>
|
||||
<my:typename gml:id="typename.400">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>84 -64</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>4</my:id>
|
||||
<my:ogc_fid>4</my:ogc_fid>
|
||||
</my:typename>
|
||||
</gml:featureMembers>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
extent = QgsRectangle(-69, 66, -61, 89)
|
||||
request = QgsFeatureRequest().setFilterRect(extent)
|
||||
values = [f['id'] for f in vl.getFeatures(request)]
|
||||
values = [f['ogc_fid'] for f in vl.getFeatures(request)]
|
||||
self.assertEqual(values, [2, 3, 4])
|
||||
|
||||
# Test RESULTTYPE=hits
|
||||
@ -1003,7 +1006,7 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
</gml:Envelope>
|
||||
</ogc:BBOX>
|
||||
<ogc:PropertyIsEqualTo xmlns:ogc="http://www.opengis.net/ogc">
|
||||
<ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">id</ogc:PropertyName>
|
||||
<ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">ogc_fid</ogc:PropertyName>
|
||||
<ogc:Literal xmlns:ogc="http://www.opengis.net/ogc">101</ogc:Literal>
|
||||
</ogc:PropertyIsEqualTo>
|
||||
</ogc:And>
|
||||
@ -1018,15 +1021,15 @@ class TestPyQgsWFSProvider(unittest.TestCase, ProviderTestCase):
|
||||
<gml:featureMembers>
|
||||
<my:typename gml:id="typename.101">
|
||||
<my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty>
|
||||
<my:id>101</my:id>
|
||||
<my:ogc_fid>101</my:ogc_fid>
|
||||
</my:typename>
|
||||
</gml:featureMembers>
|
||||
</wfs:FeatureCollection>""".encode('UTF-8'))
|
||||
|
||||
vl.dataProvider().setSubsetString('id = 101')
|
||||
vl.dataProvider().setSubsetString('ogc_fid = 101')
|
||||
extent = QgsRectangle(-69, 66, -61, 89)
|
||||
request = QgsFeatureRequest().setFilterRect(extent)
|
||||
values = [f['id'] for f in vl.getFeatures(request)]
|
||||
values = [f['ogc_fid'] for f in vl.getFeatures(request)]
|
||||
self.assertEqual(values, [101])
|
||||
|
||||
def testWFS20TruncatedResponse(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user