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 );
88 const QgsServerRequest &request,
89 QgsServerResponse &response,
90 const QgsProject *project,
93 const QStringList &typeNames,
94 const QgsServerSettings *serverSettings
98 const QgsServerRequest &request,
99 QgsServerResponse &response,
100 const QgsProject *project,
103 QgsCoordinateReferenceSystem &crs,
105 const QStringList &typeNames,
106 const QgsServerSettings *settings
110 QgsServerResponse &response,
112 const QgsFeature &feature,
114 const createFeatureParams ¶ms,
115 const QgsProject *project,
124 QgsJsonExporter mJsonExporter;
145 mWfsParameters.dump();
151 if ( doc.setContent( request.
data(),
true, &errorMsg ) )
153 QDomElement docElem = doc.documentElement();
162 QStringList typeNameList;
165 bool onlyOneLayer = ( aRequest.
queries.size() == 1 );
168 int requestPrecision = 6;
172 QList<getFeatureQuery>::iterator qIt = aRequest.
queries.begin();
173 for ( ; qIt != aRequest.
queries.end(); ++qIt )
175 typeNameList << ( *qIt ).typeName;
181 QMap<QString, QgsMapLayer *> mapLayerMap;
182 for (
int i = 0; i < wfsLayerIds.size(); ++i )
196 if ( typeNameList.contains( name ) )
199 mapLayerMap[name] = layer;
203 requestRect = layer->
extent();
204 requestCrs = layer->
crs();
223 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
230 for (
const QString &typeName : typeNameList )
232 if ( !mapLayerMap.contains( typeName ) )
238#ifdef HAVE_SERVER_PYTHON_PLUGINS
242 auto filterRestorer = std::make_unique<QgsOWSServerFilterRestorer>();
244 ( void ) serverIface;
248 long sentFeatures = 0;
249 long iteratedFeatures = 0;
252 qIt = aRequest.
queries.begin();
253 for ( ; qIt != aRequest.
queries.end(); ++qIt )
259#ifdef HAVE_SERVER_PYTHON_PLUGINS
277#ifdef HAVE_SERVER_PYTHON_PLUGINS
284 QMap<int, QString> layerAliasInfo;
286 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
287 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
290 if ( attrIndex != -1 )
292 layerAliasInfo.insert( attrIndex, aliasIt.value() );
302 bool withGeom =
true;
303 if ( !propertyList.isEmpty() && propertyList.first() !=
"*"_L1 )
306 QStringList::const_iterator plstIt;
309 QList<QString> propertynames;
310 QList<QString> fieldnames;
311 for (
const QgsField &field : fields )
313 fieldnames.append( field.name() );
314 const thread_local QRegularExpression sCleanTagNameRegExp( u
"[^\\w\\.-_]"_s, QRegularExpression::PatternOption::UseUnicodePropertiesOption );
315 propertynames.append( field.name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() ) );
318 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
321 int fieldNameIdx = propertynames.indexOf( fieldName );
322 if ( fieldNameIdx == -1 )
324 fieldNameIdx = fieldnames.indexOf( fieldName );
326 if ( fieldNameIdx > -1 )
328 idxList.append( fieldNameIdx );
330 else if ( fieldName ==
"geometry"_L1 )
335 if ( !idxList.isEmpty() )
337 attrIndexes = idxList;
342 if ( !attrIndexes.isEmpty() )
344 for (
const QgsField &field : fields )
348 int fieldNameIdx = fields.indexOf( field.name() );
349 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
351 attrIndexes.removeOne( fieldNameIdx );
382#ifdef HAVE_SERVER_PYTHON_PLUGINS
401 QStringList attributes = QStringList();
402 for (
int idx : std::as_const( attrIndexes ) )
413 if ( !pkAttributes.isEmpty() )
416 for (
int idx : pkAttributes )
418 if ( !subsetOfAttrs.contains( idx ) )
420 subsetOfAttrs.prepend( idx );
447 geometryName =
"NONE"_L1;
455 QString outputSrsName;
456 if ( !query.
srsName.isEmpty() )
460 else if ( !requestSrsName.isEmpty() )
462 outputSrsName = requestSrsName;
504 if ( iteratedFeatures >= aRequest.
startIndex )
518 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) && outputCrs.
hasAxisInverted() && !outputSrsName.startsWith(
"EPSG:"_L1 ) };
520 const createFeatureParams cfp = { layerPrecision, layerCrs, attrIndexes, typeName, withGeom, geometryName, outputCrs, forceGeomToMulti, outputSrsName, invertAxis };
527 if ( iteratedFeatures == aRequest.
startIndex )
528 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
530 if ( iteratedFeatures >= aRequest.
startIndex )
540#ifdef HAVE_SERVER_PYTHON_PLUGINS
542 filterRestorer.reset();
547 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
552 if ( iteratedFeatures <= aRequest.
startIndex )
553 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
561 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
562 request.
startIndex = mWfsParameters.startIndexAsInt();
566 QStringList fidList = mWfsParameters.featureIds();
567 bool paramContainsFeatureIds = !fidList.isEmpty();
568 QStringList filterList = mWfsParameters.filters();
569 bool paramContainsFilters = !filterList.isEmpty();
570 QString bbox = mWfsParameters.bbox();
571 bool paramContainsBbox = !bbox.isEmpty();
572 if ( ( paramContainsFeatureIds && ( paramContainsFilters || paramContainsBbox ) )
573 || ( paramContainsFilters && ( paramContainsFeatureIds || paramContainsBbox ) )
574 || ( paramContainsBbox && ( paramContainsFeatureIds || paramContainsFilters ) ) )
580 QStringList propertyNameList = mWfsParameters.propertyNames();
583 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
585 QStringList typeNameList;
587 if ( paramContainsFeatureIds )
590 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
594 if ( propertyNameList.isEmpty() )
596 for (
int i = 0; i < fidList.size(); ++i )
598 propertyNameList << u
"*"_s;
602 QMap<QString, QStringList> fidsMap;
604 QStringList::const_iterator fidIt = fidList.constBegin();
605 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
606 for ( ; fidIt != fidList.constEnd(); ++fidIt )
609 QString fid = *fidIt;
612 QString propertyName;
613 if ( propertyNameIt != propertyNameList.constEnd() )
615 propertyName = *propertyNameIt;
618 if ( !fid.contains(
'.' ) )
623 QString typeName = fid.section(
'.', 0, 0 );
624 fid = fid.section(
'.', 1, 1 );
625 if ( !typeNameList.contains( typeName ) )
627 typeNameList << typeName;
633 const QString key = u
"%1:%2"_s.arg( typeName, propertyName );
635 if ( fidsMap.contains( key ) )
637 fids = fidsMap.value( key );
640 fidsMap.insert( key, fids );
642 if ( propertyNameIt != propertyNameList.constEnd() )
648 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
649 while ( fidsMapIt != fidsMap.constEnd() )
651 QString key = fidsMapIt.key();
655 const QString typeName = key.section(
':', 0, 0 );
656 const QString propertyName = key.section(
':', 1, 1 );
660 query.
srsName = mWfsParameters.srsName();
663 if ( propertyName !=
"*"_L1 )
665 QStringList propertyList;
667 const QStringList attrList = propertyName.split(
',' );
668 QStringList::const_iterator alstIt;
669 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
671 QString fieldName = *alstIt;
672 fieldName = fieldName.trimmed();
673 if ( fieldName.contains(
':' ) )
675 fieldName = fieldName.section(
':', 1, 1 );
677 if ( fieldName.contains(
'/' ) )
679 if ( fieldName.section(
'/', 0, 0 ) != typeName )
683 fieldName = fieldName.section(
'/', 1, 1 );
685 propertyList.append( fieldName );
692 request.
queries.append( query );
698 if ( !mRequestParameters.contains( u
"TYPENAME"_s ) )
703 typeNameList = mWfsParameters.typeNames();
705 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
709 if ( propertyNameList.isEmpty() )
711 for (
int i = 0; i < typeNameList.size(); ++i )
713 propertyNameList << u
"*"_s;
718 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
719 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
720 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
722 QString typeName = *typeNameIt;
723 typeName = typeName.trimmed();
725 QString propertyName;
726 if ( propertyNameIt != propertyNameList.constEnd() )
728 propertyName = *propertyNameIt;
733 query.
srsName = mWfsParameters.srsName();
736 if ( propertyName !=
"*"_L1 )
738 QStringList propertyList;
740 const QStringList attrList = propertyName.split(
',' );
741 QStringList::const_iterator alstIt;
742 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
744 QString fieldName = *alstIt;
745 fieldName = fieldName.trimmed();
746 if ( fieldName.contains(
':' ) )
748 fieldName = fieldName.section(
':', 1, 1 );
750 if ( fieldName.contains(
'/' ) )
752 if ( fieldName.section(
'/', 0, 0 ) != typeName )
756 fieldName = fieldName.section(
'/', 1, 1 );
758 propertyList.append( fieldName );
763 request.
queries.append( query );
765 if ( propertyNameIt != propertyNameList.constEnd() )
772 QStringList expFilterList = mWfsParameters.expFilters();
773 if ( !expFilterList.isEmpty() )
776 if ( request.
queries.size() == expFilterList.size() )
779 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
780 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
781 for ( ; qIt != request.
queries.end(); ++qIt )
785 const QString expFilter = *expFilterIt++;
786 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
789 if ( filter->hasParserError() )
793 if ( filter->needsGeometry() )
807 if ( paramContainsBbox )
812 QString extentSrsName { mWfsParameters.srsName() };
815 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && !mWfsParameters.srsName().isEmpty() )
817 QString crs( mWfsParameters.bbox().split(
',' )[4] );
818 if ( crs != mWfsParameters.srsName() )
856 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
857 for ( ; qIt != request.
queries.end(); ++qIt )
863 else if ( paramContainsFilters )
866 if ( request.
queries.size() != filterList.size() )
872 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
873 QStringList::const_iterator filterIt = filterList.constBegin();
874 for ( ; qIt != request.
queries.end(); ++qIt )
879 if ( filterIt != filterList.constEnd() )
882 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
888 QDomElement filterElem = filter.firstChildElement();
889 QStringList serverFids;
893 if ( filterIt != filterList.constEnd() )
900 QStringList sortByList = mWfsParameters.sortBy();
901 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
904 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
905 QStringList::const_iterator sortByIt = sortByList.constBegin();
906 for ( ; qIt != request.
queries.end(); ++qIt )
911 if ( sortByIt != sortByList.constEnd() )
915 for (
const QString &attribute : sortBy.split(
',' ) )
917 if ( attribute.endsWith(
" D"_L1 ) || attribute.endsWith(
"+D"_L1 ) )
921 else if ( attribute.endsWith(
" DESC"_L1 ) || attribute.endsWith(
"+DESC"_L1 ) )
925 else if ( attribute.endsWith(
" A"_L1 ) || attribute.endsWith(
"+A"_L1 ) )
929 else if ( attribute.endsWith(
" ASC"_L1 ) || attribute.endsWith(
"+ASC"_L1 ) )
947 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
948 request.
startIndex = mWfsParameters.startIndexAsInt();
951 QDomNodeList queryNodes = docElem.elementsByTagName( u
"Query"_s );
952 QDomElement queryElem;
953 for (
int i = 0; i < queryNodes.size(); i++ )
955 queryElem = queryNodes.at( i ).toElement();
957 request.
queries.append( query );
964 QDomNodeList sortByNodes = sortByElem.childNodes();
965 if ( sortByNodes.size() )
967 for (
int i = 0; i < sortByNodes.size(); i++ )
969 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
970 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
971 if ( sortPropChildNodes.size() )
974 bool ascending =
true;
975 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
977 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
978 if ( sortPropChildElem.tagName() ==
"PropertyName"_L1 )
980 fieldName = sortPropChildElem.text().trimmed();
982 else if ( sortPropChildElem.tagName() ==
"SortOrder"_L1 )
984 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
985 if ( sortOrder ==
"DESC"_L1 || sortOrder ==
"D"_L1 )
990 if ( fieldName.contains(
':' ) )
992 fieldName = fieldName.section(
':', 1, 1 );
994 if ( fieldName.contains(
'/' ) )
996 if ( fieldName.section(
'/', 0, 0 ) != typeName )
1000 fieldName = fieldName.section(
'/', 1, 1 );
1003 if ( !fieldName.isEmpty() )
1004 featureRequest.
addOrderBy( fieldName, ascending );
1012 QString typeName = queryElem.attribute( u
"typeName"_s, QString() );
1013 if ( typeName.contains(
':' ) )
1015 typeName = typeName.section(
':', 1, 1 );
1019 QStringList serverFids;
1020 QStringList propertyList;
1021 QDomNodeList queryChildNodes = queryElem.childNodes();
1022 if ( queryChildNodes.size() )
1024 QDomElement sortByElem;
1025 for (
int q = 0; q < queryChildNodes.size(); q++ )
1027 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
1028 if ( queryChildElem.tagName() ==
"PropertyName"_L1 )
1030 QString fieldName = queryChildElem.text().trimmed();
1031 if ( fieldName.contains(
':' ) )
1033 fieldName = fieldName.section(
':', 1, 1 );
1035 if ( fieldName.contains(
'/' ) )
1037 if ( fieldName.section(
'/', 0, 0 ) != typeName )
1041 fieldName = fieldName.section(
'/', 1, 1 );
1043 propertyList.append( fieldName );
1045 else if ( queryChildElem.tagName() ==
"Filter"_L1 )
1047 featureRequest =
parseFilterElement( typeName, queryChildElem, serverFids, project );
1049 else if ( queryChildElem.tagName() ==
"SortBy"_L1 )
1051 sortByElem = queryChildElem;
1058 QString srsName = queryElem.attribute( u
"srsName"_s, QString() );
1071 static QSet<QString>
1072 sParamFilter { u
"REQUEST"_s, u
"FORMAT"_s, u
"OUTPUTFORMAT"_s, u
"BBOX"_s, u
"FEATUREID"_s, u
"TYPENAME"_s, u
"FILTER"_s, u
"EXP_FILTER"_s, u
"MAXFEATURES"_s, u
"STARTINDEX"_s, u
"PROPERTYNAME"_s, u
"_DC"_s };
1076 const QgsServerRequest &request, QgsServerResponse &response,
const QgsProject *project,
QgsWfsParameters::Format format,
int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings
1079 QDateTime now = QDateTime::currentDateTime();
1084 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1085 fcString = u
"{\"type\": \"FeatureCollection\",\n"_s;
1086 fcString += u
" \"timeStamp\": \"%1\",\n"_s.arg( now.toString( Qt::ISODate ) );
1087 fcString += u
" \"numberOfFeatures\": %1\n"_s.arg( QString::number( numberOfFeatures ) );
1093 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1095 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1098 QString hrefString =
serviceUrl( request, project, *settings );
1100 QUrl mapUrl( hrefString );
1102 QUrlQuery query( mapUrl );
1103 query.addQueryItem( u
"SERVICE"_s, u
"WFS"_s );
1105 if ( mWfsParameters.version().isEmpty() )
1107 else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1108 query.addQueryItem( u
"VERSION"_s, u
"1.1.0"_s );
1110 query.addQueryItem( u
"VERSION"_s, u
"1.0.0"_s );
1112 const auto constItems { query.queryItems() };
1113 for (
const auto ¶m : std::as_const( constItems ) )
1115 if ( sParamFilter.contains( param.first.toUpper() ) )
1116 query.removeAllQueryItems( param.first );
1119 query.addQueryItem( u
"REQUEST"_s, u
"DescribeFeatureType"_s );
1120 query.addQueryItem( u
"TYPENAME"_s, typeNames.join(
',' ) );
1121 if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1124 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/2.1.2"_s );
1126 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/3.1.1"_s );
1129 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"XMLSCHEMA"_s );
1131 mapUrl.setQuery( query );
1133 hrefString = mapUrl.toString();
1136 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1137 wfsSchema = u
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"_s;
1139 wfsSchema = u
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd"_s;
1142 fcString = u
"<wfs:FeatureCollection"_s;
1146 fcString +=
" xmlns:ows=\"http://www.opengis.net/ows\""_L1;
1147 fcString +=
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""_L1;
1149 fcString +=
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""_L1;
1150 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace(
"&"_L1,
"&"_L1 ) +
"\"";
1151 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1152 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1153 fcString +=
">\n"_L1;
1154 fcString +=
"</wfs:FeatureCollection>"_L1;
1157 response.
write( fcString.toUtf8() );
1161 void startGetFeature(
1162 const QgsServerRequest &request,
1163 QgsServerResponse &response,
1164 const QgsProject *project,
1167 QgsCoordinateReferenceSystem &crs,
1169 const QStringList &typeNames,
1170 const QgsServerSettings *settings
1175 std::unique_ptr<QgsRectangle> transformedRect;
1179 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1184 QgsCoordinateTransform transform;
1191 transformedRect = std::make_unique<QgsRectangle>( exportGeom.
boundingBox() );
1192 rect = transformedRect.get();
1195 catch ( QgsException &cse )
1201 rect =
new QgsRectangle( rect->
intersect( QgsRectangle( -180.0, -90.0, 180.0, 90.0 ) ) );
1203 fcString = u
"{\"type\": \"FeatureCollection\",\n"_s;
1204 fcString +=
" \"bbox\": [ "
1215 const QgsCoordinateReferenceSystem destinationCrs { srsName.isEmpty() ? u
"EPSG:4326"_s : srsName };
1216 if ( !destinationCrs.
isValid() )
1223 for (
const auto &it : value.items() )
1225 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1228 fcString +=
" \"features\": [\n"_L1;
1229 response.
write( fcString.toUtf8() );
1234 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1236 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1239 QString hrefString =
serviceUrl( request, project, *settings );
1241 QUrl mapUrl( hrefString );
1243 QUrlQuery query( mapUrl );
1244 query.addQueryItem( u
"SERVICE"_s, u
"WFS"_s );
1246 if ( mWfsParameters.version().isEmpty() )
1248 else if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1249 query.addQueryItem( u
"VERSION"_s, u
"1.1.0"_s );
1251 query.addQueryItem( u
"VERSION"_s, u
"1.0.0"_s );
1253 const auto queryItems { query.queryItems() };
1254 for (
auto param : std::as_const( queryItems ) )
1256 if ( sParamFilter.contains( param.first.toUpper() ) )
1257 query.removeAllQueryItems( param.first );
1260 query.addQueryItem( u
"REQUEST"_s, u
"DescribeFeatureType"_s );
1261 query.addQueryItem( u
"TYPENAME"_s, typeNames.join(
',' ) );
1262 if ( mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1265 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/2.1.2"_s );
1267 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"text/xml; subtype=gml/3.1.1"_s );
1270 query.addQueryItem( u
"OUTPUTFORMAT"_s, u
"XMLSCHEMA"_s );
1272 mapUrl.setQuery( query );
1274 hrefString = mapUrl.toString();
1277 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) )
1278 wfsSchema = u
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"_s;
1280 wfsSchema = u
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd"_s;
1283 fcString = u
"<wfs:FeatureCollection"_s;
1287 fcString +=
" xmlns:ows=\"http://www.opengis.net/ows\""_L1;
1288 fcString +=
" xmlns:xlink=\"http://www.w3.org/1999/xlink\""_L1;
1290 fcString +=
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""_L1;
1291 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace(
"&"_L1,
"&"_L1 ) +
"\"";
1292 fcString +=
">\n"_L1;
1294 response.
write( fcString.toUtf8() );
1298 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1303 const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName :
getSrsNameFromVersion( crs );
1304 QgsCoordinateReferenceSystem outputCrs;
1307 QgsCoordinateTransform transform;
1310 QgsRectangle crsCorrectedRect { rect ? *rect : QgsRectangle() };
1316 catch ( QgsException &cse )
1326 const bool invertAxis { mWfsParameters.versionAsNumber() >= QgsProjectVersion( 1, 1, 0 ) && outputCrs.
hasAxisInverted() && !outputSrsName.startsWith(
"EPSG:"_L1 ) };
1329 if ( !envElem.isNull() )
1331 if ( crs.
isValid() && outputSrsName.isEmpty() )
1335 bbElem.appendChild( envElem );
1336 doc.appendChild( bbElem );
1342 if ( !boxElem.isNull() )
1348 bbElem.appendChild( boxElem );
1349 doc.appendChild( bbElem );
1352 response.
write( doc.toByteArray() );
1358 QgsServerResponse &response,
QgsWfsParameters::Format format,
const QgsFeature &feature,
int featIdx,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes
1370 fcString +=
" ,"_L1;
1372 const QgsCoordinateReferenceSystem destinationCrs { params.srsName.isEmpty() ? u
"EPSG:4326"_s : params.srsName };
1373 if ( !destinationCrs.
isValid() )
1378 mJsonExporter.setDestinationCrs( destinationCrs );
1379 mJsonExporter.setTransformGeometries(
true );
1380 mJsonExporter.setSourceCrs( params.crs );
1381 mJsonExporter.setIncludeGeometry(
false );
1382 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1383 mJsonExporter.setAttributes( params.attributeIndexes );
1384 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1385 fcString +=
"\n"_L1;
1387 response.
write( fcString.toUtf8() );
1391 QDomDocument gmlDoc;
1392 QDomElement featureElement;
1395 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1396 gmlDoc.appendChild( featureElement );
1400 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1401 gmlDoc.appendChild( featureElement );
1403 response.
write( gmlDoc.toByteArray() );
1415 fcString +=
" ]\n"_L1;
1420 fcString = u
"</wfs:FeatureCollection>\n"_s;
1422 response.
write( fcString.toUtf8() );
1426 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1434 QgsFeature f( feature );
1435 QgsGeometry geom = feature.
geometry();
1436 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1438 mJsonExporter.setIncludeGeometry(
true );
1439 if ( params.geometryName ==
"EXTENT"_L1 )
1444 else if ( params.geometryName ==
"CENTROID"_L1 )
1450 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1454 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1457 QDomElement featureElement = doc.createElement( u
"gml:featureMember"_s );
1460 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1462 typeNameElement.setAttribute( u
"fid"_s,
id );
1463 featureElement.appendChild( typeNameElement );
1466 QgsGeometry geom = feature.
geometry();
1467 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1469 int prec = params.precision;
1470 QgsCoordinateReferenceSystem crs = params.crs;
1471 QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1474 QgsGeometry transformed = geom;
1478 crs = params.outputCrs;
1479 if ( crs.
isGeographic() && !params.crs.isGeographic() )
1480 prec = std::min( params.precision + 3, 6 );
1483 catch ( QgsCsException &cse )
1488 QDomElement geomElem = doc.createElement( u
"qgs:geometry"_s );
1489 QDomElement gmlElem;
1490 QgsGeometry cloneGeom( geom );
1491 if ( params.geometryName ==
"EXTENT"_L1 )
1495 else if ( params.geometryName ==
"CENTROID"_L1 )
1501 cloneGeom.convertToMultiType();
1503 const QgsAbstractGeometry *abstractGeom = cloneGeom.constGet();
1506 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1509 if ( !gmlElem.isNull() )
1512 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1521 bbElem.appendChild( boxElem );
1522 typeNameElement.appendChild( bbElem );
1524 geomElem.appendChild( gmlElem );
1525 typeNameElement.appendChild( geomElem );
1530 const QgsAttributes featureAttributes = feature.
attributes();
1531 const QgsFields fields = feature.
fields();
1532 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1534 int idx = params.attributeIndexes[i];
1540 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1541 typeNameElement.appendChild( fieldElem );
1544 return featureElement;
1547 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1550 QDomElement featureElement = doc.createElement( u
"gml:featureMember"_s );
1553 QDomElement typeNameElement = doc.createElement( u
"qgs:"_s + params.typeName );
1555 typeNameElement.setAttribute( u
"gml:id"_s,
id );
1556 featureElement.appendChild( typeNameElement );
1559 QgsGeometry geom = feature.
geometry();
1560 if ( !geom.
isNull() && params.withGeom && params.geometryName !=
"NONE"_L1 )
1562 int prec = params.precision;
1563 QgsCoordinateReferenceSystem crs = params.crs;
1564 QgsCoordinateTransform mTransform( crs, params.outputCrs, project );
1567 QgsGeometry transformed = geom;
1571 crs = params.outputCrs;
1572 if ( crs.
isGeographic() && !params.crs.isGeographic() )
1573 prec = std::min( params.precision + 3, 6 );
1576 catch ( QgsCsException &cse )
1581 QDomElement geomElem = doc.createElement( u
"qgs:geometry"_s );
1582 QDomElement gmlElem;
1583 QgsGeometry cloneGeom( geom );
1584 if ( params.geometryName ==
"EXTENT"_L1 )
1588 else if ( params.geometryName ==
"CENTROID"_L1 )
1594 cloneGeom.convertToMultiType();
1596 const QgsAbstractGeometry *abstractGeom = cloneGeom.constGet();
1602 if ( !gmlElem.isNull() )
1605 QDomElement bbElem = doc.createElement( u
"gml:boundedBy"_s );
1608 if ( crs.
isValid() && params.srsName.isEmpty() )
1613 else if ( !params.srsName.isEmpty() )
1615 gmlElem.setAttribute( u
"srsName"_s, params.srsName );
1618 bbElem.appendChild( boxElem );
1619 typeNameElement.appendChild( bbElem );
1621 geomElem.appendChild( gmlElem );
1622 typeNameElement.appendChild( geomElem );
1627 const QgsAttributes featureAttributes = feature.
attributes();
1628 const QgsFields fields = feature.
fields();
1629 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1631 int idx = params.attributeIndexes[i];
1637 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1638 typeNameElement.appendChild( fieldElem );
1641 return featureElement;
1644 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1647 const thread_local QRegularExpression sCleanTagNameRegExp( u
"[^\\w\\.-_]"_s, QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1648 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1649 QDomElement fieldElem = doc.createElement( u
"qgs:"_s + attributeName );
1652 fieldElem.setAttribute( u
"xsi:nil"_s, u
"true"_s );
1656 const QString fieldText = encodeValueToText( value, setup );
1658 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1660 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1664 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1670 QString encodeValueToText(
const QVariant &value,
const QgsEditorWidgetSetup &setup )
1675 if ( setup.
type() ==
"DateTime"_L1 )
1678 if ( value.userType() == QMetaType::Type::QTime )
1684 const QVariantMap config = setup.
config();
1687 const QString fieldFormat = config.value( u
"field_iso_format"_s,
false ).toBool()
1692 QDateTime date = value.toDateTime();
1694 if ( !date.isValid() )
1696 date = QDateTime::fromString( value.toString(), fieldFormat );
1699 if ( date.isValid() )
1701 return date.toString( fieldFormat );
1704 return value.toString();
1706 else if ( setup.
type() ==
"Range"_L1 )
1708 const QVariantMap config = setup.
config();
1709 if ( config.contains( u
"Precision"_s ) )
1713 int precision( config[u
"Precision"_s].toInt( &ok ) );
1715 return QString::number( value.toDouble(),
'f', precision );
1719 switch ( value.userType() )
1721 case QMetaType::Type::Int:
1722 case QMetaType::Type::UInt:
1723 case QMetaType::Type::LongLong:
1724 case QMetaType::Type::ULongLong:
1725 case QMetaType::Type::Double:
1726 return value.toString();
1728 case QMetaType::Type::Bool:
1729 return value.toBool() ? u
"true"_s : u
"false"_s;
1731 case QMetaType::Type::QStringList:
1732 case QMetaType::Type::QVariantList:
1733 case QMetaType::Type::QVariantMap:
1737 case QMetaType::Type::QString:
1738 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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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