42#include <QRegularExpression>
49 struct createFeatureParams
72 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes );
74 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc );
78 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
80 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
95 QgsWfsParameters mWfsParameters;
108 mWfsParameters.dump();
114 if ( doc.setContent( request.
data(),
true, &errorMsg ) )
116 QDomElement docElem = doc.documentElement();
125 QStringList typeNameList;
128 bool onlyOneLayer = ( aRequest.
queries.size() == 1 );
131 int requestPrecision = 6;
135 QList<getFeatureQuery>::iterator qIt = aRequest.
queries.begin();
136 for ( ; qIt != aRequest.
queries.end(); ++qIt )
138 typeNameList << ( *qIt ).typeName;
144 QMap<QString, QgsMapLayer *> mapLayerMap;
145 for (
int i = 0; i < wfsLayerIds.size(); ++i )
159 if ( typeNameList.contains( name ) )
162 mapLayerMap[name] = layer;
166 requestRect = layer->
extent();
167 requestCrs = layer->
crs();
186 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
193 for (
const QString &
typeName : typeNameList )
195 if ( !mapLayerMap.contains(
typeName ) )
201#ifdef HAVE_SERVER_PYTHON_PLUGINS
211 long sentFeatures = 0;
212 long iteratedFeatures = 0;
215 qIt = aRequest.
queries.begin();
216 for ( ; qIt != aRequest.
queries.end(); ++qIt )
222#ifdef HAVE_SERVER_PYTHON_PLUGINS
240#ifdef HAVE_SERVER_PYTHON_PLUGINS
247 QMap< int, QString > layerAliasInfo;
249 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
250 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
253 if ( attrIndex != -1 )
255 layerAliasInfo.insert( attrIndex, aliasIt.value() );
266 if ( !propertyList.isEmpty() && propertyList.first() != QLatin1String(
"*" ) )
269 QStringList::const_iterator plstIt;
272 QList<QString> propertynames;
273 QList<QString> fieldnames;
277 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
278 propertynames.append(
field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() ) );
281 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
284 int fieldNameIdx = propertynames.indexOf( fieldName );
285 if ( fieldNameIdx == -1 )
287 fieldNameIdx = fieldnames.indexOf( fieldName );
289 if ( fieldNameIdx > -1 )
291 idxList.append( fieldNameIdx );
293 else if ( fieldName == QLatin1String(
"geometry" ) )
298 if ( !idxList.isEmpty() )
300 attrIndexes = idxList;
305 if ( !attrIndexes.isEmpty() )
312 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
314 attrIndexes.removeOne( fieldNameIdx );
347#ifdef HAVE_SERVER_PYTHON_PLUGINS
366 QStringList attributes = QStringList();
367 for (
int idx : std::as_const( attrIndexes ) )
380 if ( !pkAttributes.isEmpty() )
383 for (
int idx : pkAttributes )
385 if ( !subsetOfAttrs.contains( idx ) )
387 subsetOfAttrs.prepend( idx );
418 if ( !query.
srsName.isEmpty() )
453 if ( iteratedFeatures >= aRequest.
startIndex )
465 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
467 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
469 const createFeatureParams cfp = { layerPrecision,
486 if ( iteratedFeatures == aRequest.
startIndex )
487 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
489 if ( iteratedFeatures >= aRequest.
startIndex )
499#ifdef HAVE_SERVER_PYTHON_PLUGINS
501 filterRestorer.reset();
506 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
511 if ( iteratedFeatures <= aRequest.
startIndex )
512 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
521 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
522 request.
startIndex = mWfsParameters.startIndexAsInt();
526 QStringList fidList = mWfsParameters.featureIds();
527 bool paramContainsFeatureIds = !fidList.isEmpty();
528 QStringList filterList = mWfsParameters.filters();
529 bool paramContainsFilters = !filterList.isEmpty();
530 QString bbox = mWfsParameters.bbox();
531 bool paramContainsBbox = !bbox.isEmpty();
532 if ( ( paramContainsFeatureIds
533 && ( paramContainsFilters || paramContainsBbox ) )
534 || ( paramContainsFilters
535 && ( paramContainsFeatureIds || paramContainsBbox ) )
536 || ( paramContainsBbox
537 && ( paramContainsFeatureIds || paramContainsFilters ) )
544 QStringList propertyNameList = mWfsParameters.propertyNames();
547 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
549 QStringList typeNameList;
551 if ( paramContainsFeatureIds )
554 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
558 if ( propertyNameList.isEmpty() )
560 for (
int i = 0; i < fidList.size(); ++i )
562 propertyNameList << QStringLiteral(
"*" );
566 QMap<QString, QStringList> fidsMap;
568 QStringList::const_iterator fidIt = fidList.constBegin();
569 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
570 for ( ; fidIt != fidList.constEnd(); ++fidIt )
573 QString fid = *fidIt;
576 QString propertyName;
577 if ( propertyNameIt != propertyNameList.constEnd() )
579 propertyName = *propertyNameIt;
582 if ( !fid.contains(
'.' ) )
587 QString
typeName = fid.section(
'.', 0, 0 );
588 fid = fid.section(
'.', 1, 1 );
589 if ( !typeNameList.contains(
typeName ) )
597 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
599 if ( fidsMap.contains( key ) )
601 fids = fidsMap.value( key );
604 fidsMap.insert( key, fids );
606 if ( propertyNameIt != propertyNameList.constEnd() )
612 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
613 while ( fidsMapIt != fidsMap.constEnd() )
615 QString key = fidsMapIt.key();
619 const QString
typeName = key.section(
':', 0, 0 );
620 const QString propertyName = key.section(
':', 1, 1 );
624 query.
srsName = mWfsParameters.srsName();
627 if ( propertyName != QLatin1String(
"*" ) )
629 QStringList propertyList;
631 const QStringList attrList = propertyName.split(
',' );
632 QStringList::const_iterator alstIt;
633 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
635 QString fieldName = *alstIt;
636 fieldName = fieldName.trimmed();
637 if ( fieldName.contains(
':' ) )
639 fieldName = fieldName.section(
':', 1, 1 );
641 if ( fieldName.contains(
'/' ) )
643 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
647 fieldName = fieldName.section(
'/', 1, 1 );
649 propertyList.append( fieldName );
658 request.
queries.append( query );
664 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
669 typeNameList = mWfsParameters.typeNames();
671 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
675 if ( propertyNameList.isEmpty() )
677 for (
int i = 0; i < typeNameList.size(); ++i )
679 propertyNameList << QStringLiteral(
"*" );
684 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
685 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
686 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
691 QString propertyName;
692 if ( propertyNameIt != propertyNameList.constEnd() )
694 propertyName = *propertyNameIt;
699 query.
srsName = mWfsParameters.srsName();
702 if ( propertyName != QLatin1String(
"*" ) )
704 QStringList propertyList;
706 const QStringList attrList = propertyName.split(
',' );
707 QStringList::const_iterator alstIt;
708 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
710 QString fieldName = *alstIt;
711 fieldName = fieldName.trimmed();
712 if ( fieldName.contains(
':' ) )
714 fieldName = fieldName.section(
':', 1, 1 );
716 if ( fieldName.contains(
'/' ) )
718 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
722 fieldName = fieldName.section(
'/', 1, 1 );
724 propertyList.append( fieldName );
729 request.
queries.append( query );
731 if ( propertyNameIt != propertyNameList.constEnd() )
738 QStringList expFilterList = mWfsParameters.expFilters();
739 if ( !expFilterList.isEmpty() )
742 if ( request.
queries.size() == expFilterList.size() )
745 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
746 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
747 for ( ; qIt != request.
queries.end(); ++qIt )
751 const QString expFilter = *expFilterIt++;
752 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
755 if ( filter->hasParserError() )
759 if ( filter->needsGeometry() )
773 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
1042 QStringLiteral(
"REQUEST" ),
1043 QStringLiteral(
"FORMAT" ),
1044 QStringLiteral(
"OUTPUTFORMAT" ),
1045 QStringLiteral(
"BBOX" ),
1046 QStringLiteral(
"FEATUREID" ),
1047 QStringLiteral(
"TYPENAME" ),
1048 QStringLiteral(
"FILTER" ),
1049 QStringLiteral(
"EXP_FILTER" ),
1050 QStringLiteral(
"MAXFEATURES" ),
1051 QStringLiteral(
"STARTINDEX" ),
1052 QStringLiteral(
"PROPERTYNAME" ),
1053 QStringLiteral(
"_DC" )
1058 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1060 QDateTime now = QDateTime::currentDateTime();
1065 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1066 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1067 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1068 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1069 fcString += QLatin1Char(
'}' );
1074 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1076 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1079 QString hrefString =
serviceUrl( request, project, *settings );
1081 QUrl mapUrl( hrefString );
1083 QUrlQuery query( mapUrl );
1084 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1086 if ( mWfsParameters.version().isEmpty() )
1089 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1091 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1093 for (
auto param : query.queryItems() )
1095 if ( sParamFilter.contains( param.first.toUpper() ) )
1096 query.removeAllQueryItems( param.first );
1099 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1100 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1104 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1106 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1109 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1111 mapUrl.setQuery( query );
1113 hrefString = mapUrl.toString();
1116 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1117 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1119 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1122 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1126 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1127 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1129 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1130 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1131 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1132 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1133 fcString += QLatin1String(
">\n" );
1134 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1137 response.
write( fcString.toUtf8() );
1146 std::unique_ptr< QgsRectangle > transformedRect;
1150 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1163 rect = transformedRect.get();
1174 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1176 fcString += QLatin1String(
" \"features\": [\n" );
1177 response.
write( fcString.toUtf8() );
1182 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1184 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1187 QString hrefString =
serviceUrl( request, project, *settings );
1189 QUrl mapUrl( hrefString );
1191 QUrlQuery query( mapUrl );
1192 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1194 if ( mWfsParameters.version().isEmpty() )
1197 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1199 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1201 const auto queryItems {query.queryItems()};
1202 for (
auto param : std::as_const( queryItems ) )
1204 if ( sParamFilter.contains( param.first.toUpper() ) )
1205 query.removeAllQueryItems( param.first );
1208 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1209 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1213 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1215 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1218 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1220 mapUrl.setQuery( query );
1222 hrefString = mapUrl.toString();
1225 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1226 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1228 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1231 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1235 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1236 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1238 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1239 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1240 fcString += QLatin1String(
">\n" );
1242 response.
write( fcString.toUtf8() );
1246 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1251 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1253 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1271 if ( !envElem.isNull() )
1275 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1277 bbElem.appendChild( envElem );
1278 doc.appendChild( bbElem );
1284 if ( !boxElem.isNull() )
1288 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1290 bbElem.appendChild( boxElem );
1291 doc.appendChild( bbElem );
1294 response.
write( doc.toByteArray() );
1309 fcString += QLatin1String(
" " );
1311 fcString += QLatin1String(
" ," );
1312 mJsonExporter.setSourceCrs( params.crs );
1313 mJsonExporter.setIncludeGeometry(
false );
1314 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1315 mJsonExporter.setAttributes( params.attributeIndexes );
1316 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1317 fcString += QLatin1String(
"\n" );
1319 response.
write( fcString.toUtf8() );
1323 QDomDocument gmlDoc;
1324 QDomElement featureElement;
1327 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1328 gmlDoc.appendChild( featureElement );
1332 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1333 gmlDoc.appendChild( featureElement );
1335 response.
write( gmlDoc.toByteArray() );
1347 fcString += QLatin1String(
" ]\n" );
1348 fcString += QLatin1Char(
'}' );
1352 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1354 response.
write( fcString.toUtf8() );
1358 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1368 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1370 mJsonExporter.setIncludeGeometry(
true );
1371 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1376 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1382 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1386 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1389 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1392 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1394 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1395 featureElement.appendChild( typeNameElement );
1399 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1401 int prec = params.precision;
1410 crs = params.outputCrs;
1412 prec = std::min( params.precision + 3, 6 );
1420 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1421 QDomElement gmlElem;
1423 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1427 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1438 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1441 if ( !gmlElem.isNull() )
1444 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1449 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1450 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1453 bbElem.appendChild( boxElem );
1454 typeNameElement.appendChild( bbElem );
1456 geomElem.appendChild( gmlElem );
1457 typeNameElement.appendChild( geomElem );
1464 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1466 int idx = params.attributeIndexes[i];
1467 if ( idx >= fields.
count() )
1472 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1473 typeNameElement.appendChild( fieldElem );
1476 return featureElement;
1479 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1482 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1485 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1487 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1488 featureElement.appendChild( typeNameElement );
1492 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1494 int prec = params.precision;
1503 crs = params.outputCrs;
1505 prec = std::min( params.precision + 3, 6 );
1513 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1514 QDomElement gmlElem;
1516 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1520 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1534 if ( !gmlElem.isNull() )
1537 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1540 if (
crs.
isValid() && params.srsName.isEmpty() )
1542 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1543 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1545 else if ( !params.srsName.isEmpty() )
1547 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1550 bbElem.appendChild( boxElem );
1551 typeNameElement.appendChild( bbElem );
1553 geomElem.appendChild( gmlElem );
1554 typeNameElement.appendChild( geomElem );
1561 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1563 int idx = params.attributeIndexes[i];
1564 if ( idx >= fields.
count() )
1569 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1570 typeNameElement.appendChild( fieldElem );
1573 return featureElement;
1576 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc )
1579 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1580 const QString attributeName =
field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1581 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1584 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1588 const QString fieldText = encodeValueToText( value, setup );
1590 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1592 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1596 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1607 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1610 if ( value.type() == QVariant::Time )
1616 const QVariantMap config = setup.
config();
1619 const QString fieldFormat =
1620 config.value( QStringLiteral(
"field_iso_format" ),
false ).toBool() ?
1625 QDateTime date = value.toDateTime();
1627 if ( !date.isValid() )
1629 date = QDateTime::fromString( value.toString(), fieldFormat );
1632 if ( date.isValid() )
1634 return date.toString( fieldFormat );
1637 return value.toString();
1639 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1641 const QVariantMap config = setup.
config();
1642 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1646 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1648 return QString::number( value.toDouble(),
'f',
precision );
1652 switch ( value.type() )
1655 case QVariant::UInt:
1656 case QVariant::LongLong:
1657 case QVariant::ULongLong:
1658 case QVariant::Double:
1659 return value.toString();
1661 case QVariant::Bool:
1662 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1664 case QVariant::StringList:
1665 case QVariant::List:
1670 case QVariant::String:
1671 return value.toString();
@ Success
Operation succeeded.
Abstract base class for all geometries.
@ YX
Y comes before X (or lat before lon)
virtual QDomElement asGml2(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML2 representation of the geometry.
virtual void swapXy()=0
Swaps the x and y coordinates from the geometry.
virtual QDomElement asGml3(QDomDocument &doc, int precision=17, const QString &ns="gml", AxisOrder axisOrder=QgsAbstractGeometry::AxisOrder::XY) const =0
Returns a GML3 representation of the geometry.
A helper class that centralizes restrictions given by all the access control filter plugins.
QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const override
Returns the authorized layer attributes.
void filterFeatures(const QgsVectorLayer *layer, QgsFeatureRequest &filterFeatures) const override
Filter the features of the layer.
bool layerReadPermission(const QgsMapLayer *layer) const
Returns the layer read right.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Custom exception class for Coordinate Reference System related exceptions.
Defines a QGIS exception class.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
Flags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
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.
FilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
@ FilterFid
Filter using feature ID.
@ FilterFids
Filter using feature IDs.
@ FilterNone
No filter is applied.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool isValid() const
Returns the validity of this feature.
Encapsulate a field in an attribute table or data source.
ConfigurationFlags configurationFlags
@ HideFromWfs
Field is not available if layer is served as WFS from QGIS server.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
int count() const
Returns number of items.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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.
Handles exporting QgsFeature features to GeoJSON features.
static Q_INVOKABLE QString encodeValue(const QVariant &value)
Encodes a value to a JSON string representation, adding appropriate quotations and escaping where req...
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
RAII class to restore layer filters on destruction.
static void applyAccessControlLayerFilters(const QgsAccessControl *accessControl, QgsMapLayer *mapLayer, QHash< QgsMapLayer *, QString > &originalLayerFilters)
Apply filter from AccessControl.
static QDomElement rectangleToGMLEnvelope(QgsRectangle *env, QDomDocument &doc, int precision=17)
Exports the rectangle to GML3 Envelope.
static QDomElement rectangleToGMLBox(QgsRectangle *box, QDomDocument &doc, int precision=17)
Exports the rectangle to GML2 Box.
A class to describe the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool isEmpty() const
Returns true if the rectangle is empty.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
QgsServerInterface Class defining interfaces exposed by QGIS Server and made available to plugins.
virtual QgsAccessControl * accessControls() const =0
Gets the registered access control filters.
virtual QgsServerSettings * serverSettings()=0
Returns the server settings.
QString value(const QString &key) const
Returns the value of a parameter.
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QgsServerParameters serverParameters() const
Returns parameters.
QgsServerRequest::Parameters parameters() const
Returns a map of query parameters with keys converted to uppercase.
QMap< QString, QString > Parameters
virtual QByteArray data() const
Returns post/put data Check for QByteArray::isNull() to check if data is available.
QgsServerResponse Class defining response interface passed to services QgsService::executeRequest() m...
virtual void write(const QString &data)
Write string This is a convenient method that will write directly to the underlying I/O device.
virtual void flush()
Flushes the current output buffer to the network.
virtual void setHeader(const QString &key, const QString &value)=0
Set Header entry Add Header entry to the response Note that it is usually an error to set Header afte...
Provides a way to retrieve settings by prioritizing according to environment variables,...
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
This is the base class for vector data providers.
virtual QgsAttributeList pkAttributeIndexes() const
Returns list of indexes of fields that make up the primary key.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Exception thrown when data access violates access controls.
Provides an interface to retrieve and manipulate WFS parameters received from the client.
Format
Output format for the response.
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
@ VectorLayer
Vector layer.
SERVER_EXPORT QgsFeatureRequest updateFeatureRequestFromServerFids(QgsFeatureRequest &featureRequest, const QStringList &serverFids, const QgsVectorDataProvider *provider)
Returns the feature request based on feature ids build with primary keys.
SERVER_EXPORT QString getServerFid(const QgsFeature &feature, const QgsAttributeList &pkAttributes)
Returns the feature id based on primary keys.
SERVER_EXPORT QStringList wfsLayerIds(const QgsProject &project)
Returns the Layer ids list defined in a QGIS project as published in WFS.
SERVER_EXPORT int wfsLayerPrecision(const QgsProject &project, const QString &layerId)
Returns the Layer precision defined in a QGIS project for the WFS GetFeature.
QString layerTypeName(const QgsMapLayer *layer)
Returns typename from vector layer.
QString implementationVersion()
Returns the highest version supported by this implementation.
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Service URL string.
const QString OGC_NAMESPACE
const QString GML_NAMESPACE
const QString WFS_NAMESPACE
getFeatureRequest parseGetFeatureRequestBody(QDomElement &docElem, const QgsProject *project)
Transform RequestBody root element to getFeatureRequest.
getFeatureQuery parseQueryElement(QDomElement &queryElem, const QgsProject *project)
Transform Query element to getFeatureQuery.
const QString QGS_NAMESPACE
getFeatureRequest parseGetFeatureParameters(const QgsProject *project)
Transform parameters to getFeatureRequest.
void parseSortByElement(QDomElement &sortByElem, QgsFeatureRequest &featureRequest, const QString &typeName)
Add SortBy element to featureRequest.
void writeGetFeature(QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, QgsServerResponse &response)
Output WFS GetFeature response.
QgsFeatureRequest parseFilterElement(const QString &typeName, QDomElement &filterElem, QgsProject *project)
Transform a Filter element to a feature request.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QMap< QString, QString > QgsStringMap
QList< int > QgsAttributeList
const QString & geometryName
const QgsCoordinateReferenceSystem & outputCrs
const QgsCoordinateReferenceSystem & crs
const QgsAttributeList & attributeIndexes
QgsFeatureRequest featureRequest
QgsWfsParameters::Format outputFormat
QList< getFeatureQuery > queries