42#include <QRegularExpression>
49 struct createFeatureParams
72 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes );
74 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc );
78 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
80 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
95 QgsWfsParameters mWfsParameters;
108 mWfsParameters.dump();
114 if ( doc.setContent( request.
data(),
true, &errorMsg ) )
116 QDomElement docElem = doc.documentElement();
125 QStringList typeNameList;
128 bool onlyOneLayer = ( aRequest.
queries.size() == 1 );
131 int requestPrecision = 6;
135 QList<getFeatureQuery>::iterator qIt = aRequest.
queries.begin();
136 for ( ; qIt != aRequest.
queries.end(); ++qIt )
138 typeNameList << ( *qIt ).typeName;
144 QMap<QString, QgsMapLayer *> mapLayerMap;
152 if ( layer->
type() != Qgis::LayerType::Vector )
159 if ( typeNameList.contains( name ) )
162 mapLayerMap[name] = layer;
166 requestRect = layer->
extent();
167 requestCrs = layer->
crs();
186 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
193 for (
const QString &
typeName : typeNameList )
195 if ( !mapLayerMap.contains(
typeName ) )
201#ifdef HAVE_SERVER_PYTHON_PLUGINS
211 long sentFeatures = 0;
212 long iteratedFeatures = 0;
215 qIt = aRequest.
queries.begin();
216 for ( ; qIt != aRequest.
queries.end(); ++qIt )
222#ifdef HAVE_SERVER_PYTHON_PLUGINS
240#ifdef HAVE_SERVER_PYTHON_PLUGINS
247 QMap< int, QString > layerAliasInfo;
249 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
250 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
253 if ( attrIndex != -1 )
255 layerAliasInfo.insert( attrIndex, aliasIt.value() );
266 if ( !propertyList.isEmpty() && propertyList.first() != QLatin1String(
"*" ) )
269 QStringList::const_iterator plstIt;
272 QList<QString> propertynames;
273 QList<QString> fieldnames;
277 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
278 propertynames.append(
field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() ) );
281 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
284 int fieldNameIdx = propertynames.indexOf( fieldName );
285 if ( fieldNameIdx == -1 )
287 fieldNameIdx = fieldnames.indexOf( fieldName );
289 if ( fieldNameIdx > -1 )
291 idxList.append( fieldNameIdx );
293 else if ( fieldName == QLatin1String(
"geometry" ) )
298 if ( !idxList.isEmpty() )
300 attrIndexes = idxList;
305 if ( !attrIndexes.isEmpty() )
311 int fieldNameIdx = fields.indexOf(
field.
name() );
312 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
314 attrIndexes.removeOne( fieldNameIdx );
347#ifdef HAVE_SERVER_PYTHON_PLUGINS
366 QStringList attributes = QStringList();
367 for (
int idx : std::as_const( attrIndexes ) )
380 if ( !pkAttributes.isEmpty() )
383 for (
int idx : pkAttributes )
385 if ( !subsetOfAttrs.contains( idx ) )
387 subsetOfAttrs.prepend( idx );
418 if ( !query.
srsName.isEmpty() )
445 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
453 if ( iteratedFeatures >= aRequest.
startIndex )
465 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
467 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
469 const createFeatureParams cfp = { layerPrecision,
486 if ( iteratedFeatures == aRequest.
startIndex )
487 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
489 if ( iteratedFeatures >= aRequest.
startIndex )
499#ifdef HAVE_SERVER_PYTHON_PLUGINS
501 filterRestorer.reset();
504 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
506 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
511 if ( iteratedFeatures <= aRequest.
startIndex )
512 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
521 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
522 request.
startIndex = mWfsParameters.startIndexAsInt();
526 QStringList fidList = mWfsParameters.featureIds();
527 bool paramContainsFeatureIds = !fidList.isEmpty();
528 QStringList filterList = mWfsParameters.filters();
529 bool paramContainsFilters = !filterList.isEmpty();
530 QString bbox = mWfsParameters.bbox();
531 bool paramContainsBbox = !bbox.isEmpty();
532 if ( ( paramContainsFeatureIds
533 && ( paramContainsFilters || paramContainsBbox ) )
534 || ( paramContainsFilters
535 && ( paramContainsFeatureIds || paramContainsBbox ) )
536 || ( paramContainsBbox
537 && ( paramContainsFeatureIds || paramContainsFilters ) )
544 QStringList propertyNameList = mWfsParameters.propertyNames();
547 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
549 QStringList typeNameList;
551 if ( paramContainsFeatureIds )
554 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
558 if ( propertyNameList.isEmpty() )
560 for (
int i = 0; i < fidList.size(); ++i )
562 propertyNameList << QStringLiteral(
"*" );
566 QMap<QString, QStringList> fidsMap;
568 QStringList::const_iterator fidIt = fidList.constBegin();
569 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
570 for ( ; fidIt != fidList.constEnd(); ++fidIt )
573 QString fid = *fidIt;
576 QString propertyName;
577 if ( propertyNameIt != propertyNameList.constEnd() )
579 propertyName = *propertyNameIt;
582 if ( !fid.contains(
'.' ) )
587 QString
typeName = fid.section(
'.', 0, 0 );
588 fid = fid.section(
'.', 1, 1 );
589 if ( !typeNameList.contains(
typeName ) )
597 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
599 if ( fidsMap.contains( key ) )
601 fids = fidsMap.value( key );
604 fidsMap.insert( key, fids );
606 if ( propertyNameIt != propertyNameList.constEnd() )
612 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
613 while ( fidsMapIt != fidsMap.constEnd() )
615 QString key = fidsMapIt.key();
619 const QString
typeName = key.section(
':', 0, 0 );
620 const QString propertyName = key.section(
':', 1, 1 );
624 query.
srsName = mWfsParameters.srsName();
627 if ( propertyName != QLatin1String(
"*" ) )
629 QStringList propertyList;
631 const QStringList attrList = propertyName.split(
',' );
632 QStringList::const_iterator alstIt;
633 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
635 QString fieldName = *alstIt;
636 fieldName = fieldName.trimmed();
637 if ( fieldName.contains(
':' ) )
639 fieldName = fieldName.section(
':', 1, 1 );
641 if ( fieldName.contains(
'/' ) )
643 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
647 fieldName = fieldName.section(
'/', 1, 1 );
649 propertyList.append( fieldName );
658 request.
queries.append( query );
664 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
669 typeNameList = mWfsParameters.typeNames();
671 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
675 if ( propertyNameList.isEmpty() )
677 for (
int i = 0; i < typeNameList.size(); ++i )
679 propertyNameList << QStringLiteral(
"*" );
684 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
685 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
686 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
691 QString propertyName;
692 if ( propertyNameIt != propertyNameList.constEnd() )
694 propertyName = *propertyNameIt;
699 query.
srsName = mWfsParameters.srsName();
702 if ( propertyName != QLatin1String(
"*" ) )
704 QStringList propertyList;
706 const QStringList attrList = propertyName.split(
',' );
707 QStringList::const_iterator alstIt;
708 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
710 QString fieldName = *alstIt;
711 fieldName = fieldName.trimmed();
712 if ( fieldName.contains(
':' ) )
714 fieldName = fieldName.section(
':', 1, 1 );
716 if ( fieldName.contains(
'/' ) )
718 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
722 fieldName = fieldName.section(
'/', 1, 1 );
724 propertyList.append( fieldName );
729 request.
queries.append( query );
731 if ( propertyNameIt != propertyNameList.constEnd() )
738 QStringList expFilterList = mWfsParameters.expFilters();
739 if ( !expFilterList.isEmpty() )
742 if ( request.
queries.size() == expFilterList.size() )
745 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
746 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
747 for ( ; qIt != request.
queries.end(); ++qIt )
751 const QString expFilter = *expFilterIt++;
752 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
755 if ( filter->hasParserError() )
759 if ( filter->needsGeometry() )
773 if ( paramContainsBbox )
779 QString extentSrsName { mWfsParameters.srsName() };
782 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
784 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
785 if (
crs != mWfsParameters.srsName() )
815 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && ! extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
819 extent = geom.boundingBox();
823 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
824 for ( ; qIt != request.
queries.end(); ++qIt )
831 else if ( paramContainsFilters )
834 if ( request.
queries.size() != filterList.size() )
840 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
841 QStringList::const_iterator filterIt = filterList.constBegin();
842 for ( ; qIt != request.
queries.end(); ++qIt )
847 if ( filterIt != filterList.constEnd() )
850 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
856 QDomElement filterElem = filter.firstChildElement();
857 QStringList serverFids;
861 if ( filterIt != filterList.constEnd() )
869 QStringList sortByList = mWfsParameters.sortBy();
870 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
873 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
874 QStringList::const_iterator sortByIt = sortByList.constBegin();
875 for ( ; qIt != request.
queries.end(); ++qIt )
880 if ( sortByIt != sortByList.constEnd() )
884 for (
const QString &attribute : sortBy.split(
',' ) )
886 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
890 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
894 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
898 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
916 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
917 request.
startIndex = mWfsParameters.startIndexAsInt();
920 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
921 QDomElement queryElem;
922 for (
int i = 0; i < queryNodes.size(); i++ )
924 queryElem = queryNodes.at( i ).toElement();
926 request.
queries.append( query );
933 QDomNodeList sortByNodes = sortByElem.childNodes();
934 if ( sortByNodes.size() )
936 for (
int i = 0; i < sortByNodes.size(); i++ )
938 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
939 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
940 if ( sortPropChildNodes.size() )
943 bool ascending =
true;
944 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
946 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
947 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
949 fieldName = sortPropChildElem.text().trimmed();
951 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
953 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
954 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
959 if ( fieldName.contains(
':' ) )
961 fieldName = fieldName.section(
':', 1, 1 );
963 if ( fieldName.contains(
'/' ) )
965 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
969 fieldName = fieldName.section(
'/', 1, 1 );
972 if ( !fieldName.isEmpty() )
973 featureRequest.
addOrderBy( fieldName, ascending );
981 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
988 QStringList serverFids;
989 QStringList propertyList;
990 QDomNodeList queryChildNodes = queryElem.childNodes();
991 if ( queryChildNodes.size() )
993 QDomElement sortByElem;
994 for (
int q = 0; q < queryChildNodes.size(); q++ )
996 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
997 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
999 QString fieldName = queryChildElem.text().trimmed();
1000 if ( fieldName.contains(
':' ) )
1002 fieldName = fieldName.section(
':', 1, 1 );
1004 if ( fieldName.contains(
'/' ) )
1006 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
1010 fieldName = fieldName.section(
'/', 1, 1 );
1012 propertyList.append( fieldName );
1014 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
1018 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
1020 sortByElem = queryChildElem;
1027 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1040 static QSet< QString > sParamFilter
1042 QStringLiteral(
"REQUEST" ),
1043 QStringLiteral(
"FORMAT" ),
1044 QStringLiteral(
"OUTPUTFORMAT" ),
1045 QStringLiteral(
"BBOX" ),
1046 QStringLiteral(
"FEATUREID" ),
1047 QStringLiteral(
"TYPENAME" ),
1048 QStringLiteral(
"FILTER" ),
1049 QStringLiteral(
"EXP_FILTER" ),
1050 QStringLiteral(
"MAXFEATURES" ),
1051 QStringLiteral(
"STARTINDEX" ),
1052 QStringLiteral(
"PROPERTYNAME" ),
1053 QStringLiteral(
"_DC" )
1058 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1060 QDateTime now = QDateTime::currentDateTime();
1063 if ( format == QgsWfsParameters::Format::GeoJSON )
1065 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1066 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1067 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1068 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1069 fcString += QLatin1Char(
'}' );
1073 if ( format == QgsWfsParameters::Format::GML2 )
1074 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1076 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1079 QString hrefString =
serviceUrl( request, project, *settings );
1081 QUrl mapUrl( hrefString );
1083 QUrlQuery query( mapUrl );
1084 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1086 if ( mWfsParameters.version().isEmpty() )
1089 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1091 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1093 const auto constItems { query.queryItems() };
1094 for (
const auto ¶m : std::as_const( constItems ) )
1096 if ( sParamFilter.contains( param.first.toUpper() ) )
1097 query.removeAllQueryItems( param.first );
1100 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1101 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1104 if ( format == QgsWfsParameters::Format::GML2 )
1105 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1107 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1110 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1112 mapUrl.setQuery( query );
1114 hrefString = mapUrl.toString();
1117 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1118 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1120 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1123 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1127 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1128 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1130 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1131 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1132 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1133 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1134 fcString += QLatin1String(
">\n" );
1135 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1138 response.
write( fcString.toUtf8() );
1147 std::unique_ptr< QgsRectangle > transformedRect;
1149 if ( format == QgsWfsParameters::Format::GeoJSON )
1151 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1164 rect = transformedRect.get();
1175 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1177 fcString += QLatin1String(
" \"features\": [\n" );
1178 response.
write( fcString.toUtf8() );
1182 if ( format == QgsWfsParameters::Format::GML2 )
1183 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1185 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1188 QString hrefString =
serviceUrl( request, project, *settings );
1190 QUrl mapUrl( hrefString );
1192 QUrlQuery query( mapUrl );
1193 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1195 if ( mWfsParameters.version().isEmpty() )
1198 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1200 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1202 const auto queryItems {query.queryItems()};
1203 for (
auto param : std::as_const( queryItems ) )
1205 if ( sParamFilter.contains( param.first.toUpper() ) )
1206 query.removeAllQueryItems( param.first );
1209 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1210 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1213 if ( format == QgsWfsParameters::Format::GML2 )
1214 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1216 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1219 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1221 mapUrl.setQuery( query );
1223 hrefString = mapUrl.toString();
1226 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1227 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1229 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1232 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1236 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1237 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1239 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1240 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1241 fcString += QLatin1String(
">\n" );
1243 response.
write( fcString.toUtf8() );
1247 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1248 if ( format == QgsWfsParameters::Format::GML3 )
1252 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1254 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1272 if ( !envElem.isNull() )
1276 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1278 bbElem.appendChild( envElem );
1279 doc.appendChild( bbElem );
1285 if ( !boxElem.isNull() )
1289 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1291 bbElem.appendChild( boxElem );
1292 doc.appendChild( bbElem );
1295 response.
write( doc.toByteArray() );
1306 if ( format == QgsWfsParameters::Format::GeoJSON )
1310 fcString += QLatin1String(
" " );
1312 fcString += QLatin1String(
" ," );
1315 if ( ! destinationCrs.isValid() )
1317 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg( params.srsName ) );
1320 mJsonExporter.setDestinationCrs( destinationCrs );
1321 mJsonExporter.setTransformGeometries(
true );
1322 mJsonExporter.setSourceCrs( params.crs );
1323 mJsonExporter.setIncludeGeometry(
false );
1324 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1325 mJsonExporter.setAttributes( params.attributeIndexes );
1326 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1327 fcString += QLatin1String(
"\n" );
1329 response.
write( fcString.toUtf8() );
1333 QDomDocument gmlDoc;
1334 QDomElement featureElement;
1335 if ( format == QgsWfsParameters::Format::GML3 )
1337 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1338 gmlDoc.appendChild( featureElement );
1342 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1343 gmlDoc.appendChild( featureElement );
1345 response.
write( gmlDoc.toByteArray() );
1355 if ( format == QgsWfsParameters::Format::GeoJSON )
1357 fcString += QLatin1String(
" ]\n" );
1358 fcString += QLatin1Char(
'}' );
1362 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1364 response.
write( fcString.toUtf8() );
1368 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1378 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1380 mJsonExporter.setIncludeGeometry(
true );
1381 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1386 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1392 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1396 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1399 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1402 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1404 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1405 featureElement.appendChild( typeNameElement );
1409 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1411 int prec = params.precision;
1420 crs = params.outputCrs;
1422 prec = std::min( params.precision + 3, 6 );
1430 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1431 QDomElement gmlElem;
1433 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1437 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1448 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1451 if ( !gmlElem.isNull() )
1454 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1459 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1460 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1463 bbElem.appendChild( boxElem );
1464 typeNameElement.appendChild( bbElem );
1466 geomElem.appendChild( gmlElem );
1467 typeNameElement.appendChild( geomElem );
1474 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1476 int idx = params.attributeIndexes[i];
1482 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1483 typeNameElement.appendChild( fieldElem );
1486 return featureElement;
1489 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1492 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1495 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1497 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1498 featureElement.appendChild( typeNameElement );
1502 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1504 int prec = params.precision;
1513 crs = params.outputCrs;
1515 prec = std::min( params.precision + 3, 6 );
1523 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1524 QDomElement gmlElem;
1526 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1530 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1541 gmlElem = abstractGeom->
asGml3( doc, prec,
"http://www.opengis.net/gml", params.hasAxisInverted ? QgsAbstractGeometry::AxisOrder::YX : QgsAbstractGeometry::AxisOrder::XY );
1544 if ( !gmlElem.isNull() )
1547 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1550 if (
crs.
isValid() && params.srsName.isEmpty() )
1552 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1553 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1555 else if ( !params.srsName.isEmpty() )
1557 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1560 bbElem.appendChild( boxElem );
1561 typeNameElement.appendChild( bbElem );
1563 geomElem.appendChild( gmlElem );
1564 typeNameElement.appendChild( geomElem );
1571 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1573 int idx = params.attributeIndexes[i];
1579 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1580 typeNameElement.appendChild( fieldElem );
1583 return featureElement;
1586 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc )
1589 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1590 const QString attributeName =
field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1591 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1594 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1598 const QString fieldText = encodeValueToText( value, setup );
1600 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1602 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1606 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1617 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1620 if ( value.type() == QVariant::Time )
1626 const QVariantMap config = setup.
config();
1629 const QString fieldFormat =
1630 config.value( QStringLiteral(
"field_iso_format" ),
false ).toBool() ?
1635 QDateTime date = value.toDateTime();
1637 if ( !date.isValid() )
1639 date = QDateTime::fromString( value.toString(), fieldFormat );
1642 if ( date.isValid() )
1644 return date.toString( fieldFormat );
1647 return value.toString();
1649 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1651 const QVariantMap config = setup.
config();
1652 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1656 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1658 return QString::number( value.toDouble(),
'f',
precision );
1662 switch ( value.type() )
1665 case QVariant::UInt:
1666 case QVariant::LongLong:
1667 case QVariant::ULongLong:
1668 case QVariant::Double:
1669 return value.toString();
1671 case QVariant::Bool:
1672 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1674 case QVariant::StringList:
1675 case QVariant::List:
1680 case QVariant::String:
1681 return value.toString();
@ Success
Operation succeeded.
Abstract base class for all geometries.
virtual QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML2 representation of the geometry.
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.
A helper class that centralizes restrictions given by all the access control filter plugins.
QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const override
Returns the authorized layer attributes.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
Custom exception class for Coordinate Reference System related exceptions.
Defines a QGIS exception class.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
Flags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
QgsAttributeList subsetOfAttributes() const
Returns the subset of attributes which at least need to be fetched.
FilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
@ FilterFid
Filter using feature ID.
@ FilterFids
Filter using feature IDs.
@ FilterNone
No filter is applied.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool isValid() const
Returns the validity of this feature.
Encapsulate a field in an attribute table or data source.
ConfigurationFlags configurationFlags
@ HideFromWfs
Field is not available if layer is served as WFS from QGIS server.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
int count() const
Returns number of items.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Handles exporting QgsFeature features to GeoJSON features.
static Q_INVOKABLE QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
RAII class to restore layer filters on destruction.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
A class to describe the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A rectangle specified with double values.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool isEmpty() const
Returns true if the rectangle is empty.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
virtual QgsServerSettings * serverSettings()=0
Returns the server settings.
QString value(const QString &key) const
Returns the value of a parameter.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerParameters serverParameters() const
Returns parameters.
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void flush() SIP_THROW(QgsServerException)
Flushes the current output buffer to the network.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
Provides a way to retrieve settings by prioritizing according to environment variables,...
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
This is the base class for vector data providers.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Exception thrown when data access violates access controls.
Provides an interface to retrieve and manipulate WFS parameters received from the client.
Format
Output format for the response.
static bool isMultiType(Qgis::WkbType type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT int wfsLayerPrecision(const QgsProject &project, const QString &layerId)
Returns the Layer precision defined in a QGIS project for the WFS GetFeature.
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
QString implementationVersion()
Returns the highest version supported by this implementation.
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Service URL string.
const QString OGC_NAMESPACE
const QString GML_NAMESPACE
const QString WFS_NAMESPACE
getFeatureRequest parseGetFeatureRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
getFeatureQuery parseQueryElement(QDomElement &queryElem, const QgsProject *project)
Transform Query element to getFeatureQuery.
const QString QGS_NAMESPACE
getFeatureRequest parseGetFeatureParameters(const QgsProject *project)
Transform parameters to getFeatureRequest.
void parseSortByElement(QDomElement &sortByElem, QgsFeatureRequest &featureRequest, const QString &typeName)
Add SortBy element to featureRequest.
void writeGetFeature(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS GetFeature response.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, QgsProject *project)
Transform a Filter element to a feature request.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QMap< QString, QString > QgsStringMap
QList< int > QgsAttributeList
const QString & geometryName
const QgsCoordinateReferenceSystem & outputCrs
const QgsCoordinateReferenceSystem & crs
const QgsAttributeList & attributeIndexes
QgsFeatureRequest featureRequest
QgsWfsParameters::Format outputFormat
QList< getFeatureQuery > queries