void QgsWFSSourceSelect::on_cmbConnections_activated( int index ) { Q_UNUSED( index ); QgsWFSConnection::setSelectedConnection( cmbConnections->currentText() ); QgsWFSConnection connection( cmbConnections->currentText() ); delete mCapabilities; mCapabilities = new QgsWFSCapabilities( connection.uri().uri() ); connect( mCapabilities, SIGNAL( gotCapabilities() ), this, SLOT( capabilitiesReplyFinished() ) ); }
bool QgsWfsCapabilities::requestCapabilities( bool synchronous ) { QUrl url( baseURL() ); url.addQueryItem( "REQUEST", "GetCapabilities" ); const QString& version = mUri.version(); if ( version == QgsWFSConstants::VERSION_AUTO ) // MapServer honours the order with the first value being the preferred one url.addQueryItem( "ACCEPTVERSIONS", "2.0.0,1.1.0,1.0.0" ); else url.addQueryItem( "VERSION", version ); if ( !sendGET( url, synchronous, false ) ) { emit gotCapabilities(); return false; } return true; }
void QgsWFSSourceSelect::populateConnectionList() { QStringList keys = QgsWFSConnection::connectionList(); QStringList::Iterator it = keys.begin(); cmbConnections->clear(); while ( it != keys.end() ) { cmbConnections->addItem( *it ); ++it; } if ( keys.begin() != keys.end() ) { // Connections available - enable various buttons btnConnect->setEnabled( true ); btnEdit->setEnabled( true ); btnDelete->setEnabled( true ); btnSave->setEnabled( true ); } else { // No connections available - disable various buttons btnConnect->setEnabled( false ); btnEdit->setEnabled( false ); btnDelete->setEnabled( false ); btnSave->setEnabled( false ); } //set last used connection QString selectedConnection = QgsWFSConnection::selectedConnection(); int index = cmbConnections->findText( selectedConnection ); if ( index != -1 ) { cmbConnections->setCurrentIndex( index ); } QgsWFSConnection connection( cmbConnections->currentText() ); delete mCapabilities; mCapabilities = new QgsWFSCapabilities( connection.uri().uri() ); connect( mCapabilities, SIGNAL( gotCapabilities() ), this, SLOT( capabilitiesReplyFinished() ) ); }
void QgsWFSCapabilities::capabilitiesReplyFinished() { QNetworkReply *reply = mCapabilitiesReply; reply->deleteLater(); mCapabilitiesReply = 0; // handle network errors if ( reply->error() != QNetworkReply::NoError ) { mErrorCode = QgsWFSCapabilities::NetworkError; mErrorMessage = reply->errorString(); emit gotCapabilities(); return; } // handle HTTP redirects QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ); if ( !redirect.isNull() ) { QgsDebugMsg( "redirecting to " + redirect.toUrl().toString() ); QNetworkRequest request( redirect.toUrl() ); setAuthorization( request ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork ); request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); mCapabilitiesReply = QgsNetworkAccessManager::instance()->get( request ); connect( mCapabilitiesReply, SIGNAL( finished() ), this, SLOT( capabilitiesReplyFinished() ) ); return; } QByteArray buffer = reply->readAll(); QgsDebugMsg( "parsing capabilities: " + buffer ); // parse XML QString capabilitiesDocError; QDomDocument capabilitiesDocument; if ( !capabilitiesDocument.setContent( buffer, true, &capabilitiesDocError ) ) { mErrorCode = QgsWFSCapabilities::XmlError; mErrorMessage = capabilitiesDocError; emit gotCapabilities(); return; } QDomElement doc = capabilitiesDocument.documentElement(); // hangle exceptions if ( doc.tagName() == "ExceptionReport" ) { QDomNode ex = doc.firstChild(); QString exc = ex.toElement().attribute( "exceptionCode", "Exception" ); QDomElement ext = ex.firstChild().toElement(); mErrorCode = QgsWFSCapabilities::ServerExceptionError; mErrorMessage = exc + ": " + ext.firstChild().nodeValue(); emit gotCapabilities(); return; } mCaps.clear(); //test wfs version QString version = capabilitiesDocument.documentElement().attribute( "version" ); if ( version != "1.0.0" && version != "1.0" ) { mErrorCode = WFSVersionNotSupported; mErrorMessage = tr( "Either the WFS server does not support WFS version 1.0.0 or the WFS url is wrong" ); emit gotCapabilities(); return; } // get the <FeatureType> elements QDomNodeList featureTypeList = capabilitiesDocument.elementsByTagNameNS( WFS_NAMESPACE, "FeatureType" ); for ( unsigned int i = 0; i < featureTypeList.length(); ++i ) { FeatureType featureType; QDomElement featureTypeElem = featureTypeList.at( i ).toElement(); //Name QDomNodeList nameList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Name" ); if ( nameList.length() > 0 ) { featureType.name = nameList.at( 0 ).toElement().text(); } //Title QDomNodeList titleList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Title" ); if ( titleList.length() > 0 ) { featureType.title = titleList.at( 0 ).toElement().text(); } //Abstract QDomNodeList abstractList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "Abstract" ); if ( abstractList.length() > 0 ) { featureType.abstract = abstractList.at( 0 ).toElement().text(); } //DefaultSRS is always the first entry in the feature srs list QDomNodeList defaultCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "DefaultSRS" ); if ( defaultCRSList.length() > 0 ) { featureType.crslist.append( defaultCRSList.at( 0 ).toElement().text() ); } //OtherSRS QDomNodeList otherCRSList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "OtherSRS" ); for ( unsigned int i = 0; i < otherCRSList.length(); ++i ) { featureType.crslist.append( otherCRSList.at( i ).toElement().text() ); } //Support <SRS> for compatibility with older versions QDomNodeList srsList = featureTypeElem.elementsByTagNameNS( WFS_NAMESPACE, "SRS" ); for ( unsigned int i = 0; i < srsList.length(); ++i ) { featureType.crslist.append( srsList.at( i ).toElement().text() ); } mCaps.featureTypes.append( featureType ); } emit gotCapabilities(); }
void QgsWfsCapabilities::capabilitiesReplyFinished() { const QByteArray& buffer = mResponse; QgsDebugMsg( "parsing capabilities: " + buffer ); // parse XML QString capabilitiesDocError; QDomDocument capabilitiesDocument; if ( !capabilitiesDocument.setContent( buffer, true, &capabilitiesDocError ) ) { mErrorCode = QgsWfsRequest::XmlError; mErrorMessage = capabilitiesDocError; emit gotCapabilities(); return; } QDomElement doc = capabilitiesDocument.documentElement(); // handle exceptions if ( doc.tagName() == "ExceptionReport" ) { QDomNode ex = doc.firstChild(); QString exc = ex.toElement().attribute( "exceptionCode", "Exception" ); QDomElement ext = ex.firstChild().toElement(); mErrorCode = QgsWfsRequest::ServerExceptionError; mErrorMessage = exc + ": " + ext.firstChild().nodeValue(); emit gotCapabilities(); return; } mCaps.clear(); //test wfs version mCaps.version = doc.attribute( "version" ); if ( !mCaps.version.startsWith( "1.0" ) && !mCaps.version.startsWith( "1.1" ) && !mCaps.version.startsWith( "2.0" ) ) { mErrorCode = WFSVersionNotSupported; mErrorMessage = tr( "WFS version %1 not supported" ).arg( mCaps.version ); emit gotCapabilities(); return; } // WFS 2.0 implementation are supposed to implement resultType=hits, and some // implementations (GeoServer) might advertize it, whereas others (MapServer) do not. // WFS 1.1 implementation too I think, but in the examples of the GetCapabilites // response of the WFS 1.1 standard (and in common implementations), this is // explictly advertized if ( mCaps.version.startsWith( "2.0" ) ) mCaps.supportsHits = true; // Note: for conveniency, we do not use the elementsByTagNameNS() method as // the WFS and OWS namespaces URI are not the same in all versions // find <ows:OperationsMetadata> QDomElement operationsMetadataElem = doc.firstChildElement( "OperationsMetadata" ); if ( !operationsMetadataElem.isNull() ) { QDomNodeList contraintList = operationsMetadataElem.elementsByTagName( "Constraint" ); for ( int i = 0; i < contraintList.size(); ++i ) { QDomElement contraint = contraintList.at( i ).toElement(); if ( contraint.attribute( "name" ) == "DefaultMaxFeatures" /* WFS 1.1 */ ) { QDomElement value = contraint.firstChildElement( "Value" ); if ( !value.isNull() ) { mCaps.maxFeatures = value.text().toInt(); QgsDebugMsg( QString( "maxFeatures: %1" ).arg( mCaps.maxFeatures ) ); } } else if ( contraint.attribute( "name" ) == "CountDefault" /* WFS 2.0 (e.g. MapServer) */ ) { QDomElement value = contraint.firstChildElement( "DefaultValue" ); if ( !value.isNull() ) { mCaps.maxFeatures = value.text().toInt(); QgsDebugMsg( QString( "maxFeatures: %1" ).arg( mCaps.maxFeatures ) ); } } else if ( contraint.attribute( "name" ) == "ImplementsResultPaging" /* WFS 2.0 */ ) { QDomElement value = contraint.firstChildElement( "DefaultValue" ); if ( !value.isNull() && value.text() == "TRUE" ) { mCaps.supportsPaging = true; QgsDebugMsg( "Supports paging" ); } } else if ( contraint.attribute( "name" ) == "ImplementsStandardJoins" || contraint.attribute( "name" ) == "ImplementsSpatialJoins" /* WFS 2.0 */ ) { QDomElement value = contraint.firstChildElement( "DefaultValue" ); if ( !value.isNull() && value.text() == "TRUE" ) { mCaps.supportsJoins = true; QgsDebugMsg( "Supports joins" ); } } } // In WFS 2.0, max features can also be set in Operation.GetFeature (e.g. GeoServer) // and we are also interested by resultType=hits for WFS 1.1 QDomNodeList operationList = operationsMetadataElem.elementsByTagName( "Operation" ); for ( int i = 0; i < operationList.size(); ++i ) { QDomElement operation = operationList.at( i ).toElement(); if ( operation.attribute( "name" ) == "GetFeature" ) { QDomNodeList operationContraintList = operation.elementsByTagName( "Constraint" ); for ( int j = 0; j < operationContraintList.size(); ++j ) { QDomElement contraint = operationContraintList.at( j ).toElement(); if ( contraint.attribute( "name" ) == "CountDefault" ) { QDomElement value = contraint.firstChildElement( "DefaultValue" ); if ( !value.isNull() ) { mCaps.maxFeatures = value.text().toInt(); QgsDebugMsg( QString( "maxFeatures: %1" ).arg( mCaps.maxFeatures ) ); } break; } } QDomNodeList parameterList = operation.elementsByTagName( "Parameter" ); for ( int j = 0; j < parameterList.size(); ++j ) { QDomElement parameter = parameterList.at( j ).toElement(); if ( parameter.attribute( "name" ) == "resultType" ) { QDomNodeList valueList = parameter.elementsByTagName( "Value" ); for ( int k = 0; k < valueList.size(); ++k ) { QDomElement value = valueList.at( k ).toElement(); if ( value.text() == "hits" ) { mCaps.supportsHits = true; QgsDebugMsg( "Support hits" ); break; } } } } break; } } } //go to <FeatureTypeList> QDomElement featureTypeListElem = doc.firstChildElement( "FeatureTypeList" ); if ( featureTypeListElem.isNull() ) { emit gotCapabilities(); return; } // Parse operations supported for all feature types bool insertCap, updateCap, deleteCap; parseSupportedOperations( featureTypeListElem.firstChildElement( "Operations" ), insertCap, updateCap, deleteCap ); // get the <FeatureType> elements QDomNodeList featureTypeList = featureTypeListElem.elementsByTagName( "FeatureType" ); for ( int i = 0; i < featureTypeList.size(); ++i ) { FeatureType featureType; QDomElement featureTypeElem = featureTypeList.at( i ).toElement(); //Name QDomNodeList nameList = featureTypeElem.elementsByTagName( "Name" ); if ( nameList.length() > 0 ) { featureType.name = nameList.at( 0 ).toElement().text(); } //Title QDomNodeList titleList = featureTypeElem.elementsByTagName( "Title" ); if ( titleList.length() > 0 ) { featureType.title = titleList.at( 0 ).toElement().text(); } //Abstract QDomNodeList abstractList = featureTypeElem.elementsByTagName( "Abstract" ); if ( abstractList.length() > 0 ) { featureType.abstract = abstractList.at( 0 ).toElement().text(); } //DefaultSRS is always the first entry in the feature srs list QDomNodeList defaultCRSList = featureTypeElem.elementsByTagName( "DefaultSRS" ); if ( defaultCRSList.length() == 0 ) // In WFS 2.0, this is spelled DefaultCRS... defaultCRSList = featureTypeElem.elementsByTagName( "DefaultCRS" ); if ( defaultCRSList.length() > 0 ) { QString srsname( defaultCRSList.at( 0 ).toElement().text() ); // Some servers like Geomedia advertize EPSG:XXXX even in WFS 1.1 or 2.0 if ( srsname.startsWith( "EPSG:" ) ) mCaps.useEPSGColumnFormat = true; featureType.crslist.append( NormalizeSRSName( srsname ) ); } //OtherSRS QDomNodeList otherCRSList = featureTypeElem.elementsByTagName( "OtherSRS" ); if ( otherCRSList.length() == 0 ) // In WFS 2.0, this is spelled OtherCRS... otherCRSList = featureTypeElem.elementsByTagName( "OtherCRS" ); for ( int i = 0; i < otherCRSList.size(); ++i ) { featureType.crslist.append( NormalizeSRSName( otherCRSList.at( i ).toElement().text() ) ); } //Support <SRS> for compatibility with older versions QDomNodeList srsList = featureTypeElem.elementsByTagName( "SRS" ); for ( int i = 0; i < srsList.size(); ++i ) { featureType.crslist.append( NormalizeSRSName( srsList.at( i ).toElement().text() ) ); } // Get BBox WFS 1.0 way QDomElement latLongBB = featureTypeElem.firstChildElement( "LatLongBoundingBox" ); if ( latLongBB.hasAttributes() ) { // Despite the name LatLongBoundingBox, the coordinates are supposed to // be expressed in <SRS>. From the WFS schema; // <!-- The LatLongBoundingBox element is used to indicate the edges of // an enclosing rectangle in the SRS of the associated feature type. featureType.bbox = QgsRectangle( latLongBB.attribute( "minx" ).toDouble(), latLongBB.attribute( "miny" ).toDouble(), latLongBB.attribute( "maxx" ).toDouble(), latLongBB.attribute( "maxy" ).toDouble() ); featureType.bboxSRSIsWGS84 = false; // But some servers do not honour this and systematically reproject to WGS84 // such as GeoServer. See http://osgeo-org.1560.x6.nabble.com/WFS-LatLongBoundingBox-td3813810.html // This is also true of TinyOWS if ( !featureType.crslist.isEmpty() && featureType.bbox.xMinimum() >= -180 && featureType.bbox.yMinimum() >= -90 && featureType.bbox.xMaximum() <= 180 && featureType.bbox.yMaximum() < 90 ) { QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( featureType.crslist[0] ); if ( !crs.isGeographic() ) { // If the CRS is projected then check that projecting the corner of the bbox, assumed to be in WGS84, // into the CRS, and then back to WGS84, works (check that we are in the validity area) QgsCoordinateReferenceSystem crsWGS84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs( "CRS:84" ); QgsCoordinateTransform ct( crsWGS84, crs ); QgsPoint ptMin( featureType.bbox.xMinimum(), featureType.bbox.yMinimum() ); QgsPoint ptMinBack( ct.transform( ct.transform( ptMin, QgsCoordinateTransform::ForwardTransform ), QgsCoordinateTransform::ReverseTransform ) ); QgsPoint ptMax( featureType.bbox.xMaximum(), featureType.bbox.yMaximum() ); QgsPoint ptMaxBack( ct.transform( ct.transform( ptMax, QgsCoordinateTransform::ForwardTransform ), QgsCoordinateTransform::ReverseTransform ) ); QgsDebugMsg( featureType.bbox.toString() ); QgsDebugMsg( ptMinBack.toString() ); QgsDebugMsg( ptMaxBack.toString() ); if ( fabs( featureType.bbox.xMinimum() - ptMinBack.x() ) < 1e-5 && fabs( featureType.bbox.yMinimum() - ptMinBack.y() ) < 1e-5 && fabs( featureType.bbox.xMaximum() - ptMaxBack.x() ) < 1e-5 && fabs( featureType.bbox.yMaximum() - ptMaxBack.y() ) < 1e-5 ) { QgsDebugMsg( "Values of LatLongBoundingBox are consistent with WGS84 long/lat bounds, so as the CRS is projected, assume they are indeed in WGS84 and not in the CRS units" ); featureType.bboxSRSIsWGS84 = true; } } } } else { // WFS 1.1 way QDomElement WGS84BoundingBox = featureTypeElem.firstChildElement( "WGS84BoundingBox" ); if ( !WGS84BoundingBox.isNull() ) { QDomElement lowerCorner = WGS84BoundingBox.firstChildElement( "LowerCorner" ); QDomElement upperCorner = WGS84BoundingBox.firstChildElement( "UpperCorner" ); if ( !lowerCorner.isNull() && !upperCorner.isNull() ) { QStringList lowerCornerList = lowerCorner.text().split( " ", QString::SkipEmptyParts ); QStringList upperCornerList = upperCorner.text().split( " ", QString::SkipEmptyParts ); if ( lowerCornerList.size() == 2 && upperCornerList.size() == 2 ) { featureType.bbox = QgsRectangle( lowerCornerList[0].toDouble(), lowerCornerList[1].toDouble(), upperCornerList[0].toDouble(), upperCornerList[1].toDouble() ); featureType.bboxSRSIsWGS84 = true; } } } } // Parse Operations specific to the type name parseSupportedOperations( featureTypeElem.firstChildElement( "Operations" ), featureType.insertCap, featureType.updateCap, featureType.deleteCap ); featureType.insertCap |= insertCap; featureType.updateCap |= updateCap; featureType.deleteCap |= deleteCap; mCaps.featureTypes.push_back( featureType ); } Q_FOREACH ( const FeatureType& f, mCaps.featureTypes ) { mCaps.setAllTypenames.insert( f.name ); QString unprefixed( QgsWFSUtils::removeNamespacePrefix( f.name ) ); if ( !mCaps.setAmbiguousUnprefixedTypename.contains( unprefixed ) ) { if ( mCaps.mapUnprefixedTypenameToPrefixedTypename.contains( unprefixed ) ) { mCaps.setAmbiguousUnprefixedTypename.insert( unprefixed ); mCaps.mapUnprefixedTypenameToPrefixedTypename.remove( unprefixed ); } else { mCaps.mapUnprefixedTypenameToPrefixedTypename[unprefixed] = f.name; } } } //go to <Filter_Capabilities> QDomElement filterCapabilitiesElem = doc.firstChildElement( "Filter_Capabilities" ); if ( !filterCapabilitiesElem.isNull() ) parseFilterCapabilities( filterCapabilitiesElem ); // Hard-coded functions Function f_ST_GeometryFromText( "ST_GeometryFromText", 1, 2 ); f_ST_GeometryFromText.returnType = "gml:AbstractGeometryType"; f_ST_GeometryFromText.argumentList << Argument( "wkt", "xs:string" ); f_ST_GeometryFromText.argumentList << Argument( "srsname", "xs:string" ); mCaps.functionList << f_ST_GeometryFromText; Function f_ST_GeomFromGML( "ST_GeomFromGML", 1 ); f_ST_GeomFromGML.returnType = "gml:AbstractGeometryType"; f_ST_GeomFromGML.argumentList << Argument( "gml", "xs:string" ); mCaps.functionList << f_ST_GeomFromGML; Function f_ST_MakeEnvelope( "ST_MakeEnvelope", 4, 5 ); f_ST_MakeEnvelope.returnType = "gml:AbstractGeometryType"; f_ST_MakeEnvelope.argumentList << Argument( "minx", "xs:double" ); f_ST_MakeEnvelope.argumentList << Argument( "miny", "xs:double" ); f_ST_MakeEnvelope.argumentList << Argument( "maxx", "xs:double" ); f_ST_MakeEnvelope.argumentList << Argument( "maxy", "xs:double" ); f_ST_MakeEnvelope.argumentList << Argument( "srsname", "xs:string" ); mCaps.functionList << f_ST_MakeEnvelope; emit gotCapabilities(); }