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;
145 for (
int i = 0; i < wfsLayerIds.size(); ++i )
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;
274 for (
const QgsField &field : fields )
276 fieldnames.
append( field.name() );
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() )
307 for (
const QgsField &field : fields )
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() )
453 if ( iteratedFeatures >= aRequest.
startIndex )
475 !requestSrsName.isEmpty() ? requestSrsName :
478 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
480 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
482 const createFeatureParams cfp = { layerPrecision,
499 if ( iteratedFeatures == aRequest.
startIndex )
500 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
502 if ( iteratedFeatures >= aRequest.
startIndex )
512#ifdef HAVE_SERVER_PYTHON_PLUGINS
514 filterRestorer.reset();
519 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
524 if ( iteratedFeatures <= aRequest.
startIndex )
525 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
534 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
535 request.
startIndex = mWfsParameters.startIndexAsInt();
539 QStringList fidList = mWfsParameters.featureIds();
540 bool paramContainsFeatureIds = !fidList.isEmpty();
541 QStringList filterList = mWfsParameters.filters();
542 bool paramContainsFilters = !filterList.isEmpty();
543 QString bbox = mWfsParameters.bbox();
544 bool paramContainsBbox = !bbox.isEmpty();
545 if ( ( paramContainsFeatureIds
546 && ( paramContainsFilters || paramContainsBbox ) )
547 || ( paramContainsFilters
548 && ( paramContainsFeatureIds || paramContainsBbox ) )
549 || ( paramContainsBbox
550 && ( paramContainsFeatureIds || paramContainsFilters ) )
557 QStringList propertyNameList = mWfsParameters.propertyNames();
560 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
562 QStringList typeNameList;
564 if ( paramContainsFeatureIds )
567 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
571 if ( propertyNameList.isEmpty() )
573 for (
int i = 0; i < fidList.size(); ++i )
575 propertyNameList << QStringLiteral(
"*" );
579 QMap<QString, QStringList> fidsMap;
581 QStringList::const_iterator fidIt = fidList.constBegin();
582 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
583 for ( ; fidIt != fidList.constEnd(); ++fidIt )
586 QString fid = *fidIt;
589 QString propertyName;
590 if ( propertyNameIt != propertyNameList.constEnd() )
592 propertyName = *propertyNameIt;
595 if ( !fid.contains(
'.' ) )
600 QString
typeName = fid.section(
'.', 0, 0 );
601 fid = fid.section(
'.', 1, 1 );
602 if ( !typeNameList.contains(
typeName ) )
610 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
612 if ( fidsMap.contains( key ) )
614 fids = fidsMap.value( key );
617 fidsMap.insert( key, fids );
619 if ( propertyNameIt != propertyNameList.constEnd() )
625 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
626 while ( fidsMapIt != fidsMap.constEnd() )
628 QString key = fidsMapIt.key();
632 const QString
typeName = key.section(
':', 0, 0 );
633 const QString propertyName = key.section(
':', 1, 1 );
637 query.
srsName = mWfsParameters.srsName();
640 if ( propertyName != QLatin1String(
"*" ) )
642 QStringList propertyList;
644 const QStringList attrList = propertyName.split(
',' );
645 QStringList::const_iterator alstIt;
646 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
648 QString fieldName = *alstIt;
649 fieldName = fieldName.trimmed();
650 if ( fieldName.contains(
':' ) )
652 fieldName = fieldName.section(
':', 1, 1 );
654 if ( fieldName.contains(
'/' ) )
656 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
660 fieldName = fieldName.section(
'/', 1, 1 );
662 propertyList.append( fieldName );
671 request.
queries.append( query );
677 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
682 typeNameList = mWfsParameters.typeNames();
684 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
688 if ( propertyNameList.isEmpty() )
690 for (
int i = 0; i < typeNameList.size(); ++i )
692 propertyNameList << QStringLiteral(
"*" );
697 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
698 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
699 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
704 QString propertyName;
705 if ( propertyNameIt != propertyNameList.constEnd() )
707 propertyName = *propertyNameIt;
712 query.
srsName = mWfsParameters.srsName();
715 if ( propertyName != QLatin1String(
"*" ) )
717 QStringList propertyList;
719 const QStringList attrList = propertyName.split(
',' );
720 QStringList::const_iterator alstIt;
721 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
723 QString fieldName = *alstIt;
724 fieldName = fieldName.trimmed();
725 if ( fieldName.contains(
':' ) )
727 fieldName = fieldName.section(
':', 1, 1 );
729 if ( fieldName.contains(
'/' ) )
731 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
735 fieldName = fieldName.section(
'/', 1, 1 );
737 propertyList.append( fieldName );
742 request.
queries.append( query );
744 if ( propertyNameIt != propertyNameList.constEnd() )
751 QStringList expFilterList = mWfsParameters.expFilters();
752 if ( !expFilterList.isEmpty() )
755 if ( request.
queries.size() == expFilterList.size() )
758 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
759 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
760 for ( ; qIt != request.
queries.end(); ++qIt )
764 const QString expFilter = *expFilterIt++;
765 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
768 if ( filter->hasParserError() )
772 if ( filter->needsGeometry() )
786 if ( paramContainsBbox )
792 QString extentSrsName { mWfsParameters.srsName() };
795 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
797 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
798 if (
crs != mWfsParameters.srsName() )
828 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && ! extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
832 extent = geom.boundingBox();
836 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
837 for ( ; qIt != request.
queries.end(); ++qIt )
844 else if ( paramContainsFilters )
847 if ( request.
queries.size() != filterList.size() )
853 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
854 QStringList::const_iterator filterIt = filterList.constBegin();
855 for ( ; qIt != request.
queries.end(); ++qIt )
860 if ( filterIt != filterList.constEnd() )
863 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
869 QDomElement filterElem = filter.firstChildElement();
870 QStringList serverFids;
874 if ( filterIt != filterList.constEnd() )
882 QStringList sortByList = mWfsParameters.sortBy();
883 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
886 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
887 QStringList::const_iterator sortByIt = sortByList.constBegin();
888 for ( ; qIt != request.
queries.end(); ++qIt )
893 if ( sortByIt != sortByList.constEnd() )
897 for (
const QString &attribute : sortBy.split(
',' ) )
899 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
903 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
907 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
911 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
929 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
930 request.
startIndex = mWfsParameters.startIndexAsInt();
933 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
934 QDomElement queryElem;
935 for (
int i = 0; i < queryNodes.size(); i++ )
937 queryElem = queryNodes.at( i ).toElement();
939 request.
queries.append( query );
946 QDomNodeList sortByNodes = sortByElem.childNodes();
947 if ( sortByNodes.size() )
949 for (
int i = 0; i < sortByNodes.size(); i++ )
951 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
952 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
953 if ( sortPropChildNodes.size() )
956 bool ascending =
true;
957 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
959 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
960 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
962 fieldName = sortPropChildElem.text().trimmed();
964 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
966 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
967 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
972 if ( fieldName.contains(
':' ) )
974 fieldName = fieldName.section(
':', 1, 1 );
976 if ( fieldName.contains(
'/' ) )
978 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
982 fieldName = fieldName.section(
'/', 1, 1 );
985 if ( !fieldName.isEmpty() )
986 featureRequest.
addOrderBy( fieldName, ascending );
994 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
1001 QStringList serverFids;
1002 QStringList propertyList;
1003 QDomNodeList queryChildNodes = queryElem.childNodes();
1004 if ( queryChildNodes.size() )
1006 QDomElement sortByElem;
1007 for (
int q = 0; q < queryChildNodes.size(); q++ )
1009 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
1010 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
1012 QString fieldName = queryChildElem.text().trimmed();
1013 if ( fieldName.contains(
':' ) )
1015 fieldName = fieldName.section(
':', 1, 1 );
1017 if ( fieldName.contains(
'/' ) )
1019 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
1023 fieldName = fieldName.section(
'/', 1, 1 );
1025 propertyList.append( fieldName );
1027 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
1031 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
1033 sortByElem = queryChildElem;
1040 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1053 static QSet< QString > sParamFilter
1055 QStringLiteral(
"REQUEST" ),
1056 QStringLiteral(
"FORMAT" ),
1057 QStringLiteral(
"OUTPUTFORMAT" ),
1058 QStringLiteral(
"BBOX" ),
1059 QStringLiteral(
"FEATUREID" ),
1060 QStringLiteral(
"TYPENAME" ),
1061 QStringLiteral(
"FILTER" ),
1062 QStringLiteral(
"EXP_FILTER" ),
1063 QStringLiteral(
"MAXFEATURES" ),
1064 QStringLiteral(
"STARTINDEX" ),
1065 QStringLiteral(
"PROPERTYNAME" ),
1066 QStringLiteral(
"_DC" )
1071 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1073 QDateTime now = QDateTime::currentDateTime();
1078 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1079 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1080 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1081 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1082 fcString += QLatin1Char(
'}' );
1087 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1089 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1092 QString hrefString =
serviceUrl( request, project, *settings );
1094 QUrl mapUrl( hrefString );
1096 QUrlQuery query( mapUrl );
1097 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1099 if ( mWfsParameters.version().isEmpty() )
1102 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1104 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1106 const auto constItems { query.queryItems() };
1107 for (
const auto ¶m : std::as_const( constItems ) )
1109 if ( sParamFilter.contains( param.first.toUpper() ) )
1110 query.removeAllQueryItems( param.first );
1113 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1114 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1118 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1120 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1123 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1125 mapUrl.setQuery( query );
1127 hrefString = mapUrl.toString();
1130 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1131 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1133 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1136 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1140 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1141 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1143 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1144 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1145 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1146 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1147 fcString += QLatin1String(
">\n" );
1148 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1151 response.
write( fcString.toUtf8() );
1160 std::unique_ptr< QgsRectangle > transformedRect;
1164 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1177 rect = transformedRect.get();
1188 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1193 if ( ! destinationCrs.isValid() )
1195 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg(
srsName ) );
1200 for (
const auto &it : value.items() )
1202 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1205 fcString += QLatin1String(
" \"features\": [\n" );
1206 response.
write( fcString.toUtf8() );
1211 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1213 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1216 QString hrefString =
serviceUrl( request, project, *settings );
1218 QUrl mapUrl( hrefString );
1220 QUrlQuery query( mapUrl );
1221 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1223 if ( mWfsParameters.version().isEmpty() )
1226 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1228 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1230 const auto queryItems {query.queryItems()};
1231 for (
auto param : std::as_const( queryItems ) )
1233 if ( sParamFilter.contains( param.first.toUpper() ) )
1234 query.removeAllQueryItems( param.first );
1237 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1238 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1242 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1244 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1247 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1249 mapUrl.setQuery( query );
1251 hrefString = mapUrl.toString();
1254 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1255 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1257 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1260 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1264 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1265 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1267 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1268 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1269 fcString += QLatin1String(
">\n" );
1271 response.
write( fcString.toUtf8() );
1275 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1284 const QString
srsName = !requestSrsName.isEmpty() ? requestSrsName :
crs.
authid();
1285 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1287 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1305 if ( !envElem.isNull() )
1309 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1311 bbElem.appendChild( envElem );
1312 doc.appendChild( bbElem );
1318 if ( !boxElem.isNull() )
1322 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1324 bbElem.appendChild( boxElem );
1325 doc.appendChild( bbElem );
1328 response.
write( doc.toByteArray() );
1343 fcString += QLatin1String(
" " );
1345 fcString += QLatin1String(
" ," );
1348 if ( ! destinationCrs.isValid() )
1350 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg( params.srsName ) );
1353 mJsonExporter.setDestinationCrs( destinationCrs );
1354 mJsonExporter.setTransformGeometries(
true );
1355 mJsonExporter.setSourceCrs( params.crs );
1356 mJsonExporter.setIncludeGeometry(
false );
1357 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1358 mJsonExporter.setAttributes( params.attributeIndexes );
1359 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1360 fcString += QLatin1String(
"\n" );
1362 response.
write( fcString.toUtf8() );
1366 QDomDocument gmlDoc;
1367 QDomElement featureElement;
1370 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1371 gmlDoc.appendChild( featureElement );
1375 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1376 gmlDoc.appendChild( featureElement );
1378 response.
write( gmlDoc.toByteArray() );
1390 fcString += QLatin1String(
" ]\n" );
1391 fcString += QLatin1Char(
'}' );
1395 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1397 response.
write( fcString.toUtf8() );
1401 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1411 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1413 mJsonExporter.setIncludeGeometry(
true );
1414 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1419 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1425 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1429 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1432 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1435 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1437 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1438 featureElement.appendChild( typeNameElement );
1442 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1444 int prec = params.precision;
1453 crs = params.outputCrs;
1455 prec = std::min( params.precision + 3, 6 );
1463 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1464 QDomElement gmlElem;
1466 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1470 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1481 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1484 if ( !gmlElem.isNull() )
1487 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1492 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1493 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1496 bbElem.appendChild( boxElem );
1497 typeNameElement.appendChild( bbElem );
1499 geomElem.appendChild( gmlElem );
1500 typeNameElement.appendChild( geomElem );
1507 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1509 int idx = params.attributeIndexes[i];
1515 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1516 typeNameElement.appendChild( fieldElem );
1519 return featureElement;
1522 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1525 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1528 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1530 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1531 featureElement.appendChild( typeNameElement );
1535 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1537 int prec = params.precision;
1546 crs = params.outputCrs;
1548 prec = std::min( params.precision + 3, 6 );
1556 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1557 QDomElement gmlElem;
1559 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1563 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1577 if ( !gmlElem.isNull() )
1580 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1583 if (
crs.
isValid() && params.srsName.isEmpty() )
1585 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1586 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1588 else if ( !params.srsName.isEmpty() )
1590 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1593 bbElem.appendChild( boxElem );
1594 typeNameElement.appendChild( bbElem );
1596 geomElem.appendChild( gmlElem );
1597 typeNameElement.appendChild( geomElem );
1604 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1606 int idx = params.attributeIndexes[i];
1612 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1613 typeNameElement.appendChild( fieldElem );
1616 return featureElement;
1619 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1622 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1623 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1624 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1627 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1631 const QString fieldText = encodeValueToText( value, setup );
1633 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1635 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1639 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1650 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1653 if ( value.userType() == QMetaType::Type::QTime )
1659 const QVariantMap config = setup.
config();
1662 const QString fieldFormat =
1663 config.value( QStringLiteral(
"field_iso_format" ),
false ).toBool() ?
1668 QDateTime date = value.toDateTime();
1670 if ( !date.isValid() )
1672 date = QDateTime::fromString( value.toString(), fieldFormat );
1675 if ( date.isValid() )
1677 return date.toString( fieldFormat );
1680 return value.toString();
1682 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1684 const QVariantMap config = setup.
config();
1685 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1689 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1691 return QString::number( value.toDouble(),
'f',
precision );
1695 switch ( value.userType() )
1697 case QMetaType::Type::Int:
1698 case QMetaType::Type::UInt:
1699 case QMetaType::Type::LongLong:
1700 case QMetaType::Type::ULongLong:
1701 case QMetaType::Type::Double:
1702 return value.toString();
1704 case QMetaType::Type::Bool:
1705 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1707 case QMetaType::Type::QStringList:
1708 case QMetaType::Type::QVariantList:
1709 case QMetaType::Type::QVariantMap:
1713 case QMetaType::Type::QString:
1714 return value.toString();
@ Success
Operation succeeded.
@ Fid
Filter using feature ID.
@ Fids
Filter using feature IDs.
@ NoFilter
No filter is applied.
@ 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.
@ NoFlags
No flags are set.
@ HideFromWfs
Field is not available if layer is served as WFS from QGIS server.
Abstract base class for all geometries.
@ YX
Y comes before X (or lat before lon)
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)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
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 & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
Qgis::FeatureRequestFlags 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.
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.
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.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
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).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
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.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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...
static void addCrsInfo(json &value, const QgsCoordinateReferenceSystem &crs)
Add crs information entry in json object regarding old GeoJSON specification format if it differs fro...
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
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 xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top 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 has no area.
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()
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, bool silenceNullWarnings=false)
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.
Q_INVOKABLE QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
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)
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