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;
274 for (
const QgsField &field : fields )
276 fieldnames.
append( field.name() );
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() )
307 for (
const QgsField &field : fields )
311 int fieldNameIdx = fields.
indexOf( field.name() );
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 );
422 QString outputSrsName;
423 if ( !query.
srsName.isEmpty() )
427 else if ( !requestSrsName.isEmpty() )
429 outputSrsName = requestSrsName;
471 if ( iteratedFeatures >= aRequest.
startIndex )
486 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
488 ! outputSrsName.startsWith( QLatin1String(
"EPSG:" ) ) };
490 const createFeatureParams cfp = { layerPrecision,
507 if ( iteratedFeatures == aRequest.
startIndex )
508 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
510 if ( iteratedFeatures >= aRequest.
startIndex )
520#ifdef HAVE_SERVER_PYTHON_PLUGINS
522 filterRestorer.reset();
527 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
532 if ( iteratedFeatures <= aRequest.
startIndex )
533 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
542 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
543 request.
startIndex = mWfsParameters.startIndexAsInt();
547 QStringList fidList = mWfsParameters.featureIds();
548 bool paramContainsFeatureIds = !fidList.isEmpty();
549 QStringList filterList = mWfsParameters.filters();
550 bool paramContainsFilters = !filterList.isEmpty();
551 QString bbox = mWfsParameters.bbox();
552 bool paramContainsBbox = !bbox.isEmpty();
553 if ( ( paramContainsFeatureIds
554 && ( paramContainsFilters || paramContainsBbox ) )
555 || ( paramContainsFilters
556 && ( paramContainsFeatureIds || paramContainsBbox ) )
557 || ( paramContainsBbox
558 && ( paramContainsFeatureIds || paramContainsFilters ) )
565 QStringList propertyNameList = mWfsParameters.propertyNames();
568 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
570 QStringList typeNameList;
572 if ( paramContainsFeatureIds )
575 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
579 if ( propertyNameList.isEmpty() )
581 for (
int i = 0; i < fidList.size(); ++i )
583 propertyNameList << QStringLiteral(
"*" );
587 QMap<QString, QStringList> fidsMap;
589 QStringList::const_iterator fidIt = fidList.constBegin();
590 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
591 for ( ; fidIt != fidList.constEnd(); ++fidIt )
594 QString fid = *fidIt;
597 QString propertyName;
598 if ( propertyNameIt != propertyNameList.constEnd() )
600 propertyName = *propertyNameIt;
603 if ( !fid.contains(
'.' ) )
608 QString
typeName = fid.section(
'.', 0, 0 );
609 fid = fid.section(
'.', 1, 1 );
610 if ( !typeNameList.contains(
typeName ) )
618 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
620 if ( fidsMap.contains( key ) )
622 fids = fidsMap.value( key );
625 fidsMap.insert( key, fids );
627 if ( propertyNameIt != propertyNameList.constEnd() )
633 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
634 while ( fidsMapIt != fidsMap.constEnd() )
636 QString key = fidsMapIt.key();
640 const QString
typeName = key.section(
':', 0, 0 );
641 const QString propertyName = key.section(
':', 1, 1 );
645 query.
srsName = mWfsParameters.srsName();
648 if ( propertyName != QLatin1String(
"*" ) )
650 QStringList propertyList;
652 const QStringList attrList = propertyName.split(
',' );
653 QStringList::const_iterator alstIt;
654 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
656 QString fieldName = *alstIt;
657 fieldName = fieldName.trimmed();
658 if ( fieldName.contains(
':' ) )
660 fieldName = fieldName.section(
':', 1, 1 );
662 if ( fieldName.contains(
'/' ) )
664 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
668 fieldName = fieldName.section(
'/', 1, 1 );
670 propertyList.append( fieldName );
679 request.
queries.append( query );
685 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
690 typeNameList = mWfsParameters.typeNames();
692 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
696 if ( propertyNameList.isEmpty() )
698 for (
int i = 0; i < typeNameList.size(); ++i )
700 propertyNameList << QStringLiteral(
"*" );
705 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
706 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
707 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
712 QString propertyName;
713 if ( propertyNameIt != propertyNameList.constEnd() )
715 propertyName = *propertyNameIt;
720 query.
srsName = mWfsParameters.srsName();
723 if ( propertyName != QLatin1String(
"*" ) )
725 QStringList propertyList;
727 const QStringList attrList = propertyName.split(
',' );
728 QStringList::const_iterator alstIt;
729 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
731 QString fieldName = *alstIt;
732 fieldName = fieldName.trimmed();
733 if ( fieldName.contains(
':' ) )
735 fieldName = fieldName.section(
':', 1, 1 );
737 if ( fieldName.contains(
'/' ) )
739 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
743 fieldName = fieldName.section(
'/', 1, 1 );
745 propertyList.append( fieldName );
750 request.
queries.append( query );
752 if ( propertyNameIt != propertyNameList.constEnd() )
759 QStringList expFilterList = mWfsParameters.expFilters();
760 if ( !expFilterList.isEmpty() )
763 if ( request.
queries.size() == expFilterList.size() )
766 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
767 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
768 for ( ; qIt != request.
queries.end(); ++qIt )
772 const QString expFilter = *expFilterIt++;
773 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
776 if ( filter->hasParserError() )
780 if ( filter->needsGeometry() )
794 if ( paramContainsBbox )
800 QString extentSrsName { mWfsParameters.srsName() };
803 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
805 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
806 if (
crs != mWfsParameters.srsName() )
836 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && ! extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
840 extent = geom.boundingBox();
844 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
845 for ( ; qIt != request.
queries.end(); ++qIt )
852 else if ( paramContainsFilters )
855 if ( request.
queries.size() != filterList.size() )
861 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
862 QStringList::const_iterator filterIt = filterList.constBegin();
863 for ( ; qIt != request.
queries.end(); ++qIt )
868 if ( filterIt != filterList.constEnd() )
871 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
877 QDomElement filterElem = filter.firstChildElement();
878 QStringList serverFids;
882 if ( filterIt != filterList.constEnd() )
890 QStringList sortByList = mWfsParameters.sortBy();
891 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
894 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
895 QStringList::const_iterator sortByIt = sortByList.constBegin();
896 for ( ; qIt != request.
queries.end(); ++qIt )
901 if ( sortByIt != sortByList.constEnd() )
905 for (
const QString &attribute : sortBy.split(
',' ) )
907 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
911 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
915 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
919 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
937 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
938 request.
startIndex = mWfsParameters.startIndexAsInt();
941 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
942 QDomElement queryElem;
943 for (
int i = 0; i < queryNodes.size(); i++ )
945 queryElem = queryNodes.at( i ).toElement();
947 request.
queries.append( query );
954 QDomNodeList sortByNodes = sortByElem.childNodes();
955 if ( sortByNodes.size() )
957 for (
int i = 0; i < sortByNodes.size(); i++ )
959 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
960 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
961 if ( sortPropChildNodes.size() )
964 bool ascending =
true;
965 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
967 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
968 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
970 fieldName = sortPropChildElem.text().trimmed();
972 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
974 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
975 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
980 if ( fieldName.contains(
':' ) )
982 fieldName = fieldName.section(
':', 1, 1 );
984 if ( fieldName.contains(
'/' ) )
986 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
990 fieldName = fieldName.section(
'/', 1, 1 );
993 if ( !fieldName.isEmpty() )
994 featureRequest.
addOrderBy( fieldName, ascending );
1002 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
1009 QStringList serverFids;
1010 QStringList propertyList;
1011 QDomNodeList queryChildNodes = queryElem.childNodes();
1012 if ( queryChildNodes.size() )
1014 QDomElement sortByElem;
1015 for (
int q = 0; q < queryChildNodes.size(); q++ )
1017 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
1018 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
1020 QString fieldName = queryChildElem.text().trimmed();
1021 if ( fieldName.contains(
':' ) )
1023 fieldName = fieldName.section(
':', 1, 1 );
1025 if ( fieldName.contains(
'/' ) )
1027 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
1031 fieldName = fieldName.section(
'/', 1, 1 );
1033 propertyList.append( fieldName );
1035 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
1039 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
1041 sortByElem = queryChildElem;
1048 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1061 static QSet< QString > sParamFilter
1063 QStringLiteral(
"REQUEST" ),
1064 QStringLiteral(
"FORMAT" ),
1065 QStringLiteral(
"OUTPUTFORMAT" ),
1066 QStringLiteral(
"BBOX" ),
1067 QStringLiteral(
"FEATUREID" ),
1068 QStringLiteral(
"TYPENAME" ),
1069 QStringLiteral(
"FILTER" ),
1070 QStringLiteral(
"EXP_FILTER" ),
1071 QStringLiteral(
"MAXFEATURES" ),
1072 QStringLiteral(
"STARTINDEX" ),
1073 QStringLiteral(
"PROPERTYNAME" ),
1074 QStringLiteral(
"_DC" )
1079 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1081 QDateTime now = QDateTime::currentDateTime();
1086 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1087 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1088 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1089 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1090 fcString += QLatin1Char(
'}' );
1095 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1097 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1100 QString hrefString =
serviceUrl( request, project, *settings );
1102 QUrl mapUrl( hrefString );
1104 QUrlQuery query( mapUrl );
1105 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1107 if ( mWfsParameters.version().isEmpty() )
1110 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1112 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1114 const auto constItems { query.queryItems() };
1115 for (
const auto ¶m : std::as_const( constItems ) )
1117 if ( sParamFilter.contains( param.first.toUpper() ) )
1118 query.removeAllQueryItems( param.first );
1121 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1122 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1126 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1128 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1131 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1133 mapUrl.setQuery( query );
1135 hrefString = mapUrl.toString();
1138 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1139 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1141 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1144 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1148 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1149 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1151 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1152 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1153 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1154 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1155 fcString += QLatin1String(
">\n" );
1156 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1159 response.
write( fcString.toUtf8() );
1168 std::unique_ptr< QgsRectangle > transformedRect;
1172 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1185 rect = transformedRect.get();
1196 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1201 if ( ! destinationCrs.isValid() )
1203 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg(
srsName ) );
1208 for (
const auto &it : value.items() )
1210 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1213 fcString += QLatin1String(
" \"features\": [\n" );
1214 response.
write( fcString.toUtf8() );
1219 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1221 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1224 QString hrefString =
serviceUrl( request, project, *settings );
1226 QUrl mapUrl( hrefString );
1228 QUrlQuery query( mapUrl );
1229 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1231 if ( mWfsParameters.version().isEmpty() )
1234 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1236 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1238 const auto queryItems {query.queryItems()};
1239 for (
auto param : std::as_const( queryItems ) )
1241 if ( sParamFilter.contains( param.first.toUpper() ) )
1242 query.removeAllQueryItems( param.first );
1245 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1246 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1250 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1252 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1255 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1257 mapUrl.setQuery( query );
1259 hrefString = mapUrl.toString();
1262 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1263 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1265 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1268 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1272 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1273 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1275 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1276 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1277 fcString += QLatin1String(
">\n" );
1279 response.
write( fcString.toUtf8() );
1283 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1288 const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName :
crs.
authid();
1311 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1313 !outputSrsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1316 if ( !envElem.isNull() )
1318 if (
crs.
isValid() && outputSrsName.isEmpty() )
1320 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1322 bbElem.appendChild( envElem );
1323 doc.appendChild( bbElem );
1329 if ( !boxElem.isNull() )
1333 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1335 bbElem.appendChild( boxElem );
1336 doc.appendChild( bbElem );
1339 response.
write( doc.toByteArray() );
1354 fcString += QLatin1String(
" " );
1356 fcString += QLatin1String(
" ," );
1359 if ( ! destinationCrs.isValid() )
1361 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg( params.srsName ) );
1364 mJsonExporter.setDestinationCrs( destinationCrs );
1365 mJsonExporter.setTransformGeometries(
true );
1366 mJsonExporter.setSourceCrs( params.crs );
1367 mJsonExporter.setIncludeGeometry(
false );
1368 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1369 mJsonExporter.setAttributes( params.attributeIndexes );
1370 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1371 fcString += QLatin1String(
"\n" );
1373 response.
write( fcString.toUtf8() );
1377 QDomDocument gmlDoc;
1378 QDomElement featureElement;
1381 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1382 gmlDoc.appendChild( featureElement );
1386 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1387 gmlDoc.appendChild( featureElement );
1389 response.
write( gmlDoc.toByteArray() );
1401 fcString += QLatin1String(
" ]\n" );
1402 fcString += QLatin1Char(
'}' );
1406 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1408 response.
write( fcString.toUtf8() );
1412 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1422 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1424 mJsonExporter.setIncludeGeometry(
true );
1425 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1430 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1436 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1440 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1443 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1446 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1448 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1449 featureElement.appendChild( typeNameElement );
1453 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1455 int prec = params.precision;
1464 crs = params.outputCrs;
1466 prec = std::min( params.precision + 3, 6 );
1474 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1475 QDomElement gmlElem;
1477 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1481 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1492 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1495 if ( !gmlElem.isNull() )
1498 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1503 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1504 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1507 bbElem.appendChild( boxElem );
1508 typeNameElement.appendChild( bbElem );
1510 geomElem.appendChild( gmlElem );
1511 typeNameElement.appendChild( geomElem );
1518 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1520 int idx = params.attributeIndexes[i];
1526 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1527 typeNameElement.appendChild( fieldElem );
1530 return featureElement;
1533 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1536 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1539 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1541 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1542 featureElement.appendChild( typeNameElement );
1546 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1548 int prec = params.precision;
1557 crs = params.outputCrs;
1559 prec = std::min( params.precision + 3, 6 );
1567 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1568 QDomElement gmlElem;
1570 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1574 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1588 if ( !gmlElem.isNull() )
1591 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1594 if (
crs.
isValid() && params.srsName.isEmpty() )
1596 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1597 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1599 else if ( !params.srsName.isEmpty() )
1601 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1604 bbElem.appendChild( boxElem );
1605 typeNameElement.appendChild( bbElem );
1607 geomElem.appendChild( gmlElem );
1608 typeNameElement.appendChild( geomElem );
1615 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1617 int idx = params.attributeIndexes[i];
1623 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1624 typeNameElement.appendChild( fieldElem );
1627 return featureElement;
1630 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1633 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1634 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1635 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1638 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1642 const QString fieldText = encodeValueToText( value, setup );
1644 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1646 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1650 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1661 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1664 if ( value.userType() == QMetaType::Type::QTime )
1670 const QVariantMap config = setup.
config();
1673 const QString fieldFormat =
1674 config.value( QStringLiteral(
"field_iso_format" ),
false ).toBool() ?
1679 QDateTime date = value.toDateTime();
1681 if ( !date.isValid() )
1683 date = QDateTime::fromString( value.toString(), fieldFormat );
1686 if ( date.isValid() )
1688 return date.toString( fieldFormat );
1691 return value.toString();
1693 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1695 const QVariantMap config = setup.
config();
1696 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1700 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1702 return QString::number( value.toDouble(),
'f',
precision );
1706 switch ( value.userType() )
1708 case QMetaType::Type::Int:
1709 case QMetaType::Type::UInt:
1710 case QMetaType::Type::LongLong:
1711 case QMetaType::Type::ULongLong:
1712 case QMetaType::Type::Double:
1713 return value.toString();
1715 case QMetaType::Type::Bool:
1716 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1718 case QMetaType::Type::QStringList:
1719 case QMetaType::Type::QVariantList:
1720 case QMetaType::Type::QVariantMap:
1724 case QMetaType::Type::QString:
1725 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.
This class 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...
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)
Fetch next feature and stores in f, returns true on success.
This class 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)
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 has no area.
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, bool silenceNullWarnings=false)
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 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 bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
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