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 );
91 QgsWfsParameters mWfsParameters;
102 mWfsParameters.dump();
108 if ( doc.setContent( request.
data(),
true, &errorMsg ) )
110 QDomElement docElem = doc.documentElement();
119 QStringList typeNameList;
122 bool onlyOneLayer = ( aRequest.
queries.size() == 1 );
125 int requestPrecision = 6;
129 QList<getFeatureQuery>::iterator qIt = aRequest.
queries.begin();
130 for ( ; qIt != aRequest.
queries.end(); ++qIt )
132 typeNameList << ( *qIt ).typeName;
138 QMap<QString, QgsMapLayer *> mapLayerMap;
139 for (
int i = 0; i < wfsLayerIds.size(); ++i )
153 if ( typeNameList.contains( name ) )
156 mapLayerMap[name] = layer;
160 requestRect = layer->
extent();
161 requestCrs = layer->
crs();
180 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
187 for (
const QString &
typeName : typeNameList )
189 if ( !mapLayerMap.contains(
typeName ) )
195#ifdef HAVE_SERVER_PYTHON_PLUGINS
199 auto filterRestorer = std::make_unique<QgsOWSServerFilterRestorer>();
201 ( void ) serverIface;
205 long sentFeatures = 0;
206 long iteratedFeatures = 0;
209 qIt = aRequest.
queries.begin();
210 for ( ; qIt != aRequest.
queries.end(); ++qIt )
216#ifdef HAVE_SERVER_PYTHON_PLUGINS
234#ifdef HAVE_SERVER_PYTHON_PLUGINS
241 QMap<int, QString> layerAliasInfo;
243 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
244 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
247 if ( attrIndex != -1 )
249 layerAliasInfo.insert( attrIndex, aliasIt.value() );
260 if ( !propertyList.isEmpty() && propertyList.first() != QLatin1String(
"*" ) )
263 QStringList::const_iterator plstIt;
266 QList<QString> propertynames;
267 QList<QString> fieldnames;
268 for (
const QgsField &field : fields )
270 fieldnames.
append( field.name() );
271 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
272 propertynames.append( field.name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() ) );
275 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
278 int fieldNameIdx = propertynames.indexOf( fieldName );
279 if ( fieldNameIdx == -1 )
281 fieldNameIdx = fieldnames.indexOf( fieldName );
283 if ( fieldNameIdx > -1 )
285 idxList.append( fieldNameIdx );
287 else if ( fieldName == QLatin1String(
"geometry" ) )
292 if ( !idxList.isEmpty() )
294 attrIndexes = idxList;
299 if ( !attrIndexes.isEmpty() )
301 for (
const QgsField &field : fields )
305 int fieldNameIdx = fields.
indexOf( field.name() );
306 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
308 attrIndexes.removeOne( fieldNameIdx );
341#ifdef HAVE_SERVER_PYTHON_PLUGINS
362 QStringList attributes = QStringList();
363 for (
int idx : std::as_const( attrIndexes ) )
377 if ( !pkAttributes.isEmpty() )
380 for (
int idx : pkAttributes )
382 if ( !subsetOfAttrs.contains( idx ) )
384 subsetOfAttrs.prepend( idx );
419 QString outputSrsName;
420 if ( !query.
srsName.isEmpty() )
424 else if ( !requestSrsName.isEmpty() )
426 outputSrsName = requestSrsName;
468 if ( iteratedFeatures >= aRequest.
startIndex )
491 if ( iteratedFeatures == aRequest.
startIndex )
492 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
494 if ( iteratedFeatures >= aRequest.
startIndex )
504#ifdef HAVE_SERVER_PYTHON_PLUGINS
506 filterRestorer.reset();
511 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
516 if ( iteratedFeatures <= aRequest.
startIndex )
517 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
525 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
526 request.
startIndex = mWfsParameters.startIndexAsInt();
530 QStringList fidList = mWfsParameters.featureIds();
531 bool paramContainsFeatureIds = !fidList.isEmpty();
532 QStringList filterList = mWfsParameters.filters();
533 bool paramContainsFilters = !filterList.isEmpty();
534 QString bbox = mWfsParameters.bbox();
535 bool paramContainsBbox = !bbox.isEmpty();
536 if ( ( paramContainsFeatureIds
537 && ( paramContainsFilters || paramContainsBbox ) )
538 || ( paramContainsFilters && ( paramContainsFeatureIds || paramContainsBbox ) )
539 || ( paramContainsBbox && ( paramContainsFeatureIds || paramContainsFilters ) ) )
545 QStringList propertyNameList = mWfsParameters.propertyNames();
548 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
550 QStringList typeNameList;
552 if ( paramContainsFeatureIds )
555 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
559 if ( propertyNameList.isEmpty() )
561 for (
int i = 0; i < fidList.size(); ++i )
563 propertyNameList << QStringLiteral(
"*" );
567 QMap<QString, QStringList> fidsMap;
569 QStringList::const_iterator fidIt = fidList.constBegin();
570 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
571 for ( ; fidIt != fidList.constEnd(); ++fidIt )
574 QString fid = *fidIt;
577 QString propertyName;
578 if ( propertyNameIt != propertyNameList.constEnd() )
580 propertyName = *propertyNameIt;
583 if ( !fid.contains(
'.' ) )
588 QString
typeName = fid.section(
'.', 0, 0 );
589 fid = fid.section(
'.', 1, 1 );
590 if ( !typeNameList.contains(
typeName ) )
598 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
600 if ( fidsMap.contains( key ) )
602 fids = fidsMap.value( key );
605 fidsMap.insert( key, fids );
607 if ( propertyNameIt != propertyNameList.constEnd() )
613 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
614 while ( fidsMapIt != fidsMap.constEnd() )
616 QString key = fidsMapIt.key();
620 const QString
typeName = key.section(
':', 0, 0 );
621 const QString propertyName = key.section(
':', 1, 1 );
625 query.
srsName = mWfsParameters.srsName();
628 if ( propertyName != QLatin1String(
"*" ) )
630 QStringList propertyList;
632 const QStringList attrList = propertyName.split(
',' );
633 QStringList::const_iterator alstIt;
634 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
636 QString fieldName = *alstIt;
637 fieldName = fieldName.trimmed();
638 if ( fieldName.contains(
':' ) )
640 fieldName = fieldName.section(
':', 1, 1 );
642 if ( fieldName.contains(
'/' ) )
644 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
648 fieldName = fieldName.section(
'/', 1, 1 );
650 propertyList.append( fieldName );
659 request.
queries.append( query );
665 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
670 typeNameList = mWfsParameters.typeNames();
672 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
676 if ( propertyNameList.isEmpty() )
678 for (
int i = 0; i < typeNameList.size(); ++i )
680 propertyNameList << QStringLiteral(
"*" );
685 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
686 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
687 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
692 QString propertyName;
693 if ( propertyNameIt != propertyNameList.constEnd() )
695 propertyName = *propertyNameIt;
700 query.
srsName = mWfsParameters.srsName();
703 if ( propertyName != QLatin1String(
"*" ) )
705 QStringList propertyList;
707 const QStringList attrList = propertyName.split(
',' );
708 QStringList::const_iterator alstIt;
709 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
711 QString fieldName = *alstIt;
712 fieldName = fieldName.trimmed();
713 if ( fieldName.contains(
':' ) )
715 fieldName = fieldName.section(
':', 1, 1 );
717 if ( fieldName.contains(
'/' ) )
719 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
723 fieldName = fieldName.section(
'/', 1, 1 );
725 propertyList.append( fieldName );
730 request.
queries.append( query );
732 if ( propertyNameIt != propertyNameList.constEnd() )
739 QStringList expFilterList = mWfsParameters.expFilters();
740 if ( !expFilterList.isEmpty() )
743 if ( request.
queries.size() == expFilterList.size() )
746 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
747 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
748 for ( ; qIt != request.
queries.end(); ++qIt )
752 const QString expFilter = *expFilterIt++;
753 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
756 if ( filter->hasParserError() )
760 if ( filter->needsGeometry() )
774 if ( paramContainsBbox )
779 QString extentSrsName { mWfsParameters.srsName() };
782 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && !mWfsParameters.srsName().isEmpty() )
784 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
785 if (
crs != mWfsParameters.srsName() )
815 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && !extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
819 extent = geom.boundingBox();
823 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
824 for ( ; qIt != request.
queries.end(); ++qIt )
831 else if ( paramContainsFilters )
834 if ( request.
queries.size() != filterList.size() )
840 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
841 QStringList::const_iterator filterIt = filterList.constBegin();
842 for ( ; qIt != request.
queries.end(); ++qIt )
847 if ( filterIt != filterList.constEnd() )
850 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
856 QDomElement filterElem = filter.firstChildElement();
857 QStringList serverFids;
861 if ( filterIt != filterList.constEnd() )
869 QStringList sortByList = mWfsParameters.sortBy();
870 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
873 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
874 QStringList::const_iterator sortByIt = sortByList.constBegin();
875 for ( ; qIt != request.
queries.end(); ++qIt )
880 if ( sortByIt != sortByList.constEnd() )
884 for (
const QString &attribute : sortBy.split(
',' ) )
886 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
890 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
894 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
898 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
916 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
917 request.
startIndex = mWfsParameters.startIndexAsInt();
920 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
921 QDomElement queryElem;
922 for (
int i = 0; i < queryNodes.size(); i++ )
924 queryElem = queryNodes.at( i ).toElement();
926 request.
queries.append( query );
933 QDomNodeList sortByNodes = sortByElem.childNodes();
934 if ( sortByNodes.size() )
936 for (
int i = 0; i < sortByNodes.size(); i++ )
938 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
939 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
940 if ( sortPropChildNodes.size() )
943 bool ascending =
true;
944 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
946 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
947 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
949 fieldName = sortPropChildElem.text().trimmed();
951 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
953 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
954 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
959 if ( fieldName.contains(
':' ) )
961 fieldName = fieldName.section(
':', 1, 1 );
963 if ( fieldName.contains(
'/' ) )
965 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
969 fieldName = fieldName.section(
'/', 1, 1 );
972 if ( !fieldName.isEmpty() )
973 featureRequest.
addOrderBy( fieldName, ascending );
981 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
988 QStringList serverFids;
989 QStringList propertyList;
990 QDomNodeList queryChildNodes = queryElem.childNodes();
991 if ( queryChildNodes.size() )
993 QDomElement sortByElem;
994 for (
int q = 0; q < queryChildNodes.size(); q++ )
996 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
997 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
999 QString fieldName = queryChildElem.text().trimmed();
1000 if ( fieldName.contains(
':' ) )
1002 fieldName = fieldName.section(
':', 1, 1 );
1004 if ( fieldName.contains(
'/' ) )
1006 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
1010 fieldName = fieldName.section(
'/', 1, 1 );
1012 propertyList.append( fieldName );
1014 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
1018 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
1020 sortByElem = queryChildElem;
1027 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1040 static QSet<QString> sParamFilter {
1041 QStringLiteral(
"REQUEST" ),
1042 QStringLiteral(
"FORMAT" ),
1043 QStringLiteral(
"OUTPUTFORMAT" ),
1044 QStringLiteral(
"BBOX" ),
1045 QStringLiteral(
"FEATUREID" ),
1046 QStringLiteral(
"TYPENAME" ),
1047 QStringLiteral(
"FILTER" ),
1048 QStringLiteral(
"EXP_FILTER" ),
1049 QStringLiteral(
"MAXFEATURES" ),
1050 QStringLiteral(
"STARTINDEX" ),
1051 QStringLiteral(
"PROPERTYNAME" ),
1052 QStringLiteral(
"_DC" )
1058 QDateTime now = QDateTime::currentDateTime();
1063 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1064 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1065 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1066 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1067 fcString += QLatin1Char(
'}' );
1072 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1074 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1077 QString hrefString =
serviceUrl( request, project, *settings );
1079 QUrl mapUrl( hrefString );
1081 QUrlQuery query( mapUrl );
1082 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1084 if ( mWfsParameters.version().isEmpty() )
1087 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1089 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1091 const auto constItems { query.queryItems() };
1092 for (
const auto ¶m : std::as_const( constItems ) )
1094 if ( sParamFilter.contains( param.first.toUpper() ) )
1095 query.removeAllQueryItems( param.first );
1098 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1099 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1103 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1105 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1108 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1110 mapUrl.setQuery( query );
1112 hrefString = mapUrl.toString();
1115 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1116 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1118 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1121 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1125 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1126 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1128 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1129 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1130 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1131 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1132 fcString += QLatin1String(
">\n" );
1133 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1136 response.
write( fcString.toUtf8() );
1144 std::unique_ptr<QgsRectangle> transformedRect;
1148 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1161 rect = transformedRect.get();
1172 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1177 if ( !destinationCrs.isValid() )
1179 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg(
srsName ) );
1184 for (
const auto &it : value.items() )
1186 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1189 fcString += QLatin1String(
" \"features\": [\n" );
1190 response.
write( fcString.toUtf8() );
1195 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1197 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1200 QString hrefString =
serviceUrl( request, project, *settings );
1202 QUrl mapUrl( hrefString );
1204 QUrlQuery query( mapUrl );
1205 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1207 if ( mWfsParameters.version().isEmpty() )
1210 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1212 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1214 const auto queryItems { query.queryItems() };
1215 for (
auto param : std::as_const( queryItems ) )
1217 if ( sParamFilter.contains( param.first.toUpper() ) )
1218 query.removeAllQueryItems( param.first );
1221 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1222 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1226 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1228 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1231 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1233 mapUrl.setQuery( query );
1235 hrefString = mapUrl.toString();
1238 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1239 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1241 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1244 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1248 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1249 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1251 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1252 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1253 fcString += QLatin1String(
">\n" );
1255 response.
write( fcString.toUtf8() );
1259 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1264 const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName :
crs.
authid();
1290 if ( !envElem.isNull() )
1292 if (
crs.
isValid() && outputSrsName.isEmpty() )
1294 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1296 bbElem.appendChild( envElem );
1297 doc.appendChild( bbElem );
1303 if ( !boxElem.isNull() )
1307 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1309 bbElem.appendChild( boxElem );
1310 doc.appendChild( bbElem );
1313 response.
write( doc.toByteArray() );
1327 fcString += QLatin1String(
" " );
1329 fcString += QLatin1String(
" ," );
1332 if ( !destinationCrs.isValid() )
1334 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg( params.srsName ) );
1337 mJsonExporter.setDestinationCrs( destinationCrs );
1338 mJsonExporter.setTransformGeometries(
true );
1339 mJsonExporter.setSourceCrs( params.crs );
1340 mJsonExporter.setIncludeGeometry(
false );
1341 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1342 mJsonExporter.setAttributes( params.attributeIndexes );
1343 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1344 fcString += QLatin1String(
"\n" );
1346 response.
write( fcString.toUtf8() );
1350 QDomDocument gmlDoc;
1351 QDomElement featureElement;
1354 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1355 gmlDoc.appendChild( featureElement );
1359 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1360 gmlDoc.appendChild( featureElement );
1362 response.
write( gmlDoc.toByteArray() );
1374 fcString += QLatin1String(
" ]\n" );
1375 fcString += QLatin1Char(
'}' );
1379 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1381 response.
write( fcString.toUtf8() );
1385 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1395 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1397 mJsonExporter.setIncludeGeometry(
true );
1398 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1403 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1409 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1413 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1416 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1419 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1421 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1422 featureElement.appendChild( typeNameElement );
1426 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1428 int prec = params.precision;
1437 crs = params.outputCrs;
1439 prec = std::min( params.precision + 3, 6 );
1447 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1448 QDomElement gmlElem;
1450 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1454 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1465 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1468 if ( !gmlElem.isNull() )
1471 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1476 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1477 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1480 bbElem.appendChild( boxElem );
1481 typeNameElement.appendChild( bbElem );
1483 geomElem.appendChild( gmlElem );
1484 typeNameElement.appendChild( geomElem );
1491 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1493 int idx = params.attributeIndexes[i];
1499 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1500 typeNameElement.appendChild( fieldElem );
1503 return featureElement;
1506 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1509 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1512 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1514 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1515 featureElement.appendChild( typeNameElement );
1519 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1521 int prec = params.precision;
1530 crs = params.outputCrs;
1532 prec = std::min( params.precision + 3, 6 );
1540 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1541 QDomElement gmlElem;
1543 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1547 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1561 if ( !gmlElem.isNull() )
1564 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1567 if (
crs.
isValid() && params.srsName.isEmpty() )
1569 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1570 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1572 else if ( !params.srsName.isEmpty() )
1574 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1577 bbElem.appendChild( boxElem );
1578 typeNameElement.appendChild( bbElem );
1580 geomElem.appendChild( gmlElem );
1581 typeNameElement.appendChild( geomElem );
1588 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1590 int idx = params.attributeIndexes[i];
1596 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1597 typeNameElement.appendChild( fieldElem );
1600 return featureElement;
1603 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1606 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1607 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1608 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1611 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1615 const QString fieldText = encodeValueToText( value, setup );
1617 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1619 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1623 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1634 if ( setup.
type() == QLatin1String(
"DateTime" ) )
1637 if ( value.userType() == QMetaType::Type::QTime )
1643 const QVariantMap config = setup.
config();
1649 QDateTime date = value.toDateTime();
1651 if ( !date.isValid() )
1653 date = QDateTime::fromString( value.toString(), fieldFormat );
1656 if ( date.isValid() )
1658 return date.toString( fieldFormat );
1661 return value.toString();
1663 else if ( setup.
type() == QLatin1String(
"Range" ) )
1665 const QVariantMap config = setup.
config();
1666 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1670 int precision( config[QStringLiteral(
"Precision" )].toInt( &ok ) );
1672 return QString::number( value.toDouble(),
'f',
precision );
1676 switch ( value.userType() )
1678 case QMetaType::Type::Int:
1679 case QMetaType::Type::UInt:
1680 case QMetaType::Type::LongLong:
1681 case QMetaType::Type::ULongLong:
1682 case QMetaType::Type::Double:
1683 return value.toString();
1685 case QMetaType::Type::Bool:
1686 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1688 case QMetaType::Type::QStringList:
1689 case QMetaType::Type::QVariantList:
1690 case QMetaType::Type::QVariantMap:
1694 case QMetaType::Type::QString:
1695 return value.toString();
@ Success
Operation succeeded.
@ Fid
Filter using feature ID.
@ Fids
Filter using feature IDs.
@ NoFilter
No filter is applied.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ NoFlags
No flags are set.
@ HideFromWfs
Field is not available if layer is served as WFS from QGIS server.
Abstract base class for all geometries.
@ YX
Y comes before X (or lat before lon)
virtual QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML2 representation of the geometry.
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.
A helper class that centralizes restrictions given by all the access control filter plugins.
QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const override
Returns the authorized layer attributes.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
bool hasAxisInverted() const
Returns whether the axis order is inverted for the CRS compared to the order east/north (longitude/la...
Custom exception class for Coordinate Reference System related exceptions.
Defines a QGIS exception class.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
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.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Handles exporting QgsFeature features to GeoJSON features.
static Q_INVOKABLE QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
static void addCrsInfo(json &value, const QgsCoordinateReferenceSystem &crs)
Add crs information entry in json object regarding old GeoJSON specification format if it differs fro...
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, 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...
Provides a way to retrieve settings by prioritizing according to environment variables,...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Exception thrown when data access violates access controls.
Provides an interface to retrieve and manipulate WFS parameters received from the client.
Format
Output format for the response.
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
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.
#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
const QString & geometryName
const QgsCoordinateReferenceSystem & outputCrs
const QgsCoordinateReferenceSystem & crs
const QgsAttributeList & attributeIndexes
QgsFeatureRequest featureRequest
QList< getFeatureQuery > queries
QgsWfsParameters::Format outputFormat