mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
[WFS provider] Handle documents with Complex Feature schemas (using OGR GMLAS driver), and JSON'ify content of complex properties
Funded by QGIS user group Germany (QGIS Anwendergruppe Deutschland e.V.)
This commit is contained in:
parent
be57a36ffd
commit
83fe2fea87
@ -63,6 +63,7 @@ target_include_directories(provider_wfs_a PUBLIC
|
||||
|
||||
target_link_libraries(provider_wfs_a
|
||||
qgis_core
|
||||
GDAL::GDAL
|
||||
)
|
||||
|
||||
# require c++17
|
||||
@ -120,6 +121,7 @@ else()
|
||||
|
||||
target_link_libraries (provider_wfs
|
||||
qgis_core
|
||||
GDAL::GDAL
|
||||
)
|
||||
|
||||
if (WITH_GUI)
|
||||
|
@ -213,12 +213,24 @@ QUrl QgsWFSFeatureDownloaderImpl::buildURL( qint64 startIndex, long long maxFeat
|
||||
arg( minx ).arg( miny ).arg( maxx ).arg( maxy ) );
|
||||
QgsExpression bboxExp( filterBbox );
|
||||
QDomDocument bboxDoc;
|
||||
|
||||
QMap<QString, QString> fieldNameToXPathMap;
|
||||
if ( !mShared->mFieldNameToXPathAndIsNestedContentMap.isEmpty() )
|
||||
{
|
||||
for ( auto iterFieldName = mShared->mFieldNameToXPathAndIsNestedContentMap.constBegin(); iterFieldName != mShared->mFieldNameToXPathAndIsNestedContentMap.constEnd(); ++iterFieldName )
|
||||
{
|
||||
const QString &fieldName = iterFieldName.key();
|
||||
const auto &value = iterFieldName.value();
|
||||
fieldNameToXPathMap[fieldName] = value.first;
|
||||
}
|
||||
}
|
||||
|
||||
QDomElement bboxElem = QgsOgcUtils::expressionToOgcFilter( bboxExp, bboxDoc,
|
||||
gmlVersion, filterVersion,
|
||||
mShared->mLayerPropertiesList.size() == 1 ? mShared->mLayerPropertiesList[0].mNamespacePrefix : QString(),
|
||||
mShared->mLayerPropertiesList.size() == 1 ? mShared->mLayerPropertiesList[0].mNamespaceURI : QString(),
|
||||
geometryAttribute, mShared->srsName(),
|
||||
honourAxisOrientation, mShared->mURI.invertAxisOrientation() );
|
||||
honourAxisOrientation, mShared->mURI.invertAxisOrientation(), nullptr, fieldNameToXPathMap, mShared->mNamespacePrefixToURIMap );
|
||||
bboxDoc.appendChild( bboxElem );
|
||||
|
||||
filters.push_back( bboxDoc.toString() );
|
||||
|
@ -16,13 +16,17 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgis.h"
|
||||
#include "qgscplhttpfetchoverrider.h"
|
||||
#include "qgsfeature.h"
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgsfields.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgsogcutils.h"
|
||||
#include "qgsogrutils.h"
|
||||
#include "qgssqliteutils.h"
|
||||
#include "qgswfsconstants.h"
|
||||
#include "qgswfsfeatureiterator.h"
|
||||
#include "qgswfsprovider.h"
|
||||
@ -33,6 +37,10 @@
|
||||
#include "qgswfsutils.h"
|
||||
#include "qgssettings.h"
|
||||
|
||||
#include "cpl_string.h"
|
||||
#include "gdal.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDomDocument>
|
||||
@ -605,6 +613,8 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
|
||||
QgsFields fields;
|
||||
Qgis::WkbType geomType;
|
||||
if ( !readAttributesFromSchema( describeFeatureDocument,
|
||||
response,
|
||||
/* singleLayerContext = */ typenameList.size() == 1,
|
||||
typeName,
|
||||
geometryAttribute, fields, geomType, errorMsg ) )
|
||||
{
|
||||
@ -624,7 +634,7 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
|
||||
}
|
||||
}
|
||||
|
||||
setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, typenameList, errorMsg );
|
||||
setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, response, typenameList, errorMsg );
|
||||
|
||||
const QString &defaultTypeName = mShared->mURI.typeName();
|
||||
QgsWFSProviderSQLColumnRefValidator oColumnValidator(
|
||||
@ -789,7 +799,7 @@ bool QgsWFSProvider::processSQL( const QString &sqlString, QString &errorMsg, QS
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsWFSProvider::setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QStringList &typenameList, QString &errorMsg )
|
||||
bool QgsWFSProvider::setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QByteArray &response, const QStringList &typenameList, QString &errorMsg )
|
||||
{
|
||||
mShared->mLayerPropertiesList.clear();
|
||||
for ( const QString &typeName : typenameList )
|
||||
@ -798,6 +808,8 @@ bool QgsWFSProvider::setLayerPropertiesListFromDescribeFeature( QDomDocument &de
|
||||
QgsFields fields;
|
||||
Qgis::WkbType geomType;
|
||||
if ( !readAttributesFromSchema( describeFeatureDocument,
|
||||
response,
|
||||
/* singleLayerContext = */ typenameList.size() == 1,
|
||||
typeName,
|
||||
geometryAttribute, fields, geomType, errorMsg ) )
|
||||
{
|
||||
@ -1513,6 +1525,8 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields
|
||||
}
|
||||
|
||||
if ( !readAttributesFromSchema( describeFeatureDocument,
|
||||
response,
|
||||
/* singleLayerContext = */ true,
|
||||
mShared->mURI.typeName(),
|
||||
geometryAttribute, fields, geomType, errorMsg ) )
|
||||
{
|
||||
@ -1522,18 +1536,478 @@ bool QgsWFSProvider::describeFeatureType( QString &geometryAttribute, QgsFields
|
||||
return false;
|
||||
}
|
||||
|
||||
setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, {mShared->mURI.typeName()}, errorMsg );
|
||||
setLayerPropertiesListFromDescribeFeature( describeFeatureDocument, response, {mShared->mURI.typeName()}, errorMsg );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
|
||||
const QByteArray &response,
|
||||
bool singleLayerContext,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields,
|
||||
Qgis::WkbType &geomType,
|
||||
QString &errorMsg )
|
||||
{
|
||||
bool mayTryWithGMLAS = false;
|
||||
bool ret = readAttributesFromSchemaWithoutGMLAS( schemaDoc, prefixedTypename, geometryAttribute, fields, geomType, errorMsg, mayTryWithGMLAS );
|
||||
if ( singleLayerContext &&
|
||||
mayTryWithGMLAS &&
|
||||
GDALGetDriverByName( "GMLAS" ) )
|
||||
{
|
||||
QgsFields fieldsGMLAS;
|
||||
Qgis::WkbType geomTypeGMLAS;
|
||||
QString errorMsgGMLAS;
|
||||
if ( readAttributesFromSchemaWithGMLAS( response, prefixedTypename, geometryAttribute, fieldsGMLAS, geomTypeGMLAS, errorMsgGMLAS ) )
|
||||
{
|
||||
fields = fieldsGMLAS;
|
||||
geomType = geomTypeGMLAS;
|
||||
ret = true;
|
||||
}
|
||||
else if ( !ret )
|
||||
{
|
||||
errorMsg = errorMsgGMLAS;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QVariant::Type getVariantTypeFromXML( const QString &xmlType )
|
||||
{
|
||||
QVariant::Type attributeType = QVariant::Invalid;
|
||||
|
||||
const QString type = QString( xmlType )
|
||||
.replace( QLatin1String( "xs:" ), QString() )
|
||||
.replace( QLatin1String( "xsd:" ), QString() );
|
||||
|
||||
if ( type.compare( QLatin1String( "string" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "token" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "NMTOKEN" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "NCName" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "QName" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "ID" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "IDREF" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "anyURI" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "anySimpleType" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::String;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "boolean" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::Bool;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "double" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "float" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "decimal" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::Double;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "byte" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "unsignedByte" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "int" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "short" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "unsignedShort" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::Int;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "long" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "unsignedLong" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "integer" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "negativeInteger" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "nonNegativeInteger" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "positiveInteger" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::LongLong;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "date" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "gYear" ), Qt::CaseInsensitive ) == 0 ||
|
||||
type.compare( QLatin1String( "gYearMonth" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::Date;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "time" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::Time;
|
||||
}
|
||||
else if ( type.compare( QLatin1String( "dateTime" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
attributeType = QVariant::DateTime;
|
||||
}
|
||||
return attributeType;
|
||||
}
|
||||
|
||||
bool QgsWFSProvider::readAttributesFromSchemaWithGMLAS( const QByteArray &response,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields,
|
||||
Qgis::WkbType &geomType,
|
||||
QString &errorMsg )
|
||||
{
|
||||
QUrl url( mShared->mURI.requestUrl( QStringLiteral( "DescribeFeatureType" ) ) );
|
||||
QUrlQuery query( url );
|
||||
query.addQueryItem( QStringLiteral( "TYPENAME" ), prefixedTypename );
|
||||
url.setQuery( query );
|
||||
|
||||
// If a previous attempt with the same URL failed because of cancellation
|
||||
// in the past second, do not retry.
|
||||
// The main use case for that is when QgsWfsProviderMetadata::querySublayers()
|
||||
// is called when adding a layer, and several QgsWFSProvider instances are
|
||||
// quickly created.
|
||||
static QMutex mutex;
|
||||
static QUrl lastCanceledURL;
|
||||
static QDateTime lastCanceledDateTime;
|
||||
{
|
||||
QMutexLocker lock( &mutex );
|
||||
if ( lastCanceledURL == url && lastCanceledDateTime + 1 > QDateTime::currentDateTime() )
|
||||
{
|
||||
mMetadataRetrievalCanceled = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a unique /vsimem/ filename
|
||||
constexpr int TEMP_FILENAME_SIZE = 128;
|
||||
void *p = malloc( TEMP_FILENAME_SIZE );
|
||||
char *pszSchemaTempFilename = static_cast<char *>( p );
|
||||
snprintf( pszSchemaTempFilename, TEMP_FILENAME_SIZE, "/vsimem/schema_%p.xsd", p );
|
||||
|
||||
// Serialize the main schema into a temporary /vsimem/ filename
|
||||
char *pszSchema = VSIStrdup( response.constData() );
|
||||
VSILFILE *fp = VSIFileFromMemBuffer( pszSchemaTempFilename,
|
||||
reinterpret_cast<GByte *>( pszSchema ), strlen( pszSchema ), /* bTakeOwnership=*/ true );
|
||||
if ( fp )
|
||||
VSIFCloseL( fp );
|
||||
|
||||
QgsFeedback feedback;
|
||||
GDALDatasetH hDS = nullptr;
|
||||
|
||||
// Analyze the DescribeFeatureType response schema with the OGR GMLAS driver
|
||||
// in a thread, so it can get interrupted (with GDAL 3.9: https://github.com/OSGeo/gdal/pull/9019)
|
||||
const auto downloaderLambda = [pszSchemaTempFilename, &feedback, &hDS]()
|
||||
{
|
||||
QgsCPLHTTPFetchOverrider cplHTTPFetchOverrider( QString(), &feedback );
|
||||
QgsSetCPLHTTPFetchOverriderInitiatorClass( cplHTTPFetchOverrider, QStringLiteral( "WFSProviderDownloadSchema" ) )
|
||||
|
||||
char **papszOpenOptions = nullptr;
|
||||
papszOpenOptions = CSLSetNameValue( papszOpenOptions, "XSD", pszSchemaTempFilename );
|
||||
hDS = GDALOpenEx( "GMLAS:", GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr );
|
||||
CSLDestroy( papszOpenOptions );
|
||||
};
|
||||
|
||||
std::unique_ptr<_DownloaderThread> downloaderThread =
|
||||
std::make_unique<_DownloaderThread>( downloaderLambda );
|
||||
downloaderThread->start();
|
||||
|
||||
QTimer timerForHits;
|
||||
|
||||
QMessageBox *box = nullptr;
|
||||
QWidget *parentWidget = nullptr;
|
||||
if ( qApp->thread() == QThread::currentThread() )
|
||||
{
|
||||
parentWidget = QApplication::activeWindow();
|
||||
if ( !parentWidget )
|
||||
{
|
||||
const QWidgetList widgets = QgsApplication::topLevelWidgets();
|
||||
for ( QWidget *widget : widgets )
|
||||
{
|
||||
if ( widget->objectName() == QLatin1String( "QgisApp" ) )
|
||||
{
|
||||
parentWidget = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( parentWidget )
|
||||
{
|
||||
// Display an information box if within 2 seconds, the schema has not
|
||||
// been analyzed.
|
||||
box = new QMessageBox(
|
||||
QMessageBox::Information, tr( "Information" ), tr( "Download of schemas in progress..." ),
|
||||
QMessageBox::Cancel,
|
||||
parentWidget );
|
||||
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,9,0)
|
||||
connect( box, &QDialog::rejected, &feedback, &QgsFeedback::cancel );
|
||||
#else
|
||||
box->button( QMessageBox::Cancel )->setEnabled( false );
|
||||
#endif
|
||||
|
||||
QgsSettings s;
|
||||
const double settingDefaultValue = 2.0;
|
||||
const QString settingName = QStringLiteral( "qgis/wfsDownloadSchemasPopupTimeout" );
|
||||
if ( !s.contains( settingName ) )
|
||||
{
|
||||
s.setValue( settingName, settingDefaultValue );
|
||||
}
|
||||
const double timeout = s.value( settingName, settingDefaultValue ).toDouble();
|
||||
if ( timeout > 0 )
|
||||
{
|
||||
timerForHits.setInterval( static_cast<int>( 1000 * timeout ) );
|
||||
timerForHits.setSingleShot( true );
|
||||
timerForHits.start();
|
||||
connect( &timerForHits, &QTimer::timeout, box, &QDialog::exec );
|
||||
}
|
||||
|
||||
// Close dialog when download theread finishes.
|
||||
// Will actually trigger the QDialog::rejected signal...
|
||||
connect( downloaderThread.get(), &QThread::finished, box, &QDialog::accept );
|
||||
}
|
||||
|
||||
// Run an event loop until download thread finishes
|
||||
QEventLoop loop;
|
||||
connect( downloaderThread.get(), &QThread::finished, &loop, &QEventLoop::quit );
|
||||
loop.exec( QEventLoop::ExcludeUserInputEvents );
|
||||
downloaderThread->wait();
|
||||
|
||||
VSIUnlink( pszSchemaTempFilename );
|
||||
VSIFree( pszSchemaTempFilename );
|
||||
|
||||
bool ret = hDS != nullptr;
|
||||
if ( feedback.isCanceled() && !ret )
|
||||
{
|
||||
QMutexLocker lock( &mutex );
|
||||
mMetadataRetrievalCanceled = true;
|
||||
lastCanceledURL = url;
|
||||
lastCanceledDateTime = QDateTime::currentDateTime();
|
||||
errorMsg = tr( "Schema analysis interrupted by user." );
|
||||
return false;
|
||||
}
|
||||
if ( !ret )
|
||||
{
|
||||
errorMsg = tr( "Cannot analyze schema indicated in DescribeFeatureType response." );
|
||||
return false;
|
||||
}
|
||||
|
||||
gdal::dataset_unique_ptr oDSCloser( hDS );
|
||||
|
||||
// Retrieve namespace prefix and URIs
|
||||
OGRLayerH hOtherMetadataLayer = GDALDatasetGetLayerByName( hDS, "_ogr_other_metadata" );
|
||||
if ( !hOtherMetadataLayer )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find _ogr_other_metadata layer" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
auto hOtherMetadataLayerDefn = OGR_L_GetLayerDefn( hOtherMetadataLayer );
|
||||
|
||||
const int keyIdx = OGR_FD_GetFieldIndex( hOtherMetadataLayerDefn, "key" );
|
||||
if ( keyIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find key field in _ogr_other_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
const int valueIdx = OGR_FD_GetFieldIndex( hOtherMetadataLayerDefn, "value" );
|
||||
if ( valueIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find value field in _ogr_other_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<int, QPair<QString, QString>> mapPrefixIdxToPrefixAndUri;
|
||||
while ( true )
|
||||
{
|
||||
gdal::ogr_feature_unique_ptr hFeatureOtherMD(
|
||||
OGR_L_GetNextFeature( hOtherMetadataLayer ) );
|
||||
if ( !hFeatureOtherMD )
|
||||
break;
|
||||
|
||||
const QString key = QString::fromUtf8(
|
||||
OGR_F_GetFieldAsString( hFeatureOtherMD.get(), keyIdx ) );
|
||||
const QString value = QString::fromUtf8(
|
||||
OGR_F_GetFieldAsString( hFeatureOtherMD.get(), valueIdx ) );
|
||||
|
||||
if ( key.startsWith( QLatin1String( "namespace_prefix_" ) ) )
|
||||
{
|
||||
mapPrefixIdxToPrefixAndUri[key.mid( int( strlen( "namespace_prefix_" ) ) ).toInt()].first = value;
|
||||
}
|
||||
else if ( key.startsWith( QLatin1String( "namespace_uri_" ) ) )
|
||||
{
|
||||
mapPrefixIdxToPrefixAndUri[key.mid( int( strlen( "namespace_uri_" ) ) ).toInt()].second = value;
|
||||
}
|
||||
}
|
||||
for ( const auto &kv : mapPrefixIdxToPrefixAndUri )
|
||||
{
|
||||
if ( !kv.second.first.isEmpty() && !kv.second.second.isEmpty() )
|
||||
{
|
||||
mShared->mNamespacePrefixToURIMap[kv.second.first] = kv.second.second;
|
||||
QgsDebugMsgLevel( QStringLiteral( "%1 -> %2" ).arg( kv.second.first ).arg( kv.second.second ), 4 );
|
||||
}
|
||||
}
|
||||
|
||||
// Find the layer of interest
|
||||
OGRLayerH hLayersMetadata = GDALDatasetGetLayerByName( hDS, "_ogr_layers_metadata" );
|
||||
if ( !hLayersMetadata )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find _ogr_layers_metadata layer" ), 4 );
|
||||
return false;
|
||||
}
|
||||
OGR_L_SetAttributeFilter( hLayersMetadata,
|
||||
( "layer_xpath = " + QgsSqliteUtils::quotedString( prefixedTypename ).toStdString() ).c_str() );
|
||||
gdal::ogr_feature_unique_ptr hFeatureLayersMD( OGR_L_GetNextFeature( hLayersMetadata ) );
|
||||
if ( !hFeatureLayersMD )
|
||||
{
|
||||
QgsDebugMsgLevel(
|
||||
QStringLiteral( "Cannot find feature with layer_xpath = %1 in _ogr_layers_metadata" ).arg( prefixedTypename ), 4 );
|
||||
return false;
|
||||
}
|
||||
const int fldIdx = OGR_F_GetFieldIndex( hFeatureLayersMD.get(), "layer_name" );
|
||||
if ( fldIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find layer_name field in _ogr_layers_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
const QString layerName = QString::fromUtf8(
|
||||
OGR_F_GetFieldAsString( hFeatureLayersMD.get(), fldIdx ) );
|
||||
|
||||
OGRLayerH hLayer = GDALDatasetGetLayerByName(
|
||||
hDS, layerName.toStdString().c_str() );
|
||||
if ( !hLayer )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find %& layer" ).arg( layerName ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get field information
|
||||
OGRLayerH hFieldsMetadata = GDALDatasetGetLayerByName( hDS, "_ogr_fields_metadata" );
|
||||
if ( !hFieldsMetadata )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find _ogr_fields_metadata layer" ), 4 );
|
||||
return false;
|
||||
}
|
||||
OGR_L_SetAttributeFilter( hFieldsMetadata,
|
||||
( "layer_name = " + QgsSqliteUtils::quotedString( layerName ).toStdString() ).c_str() );
|
||||
|
||||
auto hFieldsMetadataDefn = OGR_L_GetLayerDefn( hFieldsMetadata );
|
||||
|
||||
const int fieldNameIdx = OGR_FD_GetFieldIndex( hFieldsMetadataDefn, "field_name" );
|
||||
if ( fieldNameIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find field_name field in _ogr_fields_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
const int fieldXPathIdx = OGR_FD_GetFieldIndex( hFieldsMetadataDefn, "field_xpath" );
|
||||
if ( fieldXPathIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find field_xpath field in _ogr_fields_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
const int fieldIsListIdx = OGR_FD_GetFieldIndex( hFieldsMetadataDefn, "field_is_list" );
|
||||
if ( fieldIsListIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find field_is_list field in _ogr_fields_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
const int fieldTypeIdx = OGR_FD_GetFieldIndex( hFieldsMetadataDefn, "field_type" );
|
||||
if ( fieldTypeIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find field_type field in _ogr_fields_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
const int fieldCategoryIdx = OGR_FD_GetFieldIndex( hFieldsMetadataDefn, "field_category" );
|
||||
if ( fieldCategoryIdx < 0 )
|
||||
{
|
||||
// should not happen
|
||||
QgsDebugMsgLevel( QStringLiteral( "Cannot find field_category field in _ogr_fields_metadata" ), 4 );
|
||||
return false;
|
||||
}
|
||||
|
||||
mShared->mFieldNameToXPathAndIsNestedContentMap.clear();
|
||||
while ( true )
|
||||
{
|
||||
gdal::ogr_feature_unique_ptr hFeatureFieldsMD( OGR_L_GetNextFeature( hFieldsMetadata ) );
|
||||
if ( !hFeatureFieldsMD )
|
||||
break;
|
||||
|
||||
QString fieldName = QString::fromUtf8( OGR_F_GetFieldAsString( hFeatureFieldsMD.get(), fieldNameIdx ) );
|
||||
const char *fieldXPath = OGR_F_GetFieldAsString( hFeatureFieldsMD.get(), fieldXPathIdx );
|
||||
// The xpath includes the one of the feature itself. We can strip it off
|
||||
const char *slash = strchr( fieldXPath, '/' );
|
||||
if ( slash )
|
||||
fieldXPath = slash + 1;
|
||||
const bool fieldIsList = OGR_F_GetFieldAsInteger( hFeatureFieldsMD.get(), fieldIsListIdx ) == 1;
|
||||
const char *fieldType = OGR_F_GetFieldAsString( hFeatureFieldsMD.get(), fieldTypeIdx );
|
||||
const char *fieldCategory = OGR_F_GetFieldAsString( hFeatureFieldsMD.get(), fieldCategoryIdx );
|
||||
|
||||
// For fields that should be linked to other tables and that we will
|
||||
// get as JSON, remove the "_pkid" suffix from the name created by GMLAS.
|
||||
if ( EQUAL( fieldCategory, "PATH_TO_CHILD_ELEMENT_WITH_LINK" ) &&
|
||||
fieldName.endsWith( QLatin1String( "_pkid" ) ) )
|
||||
{
|
||||
fieldName.resize( fieldName.size() - int( strlen( "_pkid" ) ) );
|
||||
}
|
||||
|
||||
QgsDebugMsgLevel(
|
||||
QStringLiteral( "field %1: xpath=%2 is_list=%3 type=%4 category=%5" ).
|
||||
arg( fieldName ).arg( fieldXPath ).arg( fieldIsList ).arg( fieldType ).arg( fieldCategory ), 5 );
|
||||
if ( EQUAL( fieldCategory, "REGULAR" ) && EQUAL( fieldType, "geometry" ) )
|
||||
{
|
||||
if ( geometryAttribute.isEmpty() )
|
||||
{
|
||||
mShared->mFieldNameToXPathAndIsNestedContentMap[fieldName] =
|
||||
QPair<QString, bool>( fieldXPath, false );
|
||||
geometryAttribute = fieldName;
|
||||
geomType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType(
|
||||
OGR_L_GetGeomType( hLayer ) );
|
||||
}
|
||||
}
|
||||
else if ( EQUAL( fieldCategory, "REGULAR" ) && !fieldIsList )
|
||||
{
|
||||
QVariant::Type type = getVariantTypeFromXML( QString::fromUtf8( fieldType ) );
|
||||
if ( type != QVariant::Invalid )
|
||||
{
|
||||
fields.append( QgsField( fieldName, type, fieldType ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// unhandled:duration, base64Binary, hexBinary, anyType
|
||||
QgsDebugMsgLevel(
|
||||
QStringLiteral( "unhandled type for field %1: xpath=%2 is_list=%3 type=%4 category=%5" ).
|
||||
arg( fieldName ).arg( fieldXPath ).arg( fieldIsList ).arg( fieldType ).arg( fieldCategory ), 3 );
|
||||
fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
|
||||
}
|
||||
mShared->mFieldNameToXPathAndIsNestedContentMap[fieldName] =
|
||||
QPair<QString, bool>( fieldXPath, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsField field( fieldName, QVariant::String );
|
||||
field.setEditorWidgetSetup( QgsEditorWidgetSetup( QStringLiteral( "JsonEdit" ), QVariantMap() ) );
|
||||
fields.append( field );
|
||||
mShared->mFieldNameToXPathAndIsNestedContentMap[fieldName] =
|
||||
QPair<QString, bool>( fieldXPath, true );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsWFSProvider::readAttributesFromSchemaWithoutGMLAS( QDomDocument &schemaDoc,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields,
|
||||
Qgis::WkbType &geomType,
|
||||
QString &errorMsg, bool &mayTryWithGMLAS )
|
||||
{
|
||||
mayTryWithGMLAS = false;
|
||||
|
||||
//get the <schema> root element
|
||||
QDomNodeList schemaNodeList = schemaDoc.elementsByTagNameNS( QgsWFSConstants::XMLSCHEMA_NAMESPACE, QStringLiteral( "schema" ) );
|
||||
if ( schemaNodeList.length() < 1 )
|
||||
@ -1615,6 +2089,7 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
|
||||
if ( foundImport && onlyIncludeOrImport )
|
||||
{
|
||||
errorMsg = tr( "It is probably a schema for Complex Features." );
|
||||
mayTryWithGMLAS = true;
|
||||
}
|
||||
// e.g http://services.cuzk.cz/wfs/inspire-CP-wfs.asp?SERVICE=WFS&VERSION=2.0.0&REQUEST=DescribeFeatureType
|
||||
// which has a single <include schemaLocation="http://inspire.ec.europa.eu/schemas/cp/4.0/CadastralParcels.xsd"/>
|
||||
@ -1643,17 +2118,18 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
|
||||
arg( schemaLocation, errorMsg );
|
||||
}
|
||||
|
||||
return readAttributesFromSchema( describeFeatureDocument,
|
||||
prefixedTypename,
|
||||
geometryAttribute,
|
||||
fields,
|
||||
geomType,
|
||||
errorMsg );
|
||||
return readAttributesFromSchemaWithoutGMLAS( describeFeatureDocument,
|
||||
prefixedTypename,
|
||||
geometryAttribute,
|
||||
fields,
|
||||
geomType,
|
||||
errorMsg, mayTryWithGMLAS );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMsg = tr( "Cannot find element '%1'" ).arg( unprefixedTypename );
|
||||
mayTryWithGMLAS = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1772,27 +2248,18 @@ bool QgsWFSProvider::readAttributesFromSchema( QDomDocument &schemaDoc,
|
||||
propertyType = propertyType.at( 0 ).toUpper() + propertyType.mid( 1 );
|
||||
geomType = geomTypeFromPropertyType( geometryAttribute, propertyType );
|
||||
}
|
||||
else if ( !name.isEmpty() ) //todo: distinguish between numerical and non-numerical types
|
||||
else if ( !name.isEmpty() )
|
||||
{
|
||||
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 ) )
|
||||
const QVariant::Type attributeType = getVariantTypeFromXML( type );
|
||||
if ( attributeType != QVariant::Invalid )
|
||||
{
|
||||
attributeType = QVariant::Double;
|
||||
fields.append( QgsField( name, attributeType, type ) );
|
||||
}
|
||||
else if ( type.contains( QLatin1String( "int" ), Qt::CaseInsensitive ) ||
|
||||
type.contains( QLatin1String( "short" ), Qt::CaseInsensitive ) )
|
||||
else
|
||||
{
|
||||
attributeType = QVariant::Int;
|
||||
mayTryWithGMLAS = true;
|
||||
fields.append( QgsField( name, QVariant::String, type ) );
|
||||
}
|
||||
else if ( type.contains( QLatin1String( "long" ), Qt::CaseInsensitive ) )
|
||||
{
|
||||
attributeType = QVariant::LongLong;
|
||||
}
|
||||
else if ( type.contains( QLatin1String( "dateTime" ), Qt::CaseInsensitive ) )
|
||||
{
|
||||
attributeType = QVariant::DateTime;
|
||||
}
|
||||
fields.append( QgsField( name, attributeType, type ) );
|
||||
}
|
||||
}
|
||||
if ( !foundGeometryAttribute )
|
||||
|
@ -138,6 +138,9 @@ class QgsWFSProvider final: public QgsVectorDataProvider
|
||||
//! Perform an initial GetFeature request with a 1-feature limit.
|
||||
void issueInitialGetFeature();
|
||||
|
||||
//! Return whether metadata retrieval has been canceled (typically download of the schema)
|
||||
bool metadataRetrievalCanceled() const { return mMetadataRetrievalCanceled; }
|
||||
|
||||
private slots:
|
||||
|
||||
void featureReceivedAnalyzeOneFeature( QVector<QgsFeatureUniqueIdPair> );
|
||||
@ -171,11 +174,24 @@ class QgsWFSProvider final: public QgsVectorDataProvider
|
||||
QDomElement geometryElement( const QgsGeometry &geometry, QDomDocument &transactionDoc );
|
||||
|
||||
//! Set mShared->mLayerPropertiesList from describeFeatureDocument
|
||||
bool setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QStringList &typenameList, QString &errorMsg );
|
||||
bool setLayerPropertiesListFromDescribeFeature( QDomDocument &describeFeatureDocument, const QByteArray &response, const QStringList &typenameList, QString &errorMsg );
|
||||
|
||||
//! backup of mShared->mLayerPropertiesList on the feature type when there is no sql request
|
||||
QList< QgsOgcUtils::LayerProperties > mLayerPropertiesListWhenNoSqlRequest;
|
||||
|
||||
//! Set if metadata retrieval has been canceled (typically download of the schema)
|
||||
bool mMetadataRetrievalCanceled = false;
|
||||
|
||||
bool readAttributesFromSchemaWithoutGMLAS( QDomDocument &schemaDoc,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields, Qgis::WkbType &geomType, QString &errorMsg, bool &mayTryWithGMLAS );
|
||||
|
||||
bool readAttributesFromSchemaWithGMLAS( const QByteArray &response,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields, Qgis::WkbType &geomType, QString &errorMsg );
|
||||
|
||||
protected:
|
||||
|
||||
//! String used to define a subset of the layer
|
||||
@ -206,6 +222,8 @@ class QgsWFSProvider final: public QgsVectorDataProvider
|
||||
* thematic attributes and their types from a dom document. Returns true in case of success.
|
||||
*/
|
||||
bool readAttributesFromSchema( QDomDocument &schemaDoc,
|
||||
const QByteArray &response,
|
||||
bool singleLayerContext,
|
||||
const QString &prefixedTypename,
|
||||
QString &geometryAttribute,
|
||||
QgsFields &fields, Qgis::WkbType &geomType, QString &errorMsg );
|
||||
|
@ -236,6 +236,8 @@ QList<QgsProviderSublayerDetails> QgsWfsProviderMetadata::querySublayers( const
|
||||
QgsWFSProvider provider(
|
||||
uri + " " + QgsWFSConstants::URI_PARAM_SKIP_INITIAL_GET_FEATURE + "='true'",
|
||||
QgsDataProvider::ProviderOptions(), caps );
|
||||
if ( provider.metadataRetrievalCanceled() )
|
||||
return res;
|
||||
QgsProviderSublayerDetails details;
|
||||
details.setType( Qgis::LayerType::Vector );
|
||||
details.setProviderKey( QgsWFSProvider::WFS_PROVIDER_KEY );
|
||||
|
@ -54,6 +54,8 @@ QgsWFSSharedData *QgsWFSSharedData::clone() const
|
||||
copy->mGeometryAttribute = mGeometryAttribute;
|
||||
copy->mLayerPropertiesList = mLayerPropertiesList;
|
||||
copy->mMapFieldNameToSrcLayerNameFieldName = mMapFieldNameToSrcLayerNameFieldName;
|
||||
copy->mFieldNameToXPathAndIsNestedContentMap = mFieldNameToXPathAndIsNestedContentMap;
|
||||
copy->mNamespacePrefixToURIMap = mNamespacePrefixToURIMap;
|
||||
copy->mPageSize = mPageSize;
|
||||
copy->mCaps = mCaps;
|
||||
copy->mHasWarnedAboutMissingFeatureId = mHasWarnedAboutMissingFeatureId;
|
||||
@ -105,8 +107,23 @@ QString QgsWFSSharedData::computedExpression( const QgsExpression &expression )
|
||||
bool honourAxisOrientation = false;
|
||||
getVersionValues( gmlVersion, filterVersion, honourAxisOrientation );
|
||||
|
||||
QMap<QString, QString> fieldNameToXPathMap;
|
||||
if ( !mFieldNameToXPathAndIsNestedContentMap.isEmpty() )
|
||||
{
|
||||
for ( auto iterFieldName = mFieldNameToXPathAndIsNestedContentMap.constBegin(); iterFieldName != mFieldNameToXPathAndIsNestedContentMap.constEnd(); ++iterFieldName )
|
||||
{
|
||||
const QString &fieldName = iterFieldName.key();
|
||||
const auto &value = iterFieldName.value();
|
||||
fieldNameToXPathMap[fieldName] = value.first;
|
||||
}
|
||||
}
|
||||
|
||||
QDomDocument expressionDoc;
|
||||
QDomElement expressionElem = QgsOgcUtils::expressionToOgcExpression( expression, expressionDoc, gmlVersion, filterVersion, mGeometryAttribute, srsName(), honourAxisOrientation, mURI.invertAxisOrientation(), nullptr, true );
|
||||
QDomElement expressionElem = QgsOgcUtils::expressionToOgcExpression(
|
||||
expression, expressionDoc, gmlVersion, filterVersion, mGeometryAttribute,
|
||||
srsName(), honourAxisOrientation, mURI.invertAxisOrientation(), nullptr,
|
||||
true,
|
||||
fieldNameToXPathMap, mNamespacePrefixToURIMap );
|
||||
|
||||
if ( !expressionElem.isNull() )
|
||||
{
|
||||
@ -155,12 +172,23 @@ bool QgsWFSSharedData::computeFilter( QString &errorMsg )
|
||||
}
|
||||
}
|
||||
|
||||
QMap<QString, QString> fieldNameToXPathMap;
|
||||
if ( !mFieldNameToXPathAndIsNestedContentMap.isEmpty() )
|
||||
{
|
||||
for ( auto iterFieldName = mFieldNameToXPathAndIsNestedContentMap.constBegin(); iterFieldName != mFieldNameToXPathAndIsNestedContentMap.constEnd(); ++iterFieldName )
|
||||
{
|
||||
const QString &fieldName = iterFieldName.key();
|
||||
const auto &value = iterFieldName.value();
|
||||
fieldNameToXPathMap[fieldName] = value.first;
|
||||
}
|
||||
}
|
||||
|
||||
QDomDocument filterDoc;
|
||||
const QDomElement filterElem = QgsOgcUtils::SQLStatementToOgcFilter(
|
||||
sql, filterDoc, gmlVersion, filterVersion, mLayerPropertiesList,
|
||||
honourAxisOrientation, mURI.invertAxisOrientation(),
|
||||
mCaps.mapUnprefixedTypenameToPrefixedTypename,
|
||||
&errorMsg );
|
||||
&errorMsg, fieldNameToXPathMap, mNamespacePrefixToURIMap );
|
||||
if ( !errorMsg.isEmpty() )
|
||||
{
|
||||
errorMsg = tr( "SQL statement to OGC Filter error: " ) + errorMsg;
|
||||
@ -188,13 +216,24 @@ bool QgsWFSSharedData::computeFilter( QString &errorMsg )
|
||||
//if not, if must be a QGIS expression
|
||||
const QgsExpression filterExpression( filter );
|
||||
|
||||
QMap<QString, QString> fieldNameToXPathMap;
|
||||
if ( !mFieldNameToXPathAndIsNestedContentMap.isEmpty() )
|
||||
{
|
||||
for ( auto iterFieldName = mFieldNameToXPathAndIsNestedContentMap.constBegin(); iterFieldName != mFieldNameToXPathAndIsNestedContentMap.constEnd(); ++iterFieldName )
|
||||
{
|
||||
const QString &fieldName = iterFieldName.key();
|
||||
const auto &value = iterFieldName.value();
|
||||
fieldNameToXPathMap[fieldName] = value.first;
|
||||
}
|
||||
}
|
||||
|
||||
const QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter(
|
||||
filterExpression, filterDoc, gmlVersion, filterVersion,
|
||||
mLayerPropertiesList.size() == 1 ? mLayerPropertiesList[0].mNamespacePrefix : QString(),
|
||||
mLayerPropertiesList.size() == 1 ? mLayerPropertiesList[0].mNamespaceURI : QString(),
|
||||
mGeometryAttribute,
|
||||
srsName(), honourAxisOrientation, mURI.invertAxisOrientation(),
|
||||
&errorMsg );
|
||||
&errorMsg, fieldNameToXPathMap, mNamespacePrefixToURIMap );
|
||||
|
||||
if ( !errorMsg.isEmpty() )
|
||||
{
|
||||
@ -261,11 +300,16 @@ QgsGmlStreamingParser *QgsWFSSharedData::createParser() const
|
||||
}
|
||||
else
|
||||
{
|
||||
return new QgsGmlStreamingParser( mURI.typeName(),
|
||||
mGeometryAttribute,
|
||||
mFields,
|
||||
axisOrientationLogic,
|
||||
mURI.invertAxisOrientation() );
|
||||
auto parser = new QgsGmlStreamingParser( mURI.typeName(),
|
||||
mGeometryAttribute,
|
||||
mFields,
|
||||
axisOrientationLogic,
|
||||
mURI.invertAxisOrientation() );
|
||||
if ( !mFieldNameToXPathAndIsNestedContentMap.isEmpty() )
|
||||
{
|
||||
parser->setFieldsXPath( mFieldNameToXPathAndIsNestedContentMap, mNamespacePrefixToURIMap );
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,6 +373,29 @@ QString QgsWFSSharedData::combineWFSFilters( const std::vector<QString> &filters
|
||||
}
|
||||
envelopeFilterDoc.firstChildElement().appendChild( andElem );
|
||||
|
||||
QSet<QString> setNamespaceURI;
|
||||
for ( auto iterFieldName = mFieldNameToXPathAndIsNestedContentMap.constBegin(); iterFieldName != mFieldNameToXPathAndIsNestedContentMap.constEnd(); ++iterFieldName )
|
||||
{
|
||||
const auto &value = iterFieldName.value();
|
||||
const QStringList parts = value.first.split( '/' );
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
const QStringList subparts = part.split( ':' );
|
||||
if ( subparts.size() == 2 && subparts[0] != QLatin1String( "gml" ) )
|
||||
{
|
||||
const auto iter = mNamespacePrefixToURIMap.constFind( subparts[0] );
|
||||
if ( iter != mNamespacePrefixToURIMap.constEnd() &&
|
||||
!setNamespaceURI.contains( *iter ) )
|
||||
{
|
||||
setNamespaceURI.insert( *iter );
|
||||
QDomAttr attr = envelopeFilterDoc.createAttribute( QStringLiteral( "xmlns:" ) + subparts[0] );
|
||||
attr.setValue( *iter );
|
||||
envelopeFilterDoc.firstChildElement().setAttributeNode( attr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( mLayerPropertiesList.size() == 1 &&
|
||||
envelopeFilterDoc.firstChildElement().hasAttribute( QStringLiteral( "xmlns:" ) + mLayerPropertiesList[0].mNamespacePrefix ) )
|
||||
{
|
||||
@ -337,7 +404,6 @@ QString QgsWFSSharedData::combineWFSFilters( const std::vector<QString> &filters
|
||||
else
|
||||
{
|
||||
// add xmls:PREFIX=URI attributes to top element
|
||||
QSet<QString> setNamespaceURI;
|
||||
for ( const QgsOgcUtils::LayerProperties &props : std::as_const( mLayerPropertiesList ) )
|
||||
{
|
||||
if ( !props.mNamespacePrefix.isEmpty() && !props.mNamespaceURI.isEmpty() &&
|
||||
|
@ -97,6 +97,12 @@ class QgsWFSSharedData : public QObject, public QgsBackgroundCachedSharedData
|
||||
//! Map a field name to the pair (typename, fieldname) that describes its source field
|
||||
QMap< QString, QPair<QString, QString> > mMapFieldNameToSrcLayerNameFieldName;
|
||||
|
||||
//! Map a field name to the pair (xpath, isNestedContent)
|
||||
QMap<QString, QPair<QString, bool> > mFieldNameToXPathAndIsNestedContentMap;
|
||||
|
||||
//! Map a namespace prefix to its URI
|
||||
QMap<QString, QString> mNamespacePrefixToURIMap;
|
||||
|
||||
//! Page size for WFS 2.0. 0 = disabled
|
||||
long long mPageSize = 0;
|
||||
|
||||
|
@ -51,11 +51,18 @@ from qgis.PyQt.QtCore import (
|
||||
QObject,
|
||||
Qt,
|
||||
QTime,
|
||||
QVariant,
|
||||
)
|
||||
|
||||
import unittest
|
||||
from qgis.testing import start_app, QgisTestCase
|
||||
from utilities import compareWkt, unitTestDataPath
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
# Default value is 2 second, which is too short when run under Valgrind
|
||||
gdal.SetConfigOption('OGR_GMLAS_XERCES_MAX_TIME', '20')
|
||||
|
||||
TEST_DATA_DIR = unitTestDataPath()
|
||||
|
||||
|
||||
@ -6427,6 +6434,53 @@ Can't recognize service requested.
|
||||
self.assertEqual(sublayers[3].featureCount(), -1)
|
||||
self.assertEqual(sublayers[4].featureCount(), -1)
|
||||
|
||||
@unittest.skipIf(gdal.GetDriverByName("GMLAS") is None, "OGR GMLAS driver required")
|
||||
def testWFSComplexFeatures(self):
|
||||
"""Test reading complex features"""
|
||||
|
||||
endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_complex_features'
|
||||
|
||||
shutil.copy(os.path.join(TEST_DATA_DIR, 'provider', 'wfs', 'inspire_complexfeatures', 'getcapabilities.xml'), sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities&VERSION=2.0.0'))
|
||||
shutil.copy(os.path.join(TEST_DATA_DIR, 'provider', 'wfs', 'inspire_complexfeatures', 'describefeaturetype.xml'), sanitize(endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAMES=ps:ProtectedSite&TYPENAME=ps:ProtectedSite'))
|
||||
shutil.copy(os.path.join(TEST_DATA_DIR, 'provider', 'wfs', 'inspire_complexfeatures', 'getfeature_hits.xml'), sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=ps:ProtectedSite&RESULTTYPE=hits'))
|
||||
shutil.copy(os.path.join(TEST_DATA_DIR, 'provider', 'wfs', 'inspire_complexfeatures', 'getfeature.xml'), sanitize(endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=ps:ProtectedSite&SRSNAME=urn:ogc:def:crs:EPSG::25833'))
|
||||
|
||||
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='ps:ProtectedSite' version='2.0.0' skipInitialGetFeature='true'", 'test', 'WFS')
|
||||
self.assertTrue(vl.isValid())
|
||||
self.assertEqual(vl.featureCount(), 1228)
|
||||
self.assertEqual(len(vl.fields()), 26)
|
||||
self.assertEqual([field.name() for field in vl.fields()], ['id', 'metadataproperty', 'description_href', 'description_title', 'description_nilreason', 'description', 'descriptionreference_href', 'descriptionreference_title', 'descriptionreference_nilreason', 'identifier_codespace', 'identifier', 'name', 'location_location', 'inspireid_identifier_localid', 'inspireid_identifier_namespace', 'inspireid_identifier_versionid_nilreason', 'inspireid_identifier_versionid_nil', 'inspireid_identifier_versionid', 'legalfoundationdate_nilreason', 'legalfoundationdate', 'legalfoundationdocument_nilreason', 'legalfoundationdocument_owns', 'legalfoundationdocument_ci_citation', 'sitedesignation', 'sitename', 'siteprotectionclassification'])
|
||||
self.assertEqual(vl.fields()["sitedesignation"].type(), QVariant.String)
|
||||
|
||||
got_f = [f for f in vl.getFeatures()]
|
||||
self.assertEqual(len(got_f), 1)
|
||||
geom = got_f[0].geometry()
|
||||
self.assertFalse(geom.isNull())
|
||||
self.assertEqual(got_f[0]["id"], 'ProtectedSite_FFH_553_DE4546-303')
|
||||
self.assertEqual(got_f[0]["sitedesignation"], '{"ps:DesignationType":{"ps:designation":{"@xlink:href":"http://inspire.ec.europa.eu/codelist/Natura2000DesignationValue/specialAreaOfConservation"},"ps:designationScheme":{"@xlink:href":"http://inspire.ec.europa.eu/codelist/DesignationSchemeValue/natura2000"}}}')
|
||||
|
||||
vl = QgsVectorLayer("url='http://" + endpoint + "' typename='ps:ProtectedSite' version='2.0.0' skipInitialGetFeature='true'", 'test', 'WFS')
|
||||
vl.setSubsetString("inspireid_identifier_localid = 'ProtectedSite_FFH_553_DE4546'")
|
||||
|
||||
if int(QT_VERSION_STR.split('.')[0]) >= 6:
|
||||
attrs = 'xmlns:base="http://inspire.ec.europa.eu/schemas/base/3.3" xmlns:ps="http://inspire.ec.europa.eu/schemas/ps/4.0"'
|
||||
else:
|
||||
attrs = 'xmlns:ps="http://inspire.ec.europa.eu/schemas/ps/4.0" xmlns:base="http://inspire.ec.europa.eu/schemas/base/3.3"'
|
||||
with open(sanitize(endpoint,
|
||||
f"""?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=ps:ProtectedSite&SRSNAME=urn:ogc:def:crs:EPSG::25833&FILTER=<fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">
|
||||
<fes:PropertyIsEqualTo>
|
||||
<fes:ValueReference {attrs}>ps:inspireID/base:Identifier/base:localId</fes:ValueReference>
|
||||
<fes:Literal>ProtectedSite_FFH_553_DE4546</fes:Literal>
|
||||
</fes:PropertyIsEqualTo>
|
||||
</fes:Filter>
|
||||
"""),
|
||||
'wb') as f:
|
||||
with open(os.path.join(TEST_DATA_DIR, 'provider', 'wfs', 'inspire_complexfeatures', 'getfeature.xml'), "rb") as f_source:
|
||||
f.write(f_source.read())
|
||||
|
||||
got_f = [f for f in vl.getFeatures()]
|
||||
self.assertEqual(len(got_f), 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
6
tests/testdata/provider/wfs/inspire_complexfeatures/describefeaturetype.xml
vendored
Normal file
6
tests/testdata/provider/wfs/inspire_complexfeatures/describefeaturetype.xml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://inspire.ec.europa.eu/schemas/ps/4.0">
|
||||
<import namespace="http://inspire.ec.europa.eu/schemas/base/3.3" schemaLocation="https://inspire.ec.europa.eu/schemas/base/3.3/BaseTypes.xsd"/>
|
||||
<import namespace="http://inspire.ec.europa.eu/schemas/gn/4.0" schemaLocation="https://inspire.ec.europa.eu/schemas/gn/4.0/GeographicalNames.xsd"/>
|
||||
<include schemaLocation="https://inspire.ec.europa.eu/schemas/ps/4.0/ProtectedSites.xsd"/>
|
||||
</schema>
|
652
tests/testdata/provider/wfs/inspire_complexfeatures/getcapabilities.xml
vendored
Normal file
652
tests/testdata/provider/wfs/inspire_complexfeatures/getcapabilities.xml
vendored
Normal file
@ -0,0 +1,652 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<WFS_Capabilities version="2.0.0" xmlns="http://www.opengis.net/wfs/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:ogc="http://www.opengis.net/ogc" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd">
|
||||
<ows:ServiceIdentification>
|
||||
<ows:Title>INSPIRE Download Service: Protected Sites / Schutzgebiete des Landes Brandenburg (WFS-PS-SCHUTZG)</ows:Title>
|
||||
<ows:Abstract>Der interoperable INSPIRE-Downloaddienst (WFS) Protected Sites (Schutzgebiete des Landes Brandenburg) beinhaltet Informationen zu den Schutzgebieten nach Naturschutzrecht des Landes Brandenburg und Europäische Schutzgebiete. Zu den Schutzgebieten nach Naturschutzrecht des Landes Brandenburg zählen Naturschutzgebiete, Landschaftsschutzgebiete, Biosphärenreservate, Naturparks und Nationalparke. Zum europäischen Schutzgebietssystem Natura 2000 zählen Vogelschutzgebiete (Special Protection Area (SPA)) und Fauna-Flora-Habitat-Gebiete (FFH-Gebiete). Gemäß der INSPIRE-Datenspezifikation Protected Sites (D2.8.I.9_v3.2) liegen die Inhalte der Schutzgebiete INSPIRE-konform vor. Der WFS beinhaltet den FeatureType ProtectedSite. --- The compliant INSPIRE download service (WFS) Protected Sites (Schutzgebiete des Landes Brandenburg) provides information on protected sites in the state of Brandenburg that are legally protected by state environmental law or according to the European Natura 2000 protected areas network. Areas legally protected by state environmental law include nature reserves, protected landscapes, biosphere reserves, nature parks and national parks. Areas according to the European Natura 2000 network include Special Protection Areas (SPA) for birds and Special Areas of Conservation (SAC) defined by the European Union's Habitats Directive (92/43/EEC). The content of the protected areas is compliant to the INSPIRE data specification for the annex theme Protected Sites (D2.8.I.9_v3.2). The WFS consists of the FeatureType ProtectedSite.</ows:Abstract>
|
||||
<ows:Keywords>
|
||||
<ows:Keyword>Schutzgebiet</ows:Keyword>
|
||||
<ows:Keyword>Naturschutz</ows:Keyword>
|
||||
<ows:Keyword>Natur</ows:Keyword>
|
||||
<ows:Keyword>Umwelt</ows:Keyword>
|
||||
<ows:Keyword>Biosphärenreservat</ows:Keyword>
|
||||
<ows:Keyword>Landschaft</ows:Keyword>
|
||||
<ows:Keyword>Landschaftsschutzgebiet</ows:Keyword>
|
||||
<ows:Keyword>Nationalpark</ows:Keyword>
|
||||
<ows:Keyword>Naturpark</ows:Keyword>
|
||||
<ows:Keyword>Naturschutzgebiet</ows:Keyword>
|
||||
<ows:Keyword>Naturschutzrecht</ows:Keyword>
|
||||
<ows:Keyword>Vogelschutzgebiet</ows:Keyword>
|
||||
<ows:Keyword>Schutzgebiete</ows:Keyword>
|
||||
<ows:Keyword>Großschutzgebiet</ows:Keyword>
|
||||
<ows:Keyword>ProtectedSite</ows:Keyword>
|
||||
<ows:Keyword>Brandenburg</ows:Keyword>
|
||||
<ows:Keyword>WFS</ows:Keyword>
|
||||
<ows:Keyword>interoperabel</ows:Keyword>
|
||||
<ows:Keyword>interoperability</ows:Keyword>
|
||||
<ows:Keyword>inspireidentifiziert</ows:Keyword>
|
||||
</ows:Keywords>
|
||||
<ows:ServiceType codeSpace="http://www.opengeospatial.org/">WFS</ows:ServiceType>
|
||||
<ows:ServiceTypeVersion>2.0.0</ows:ServiceTypeVersion>
|
||||
<ows:Fees>Nutzung erfolgt derzeit kostenfrei unter Beachtung des Urheberrechts.</ows:Fees>
|
||||
<ows:AccessConstraints>Es gelten die Bedingungen der Datenlizenz Deutschland – Namensnennung – Version 2.0: https://www.govdata.de/dl-de/by-2-0. Als Bezeichnung des Bereitstellers ist „© Landesamt für Umwelt Brandenburg“ anzugeben.</ows:AccessConstraints>
|
||||
</ows:ServiceIdentification>
|
||||
<ows:ServiceProvider>
|
||||
<ows:ProviderName>Landesvermessung und Geobasisinformation Brandenburg (LGB)</ows:ProviderName>
|
||||
<ows:ProviderSite xlink:href="https://inspire.brandenburg.de"/>
|
||||
<ows:ServiceContact>
|
||||
<ows:IndividualName>INSPIRE-Zentrale im Land Brandenburg</ows:IndividualName>
|
||||
<ows:PositionName>Kundenservice</ows:PositionName>
|
||||
<ows:ContactInfo>
|
||||
<ows:Phone>
|
||||
<ows:Voice>+49-331-8844-123</ows:Voice>
|
||||
<ows:Facsimile>+49-331-8844-16123</ows:Facsimile>
|
||||
</ows:Phone>
|
||||
<ows:Address>
|
||||
<ows:DeliveryPoint>Heinrich-Mann-Allee 104 B</ows:DeliveryPoint>
|
||||
<ows:City>Potsdam</ows:City>
|
||||
<ows:AdministrativeArea>Brandenburg</ows:AdministrativeArea>
|
||||
<ows:PostalCode>14473</ows:PostalCode>
|
||||
<ows:Country>Deutschland</ows:Country>
|
||||
<ows:ElectronicMailAddress>kundenservice@inspire.brandenburg.de</ows:ElectronicMailAddress>
|
||||
</ows:Address>
|
||||
<ows:OnlineResource xlink:href="https://isk.geobasis-bb.de/produktinformation/Produktinformation-INSPIRE-WFS-PS-SCHUTZG.pdf"/>
|
||||
<ows:HoursOfService>24/7</ows:HoursOfService>
|
||||
<ows:ContactInstructions></ows:ContactInstructions>
|
||||
</ows:ContactInfo>
|
||||
<ows:Role>ServiceCenter</ows:Role>
|
||||
</ows:ServiceContact>
|
||||
</ows:ServiceProvider>
|
||||
<!-- commented because of fake urls
|
||||
<ows:OperationsMetadata>
|
||||
<ows:Operation name="GetCapabilities">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
<ows:Parameter name="AcceptVersions">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>2.0.0</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Parameter name="AcceptFormats">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>text/xml</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Parameter name="Sections">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>ServiceIdentification</ows:Value>
|
||||
<ows:Value>ServiceProvider</ows:Value>
|
||||
<ows:Value>OperationsMetadata</ows:Value>
|
||||
<ows:Value>FeatureTypeList</ows:Value>
|
||||
<ows:Value>Filter_Capabilities</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
</ows:Operation>
|
||||
<ows:Operation name="DescribeFeatureType">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
</ows:Operation>
|
||||
<ows:Operation name="ListStoredQueries">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
</ows:Operation>
|
||||
<ows:Operation name="DescribeStoredQueries">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
</ows:Operation>
|
||||
<ows:Operation name="GetFeature">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
</ows:Operation>
|
||||
<ows:Operation name="GetPropertyValue">
|
||||
<ows:DCP>
|
||||
<ows:HTTP>
|
||||
<ows:Get xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs?"/>
|
||||
<ows:Post xlink:href="https://inspire.brandenburg.de/services/ps_schutzg_wfs"/>
|
||||
</ows:HTTP>
|
||||
</ows:DCP>
|
||||
</ows:Operation>
|
||||
<ows:Parameter name="version">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>2.0.0</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Parameter name="srsName">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/25833</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/25832</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/4326</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/4258</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/3034</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/3035</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/3044</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/3045</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/3857</ows:Value>
|
||||
<ows:Value>http://www.opengis.net/def/crs/EPSG/0/4839</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Parameter name="outputFormat">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>application/gml+xml; version=3.2</ows:Value>
|
||||
<ows:Value>text/xml; subtype=gml/3.2.1</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Parameter name="resolve">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>none</ows:Value>
|
||||
<ows:Value>local</ows:Value>
|
||||
<ows:Value>remote</ows:Value>
|
||||
<ows:Value>all</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Parameter>
|
||||
<ows:Constraint name="ImplementsSimpleWFS">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsBasicWFS">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsTransactionalWFS">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsLockingWFS">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="KVPEncoding">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="XMLEncoding">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="SOAPEncoding">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsInheritance">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsRemoteResolve">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsResultPaging">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsStandardJoins">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsSpatialJoins">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsTemporalJoins">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ImplementsFeatureVersioning">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ManageStoredQueries">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="CountDefault">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>15000</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="ResolveLocalScope">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>*</ows:DefaultValue>
|
||||
</ows:Constraint>
|
||||
<ows:Constraint name="QueryExpressions">
|
||||
<ows:AllowedValues>
|
||||
<ows:Value>wfs:Query</ows:Value>
|
||||
<ows:Value>wfs:StoredQuery</ows:Value>
|
||||
</ows:AllowedValues>
|
||||
</ows:Constraint>
|
||||
<ows:ExtendedCapabilities>
|
||||
<inspire_dls:ExtendedCapabilities xmlns="http://www.deegree.org/services/metadata" xmlns:inspire_common="http://inspire.ec.europa.eu/schemas/common/1.0" xmlns:inspire_dls="http://inspire.ec.europa.eu/schemas/inspire_dls/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://inspire.ec.europa.eu/schemas/common/1.0 http://inspire.ec.europa.eu/schemas/common/1.0/common.xsd http://inspire.ec.europa.eu/schemas/inspire_dls/1.0 http://inspire.ec.europa.eu/schemas/inspire_dls/1.0/inspire_dls.xsd">
|
||||
<inspire_common:MetadataUrl>
|
||||
<inspire_common:URL>https://www.metaver.de/csw?REQUEST=GetRecordById&SERVICE=CSW&VERSION=2.0.2&id=2CB45BFD-63E5-436A-B64B-AB9799CE0088</inspire_common:URL>
|
||||
<inspire_common:MediaType>application/vnd.iso.19139+xml</inspire_common:MediaType>
|
||||
|
||||
</inspire_common:MetadataUrl>
|
||||
<inspire_common:SupportedLanguages>
|
||||
<inspire_common:DefaultLanguage>
|
||||
<inspire_common:Language>ger</inspire_common:Language>
|
||||
|
||||
</inspire_common:DefaultLanguage>
|
||||
|
||||
</inspire_common:SupportedLanguages>
|
||||
<inspire_common:ResponseLanguage>
|
||||
<inspire_common:Language>ger</inspire_common:Language>
|
||||
|
||||
</inspire_common:ResponseLanguage>
|
||||
<inspire_dls:SpatialDataSetIdentifier>
|
||||
<inspire_common:Code>9e0092a4-c397-41e9-a11d-6b09e67fae13</inspire_common:Code>
|
||||
<inspire_common:Namespace>https://registry.gdi-de.org/id/de.bb.metadata</inspire_common:Namespace>
|
||||
|
||||
</inspire_dls:SpatialDataSetIdentifier>
|
||||
|
||||
</inspire_dls:ExtendedCapabilities>
|
||||
</ows:ExtendedCapabilities>
|
||||
</ows:OperationsMetadata>
|
||||
-->
|
||||
<FeatureTypeList>
|
||||
<FeatureType>
|
||||
<Name xmlns:ps="http://inspire.ec.europa.eu/schemas/ps/4.0">ps:ProtectedSite</Name>
|
||||
<Title>ProtectedSite</Title>
|
||||
<Abstract>Naturschutzgebiete (NSG), Landschaftsschutzgebiete (LSG), Nationalparke (NatP), Naturparke (NP), Biospärenreservate (BR), Fauna-Flora-Habitat-Gebiete (FFH) und Vogelschutzgebiete (SPA, Special Protection Area) des Landes Brandenburg</Abstract>
|
||||
<DefaultCRS>http://www.opengis.net/def/crs/EPSG/0/25833</DefaultCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/25832</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/4326</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/4258</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/3034</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/3035</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/3044</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/3045</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/3857</OtherCRS>
|
||||
<OtherCRS>http://www.opengis.net/def/crs/EPSG/0/4839</OtherCRS>
|
||||
<OutputFormats>
|
||||
<Format>application/gml+xml; version=3.2</Format>
|
||||
<Format>text/xml; subtype=gml/3.2.1</Format>
|
||||
</OutputFormats>
|
||||
<ows:WGS84BoundingBox>
|
||||
<ows:LowerCorner>11.071522 51.292131</ows:LowerCorner>
|
||||
<ows:UpperCorner>14.777398 53.617170</ows:UpperCorner>
|
||||
</ows:WGS84BoundingBox>
|
||||
<MetadataURL xlink:href="https://www.metaver.de/csw?REQUEST=GetRecordById&SERVICE=CSW&VERSION=2.0.2&id=DE8B2AC4-6507-4391-AA33-D6A61996806B"/>
|
||||
</FeatureType>
|
||||
</FeatureTypeList>
|
||||
<fes:Filter_Capabilities>
|
||||
<fes:Conformance>
|
||||
<fes:Constraint name="ImplementsQuery">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsAdHocQuery">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsFunctions">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsResourceId">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsMinStandardFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsStandardFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsMinSpatialFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsSpatialFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsMinTemporalFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsTemporalFilter">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsVersionNav">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsSorting">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsExtendedOperators">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsMinimumXPath">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>TRUE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
<fes:Constraint name="ImplementsSchemaElementFunc">
|
||||
<ows:NoValues/>
|
||||
<ows:DefaultValue>FALSE</ows:DefaultValue>
|
||||
</fes:Constraint>
|
||||
</fes:Conformance>
|
||||
<fes:Id_Capabilities>
|
||||
<fes:ResourceIdentifier name="fes:ResourceId"/>
|
||||
</fes:Id_Capabilities>
|
||||
<fes:Scalar_Capabilities>
|
||||
<fes:LogicalOperators/>
|
||||
<fes:ComparisonOperators>
|
||||
<fes:ComparisonOperator name="PropertyIsEqualTo"/>
|
||||
<fes:ComparisonOperator name="PropertyIsNotEqualTo"/>
|
||||
<fes:ComparisonOperator name="PropertyIsLessThan"/>
|
||||
<fes:ComparisonOperator name="PropertyIsGreaterThan"/>
|
||||
<fes:ComparisonOperator name="PropertyIsLessThanOrEqualTo"/>
|
||||
<fes:ComparisonOperator name="PropertyIsGreaterThanOrEqualTo"/>
|
||||
<fes:ComparisonOperator name="PropertyIsLike"/>
|
||||
<fes:ComparisonOperator name="PropertyIsNull"/>
|
||||
<fes:ComparisonOperator name="PropertyIsNil"/>
|
||||
<fes:ComparisonOperator name="PropertyIsBetween"/>
|
||||
</fes:ComparisonOperators>
|
||||
</fes:Scalar_Capabilities>
|
||||
<fes:Spatial_Capabilities>
|
||||
<fes:GeometryOperands xmlns:gml="http://www.opengis.net/gml" xmlns:gml32="http://www.opengis.net/gml/3.2">
|
||||
<fes:GeometryOperand name="gml:Box"/>
|
||||
<fes:GeometryOperand name="gml:Envelope"/>
|
||||
<fes:GeometryOperand name="gml:Point"/>
|
||||
<fes:GeometryOperand name="gml:LineString"/>
|
||||
<fes:GeometryOperand name="gml:Curve"/>
|
||||
<fes:GeometryOperand name="gml:Polygon"/>
|
||||
<fes:GeometryOperand name="gml:Surface"/>
|
||||
<fes:GeometryOperand name="gml:MultiPoint"/>
|
||||
<fes:GeometryOperand name="gml:MultiLineString"/>
|
||||
<fes:GeometryOperand name="gml:MultiCurve"/>
|
||||
<fes:GeometryOperand name="gml:MultiPolygon"/>
|
||||
<fes:GeometryOperand name="gml:MultiSurface"/>
|
||||
<fes:GeometryOperand name="gml:CompositeCurve"/>
|
||||
<fes:GeometryOperand name="gml:CompositeSurface"/>
|
||||
<fes:GeometryOperand name="gml32:Envelope"/>
|
||||
<fes:GeometryOperand name="gml32:Point"/>
|
||||
<fes:GeometryOperand name="gml32:LineString"/>
|
||||
<fes:GeometryOperand name="gml32:Curve"/>
|
||||
<fes:GeometryOperand name="gml32:Polygon"/>
|
||||
<fes:GeometryOperand name="gml32:Surface"/>
|
||||
<fes:GeometryOperand name="gml32:MultiPoint"/>
|
||||
<fes:GeometryOperand name="gml32:MultiLineString"/>
|
||||
<fes:GeometryOperand name="gml32:MultiCurve"/>
|
||||
<fes:GeometryOperand name="gml32:MultiPolygon"/>
|
||||
<fes:GeometryOperand name="gml32:MultiSurface"/>
|
||||
<fes:GeometryOperand name="gml32:CompositeCurve"/>
|
||||
<fes:GeometryOperand name="gml32:CompositeSurface"/>
|
||||
</fes:GeometryOperands>
|
||||
<fes:SpatialOperators>
|
||||
<fes:SpatialOperator name="BBOX"/>
|
||||
<fes:SpatialOperator name="Equals"/>
|
||||
<fes:SpatialOperator name="Disjoint"/>
|
||||
<fes:SpatialOperator name="Intersects"/>
|
||||
<fes:SpatialOperator name="Touches"/>
|
||||
<fes:SpatialOperator name="Crosses"/>
|
||||
<fes:SpatialOperator name="Within"/>
|
||||
<fes:SpatialOperator name="Contains"/>
|
||||
<fes:SpatialOperator name="Overlaps"/>
|
||||
<fes:SpatialOperator name="Beyond"/>
|
||||
<fes:SpatialOperator name="DWithin"/>
|
||||
</fes:SpatialOperators>
|
||||
</fes:Spatial_Capabilities>
|
||||
<fes:Temporal_Capabilities>
|
||||
<fes:TemporalOperands xmlns:gml="http://www.opengis.net/gml" xmlns:gml32="http://www.opengis.net/gml/3.2">
|
||||
<fes:TemporalOperand name="gml:TimeInstant"/>
|
||||
<fes:TemporalOperand name="gml:TimePeriod"/>
|
||||
<fes:TemporalOperand name="gml32:TimeInstant"/>
|
||||
<fes:TemporalOperand name="gml32:TimePeriod"/>
|
||||
</fes:TemporalOperands>
|
||||
<fes:TemporalOperators>
|
||||
<fes:TemporalOperator name="After"/>
|
||||
<fes:TemporalOperator name="Before"/>
|
||||
<fes:TemporalOperator name="During"/>
|
||||
<fes:TemporalOperator name="TEquals"/>
|
||||
</fes:TemporalOperators>
|
||||
</fes:Temporal_Capabilities>
|
||||
<fes:Functions>
|
||||
<fes:Function name="Area">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Area">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Centroid">
|
||||
<fes:Returns xmlns:gml="http://www.opengis.net/gml">gml:Point</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Centroid">
|
||||
<fes:Returns xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:Point</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="env">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:anyType</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:anyType</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="ExtraProp">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:anyType</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="GeometryFromWKT">
|
||||
<fes:Returns xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="GeometryFromWKT">
|
||||
<fes:Returns xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:string</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="GetCurrentScale">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
</fes:Function>
|
||||
<fes:Function name="HatchingDistance">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IDiv">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IMod">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Type>
|
||||
</fes:Argument>
|
||||
<fes:Argument name="arg2">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="InteriorPoint">
|
||||
<fes:Returns xmlns:gml="http://www.opengis.net/gml">gml:Point</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="InteriorPoint">
|
||||
<fes:Returns xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:Point</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsCurve">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsCurve">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsPoint">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsPoint">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsSurface">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="IsSurface">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:boolean</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Length">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Length">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Lower">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="MoveGeometry">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml="http://www.opengis.net/gml">gml:_Geometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="MoveGeometry">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:gml32="http://www.opengis.net/gml/3.2">gml32:AbstractGeometry</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
<fes:Function name="Upper">
|
||||
<fes:Returns xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:integer</fes:Returns>
|
||||
<fes:Arguments>
|
||||
<fes:Argument name="arg1">
|
||||
<fes:Type xmlns:xsd="http://www.w3.org/2001/XMLSchema">xsd:double</fes:Type>
|
||||
</fes:Argument>
|
||||
</fes:Arguments>
|
||||
</fes:Function>
|
||||
</fes:Functions>
|
||||
</fes:Filter_Capabilities>
|
||||
</WFS_Capabilities>
|
59
tests/testdata/provider/wfs/inspire_complexfeatures/getfeature.xml
vendored
Normal file
59
tests/testdata/provider/wfs/inspire_complexfeatures/getfeature.xml
vendored
Normal file
File diff suppressed because one or more lines are too long
3
tests/testdata/provider/wfs/inspire_complexfeatures/getfeature_hits.xml
vendored
Normal file
3
tests/testdata/provider/wfs/inspire_complexfeatures/getfeature_hits.xml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<wfs:FeatureCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd" xmlns:wfs="http://www.opengis.net/wfs/2.0" timeStamp="2024-01-05T15:47:23Z" numberMatched="1228" numberReturned="0">
|
||||
</wfs:FeatureCollection>
|
Loading…
x
Reference in New Issue
Block a user