44#include <QRegularExpression>
47using namespace Qt::StringLiterals;
54 struct createFeatureParams
58 const QgsCoordinateReferenceSystem &crs;
62 const QString &typeName;
66 const QString &geometryName;
68 const QgsCoordinateReferenceSystem &outputCrs;
70 bool forceGeomToMulti;
72 const QString &srsName;
77 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes );
79 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc );
81 QString encodeValueToText(
const QVariant &value,
const QgsEditorWidgetSetup &setup );
83 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
85 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
87 void hitGetFeature(
const QgsServerRequest &request, QgsServerResponse &response,
const QgsProject *project,
QgsWfsParameters::Format format,
int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *serverSettings );
89 void startGetFeature(
const QgsServerRequest &request, QgsServerResponse &response,
const QgsProject *project,
QgsWfsParameters::Format format,
int prec, QgsCoordinateReferenceSystem &crs, QgsRectangle *rect,
const QStringList &typeNames,
const QgsServerSettings *settings );
98 QgsJsonExporter mJsonExporter;
119 mWfsParameters.dump();
125 if ( doc.setContent( request.
data(),
true, &errorMsg ) )
127 QDomElement docElem = doc.documentElement();
136 QStringList typeNameList;
139 bool onlyOneLayer = ( aRequest.
queries.size() == 1 );
142 int requestPrecision = 6;
146 QList<getFeatureQuery>::iterator qIt = aRequest.
queries.begin();
147 for ( ; qIt != aRequest.
queries.end(); ++qIt )
149 typeNameList << ( *qIt ).typeName;
155 QMap<QString, QgsMapLayer *> mapLayerMap;
156 for (
int i = 0; i < wfsLayerIds.size(); ++i )
170 if ( typeNameList.contains( name ) )
173 mapLayerMap[name] = layer;
177 requestRect = layer->
extent();
178 requestCrs = layer->
crs();
197 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
204 for (
const QString &typeName : typeNameList )
206 if ( !mapLayerMap.contains( typeName ) )
212#ifdef HAVE_SERVER_PYTHON_PLUGINS
216 auto filterRestorer = std::make_unique<QgsOWSServerFilterRestorer>();
218 ( void ) serverIface;
222 long sentFeatures = 0;
223 long iteratedFeatures = 0;
226 qIt = aRequest.
queries.begin();
227 for ( ; qIt != aRequest.
queries.end(); ++qIt )
233#ifdef HAVE_SERVER_PYTHON_PLUGINS
251#ifdef HAVE_SERVER_PYTHON_PLUGINS
258 QMap<int, QString> layerAliasInfo;
260 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
261 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
264 if ( attrIndex != -1 )
266 layerAliasInfo.insert( attrIndex, aliasIt.value() );
276 bool withGeom =
true;
277 if ( !propertyList.isEmpty() && propertyList.first() !=
"*"_L1 )
280 QStringList::const_iterator plstIt;
283 QList<QString> propertynames;
284 QList<QString> fieldnames;
285 for (
const QgsField &field : fields )
287 fieldnames.append( field.name() );
288 const thread_local QRegularExpression sCleanTagNameRegExp( u
"[^\\w\\.-_]"_s, QRegularExpression::PatternOption::UseUnicodePropertiesOption );
289 propertynames.append( field.name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() ) );
292 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
295 int fieldNameIdx = propertynames.indexOf( fieldName );
296 if ( fieldNameIdx == -1 )
298 fieldNameIdx = fieldnames.indexOf( fieldName );
300 if ( fieldNameIdx > -1 )
302 idxList.append( fieldNameIdx );
304 else if ( fieldName ==
"geometry"_L1 )
309 if ( !idxList.isEmpty() )
311 attrIndexes = idxList;
316 if ( !attrIndexes.isEmpty() )
318 for (
const QgsField &field : fields )
322 int fieldNameIdx = fields.indexOf( field.name() );
323 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
325 attrIndexes.removeOne( fieldNameIdx );
358#ifdef HAVE_SERVER_PYTHON_PLUGINS
379 QStringList attributes = QStringList();
380 for (
int idx : std::as_const( attrIndexes ) )
394 if ( !pkAttributes.isEmpty() )
397 for (
int idx : pkAttributes )
399 if ( !subsetOfAttrs.contains( idx ) )
401 subsetOfAttrs.prepend( idx );
428 geometryName =
"NONE"_L1;
436 QString outputSrsName;
437 if ( !query.
srsName.isEmpty() )
441 else if ( !requestSrsName.isEmpty() )
443 outputSrsName = requestSrsName;
485 if ( iteratedFeatures >= aRequest.
startIndex )
499 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) && outputCrs.
hasAxisInverted() && !outputSrsName.startsWith(
"EPSG:"_L1 ) };
501 const createFeatureParams cfp = { layerPrecision, layerCrs, attrIndexes, typeName, withGeom, geometryName, outputCrs, forceGeomToMulti, outputSrsName, invertAxis };
508 if ( iteratedFeatures == aRequest.
startIndex )
509 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
511 if ( iteratedFeatures >= aRequest.
startIndex )
521#ifdef HAVE_SERVER_PYTHON_PLUGINS
523 filterRestorer.reset();
528 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
533 if ( iteratedFeatures <= aRequest.
startIndex )
534 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
542 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
543 request.
startIndex = mWfsParameters.startIndexAsInt();
547 QStringList fidList = mWfsParameters.featureIds();
548 bool paramContainsFeatureIds = !fidList.isEmpty();
549 QStringList filterList = mWfsParameters.filters();
550 bool paramContainsFilters = !filterList.isEmpty();
551 QString bbox = mWfsParameters.bbox();
552 bool paramContainsBbox = !bbox.isEmpty();
553 if ( ( paramContainsFeatureIds
554 && ( paramContainsFilters || paramContainsBbox ) )
555 || ( paramContainsFilters && ( paramContainsFeatureIds || paramContainsBbox ) )
556 || ( paramContainsBbox && ( paramContainsFeatureIds || paramContainsFilters ) ) )
562 QStringList propertyNameList = mWfsParameters.propertyNames();
565 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
567 QStringList typeNameList;
569 if ( paramContainsFeatureIds )
572 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
576 if ( propertyNameList.isEmpty() )
578 for (
int i = 0; i < fidList.size(); ++i )
580 propertyNameList << u
"*"_s;
584 QMap<QString, QStringList> fidsMap;
586 QStringList::const_iterator fidIt = fidList.constBegin();
587 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
588 for ( ; fidIt != fidList.constEnd(); ++fidIt )
591 QString fid = *fidIt;
594 QString propertyName;
595 if ( propertyNameIt != propertyNameList.constEnd() )
597 propertyName = *propertyNameIt;
600 if ( !fid.contains(
'.' ) )
605 QString typeName = fid.section(
'.', 0, 0 );
606 fid = fid.section(
'.', 1, 1 );
607 if ( !typeNameList.contains( typeName ) )
609 typeNameList << typeName;
615 const QString key = u
"%1:%2"_s.arg( typeName, propertyName );
617 if ( fidsMap.contains( key ) )
619 fids = fidsMap.value( key );
622 fidsMap.insert( key, fids );
624 if ( propertyNameIt != propertyNameList.constEnd() )
630 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
631 while ( fidsMapIt != fidsMap.constEnd() )
633 QString key = fidsMapIt.key();
637 const QString typeName = key.section(
':', 0, 0 );
638 const QString propertyName = key.section(
':', 1, 1 );
642 query.
srsName = mWfsParameters.srsName();
645 if ( propertyName !=
"*"_L1 )
647 QStringList propertyList;
649 const QStringList attrList = propertyName.split(
',' );
650 QStringList::const_iterator alstIt;
651 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
653 QString fieldName = *alstIt;
654 fieldName = fieldName.trimmed();
655 if ( fieldName.contains(
':' ) )
657 fieldName = fieldName.section(
':', 1, 1 );
659 if ( fieldName.contains(
'/' ) )
661 if ( fieldName.section(
'/', 0, 0 ) != typeName )
665 fieldName = fieldName.section(
'/', 1, 1 );
667 propertyList.append( fieldName );
674 request.
queries.append( query );
680 if ( !mRequestParameters.contains( u
"TYPENAME"_s ) )
685 typeNameList = mWfsParameters.typeNames();
687 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
691 if ( propertyNameList.isEmpty() )
693 for (
int i = 0; i < typeNameList.size(); ++i )
695 propertyNameList << u
"*"_s;
700 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
701 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
702 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
704 QString typeName = *typeNameIt;
705 typeName = typeName.trimmed();
707 QString propertyName;
708 if ( propertyNameIt != propertyNameList.constEnd() )
710 propertyName = *propertyNameIt;
715 query.
srsName = mWfsParameters.srsName();
718 if ( propertyName !=
"*"_L1 )
720 QStringList propertyList;
722 const QStringList attrList = propertyName.split(
',' );
723 QStringList::const_iterator alstIt;
724 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
726 QString fieldName = *alstIt;
727 fieldName = fieldName.trimmed();
728 if ( fieldName.contains(
':' ) )
730 fieldName = fieldName.section(
':', 1, 1 );
732 if ( fieldName.contains(
'/' ) )
734 if ( fieldName.section(
'/', 0, 0 ) != typeName )
738 fieldName = fieldName.section(
'/', 1, 1 );
740 propertyList.append( fieldName );
745 request.
queries.append( query );
747 if ( propertyNameIt != propertyNameList.constEnd() )
754 QStringList expFilterList = mWfsParameters.expFilters();
755 if ( !expFilterList.isEmpty() )
758 if ( request.
queries.size() == expFilterList.size() )
761 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
762 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
763 for ( ; qIt != request.
queries.end(); ++qIt )
767 const QString expFilter = *expFilterIt++;
768 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
771 if ( filter->hasParserError() )
775 if ( filter->needsGeometry() )
789 if ( paramContainsBbox )
794 QString extentSrsName { mWfsParameters.srsName() };
797 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && !mWfsParameters.srsName().isEmpty() )
799 QString crs( mWfsParameters.bbox().split(
',' )[4] );
800 if ( crs != mWfsParameters.srsName() )
838 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
839 for ( ; qIt != request.
queries.end(); ++qIt )
845 else if ( paramContainsFilters )
848 if ( request.
queries.size() != filterList.size() )
854 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
855 QStringList::const_iterator filterIt = filterList.constBegin();
856 for ( ; qIt != request.
queries.end(); ++qIt )
861 if ( filterIt != filterList.constEnd() )
864 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
870 QDomElement filterElem = filter.firstChildElement();
871 QStringList serverFids;
875 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(
" D"_L1 ) || attribute.endsWith(
"+D"_L1 ) )
903 else if ( attribute.endsWith(
" DESC"_L1 ) || attribute.endsWith(
"+DESC"_L1 ) )
907 else if ( attribute.endsWith(
" A"_L1 ) || attribute.endsWith(
"+A"_L1 ) )
911 else if ( attribute.endsWith(
" ASC"_L1 ) || attribute.endsWith(
"+ASC"_L1 ) )
929 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
930 request.
startIndex = mWfsParameters.startIndexAsInt();
933 QDomNodeList queryNodes = docElem.elementsByTagName( u
"Query"_s );
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() ==
"PropertyName"_L1 )
962 fieldName = sortPropChildElem.text().trimmed();
964 else if ( sortPropChildElem.tagName() ==
"SortOrder"_L1 )
966 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
967 if ( sortOrder ==
"DESC"_L1 || sortOrder ==
"D"_L1 )
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( u
"typeName"_s, QString() );
995 if ( typeName.contains(
':' ) )
997 typeName = typeName.section(
':', 1, 1 );
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() ==
"PropertyName"_L1 )
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() ==
"Filter"_L1 )
1029 featureRequest =
parseFilterElement( typeName, queryChildElem, serverFids, project );
1031 else if ( queryChildElem.tagName() ==
"SortBy"_L1 )
1033 sortByElem = queryChildElem;
1040 QString srsName = queryElem.attribute( u
"srsName"_s, QString() );
1053 static QSet<QString> sParamFilter {
1069 void hitGetFeature(
const QgsServerRequest &request, QgsServerResponse &response,
const QgsProject *project,
QgsWfsParameters::Format format,
int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1071 QDateTime now = QDateTime::currentDateTime();
1076 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1077 fcString = u
"{\"type\": \"FeatureCollection\",\n"_s;
1078 fcString += u
" \"timeStamp\": \"%1\",\n"_s.arg( now.toString( Qt::ISODate ) );
1079 fcString += u
" \"numberOfFeatures\": %1\n"_s.arg( QString::number( numberOfFeatures ) );
1085 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1087 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1090 QString hrefString =
serviceUrl( request, project, *settings );
1092 QUrl mapUrl( hrefString );
1094 QUrlQuery query( mapUrl );
1095 query.addQueryItem( u
"SERVICE"_s, u
"WFS"_s );
1097 if ( mWfsParameters.version().isEmpty() )
1099 else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1100 query.addQueryItem( u
"VERSION"_s, u
"1.1.0"_s );
1102 query.addQueryItem( u
"VERSION"_s, u
"1.0.0"_s );
1104 const auto constItems { query.queryItems() };
1105 for (
const auto ¶m : std::as_const( constItems ) )
1107 if ( sParamFilter.contains( param.first.toUpper() ) )
1108 query.removeAllQueryItems( param.first );
1111 query.addQueryItem( u
"REQUEST"_s, u
"DescribeFeatureType"_s );
1112 query.addQueryItem( u
"TYPENAME"_s, typeNames.join(
',' ) );
1113 if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1116 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/2.1.2"_s );
1118 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/3.1.1"_s );
1121 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"XMLSCHEMA"_s );
1123 mapUrl.setQuery( query );
1125 hrefString = mapUrl.toString();
1128 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1129 wfsSchema = u
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"_s;
1131 wfsSchema = u
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd"_s;
1134 fcString = u
"<wfs:FeatureCollection"_s;
1138 fcString +=
" xmlns:ows=\"http://www.opengis.net/ows\""_L1;
1139 fcString +=
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""_L1;
1141 fcString +=
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""_L1;
1142 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace(
"&"_L1,
"&"_L1 ) +
"\"";
1143 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1144 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1145 fcString +=
">\n"_L1;
1146 fcString +=
"</wfs:FeatureCollection>"_L1;
1149 response.
write( fcString.toUtf8() );
1153 void startGetFeature(
const QgsServerRequest &request, QgsServerResponse &response,
const QgsProject *project,
QgsWfsParameters::Format format,
int prec, QgsCoordinateReferenceSystem &crs, QgsRectangle *rect,
const QStringList &typeNames,
const QgsServerSettings *settings )
1157 std::unique_ptr<QgsRectangle> transformedRect;
1161 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1166 QgsCoordinateTransform transform;
1173 transformedRect = std::make_unique<QgsRectangle>( exportGeom.
boundingBox() );
1174 rect = transformedRect.get();
1177 catch ( QgsException &cse )
1183 rect =
new QgsRectangle( rect->
intersect( QgsRectangle( -180.0, -90.0, 180.0, 90.0 ) ) );
1185 fcString = u
"{\"type\": \"FeatureCollection\",\n"_s;
1189 const QgsCoordinateReferenceSystem destinationCrs { srsName.isEmpty() ? u
"EPSG:4326"_s : srsName };
1190 if ( !destinationCrs.
isValid() )
1197 for (
const auto &it : value.items() )
1199 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1202 fcString +=
" \"features\": [\n"_L1;
1203 response.
write( fcString.toUtf8() );
1208 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1210 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1213 QString hrefString =
serviceUrl( request, project, *settings );
1215 QUrl mapUrl( hrefString );
1217 QUrlQuery query( mapUrl );
1218 query.addQueryItem( u
"SERVICE"_s, u
"WFS"_s );
1220 if ( mWfsParameters.version().isEmpty() )
1222 else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1223 query.addQueryItem( u
"VERSION"_s, u
"1.1.0"_s );
1225 query.addQueryItem( u
"VERSION"_s, u
"1.0.0"_s );
1227 const auto queryItems { query.queryItems() };
1228 for (
auto param : std::as_const( queryItems ) )
1230 if ( sParamFilter.contains( param.first.toUpper() ) )
1231 query.removeAllQueryItems( param.first );
1234 query.addQueryItem( u
"REQUEST"_s, u
"DescribeFeatureType"_s );
1235 query.addQueryItem( u
"TYPENAME"_s, typeNames.join(
',' ) );
1236 if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1239 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/2.1.2"_s );
1241 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/3.1.1"_s );
1244 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"XMLSCHEMA"_s );
1246 mapUrl.setQuery( query );
1248 hrefString = mapUrl.toString();
1251 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1252 wfsSchema = u
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"_s;
1254 wfsSchema = u
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd"_s;
1257 fcString = u
"<wfs:FeatureCollection"_s;
1261 fcString +=
" xmlns:ows=\"http://www.opengis.net/ows\""_L1;
1262 fcString +=
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""_L1;
1264 fcString +=
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""_L1;
1265 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace(
"&"_L1,
"&"_L1 ) +
"\"";
1266 fcString +=
">\n"_L1;
1268 response.
write( fcString.toUtf8() );
1272 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1277 const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName :
getSrsNameFromVersion( crs );
1278 QgsCoordinateReferenceSystem outputCrs;
1281 QgsCoordinateTransform transform;
1284 QgsRectangle crsCorrectedRect { rect ? *rect : QgsRectangle() };
1290 catch ( QgsException &cse )
1300 const bool invertAxis { mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) && outputCrs.
hasAxisInverted() && !outputSrsName.startsWith(
"EPSG:"_L1 ) };
1303 if ( !envElem.isNull() )
1305 if ( crs.
isValid() && outputSrsName.isEmpty() )
1309 bbElem.appendChild( envElem );
1310 doc.appendChild( bbElem );
1316 if ( !boxElem.isNull() )
1322 bbElem.appendChild( boxElem );
1323 doc.appendChild( bbElem );
1326 response.
write( doc.toByteArray() );
1331 void setGetFeature( QgsServerResponse &response,
QgsWfsParameters::Format format,
const QgsFeature &feature,
int featIdx,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1342 fcString +=
" ,"_L1;
1344 const QgsCoordinateReferenceSystem destinationCrs { params.srsName.isEmpty() ? u
"EPSG:4326"_s : params.srsName };
1345 if ( !destinationCrs.
isValid() )
1350 mJsonExporter.setDestinationCrs( destinationCrs );
1351 mJsonExporter.setTransformGeometries(
true );
1352 mJsonExporter.setSourceCrs( params.crs );
1353 mJsonExporter.setIncludeGeometry(
false );
1354 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1355 mJsonExporter.setAttributes( params.attributeIndexes );
1356 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1357 fcString +=
"\n"_L1;
1359 response.
write( fcString.toUtf8() );
1363 QDomDocument gmlDoc;
1364 QDomElement featureElement;
1367 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1368 gmlDoc.appendChild( featureElement );
1372 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1373 gmlDoc.appendChild( featureElement );
1375 response.
write( gmlDoc.toByteArray() );
1387 fcString +=
" ]\n"_L1;
1392 fcString = u
"</wfs:FeatureCollection>\n"_s;
1394 response.
write( fcString.toUtf8() );
1398 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1406 QgsFeature f( feature );
1407 QgsGeometry geom = feature.
geometry();
1408 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1410 mJsonExporter.setIncludeGeometry(
true );
1411 if ( params.geometryName ==
"EXTENT"_L1 )
1416 else if ( params.geometryName ==
"CENTROID"_L1 )
1422 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1426 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1429 QDomElement featureElement = doc.createElement( u
"gml:featureMember"_s );
1432 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1434 typeNameElement.setAttribute( u
"fid"_s,
id );
1435 featureElement.appendChild( typeNameElement );
1438 QgsGeometry geom = feature.
geometry();
1439 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1441 int prec = params.precision;
1442 QgsCoordinateReferenceSystem crs = params.crs;
1443 QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1446 QgsGeometry transformed = geom;
1450 crs = params.outputCrs;
1451 if ( crs.
isGeographic() && !params.crs.isGeographic() )
1452 prec = std::min( params.precision + 3, 6 );
1455 catch ( QgsCsException &cse )
1460 QDomElement geomElem = doc.createElement( u
"qgs:geometry"_s );
1461 QDomElement gmlElem;
1462 QgsGeometry cloneGeom( geom );
1463 if ( params.geometryName ==
"EXTENT"_L1 )
1467 else if ( params.geometryName ==
"CENTROID"_L1 )
1473 cloneGeom.convertToMultiType();
1475 const QgsAbstractGeometry *abstractGeom = cloneGeom.constGet();
1478 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1481 if ( !gmlElem.isNull() )
1484 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1493 bbElem.appendChild( boxElem );
1494 typeNameElement.appendChild( bbElem );
1496 geomElem.appendChild( gmlElem );
1497 typeNameElement.appendChild( geomElem );
1502 const QgsAttributes featureAttributes = feature.
attributes();
1503 const QgsFields fields = feature.
fields();
1504 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1506 int idx = params.attributeIndexes[i];
1512 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1513 typeNameElement.appendChild( fieldElem );
1516 return featureElement;
1519 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1522 QDomElement featureElement = doc.createElement( u
"gml:featureMember"_s );
1525 QDomElement typeNameElement = doc.createElement( u
"qgs:"_s + params.typeName );
1527 typeNameElement.setAttribute( u
"gml:id"_s,
id );
1528 featureElement.appendChild( typeNameElement );
1531 QgsGeometry geom = feature.
geometry();
1532 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1534 int prec = params.precision;
1535 QgsCoordinateReferenceSystem crs = params.crs;
1536 QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1539 QgsGeometry transformed = geom;
1543 crs = params.outputCrs;
1544 if ( crs.
isGeographic() && !params.crs.isGeographic() )
1545 prec = std::min( params.precision + 3, 6 );
1548 catch ( QgsCsException &cse )
1553 QDomElement geomElem = doc.createElement( u
"qgs:geometry"_s );
1554 QDomElement gmlElem;
1555 QgsGeometry cloneGeom( geom );
1556 if ( params.geometryName ==
"EXTENT"_L1 )
1560 else if ( params.geometryName ==
"CENTROID"_L1 )
1566 cloneGeom.convertToMultiType();
1568 const QgsAbstractGeometry *abstractGeom = cloneGeom.constGet();
1574 if ( !gmlElem.isNull() )
1577 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1580 if ( crs.
isValid() && params.srsName.isEmpty() )
1585 else if ( !params.srsName.isEmpty() )
1587 gmlElem.setAttribute( u
"srsName"_s, params.srsName );
1590 bbElem.appendChild( boxElem );
1591 typeNameElement.appendChild( bbElem );
1593 geomElem.appendChild( gmlElem );
1594 typeNameElement.appendChild( geomElem );
1599 const QgsAttributes featureAttributes = feature.
attributes();
1600 const QgsFields fields = feature.
fields();
1601 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1603 int idx = params.attributeIndexes[i];
1609 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1610 typeNameElement.appendChild( fieldElem );
1613 return featureElement;
1616 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1619 const thread_local QRegularExpression sCleanTagNameRegExp( u
"[^\\w\\.-_]"_s, QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1620 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1621 QDomElement fieldElem = doc.createElement( u
"qgs:"_s + attributeName );
1624 fieldElem.setAttribute( u
"xsi:nil"_s, u
"true"_s );
1628 const QString fieldText = encodeValueToText( value, setup );
1630 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1632 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1636 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1642 QString encodeValueToText(
const QVariant &value,
const QgsEditorWidgetSetup &setup )
1647 if ( setup.
type() ==
"DateTime"_L1 )
1650 if ( value.userType() == QMetaType::Type::QTime )
1656 const QVariantMap config = setup.
config();
1662 QDateTime date = value.toDateTime();
1664 if ( !date.isValid() )
1666 date = QDateTime::fromString( value.toString(), fieldFormat );
1669 if ( date.isValid() )
1671 return date.toString( fieldFormat );
1674 return value.toString();
1676 else if ( setup.
type() ==
"Range"_L1 )
1678 const QVariantMap config = setup.
config();
1679 if ( config.contains( u
"Precision"_s ) )
1683 int precision( config[u
"Precision"_s].toInt( &ok ) );
1685 return QString::number( value.toDouble(),
'f', precision );
1689 switch ( value.userType() )
1691 case QMetaType::Type::Int:
1692 case QMetaType::Type::UInt:
1693 case QMetaType::Type::LongLong:
1694 case QMetaType::Type::ULongLong:
1695 case QMetaType::Type::Double:
1696 return value.toString();
1698 case QMetaType::Type::Bool:
1699 return value.toBool() ? u
"true"_s : u
"false"_s;
1701 case QMetaType::Type::QStringList:
1702 case QMetaType::Type::QVariantList:
1703 case QMetaType::Type::QVariantMap:
1707 case QMetaType::Type::QString:
1708 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.
@ XY
X comes before Y (or lon before lat).
@ 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.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toOgcUrn() const
Returns the crs as OGC URN (format: urn:ogc:def:crs:OGC:1.3:CRS84) Returns an empty string on failure...
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...
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...
Handles 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.
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.
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.
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.).
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...
QString wfsTypeName() const
Returns WFS typename for the layer.
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
static QDomElement rectangleToGMLEnvelope(const QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
static QDomElement rectangleToGMLBox(const QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
Describes 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.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
static QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
static QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
Defines 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.
static QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
static int wfsLayerPrecision(const QgsProject &project, const QString &layerId)
Returns the Layer precision defined in a QGIS project for the WFS GetFeature.
Defines requests passed to QgsService classes.
QgsServerParameters serverParameters() const
Returns parameters.
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QUrl url() const
Returns the request URL as seen by QGIS server.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
Defines the response interface passed to QgsService.
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...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
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 dataset.
Q_INVOKABLE QgsAttributeList attributeList() const
Returns list of attribute indexes.
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
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 Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
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
QString getSrsNameFromVersion(const QgsCoordinateReferenceSystem &crs)
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.
#define Q_NOWARN_DEPRECATED_POP
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
#define Q_NOWARN_DEPRECATED_PUSH
QMap< QString, QString > QgsStringMap
QList< int > QgsAttributeList
QgsFeatureRequest featureRequest
QList< getFeatureQuery > queries
QgsWfsParameters::Format outputFormat