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;
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 );
343#ifdef HAVE_SERVER_PYTHON_PLUGINS
348 QStringList attributes = QStringList();
349 for (
int idx : std::as_const( attrIndexes ) )
362 if ( !pkAttributes.isEmpty() )
365 for (
int idx : pkAttributes )
367 if ( !subsetOfAttrs.contains( idx ) )
369 subsetOfAttrs.prepend( idx );
400 if ( !query.
srsName.isEmpty() )
427 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
431 if ( iteratedFeatures >= aRequest.
startIndex )
443 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
445 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
447 const createFeatureParams cfp = { layerPrecision,
460 if ( iteratedFeatures == aRequest.
startIndex )
461 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
463 if ( iteratedFeatures >= aRequest.
startIndex )
473#ifdef HAVE_SERVER_PYTHON_PLUGINS
475 filterRestorer.reset();
478 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
480 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
485 if ( iteratedFeatures <= aRequest.
startIndex )
486 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
495 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
496 request.
startIndex = mWfsParameters.startIndexAsInt();
500 QStringList fidList = mWfsParameters.featureIds();
501 bool paramContainsFeatureIds = !fidList.isEmpty();
502 QStringList filterList = mWfsParameters.filters();
503 bool paramContainsFilters = !filterList.isEmpty();
504 QString bbox = mWfsParameters.bbox();
505 bool paramContainsBbox = !bbox.isEmpty();
506 if ( ( paramContainsFeatureIds
507 && ( paramContainsFilters || paramContainsBbox ) )
508 || ( paramContainsFilters
509 && ( paramContainsFeatureIds || paramContainsBbox ) )
510 || ( paramContainsBbox
511 && ( paramContainsFeatureIds || paramContainsFilters ) )
518 QStringList propertyNameList = mWfsParameters.propertyNames();
521 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
523 QStringList typeNameList;
525 if ( paramContainsFeatureIds )
528 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
532 if ( propertyNameList.isEmpty() )
534 for (
int i = 0; i < fidList.size(); ++i )
536 propertyNameList << QStringLiteral(
"*" );
540 QMap<QString, QStringList> fidsMap;
542 QStringList::const_iterator fidIt = fidList.constBegin();
543 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
544 for ( ; fidIt != fidList.constEnd(); ++fidIt )
547 QString fid = *fidIt;
550 QString propertyName;
551 if ( propertyNameIt != propertyNameList.constEnd() )
553 propertyName = *propertyNameIt;
556 if ( !fid.contains(
'.' ) )
561 QString
typeName = fid.section(
'.', 0, 0 );
562 fid = fid.section(
'.', 1, 1 );
563 if ( !typeNameList.contains(
typeName ) )
571 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
573 if ( fidsMap.contains( key ) )
575 fids = fidsMap.value( key );
578 fidsMap.insert( key, fids );
580 if ( propertyNameIt != propertyNameList.constEnd() )
586 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
587 while ( fidsMapIt != fidsMap.constEnd() )
589 QString key = fidsMapIt.key();
593 const QString
typeName = key.section(
':', 0, 0 );
594 const QString propertyName = key.section(
':', 1, 1 );
598 query.
srsName = mWfsParameters.srsName();
601 if ( propertyName != QLatin1String(
"*" ) )
603 QStringList propertyList;
605 const QStringList attrList = propertyName.split(
',' );
606 QStringList::const_iterator alstIt;
607 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
609 QString fieldName = *alstIt;
610 fieldName = fieldName.trimmed();
611 if ( fieldName.contains(
':' ) )
613 fieldName = fieldName.section(
':', 1, 1 );
615 if ( fieldName.contains(
'/' ) )
617 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
621 fieldName = fieldName.section(
'/', 1, 1 );
623 propertyList.append( fieldName );
632 request.
queries.append( query );
638 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
643 typeNameList = mWfsParameters.typeNames();
645 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
649 if ( propertyNameList.isEmpty() )
651 for (
int i = 0; i < typeNameList.size(); ++i )
653 propertyNameList << QStringLiteral(
"*" );
658 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
659 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
660 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
665 QString propertyName;
666 if ( propertyNameIt != propertyNameList.constEnd() )
668 propertyName = *propertyNameIt;
673 query.
srsName = mWfsParameters.srsName();
676 if ( propertyName != QLatin1String(
"*" ) )
678 QStringList propertyList;
680 const QStringList attrList = propertyName.split(
',' );
681 QStringList::const_iterator alstIt;
682 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
684 QString fieldName = *alstIt;
685 fieldName = fieldName.trimmed();
686 if ( fieldName.contains(
':' ) )
688 fieldName = fieldName.section(
':', 1, 1 );
690 if ( fieldName.contains(
'/' ) )
692 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
696 fieldName = fieldName.section(
'/', 1, 1 );
698 propertyList.append( fieldName );
703 request.
queries.append( query );
705 if ( propertyNameIt != propertyNameList.constEnd() )
712 QStringList expFilterList = mWfsParameters.expFilters();
713 if ( !expFilterList.isEmpty() )
716 if ( request.
queries.size() == expFilterList.size() )
719 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
720 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
721 for ( ; qIt != request.
queries.end(); ++qIt )
725 const QString expFilter = *expFilterIt++;
726 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
729 if ( filter->hasParserError() )
733 if ( filter->needsGeometry() )
747 if ( paramContainsBbox )
753 QString extentSrsName { mWfsParameters.srsName() };
756 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
758 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
759 if (
crs != mWfsParameters.srsName() )
789 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && ! extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
793 extent = geom.boundingBox();
797 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
798 for ( ; qIt != request.
queries.end(); ++qIt )
805 else if ( paramContainsFilters )
808 if ( request.
queries.size() != filterList.size() )
814 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
815 QStringList::const_iterator filterIt = filterList.constBegin();
816 for ( ; qIt != request.
queries.end(); ++qIt )
821 if ( filterIt != filterList.constEnd() )
824 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
830 QDomElement filterElem = filter.firstChildElement();
831 QStringList serverFids;
835 if ( filterIt != filterList.constEnd() )
843 QStringList sortByList = mWfsParameters.sortBy();
844 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
847 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
848 QStringList::const_iterator sortByIt = sortByList.constBegin();
849 for ( ; qIt != request.
queries.end(); ++qIt )
854 if ( sortByIt != sortByList.constEnd() )
858 for (
const QString &attribute : sortBy.split(
',' ) )
860 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
864 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
868 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
872 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
890 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
891 request.
startIndex = mWfsParameters.startIndexAsInt();
894 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
895 QDomElement queryElem;
896 for (
int i = 0; i < queryNodes.size(); i++ )
898 queryElem = queryNodes.at( i ).toElement();
900 request.
queries.append( query );
907 QDomNodeList sortByNodes = sortByElem.childNodes();
908 if ( sortByNodes.size() )
910 for (
int i = 0; i < sortByNodes.size(); i++ )
912 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
913 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
914 if ( sortPropChildNodes.size() )
917 bool ascending =
true;
918 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
920 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
921 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
923 fieldName = sortPropChildElem.text().trimmed();
925 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
927 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
928 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
933 if ( fieldName.contains(
':' ) )
935 fieldName = fieldName.section(
':', 1, 1 );
937 if ( fieldName.contains(
'/' ) )
939 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
943 fieldName = fieldName.section(
'/', 1, 1 );
946 if ( !fieldName.isEmpty() )
947 featureRequest.
addOrderBy( fieldName, ascending );
955 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
962 QStringList serverFids;
963 QStringList propertyList;
964 QDomNodeList queryChildNodes = queryElem.childNodes();
965 if ( queryChildNodes.size() )
967 QDomElement sortByElem;
968 for (
int q = 0; q < queryChildNodes.size(); q++ )
970 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
971 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
973 QString fieldName = queryChildElem.text().trimmed();
974 if ( fieldName.contains(
':' ) )
976 fieldName = fieldName.section(
':', 1, 1 );
978 if ( fieldName.contains(
'/' ) )
980 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
984 fieldName = fieldName.section(
'/', 1, 1 );
986 propertyList.append( fieldName );
988 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
992 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
994 sortByElem = queryChildElem;
1001 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1014 static QSet< QString > sParamFilter
1016 QStringLiteral(
"REQUEST" ),
1017 QStringLiteral(
"FORMAT" ),
1018 QStringLiteral(
"OUTPUTFORMAT" ),
1019 QStringLiteral(
"BBOX" ),
1020 QStringLiteral(
"FEATUREID" ),
1021 QStringLiteral(
"TYPENAME" ),
1022 QStringLiteral(
"FILTER" ),
1023 QStringLiteral(
"EXP_FILTER" ),
1024 QStringLiteral(
"MAXFEATURES" ),
1025 QStringLiteral(
"STARTINDEX" ),
1026 QStringLiteral(
"PROPERTYNAME" ),
1027 QStringLiteral(
"_DC" )
1032 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1034 QDateTime now = QDateTime::currentDateTime();
1037 if ( format == QgsWfsParameters::Format::GeoJSON )
1039 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1040 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1041 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1042 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1043 fcString += QLatin1Char(
'}' );
1047 if ( format == QgsWfsParameters::Format::GML2 )
1048 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1050 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1053 QString hrefString =
serviceUrl( request, project, *settings );
1055 QUrl mapUrl( hrefString );
1057 QUrlQuery query( mapUrl );
1058 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1060 if ( mWfsParameters.version().isEmpty() )
1063 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1065 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1067 for (
auto param : query.queryItems() )
1069 if ( sParamFilter.contains( param.first.toUpper() ) )
1070 query.removeAllQueryItems( param.first );
1073 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1074 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1077 if ( format == QgsWfsParameters::Format::GML2 )
1078 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1080 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1083 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1085 mapUrl.setQuery( query );
1087 hrefString = mapUrl.toString();
1090 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1091 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1093 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1096 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1100 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1101 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1103 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1104 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1105 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1106 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1107 fcString += QLatin1String(
">\n" );
1108 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1111 response.
write( fcString.toUtf8() );
1120 std::unique_ptr< QgsRectangle > transformedRect;
1122 if ( format == QgsWfsParameters::Format::GeoJSON )
1124 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1137 rect = transformedRect.get();
1148 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1150 fcString += QLatin1String(
" \"features\": [\n" );
1151 response.
write( fcString.toUtf8() );
1155 if ( format == QgsWfsParameters::Format::GML2 )
1156 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1158 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1161 QString hrefString =
serviceUrl( request, project, *settings );
1163 QUrl mapUrl( hrefString );
1165 QUrlQuery query( mapUrl );
1166 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1168 if ( mWfsParameters.version().isEmpty() )
1171 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1173 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1175 const auto queryItems {query.queryItems()};
1176 for (
auto param : std::as_const( queryItems ) )
1178 if ( sParamFilter.contains( param.first.toUpper() ) )
1179 query.removeAllQueryItems( param.first );
1182 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1183 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1186 if ( format == QgsWfsParameters::Format::GML2 )
1187 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1189 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1192 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1194 mapUrl.setQuery( query );
1196 hrefString = mapUrl.toString();
1199 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1200 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1202 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1205 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1209 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1210 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1212 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1213 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1214 fcString += QLatin1String(
">\n" );
1216 response.
write( fcString.toUtf8() );
1220 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1221 if ( format == QgsWfsParameters::Format::GML3 )
1225 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1227 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1245 if ( !envElem.isNull() )
1249 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1251 bbElem.appendChild( envElem );
1252 doc.appendChild( bbElem );
1258 if ( !boxElem.isNull() )
1262 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1264 bbElem.appendChild( boxElem );
1265 doc.appendChild( bbElem );
1268 response.
write( doc.toByteArray() );
1279 if ( format == QgsWfsParameters::Format::GeoJSON )
1283 fcString += QLatin1String(
" " );
1285 fcString += QLatin1String(
" ," );
1286 mJsonExporter.setSourceCrs( params.crs );
1287 mJsonExporter.setIncludeGeometry(
false );
1288 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1289 mJsonExporter.setAttributes( params.attributeIndexes );
1290 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1291 fcString += QLatin1String(
"\n" );
1293 response.
write( fcString.toUtf8() );
1297 QDomDocument gmlDoc;
1298 QDomElement featureElement;
1299 if ( format == QgsWfsParameters::Format::GML3 )
1301 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1302 gmlDoc.appendChild( featureElement );
1306 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1307 gmlDoc.appendChild( featureElement );
1309 response.
write( gmlDoc.toByteArray() );
1319 if ( format == QgsWfsParameters::Format::GeoJSON )
1321 fcString += QLatin1String(
" ]\n" );
1322 fcString += QLatin1Char(
'}' );
1326 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1328 response.
write( fcString.toUtf8() );
1332 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1342 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1344 mJsonExporter.setIncludeGeometry(
true );
1345 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1350 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1356 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1360 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1363 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1366 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1368 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1369 featureElement.appendChild( typeNameElement );
1373 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1375 int prec = params.precision;
1384 crs = params.outputCrs;
1386 prec = std::min( params.precision + 3, 6 );
1394 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1395 QDomElement gmlElem;
1397 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1401 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1412 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1415 if ( !gmlElem.isNull() )
1418 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1423 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1424 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1427 bbElem.appendChild( boxElem );
1428 typeNameElement.appendChild( bbElem );
1430 geomElem.appendChild( gmlElem );
1431 typeNameElement.appendChild( geomElem );
1438 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1440 int idx = params.attributeIndexes[i];
1441 if ( idx >= fields.
count() )
1446 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1447 typeNameElement.appendChild( fieldElem );
1450 return featureElement;
1453 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1456 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1459 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1461 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1462 featureElement.appendChild( typeNameElement );
1466 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1468 int prec = params.precision;
1477 crs = params.outputCrs;
1479 prec = std::min( params.precision + 3, 6 );
1487 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1488 QDomElement gmlElem;
1490 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1494 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1505 gmlElem = abstractGeom->
asGml3( doc, prec,
"http://www.opengis.net/gml", params.hasAxisInverted ? QgsAbstractGeometry::AxisOrder::YX : QgsAbstractGeometry::AxisOrder::XY );
1508 if ( !gmlElem.isNull() )
1511 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1514 if (
crs.
isValid() && params.srsName.isEmpty() )
1516 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1517 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1519 else if ( !params.srsName.isEmpty() )
1521 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1524 bbElem.appendChild( boxElem );
1525 typeNameElement.appendChild( bbElem );
1527 geomElem.appendChild( gmlElem );
1528 typeNameElement.appendChild( geomElem );
1535 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1537 int idx = params.attributeIndexes[i];
1538 if ( idx >= fields.
count() )
1543 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1544 typeNameElement.appendChild( fieldElem );
1547 return featureElement;
1550 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc )
1553 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1554 const QString attributeName =
field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1555 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1558 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1562 const QString fieldText = encodeValueToText( value, setup );
1564 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1566 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1570 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1581 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1583 const QVariantMap config = setup.
config();
1585 QDateTime date = value.toDateTime();
1587 if ( date.isValid() )
1589 return date.toString( fieldFormat );
1592 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1594 const QVariantMap config = setup.
config();
1595 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1599 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1601 return QString::number( value.toDouble(),
'f',
precision );
1605 switch ( value.type() )
1608 case QVariant::UInt:
1609 case QVariant::LongLong:
1610 case QVariant::ULongLong:
1611 case QVariant::Double:
1612 return value.toString();
1614 case QVariant::Bool:
1615 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1617 case QVariant::StringList:
1618 case QVariant::List:
1623 case QVariant::String:
1624 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 axis is inverted (e.g., for WMS 1.3) for the CRS.
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.
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.
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.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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.
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.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
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.
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(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
@ VectorLayer
Vector layer.
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