Skip to content

Commit f691890

Browse files
committed
[WFS provider] Do not be confuse by elements that have ref attribute when parsing DescribeFeatureType response; and fix support of (deprecated) ref='gml:XXXXProperty' attributes
1 parent c1d339c commit f691890

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

src/providers/wfs/qgswfsprovider.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,12 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
13281328
}
13291329
}
13301330

1331+
// attribute ref
1332+
QString ref = attributeElement.attribute( QStringLiteral( "ref" ) );
1333+
13311334
QRegExp gmlPT( "gml:(.*)PropertyType" );
1335+
QRegExp gmlRefProperty( "gml:(.*)Property" );
1336+
13321337
// gmgml: is Geomedia Web Server
13331338
if ( type == QLatin1String( "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType" ) )
13341339
{
@@ -1343,15 +1348,25 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
13431348
geomType = QgsWkbTypes::MultiLineString;
13441349
}
13451350
//is it a geometry attribute?
1346-
//MH 090428: sometimes the <element> tags for geometry attributes have only attribute ref="gml:polygonProperty" and no name
13471351
// the GeometryAssociationType has been seen in #11785
1348-
else if ( type.indexOf( gmlPT ) == 0 || type == QLatin1String( "gml:GeometryAssociationType" ) || name.isEmpty() )
1352+
else if ( type.indexOf( gmlPT ) == 0 || type == QLatin1String( "gml:GeometryAssociationType" ) )
13491353
{
13501354
foundGeometryAttribute = true;
13511355
geometryAttribute = name;
13521356
geomType = geomTypeFromPropertyType( geometryAttribute, gmlPT.cap( 1 ) );
13531357
}
1354-
else //todo: distinguish between numerical and non-numerical types
1358+
//MH 090428: sometimes the <element> tags for geometry attributes have only attribute ref="gml:polygonProperty"
1359+
//Note: this was deprecated with GML3.
1360+
else if ( ref.indexOf( gmlRefProperty ) == 0 )
1361+
{
1362+
foundGeometryAttribute = true;
1363+
geometryAttribute = ref.mid( 4 ); // Strip gml: prefix
1364+
QString propertyType( gmlRefProperty.cap( 1 ) );
1365+
// Set the first character in upper case
1366+
propertyType = propertyType.left( 1 ).toUpper() + propertyType.mid( 1 );
1367+
geomType = geomTypeFromPropertyType( geometryAttribute, propertyType );
1368+
}
1369+
else if ( !name.isEmpty() ) //todo: distinguish between numerical and non-numerical types
13551370
{
13561371
QVariant::Type attributeType = QVariant::String; //string is default type
13571372
if ( type.contains( QLatin1String( "double" ), Qt::CaseInsensitive ) || type.contains( QLatin1String( "float" ), Qt::CaseInsensitive ) || type.contains( QLatin1String( "decimal" ), Qt::CaseInsensitive ) )

tests/src/python/test_provider_wfs.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,14 @@ def setUpClass(cls):
119119
<xsd:element maxOccurs="1" minOccurs="0" name="name2" nillable="true" type="xsd:string"/>
120120
<xsd:element maxOccurs="1" minOccurs="0" name="num_char" nillable="true" type="xsd:string"/>
121121
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PolygonPropertyType"/>
122+
<!-- check that an element with ref without name doesn't confuse the DescribeFeatureType analyzer -->
123+
<xsd:element maxOccurs="0" minOccurs="0" ref="my:somethingElseType"/>
122124
</xsd:sequence>
123125
</xsd:extension>
124126
</xsd:complexContent>
125127
</xsd:complexType>
126128
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
129+
<xsd:complexType name="somethingElseType"/>
127130
</xsd:schema>
128131
""".encode('UTF-8'))
129132

@@ -2486,6 +2489,73 @@ def testWFS20TransactionsEnabled(self):
24862489
self.assertNotEqual(vl.dataProvider().capabilities() & vl.dataProvider().EditingCapabilities, vl.dataProvider().NoCapabilities)
24872490
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
24882491

2492+
def testDeprecatedGML2GeometryDeclaration(self):
2493+
"""Test ref="gml:pointProperty" """
2494+
2495+
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_deprecated_gml2'
2496+
2497+
with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.0.0'), 'wb') as f:
2498+
f.write("""
2499+
<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc">
2500+
<FeatureTypeList>
2501+
<FeatureType>
2502+
<Name>my:typename</Name>
2503+
<Title>Title</Title>
2504+
<Abstract>Abstract</Abstract>
2505+
<SRS>EPSG:32631</SRS>
2506+
<!-- in WFS 1.0, LatLongBoundingBox is in SRS units, not necessarily lat/long... -->
2507+
<LatLongBoundingBox minx="400000" miny="5400000" maxx="450000" maxy="5500000"/>
2508+
</FeatureType>
2509+
</FeatureTypeList>
2510+
</WFS_Capabilities>""".encode('UTF-8'))
2511+
2512+
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename'), 'wb') as f:
2513+
f.write("""
2514+
<xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my">
2515+
<xsd:import namespace="http://www.opengis.net/gml"/>
2516+
<xsd:complexType name="typenameType">
2517+
<xsd:complexContent>
2518+
<xsd:extension base="gml:AbstractFeatureType">
2519+
<xsd:sequence>
2520+
<xsd:element maxOccurs="1" minOccurs="0" ref="gml:pointProperty"/>
2521+
<xsd:element maxOccurs="1" minOccurs="0" name="INTFIELD" nillable="true" type="xsd:int"/>
2522+
</xsd:sequence>
2523+
</xsd:extension>
2524+
</xsd:complexContent>
2525+
</xsd:complexType>
2526+
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
2527+
</xsd:schema>
2528+
""".encode('UTF-8'))
2529+
2530+
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='1.0.0'", 'test', 'WFS')
2531+
assert vl.isValid()
2532+
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
2533+
self.assertEqual(len(vl.fields()), 1)
2534+
2535+
with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631'), 'wb') as f:
2536+
f.write("""
2537+
<wfs:FeatureCollection
2538+
xmlns:wfs="http://www.opengis.net/wfs"
2539+
xmlns:gml="http://www.opengis.net/gml"
2540+
xmlns:my="http://my">
2541+
<gml:boundedBy><gml:null>unknown</gml:null></gml:boundedBy>
2542+
<gml:featureMember>
2543+
<my:typename fid="typename.0">
2544+
<gml:pointProperty>
2545+
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">426858,5427937</gml:coordinates></gml:Point>
2546+
</gml:pointProperty>
2547+
<my:INTFIELD>1</my:INTFIELD>
2548+
</my:typename>
2549+
</gml:featureMember>
2550+
</wfs:FeatureCollection>""".encode('UTF-8'))
2551+
2552+
values = [f['INTFIELD'] for f in vl.getFeatures()]
2553+
self.assertEqual(values, [1])
2554+
2555+
got_f = [f for f in vl.getFeatures()]
2556+
got = got_f[0].geometry().geometry()
2557+
self.assertEqual((got.x(), got.y()), (426858.0, 5427937.0))
2558+
24892559

24902560
if __name__ == '__main__':
24912561
unittest.main()

0 commit comments

Comments
 (0)