Skip to content

Commit

Permalink
[WFS provider] Do not be confuse by elements that have ref attribute …
Browse files Browse the repository at this point in the history
…when parsing DescribeFeatureType response; and fix support of (deprecated) ref='gml:XXXXProperty' attributes
  • Loading branch information
rouault committed Jul 2, 2017
1 parent c1d339c commit f691890
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 3 deletions.
21 changes: 18 additions & 3 deletions src/providers/wfs/qgswfsprovider.cpp
Expand Up @@ -1328,7 +1328,12 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
}
}

// attribute ref
QString ref = attributeElement.attribute( QStringLiteral( "ref" ) );

QRegExp gmlPT( "gml:(.*)PropertyType" );
QRegExp gmlRefProperty( "gml:(.*)Property" );

// gmgml: is Geomedia Web Server
if ( type == QLatin1String( "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType" ) )
{
Expand All @@ -1343,15 +1348,25 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
geomType = QgsWkbTypes::MultiLineString;
}
//is it a geometry attribute?
//MH 090428: sometimes the <element> tags for geometry attributes have only attribute ref="gml:polygonProperty" and no name
// the GeometryAssociationType has been seen in #11785
else if ( type.indexOf( gmlPT ) == 0 || type == QLatin1String( "gml:GeometryAssociationType" ) || name.isEmpty() )
else if ( type.indexOf( gmlPT ) == 0 || type == QLatin1String( "gml:GeometryAssociationType" ) )
{
foundGeometryAttribute = true;
geometryAttribute = name;
geomType = geomTypeFromPropertyType( geometryAttribute, gmlPT.cap( 1 ) );
}
else //todo: distinguish between numerical and non-numerical types
//MH 090428: sometimes the <element> tags for geometry attributes have only attribute ref="gml:polygonProperty"
//Note: this was deprecated with GML3.
else if ( ref.indexOf( gmlRefProperty ) == 0 )
{
foundGeometryAttribute = true;
geometryAttribute = ref.mid( 4 ); // Strip gml: prefix
QString propertyType( gmlRefProperty.cap( 1 ) );
// Set the first character in upper case
propertyType = propertyType.left( 1 ).toUpper() + propertyType.mid( 1 );
geomType = geomTypeFromPropertyType( geometryAttribute, propertyType );
}
else if ( !name.isEmpty() ) //todo: distinguish between numerical and non-numerical types
{
QVariant::Type attributeType = QVariant::String; //string is default type
if ( type.contains( QLatin1String( "double" ), Qt::CaseInsensitive ) || type.contains( QLatin1String( "float" ), Qt::CaseInsensitive ) || type.contains( QLatin1String( "decimal" ), Qt::CaseInsensitive ) )
Expand Down
70 changes: 70 additions & 0 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -119,11 +119,14 @@ def setUpClass(cls):
<xsd:element maxOccurs="1" minOccurs="0" name="name2" nillable="true" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="0" name="num_char" nillable="true" type="xsd:string"/>
<xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PolygonPropertyType"/>
<!-- check that an element with ref without name doesn't confuse the DescribeFeatureType analyzer -->
<xsd:element maxOccurs="0" minOccurs="0" ref="my:somethingElseType"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
<xsd:complexType name="somethingElseType"/>
</xsd:schema>
""".encode('UTF-8'))

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

def testDeprecatedGML2GeometryDeclaration(self):
"""Test ref="gml:pointProperty" """

endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_deprecated_gml2'

with open(sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.0.0'), 'wb') as f:
f.write("""
<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc">
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<SRS>EPSG:32631</SRS>
<!-- in WFS 1.0, LatLongBoundingBox is in SRS units, not necessarily lat/long... -->
<LatLongBoundingBox minx="400000" miny="5400000" maxx="450000" maxy="5500000"/>
</FeatureType>
</FeatureTypeList>
</WFS_Capabilities>""".encode('UTF-8'))

with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename'), 'wb') as f:
f.write("""
<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">
<xsd:import namespace="http://www.opengis.net/gml"/>
<xsd:complexType name="typenameType">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" ref="gml:pointProperty"/>
<xsd:element maxOccurs="1" minOccurs="0" name="INTFIELD" nillable="true" type="xsd:int"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
</xsd:schema>
""".encode('UTF-8'))

vl = QgsVectorLayer("url='http://" + endpoint + "' typename='my:typename' version='1.0.0'", 'test', 'WFS')
assert vl.isValid()
self.assertEqual(vl.wkbType(), QgsWkbTypes.Point)
self.assertEqual(len(vl.fields()), 1)

with open(sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:32631'), 'wb') as f:
f.write("""
<wfs:FeatureCollection
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:gml="http://www.opengis.net/gml"
xmlns:my="http://my">
<gml:boundedBy><gml:null>unknown</gml:null></gml:boundedBy>
<gml:featureMember>
<my:typename fid="typename.0">
<gml:pointProperty>
<gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">426858,5427937</gml:coordinates></gml:Point>
</gml:pointProperty>
<my:INTFIELD>1</my:INTFIELD>
</my:typename>
</gml:featureMember>
</wfs:FeatureCollection>""".encode('UTF-8'))

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

got_f = [f for f in vl.getFeatures()]
got = got_f[0].geometry().geometry()
self.assertEqual((got.x(), got.y()), (426858.0, 5427937.0))


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

0 comments on commit f691890

Please sign in to comment.