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
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
360 QStringList attributes = QStringList();
361 for (
int idx : std::as_const( attrIndexes ) )
375 if ( !pkAttributes.isEmpty() )
378 for (
int idx : pkAttributes )
380 if ( !subsetOfAttrs.contains( idx ) )
382 subsetOfAttrs.prepend( idx );
417 QString outputSrsName;
418 if ( !query.
srsName.isEmpty() )
422 else if ( !requestSrsName.isEmpty() )
424 outputSrsName = requestSrsName;
466 if ( iteratedFeatures >= aRequest.
startIndex )
489 if ( iteratedFeatures == aRequest.
startIndex )
490 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
492 if ( iteratedFeatures >= aRequest.
startIndex )
502#ifdef HAVE_SERVER_PYTHON_PLUGINS
504 filterRestorer.reset();
509 hitGetFeature( request, response, project, aRequest.
outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
514 if ( iteratedFeatures <= aRequest.
startIndex )
515 startGetFeature( request, response, project, aRequest.
outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
523 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
524 request.
startIndex = mWfsParameters.startIndexAsInt();
528 QStringList fidList = mWfsParameters.featureIds();
529 bool paramContainsFeatureIds = !fidList.isEmpty();
530 QStringList filterList = mWfsParameters.filters();
531 bool paramContainsFilters = !filterList.isEmpty();
532 QString bbox = mWfsParameters.bbox();
533 bool paramContainsBbox = !bbox.isEmpty();
534 if ( ( paramContainsFeatureIds
535 && ( paramContainsFilters || paramContainsBbox ) )
536 || ( paramContainsFilters && ( paramContainsFeatureIds || paramContainsBbox ) )
537 || ( paramContainsBbox && ( paramContainsFeatureIds || paramContainsFilters ) ) )
543 QStringList propertyNameList = mWfsParameters.propertyNames();
546 request.
geometryName = mWfsParameters.geometryNameAsString().toUpper();
548 QStringList typeNameList;
550 if ( paramContainsFeatureIds )
553 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
557 if ( propertyNameList.isEmpty() )
559 for (
int i = 0; i < fidList.size(); ++i )
561 propertyNameList << QStringLiteral(
"*" );
565 QMap<QString, QStringList> fidsMap;
567 QStringList::const_iterator fidIt = fidList.constBegin();
568 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
569 for ( ; fidIt != fidList.constEnd(); ++fidIt )
572 QString fid = *fidIt;
575 QString propertyName;
576 if ( propertyNameIt != propertyNameList.constEnd() )
578 propertyName = *propertyNameIt;
581 if ( !fid.contains(
'.' ) )
586 QString
typeName = fid.section(
'.', 0, 0 );
587 fid = fid.section(
'.', 1, 1 );
588 if ( !typeNameList.contains(
typeName ) )
596 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
598 if ( fidsMap.contains( key ) )
600 fids = fidsMap.value( key );
603 fidsMap.insert( key, fids );
605 if ( propertyNameIt != propertyNameList.constEnd() )
611 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
612 while ( fidsMapIt != fidsMap.constEnd() )
614 QString key = fidsMapIt.key();
618 const QString
typeName = key.section(
':', 0, 0 );
619 const QString propertyName = key.section(
':', 1, 1 );
623 query.
srsName = mWfsParameters.srsName();
626 if ( propertyName != QLatin1String(
"*" ) )
628 QStringList propertyList;
630 const QStringList attrList = propertyName.split(
',' );
631 QStringList::const_iterator alstIt;
632 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
634 QString fieldName = *alstIt;
635 fieldName = fieldName.trimmed();
636 if ( fieldName.contains(
':' ) )
638 fieldName = fieldName.section(
':', 1, 1 );
640 if ( fieldName.contains(
'/' ) )
642 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
646 fieldName = fieldName.section(
'/', 1, 1 );
648 propertyList.append( fieldName );
657 request.
queries.append( query );
663 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
668 typeNameList = mWfsParameters.typeNames();
670 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
674 if ( propertyNameList.isEmpty() )
676 for (
int i = 0; i < typeNameList.size(); ++i )
678 propertyNameList << QStringLiteral(
"*" );
683 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
684 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
685 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
690 QString propertyName;
691 if ( propertyNameIt != propertyNameList.constEnd() )
693 propertyName = *propertyNameIt;
698 query.
srsName = mWfsParameters.srsName();
701 if ( propertyName != QLatin1String(
"*" ) )
703 QStringList propertyList;
705 const QStringList attrList = propertyName.split(
',' );
706 QStringList::const_iterator alstIt;
707 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
709 QString fieldName = *alstIt;
710 fieldName = fieldName.trimmed();
711 if ( fieldName.contains(
':' ) )
713 fieldName = fieldName.section(
':', 1, 1 );
715 if ( fieldName.contains(
'/' ) )
717 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
721 fieldName = fieldName.section(
'/', 1, 1 );
723 propertyList.append( fieldName );
728 request.
queries.append( query );
730 if ( propertyNameIt != propertyNameList.constEnd() )
737 QStringList expFilterList = mWfsParameters.expFilters();
738 if ( !expFilterList.isEmpty() )
741 if ( request.
queries.size() == expFilterList.size() )
744 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
745 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
746 for ( ; qIt != request.
queries.end(); ++qIt )
750 const QString expFilter = *expFilterIt++;
751 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
754 if ( filter->hasParserError() )
758 if ( filter->needsGeometry() )
772 if ( paramContainsBbox )
777 QString extentSrsName { mWfsParameters.srsName() };
780 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && !mWfsParameters.srsName().isEmpty() )
782 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
783 if (
crs != mWfsParameters.srsName() )
813 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && !extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
817 extent = geom.boundingBox();
821 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
822 for ( ; qIt != request.
queries.end(); ++qIt )
829 else if ( paramContainsFilters )
832 if ( request.
queries.size() != filterList.size() )
838 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
839 QStringList::const_iterator filterIt = filterList.constBegin();
840 for ( ; qIt != request.
queries.end(); ++qIt )
845 if ( filterIt != filterList.constEnd() )
848 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
854 QDomElement filterElem = filter.firstChildElement();
855 QStringList serverFids;
859 if ( filterIt != filterList.constEnd() )
867 QStringList sortByList = mWfsParameters.sortBy();
868 if ( !sortByList.isEmpty() && request.
queries.size() == sortByList.size() )
871 QList<getFeatureQuery>::iterator qIt = request.
queries.begin();
872 QStringList::const_iterator sortByIt = sortByList.constBegin();
873 for ( ; qIt != request.
queries.end(); ++qIt )
878 if ( sortByIt != sortByList.constEnd() )
882 for (
const QString &attribute : sortBy.split(
',' ) )
884 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
888 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
892 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
896 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
914 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
915 request.
startIndex = mWfsParameters.startIndexAsInt();
918 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
919 QDomElement queryElem;
920 for (
int i = 0; i < queryNodes.size(); i++ )
922 queryElem = queryNodes.at( i ).toElement();
924 request.
queries.append( query );
931 QDomNodeList sortByNodes = sortByElem.childNodes();
932 if ( sortByNodes.size() )
934 for (
int i = 0; i < sortByNodes.size(); i++ )
936 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
937 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
938 if ( sortPropChildNodes.size() )
941 bool ascending =
true;
942 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
944 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
945 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
947 fieldName = sortPropChildElem.text().trimmed();
949 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
951 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
952 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
957 if ( fieldName.contains(
':' ) )
959 fieldName = fieldName.section(
':', 1, 1 );
961 if ( fieldName.contains(
'/' ) )
963 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
967 fieldName = fieldName.section(
'/', 1, 1 );
970 if ( !fieldName.isEmpty() )
971 featureRequest.
addOrderBy( fieldName, ascending );
979 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
986 QStringList serverFids;
987 QStringList propertyList;
988 QDomNodeList queryChildNodes = queryElem.childNodes();
989 if ( queryChildNodes.size() )
991 QDomElement sortByElem;
992 for (
int q = 0; q < queryChildNodes.size(); q++ )
994 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
995 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
997 QString fieldName = queryChildElem.text().trimmed();
998 if ( fieldName.contains(
':' ) )
1000 fieldName = fieldName.section(
':', 1, 1 );
1002 if ( fieldName.contains(
'/' ) )
1004 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
1008 fieldName = fieldName.section(
'/', 1, 1 );
1010 propertyList.append( fieldName );
1012 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
1016 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
1018 sortByElem = queryChildElem;
1025 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1038 static QSet<QString> sParamFilter {
1039 QStringLiteral(
"REQUEST" ),
1040 QStringLiteral(
"FORMAT" ),
1041 QStringLiteral(
"OUTPUTFORMAT" ),
1042 QStringLiteral(
"BBOX" ),
1043 QStringLiteral(
"FEATUREID" ),
1044 QStringLiteral(
"TYPENAME" ),
1045 QStringLiteral(
"FILTER" ),
1046 QStringLiteral(
"EXP_FILTER" ),
1047 QStringLiteral(
"MAXFEATURES" ),
1048 QStringLiteral(
"STARTINDEX" ),
1049 QStringLiteral(
"PROPERTYNAME" ),
1050 QStringLiteral(
"_DC" )
1056 QDateTime now = QDateTime::currentDateTime();
1061 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1062 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1063 fcString += QStringLiteral(
" \"timeStamp\": \"%1\",\n" ).arg( now.toString( Qt::ISODate ) );
1064 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1065 fcString += QLatin1Char(
'}' );
1070 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1072 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1075 QString hrefString =
serviceUrl( request, project, *settings );
1077 QUrl mapUrl( hrefString );
1079 QUrlQuery query( mapUrl );
1080 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1082 if ( mWfsParameters.version().isEmpty() )
1085 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1087 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1089 const auto constItems { query.queryItems() };
1090 for (
const auto ¶m : std::as_const( constItems ) )
1092 if ( sParamFilter.contains( param.first.toUpper() ) )
1093 query.removeAllQueryItems( param.first );
1096 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1097 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1101 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1103 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1106 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1108 mapUrl.setQuery( query );
1110 hrefString = mapUrl.toString();
1113 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1114 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1116 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1119 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1123 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1124 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1126 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1127 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1128 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1129 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1130 fcString += QLatin1String(
">\n" );
1131 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1134 response.
write( fcString.toUtf8() );
1142 std::unique_ptr<QgsRectangle> transformedRect;
1146 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1159 rect = transformedRect.get();
1170 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1175 if ( !destinationCrs.isValid() )
1177 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg(
srsName ) );
1182 for (
const auto &it : value.items() )
1184 fcString +=
" \"" + QString::fromStdString( it.key() ) +
"\": " + QString::fromStdString( it.value().dump() ) +
",\n";
1187 fcString += QLatin1String(
" \"features\": [\n" );
1188 response.
write( fcString.toUtf8() );
1193 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1195 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1198 QString hrefString =
serviceUrl( request, project, *settings );
1200 QUrl mapUrl( hrefString );
1202 QUrlQuery query( mapUrl );
1203 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1205 if ( mWfsParameters.version().isEmpty() )
1208 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1210 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1212 const auto queryItems { query.queryItems() };
1213 for (
auto param : std::as_const( queryItems ) )
1215 if ( sParamFilter.contains( param.first.toUpper() ) )
1216 query.removeAllQueryItems( param.first );
1219 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1220 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1224 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1226 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1229 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1231 mapUrl.setQuery( query );
1233 hrefString = mapUrl.toString();
1236 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1237 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1239 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1242 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1246 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1247 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1249 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1250 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1251 fcString += QLatin1String(
">\n" );
1253 response.
write( fcString.toUtf8() );
1257 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1262 const QString outputSrsName = !requestSrsName.isEmpty() ? requestSrsName :
crs.
authid();
1288 if ( !envElem.isNull() )
1290 if (
crs.
isValid() && outputSrsName.isEmpty() )
1292 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1294 bbElem.appendChild( envElem );
1295 doc.appendChild( bbElem );
1301 if ( !boxElem.isNull() )
1305 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1307 bbElem.appendChild( boxElem );
1308 doc.appendChild( bbElem );
1311 response.
write( doc.toByteArray() );
1325 fcString += QLatin1String(
" " );
1327 fcString += QLatin1String(
" ," );
1330 if ( !destinationCrs.isValid() )
1332 throw QgsRequestNotWellFormedException( QStringLiteral(
"srsName error: '%1' is not valid." ).arg( params.srsName ) );
1335 mJsonExporter.setDestinationCrs( destinationCrs );
1336 mJsonExporter.setTransformGeometries(
true );
1337 mJsonExporter.setSourceCrs( params.crs );
1338 mJsonExporter.setIncludeGeometry(
false );
1339 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1340 mJsonExporter.setAttributes( params.attributeIndexes );
1341 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1342 fcString += QLatin1String(
"\n" );
1344 response.
write( fcString.toUtf8() );
1348 QDomDocument gmlDoc;
1349 QDomElement featureElement;
1352 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1353 gmlDoc.appendChild( featureElement );
1357 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1358 gmlDoc.appendChild( featureElement );
1360 response.
write( gmlDoc.toByteArray() );
1372 fcString += QLatin1String(
" ]\n" );
1373 fcString += QLatin1Char(
'}' );
1377 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1379 response.
write( fcString.toUtf8() );
1383 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1393 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1395 mJsonExporter.setIncludeGeometry(
true );
1396 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1401 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1407 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1411 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1414 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1417 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1419 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1420 featureElement.appendChild( typeNameElement );
1424 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1426 int prec = params.precision;
1435 crs = params.outputCrs;
1437 prec = std::min( params.precision + 3, 6 );
1445 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1446 QDomElement gmlElem;
1448 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1452 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1463 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1466 if ( !gmlElem.isNull() )
1469 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1474 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1475 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1478 bbElem.appendChild( boxElem );
1479 typeNameElement.appendChild( bbElem );
1481 geomElem.appendChild( gmlElem );
1482 typeNameElement.appendChild( geomElem );
1489 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1491 int idx = params.attributeIndexes[i];
1497 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1498 typeNameElement.appendChild( fieldElem );
1501 return featureElement;
1504 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1507 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1510 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1512 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1513 featureElement.appendChild( typeNameElement );
1517 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1519 int prec = params.precision;
1528 crs = params.outputCrs;
1530 prec = std::min( params.precision + 3, 6 );
1538 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1539 QDomElement gmlElem;
1541 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1545 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1559 if ( !gmlElem.isNull() )
1562 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1565 if (
crs.
isValid() && params.srsName.isEmpty() )
1567 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1568 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1570 else if ( !params.srsName.isEmpty() )
1572 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1575 bbElem.appendChild( boxElem );
1576 typeNameElement.appendChild( bbElem );
1578 geomElem.appendChild( gmlElem );
1579 typeNameElement.appendChild( geomElem );
1586 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1588 int idx = params.attributeIndexes[i];
1594 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1595 typeNameElement.appendChild( fieldElem );
1598 return featureElement;
1601 QDomElement createFieldElement(
const QgsField &field,
const QVariant &value, QDomDocument &doc )
1604 const thread_local QRegularExpression sCleanTagNameRegExp( QStringLiteral(
"[^\\w\\.-_]" ), QRegularExpression::PatternOption::UseUnicodePropertiesOption );
1605 const QString attributeName = field.
name().replace(
' ',
'_' ).replace( sCleanTagNameRegExp, QString() );
1606 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1609 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1613 const QString fieldText = encodeValueToText( value, setup );
1615 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1617 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1621 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1632 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1635 if ( value.userType() == QMetaType::Type::QTime )
1641 const QVariantMap config = setup.
config();
1647 QDateTime date = value.toDateTime();
1649 if ( !date.isValid() )
1651 date = QDateTime::fromString( value.toString(), fieldFormat );
1654 if ( date.isValid() )
1656 return date.toString( fieldFormat );
1659 return value.toString();
1661 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1663 const QVariantMap config = setup.
config();
1664 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1668 int precision( config[QStringLiteral(
"Precision" )].toInt( &ok ) );
1670 return QString::number( value.toDouble(),
'f',
precision );
1674 switch ( value.userType() )
1676 case QMetaType::Type::Int:
1677 case QMetaType::Type::UInt:
1678 case QMetaType::Type::LongLong:
1679 case QMetaType::Type::ULongLong:
1680 case QMetaType::Type::Double:
1681 return value.toString();
1683 case QMetaType::Type::Bool:
1684 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1686 case QMetaType::Type::QStringList:
1687 case QMetaType::Type::QVariantList:
1688 case QMetaType::Type::QVariantMap:
1692 case QMetaType::Type::QString:
1693 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.
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.
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
QList< getFeatureQuery > queries
QgsWfsParameters::Format outputFormat