47 struct createFeatureParams
70 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes );
72 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc );
76 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
78 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes );
93 QgsWfsParameters mWfsParameters;
104 mRequestParameters = request.parameters();
105 mWfsParameters = QgsWfsParameters( QUrlQuery( request.url() ) );
106 mWfsParameters.dump();
107 getFeatureRequest aRequest;
112 if ( doc.setContent( request.data(), true, &errorMsg ) )
114 QDomElement docElem = doc.documentElement();
123 QStringList typeNameList;
126 bool onlyOneLayer = ( aRequest.queries.size() == 1 );
129 int requestPrecision = 6;
133 QList<getFeatureQuery>::iterator qIt = aRequest.queries.begin();
134 for ( ; qIt != aRequest.queries.end(); ++qIt )
136 typeNameList << ( *qIt ).typeName;
142 QMap<QString, QgsMapLayer *> mapLayerMap;
157 if ( typeNameList.contains( name ) )
160 mapLayerMap[name] = layer;
164 requestRect = layer->
extent();
165 requestCrs = layer->
crs();
174 requestRect = transform.transform( layer->
extent() );
184 requestRect =
QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
191 for (
const QString &
typeName : typeNameList )
193 if ( !mapLayerMap.contains(
typeName ) )
195 throw QgsRequestNotWellFormedException( QStringLiteral(
"TypeName '%1' could not be found" ).arg(
typeName ) );
199 #ifdef HAVE_SERVER_PYTHON_PLUGINS
209 long sentFeatures = 0;
210 long iteratedFeatures = 0;
213 qIt = aRequest.queries.begin();
214 for ( ; qIt != aRequest.queries.end(); ++qIt )
216 getFeatureQuery &query = *qIt;
220 #ifdef HAVE_SERVER_PYTHON_PLUGINS
223 throw QgsSecurityAccessException( QStringLiteral(
"Feature access permission denied" ) );
229 throw QgsRequestNotWellFormedException( QStringLiteral(
"TypeName '%1' layer error" ).arg(
typeName ) );
236 throw QgsRequestNotWellFormedException( QStringLiteral(
"TypeName '%1' layer's provider error" ).arg(
typeName ) );
238 #ifdef HAVE_SERVER_PYTHON_PLUGINS
245 QMap< int, QString > layerAliasInfo;
247 QgsStringMap::const_iterator aliasIt = aliasMap.constBegin();
248 for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt )
251 if ( attrIndex != -1 )
253 layerAliasInfo.insert( attrIndex, aliasIt.value() );
258 const QStringList propertyList = query.propertyList;
264 if ( !propertyList.isEmpty() && propertyList.first() != QLatin1String(
"*" ) )
267 QStringList::const_iterator plstIt;
270 QList<QString> propertynames;
271 QList<QString> fieldnames;
278 for ( plstIt = propertyList.constBegin(); plstIt != propertyList.constEnd(); ++plstIt )
281 int fieldNameIdx = propertynames.indexOf( fieldName );
282 if ( fieldNameIdx == -1 )
284 fieldNameIdx = fieldnames.indexOf( fieldName );
286 if ( fieldNameIdx > -1 )
288 idxList.append( fieldNameIdx );
290 else if ( fieldName == QLatin1String(
"geometry" ) )
295 if ( !idxList.isEmpty() )
297 attrIndexes = idxList;
302 if ( !attrIndexes.isEmpty() )
308 int fieldNameIdx = fields.indexOf(
field.
name() );
309 if ( fieldNameIdx > -1 && attrIndexes.contains( fieldNameIdx ) )
311 attrIndexes.removeOne( fieldNameIdx );
327 if ( !query.serverFids.isEmpty() )
340 #ifdef HAVE_SERVER_PYTHON_PLUGINS
345 QStringList attributes = QStringList();
346 for (
int idx : std::as_const( attrIndexes ) )
359 if ( !pkAttributes.isEmpty() )
362 for (
int idx : pkAttributes )
364 if ( !subsetOfAttrs.contains( idx ) )
366 subsetOfAttrs.prepend( idx );
380 if ( aRequest.maxFeatures > 0 )
382 featureRequest.
setLimit( aRequest.maxFeatures + aRequest.startIndex - sentFeatures );
397 if ( !query.srsName.isEmpty() )
424 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
426 while ( fit.
nextFeature( feature ) && ( aRequest.maxFeatures == -1 || sentFeatures < aRequest.maxFeatures ) )
428 if ( iteratedFeatures >= aRequest.startIndex )
440 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
442 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
444 const createFeatureParams cfp = { layerPrecision,
455 while ( fit.
nextFeature( feature ) && ( aRequest.maxFeatures == -1 || sentFeatures < aRequest.maxFeatures ) )
457 if ( iteratedFeatures == aRequest.startIndex )
458 startGetFeature( request, response, project, aRequest.outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
460 if ( iteratedFeatures >= aRequest.startIndex )
462 setGetFeature( response, aRequest.outputFormat, feature, sentFeatures, cfp, project, provider->
pkAttributeIndexes() );
470 #ifdef HAVE_SERVER_PYTHON_PLUGINS
472 filterRestorer.reset();
475 if ( mWfsParameters.resultType() == QgsWfsParameters::ResultType::HITS )
477 hitGetFeature( request, response, project, aRequest.outputFormat, sentFeatures, typeNameList, serverIface->
serverSettings() );
482 if ( iteratedFeatures <= aRequest.startIndex )
483 startGetFeature( request, response, project, aRequest.outputFormat, requestPrecision, requestCrs, &requestRect, typeNameList, serverIface->
serverSettings() );
484 endGetFeature( response, aRequest.outputFormat );
491 getFeatureRequest request;
492 request.maxFeatures = mWfsParameters.maxFeaturesAsInt();
493 request.startIndex = mWfsParameters.startIndexAsInt();
494 request.outputFormat = mWfsParameters.outputFormat();
497 QStringList fidList = mWfsParameters.featureIds();
498 bool paramContainsFeatureIds = !fidList.isEmpty();
499 QStringList filterList = mWfsParameters.filters();
500 bool paramContainsFilters = !filterList.isEmpty();
501 QString bbox = mWfsParameters.bbox();
502 bool paramContainsBbox = !bbox.isEmpty();
503 if ( ( paramContainsFeatureIds
504 && ( paramContainsFilters || paramContainsBbox ) )
505 || ( paramContainsFilters
506 && ( paramContainsFeatureIds || paramContainsBbox ) )
507 || ( paramContainsBbox
508 && ( paramContainsFeatureIds || paramContainsFilters ) )
515 QStringList propertyNameList = mWfsParameters.propertyNames();
518 request.geometryName = mWfsParameters.geometryNameAsString().toUpper();
520 QStringList typeNameList;
522 if ( paramContainsFeatureIds )
525 if ( !propertyNameList.isEmpty() && propertyNameList.size() != fidList.size() )
527 throw QgsRequestNotWellFormedException( QStringLiteral(
"There has to be a 1:1 mapping between each element in a FEATUREID and the PROPERTYNAME list" ) );
529 if ( propertyNameList.isEmpty() )
531 for (
int i = 0; i < fidList.size(); ++i )
533 propertyNameList << QStringLiteral(
"*" );
537 QMap<QString, QStringList> fidsMap;
539 QStringList::const_iterator fidIt = fidList.constBegin();
540 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
541 for ( ; fidIt != fidList.constEnd(); ++fidIt )
544 QString fid = *fidIt;
547 QString propertyName;
548 if ( propertyNameIt != propertyNameList.constEnd() )
550 propertyName = *propertyNameIt;
553 if ( !fid.contains(
'.' ) )
555 throw QgsRequestNotWellFormedException( QStringLiteral(
"FEATUREID has to have TYPENAME in the values" ) );
558 QString
typeName = fid.section(
'.', 0, 0 );
559 fid = fid.section(
'.', 1, 1 );
560 if ( !typeNameList.contains(
typeName ) )
568 const QString key = QStringLiteral(
"%1:%2" ).arg(
typeName, propertyName );
570 if ( fidsMap.contains( key ) )
572 fids = fidsMap.value( key );
575 fidsMap.insert( key, fids );
577 if ( propertyNameIt != propertyNameList.constEnd() )
583 QMap<QString, QStringList>::const_iterator fidsMapIt = fidsMap.constBegin();
584 while ( fidsMapIt != fidsMap.constEnd() )
586 QString key = fidsMapIt.key();
590 const QString
typeName = key.section(
':', 0, 0 );
591 const QString propertyName = key.section(
':', 1, 1 );
593 getFeatureQuery query;
595 query.srsName = mWfsParameters.srsName();
598 if ( propertyName != QLatin1String(
"*" ) )
600 QStringList propertyList;
602 const QStringList attrList = propertyName.split(
',' );
603 QStringList::const_iterator alstIt;
604 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
606 QString fieldName = *alstIt;
607 fieldName = fieldName.trimmed();
608 if ( fieldName.contains(
':' ) )
610 fieldName = fieldName.section(
':', 1, 1 );
612 if ( fieldName.contains(
'/' ) )
614 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
616 throw QgsRequestNotWellFormedException( QStringLiteral(
"PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg(
typeName ) );
618 fieldName = fieldName.section(
'/', 1, 1 );
620 propertyList.append( fieldName );
622 query.propertyList = propertyList;
625 query.serverFids = fidsMapIt.value();
628 query.featureRequest = featureRequest;
629 request.queries.append( query );
635 if ( !mRequestParameters.contains( QStringLiteral(
"TYPENAME" ) ) )
637 throw QgsRequestNotWellFormedException( QStringLiteral(
"TYPENAME is mandatory except if FEATUREID is used" ) );
640 typeNameList = mWfsParameters.typeNames();
642 if ( !propertyNameList.isEmpty() && typeNameList.size() != propertyNameList.size() )
644 throw QgsRequestNotWellFormedException( QStringLiteral(
"There has to be a 1:1 mapping between each element in a TYPENAME and the PROPERTYNAME list" ) );
646 if ( propertyNameList.isEmpty() )
648 for (
int i = 0; i < typeNameList.size(); ++i )
650 propertyNameList << QStringLiteral(
"*" );
655 QStringList::const_iterator typeNameIt = typeNameList.constBegin();
656 QStringList::const_iterator propertyNameIt = propertyNameList.constBegin();
657 for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
662 QString propertyName;
663 if ( propertyNameIt != propertyNameList.constEnd() )
665 propertyName = *propertyNameIt;
668 getFeatureQuery query;
670 query.srsName = mWfsParameters.srsName();
673 if ( propertyName != QLatin1String(
"*" ) )
675 QStringList propertyList;
677 const QStringList attrList = propertyName.split(
',' );
678 QStringList::const_iterator alstIt;
679 for ( alstIt = attrList.constBegin(); alstIt != attrList.constEnd(); ++alstIt )
681 QString fieldName = *alstIt;
682 fieldName = fieldName.trimmed();
683 if ( fieldName.contains(
':' ) )
685 fieldName = fieldName.section(
':', 1, 1 );
687 if ( fieldName.contains(
'/' ) )
689 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
691 throw QgsRequestNotWellFormedException( QStringLiteral(
"PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg(
typeName ) );
693 fieldName = fieldName.section(
'/', 1, 1 );
695 propertyList.append( fieldName );
697 query.propertyList = propertyList;
700 request.queries.append( query );
702 if ( propertyNameIt != propertyNameList.constEnd() )
709 QStringList expFilterList = mWfsParameters.expFilters();
710 if ( !expFilterList.isEmpty() )
713 if ( request.queries.size() == expFilterList.size() )
716 QList<getFeatureQuery>::iterator qIt = request.queries.begin();
717 QStringList::const_iterator expFilterIt = expFilterList.constBegin();
718 for ( ; qIt != request.queries.end(); ++qIt )
720 getFeatureQuery &query = *qIt;
722 const QString expFilter = *expFilterIt++;
723 std::shared_ptr<QgsExpression> filter(
new QgsExpression( expFilter ) );
726 if ( filter->hasParserError() )
728 throw QgsRequestNotWellFormedException( QStringLiteral(
"The EXP_FILTER expression has errors: %1" ).arg( filter->parserErrorString() ) );
730 if ( filter->needsGeometry() )
734 query.featureRequest.setFilterExpression( filter->expression() );
744 if ( paramContainsBbox )
750 QString extentSrsName { mWfsParameters.srsName() };
753 if ( mWfsParameters.bbox().split(
',' ).size() == 5 && ! mWfsParameters.srsName().isEmpty() )
755 QString
crs( mWfsParameters.bbox().split(
',' )[4] );
756 if (
crs != mWfsParameters.srsName() )
761 if ( sourceCrs.isValid() && destinationCrs.isValid( ) )
786 if ( extentCrs.
isValid() && extentCrs.
hasAxisInverted() && ! extentSrsName.startsWith( QLatin1String(
"EPSG:" ) ) )
790 extent = geom.boundingBox();
794 QList<getFeatureQuery>::iterator qIt = request.queries.begin();
795 for ( ; qIt != request.queries.end(); ++qIt )
797 getFeatureQuery &query = *qIt;
802 else if ( paramContainsFilters )
805 if ( request.queries.size() != filterList.size() )
807 throw QgsRequestNotWellFormedException( QStringLiteral(
"There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
811 QList<getFeatureQuery>::iterator qIt = request.queries.begin();
812 QStringList::const_iterator filterIt = filterList.constBegin();
813 for ( ; qIt != request.queries.end(); ++qIt )
815 getFeatureQuery &query = *qIt;
818 if ( filterIt != filterList.constEnd() )
821 if ( !filter.setContent( *filterIt,
true, &errorMsg ) )
823 throw QgsRequestNotWellFormedException( QStringLiteral(
"error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
827 QDomElement filterElem = filter.firstChildElement();
828 QStringList serverFids;
829 query.featureRequest =
parseFilterElement( query.typeName, filterElem, serverFids, project );
830 query.serverFids = serverFids;
832 if ( filterIt != filterList.constEnd() )
840 QStringList sortByList = mWfsParameters.sortBy();
841 if ( !sortByList.isEmpty() && request.queries.size() == sortByList.size() )
844 QList<getFeatureQuery>::iterator qIt = request.queries.begin();
845 QStringList::const_iterator sortByIt = sortByList.constBegin();
846 for ( ; qIt != request.queries.end(); ++qIt )
848 getFeatureQuery &query = *qIt;
851 if ( sortByIt != sortByList.constEnd() )
855 for (
const QString &attribute : sortBy.split(
',' ) )
857 if ( attribute.endsWith( QLatin1String(
" D" ) ) || attribute.endsWith( QLatin1String(
"+D" ) ) )
859 query.featureRequest.addOrderBy( attribute.left( attribute.size() - 2 ),
false );
861 else if ( attribute.endsWith( QLatin1String(
" DESC" ) ) || attribute.endsWith( QLatin1String(
"+DESC" ) ) )
863 query.featureRequest.addOrderBy( attribute.left( attribute.size() - 5 ),
false );
865 else if ( attribute.endsWith( QLatin1String(
" A" ) ) || attribute.endsWith( QLatin1String(
"+A" ) ) )
867 query.featureRequest.addOrderBy( attribute.left( attribute.size() - 2 ) );
869 else if ( attribute.endsWith( QLatin1String(
" ASC" ) ) || attribute.endsWith( QLatin1String(
"+ASC" ) ) )
871 query.featureRequest.addOrderBy( attribute.left( attribute.size() - 4 ) );
875 query.featureRequest.addOrderBy( attribute );
886 getFeatureRequest request;
887 request.
maxFeatures = mWfsParameters.maxFeaturesAsInt();
888 request.startIndex = mWfsParameters.startIndexAsInt();
889 request.outputFormat = mWfsParameters.outputFormat();
891 QDomNodeList queryNodes = docElem.elementsByTagName( QStringLiteral(
"Query" ) );
892 QDomElement queryElem;
893 for (
int i = 0; i < queryNodes.size(); i++ )
895 queryElem = queryNodes.at( i ).toElement();
897 request.queries.append( query );
904 QDomNodeList sortByNodes = sortByElem.childNodes();
905 if ( sortByNodes.size() )
907 for (
int i = 0; i < sortByNodes.size(); i++ )
909 QDomElement sortPropElem = sortByNodes.at( i ).toElement();
910 QDomNodeList sortPropChildNodes = sortPropElem.childNodes();
911 if ( sortPropChildNodes.size() )
914 bool ascending =
true;
915 for (
int j = 0; j < sortPropChildNodes.size(); j++ )
917 QDomElement sortPropChildElem = sortPropChildNodes.at( j ).toElement();
918 if ( sortPropChildElem.tagName() == QLatin1String(
"PropertyName" ) )
920 fieldName = sortPropChildElem.text().trimmed();
922 else if ( sortPropChildElem.tagName() == QLatin1String(
"SortOrder" ) )
924 QString sortOrder = sortPropChildElem.text().trimmed().toUpper();
925 if ( sortOrder == QLatin1String(
"DESC" ) || sortOrder == QLatin1String(
"D" ) )
930 if ( fieldName.contains(
':' ) )
932 fieldName = fieldName.section(
':', 1, 1 );
934 if ( fieldName.contains(
'/' ) )
936 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
938 throw QgsRequestNotWellFormedException( QStringLiteral(
"PropertyName text '%1' has to contain TypeName '%2'" ).arg( fieldName ).arg(
typeName ) );
940 fieldName = fieldName.section(
'/', 1, 1 );
943 if ( !fieldName.isEmpty() )
944 featureRequest.
addOrderBy( fieldName, ascending );
952 QString
typeName = queryElem.attribute( QStringLiteral(
"typeName" ), QString() );
959 QStringList serverFids;
960 QStringList propertyList;
961 QDomNodeList queryChildNodes = queryElem.childNodes();
962 if ( queryChildNodes.size() )
964 QDomElement sortByElem;
965 for (
int q = 0; q < queryChildNodes.size(); q++ )
967 QDomElement queryChildElem = queryChildNodes.at( q ).toElement();
968 if ( queryChildElem.tagName() == QLatin1String(
"PropertyName" ) )
970 QString fieldName = queryChildElem.text().trimmed();
971 if ( fieldName.contains(
':' ) )
973 fieldName = fieldName.section(
':', 1, 1 );
975 if ( fieldName.contains(
'/' ) )
977 if ( fieldName.section(
'/', 0, 0 ) !=
typeName )
981 fieldName = fieldName.section(
'/', 1, 1 );
983 propertyList.append( fieldName );
985 else if ( queryChildElem.tagName() == QLatin1String(
"Filter" ) )
989 else if ( queryChildElem.tagName() == QLatin1String(
"SortBy" ) )
991 sortByElem = queryChildElem;
998 QString
srsName = queryElem.attribute( QStringLiteral(
"srsName" ), QString() );
1000 getFeatureQuery query;
1003 query.featureRequest = featureRequest;
1004 query.serverFids = serverFids;
1005 query.propertyList = propertyList;
1011 static QSet< QString > sParamFilter
1013 QStringLiteral(
"REQUEST" ),
1014 QStringLiteral(
"FORMAT" ),
1015 QStringLiteral(
"OUTPUTFORMAT" ),
1016 QStringLiteral(
"BBOX" ),
1017 QStringLiteral(
"FEATUREID" ),
1018 QStringLiteral(
"TYPENAME" ),
1019 QStringLiteral(
"FILTER" ),
1020 QStringLiteral(
"EXP_FILTER" ),
1021 QStringLiteral(
"MAXFEATURES" ),
1022 QStringLiteral(
"STARTINDEX" ),
1023 QStringLiteral(
"PROPERTYNAME" ),
1024 QStringLiteral(
"_DC" )
1029 int numberOfFeatures,
const QStringList &typeNames,
const QgsServerSettings *settings )
1031 QDateTime now = QDateTime::currentDateTime();
1034 if ( format == QgsWfsParameters::Format::GeoJSON )
1036 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1037 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1038 fcString += QStringLiteral(
" \"timeStamp\": \"%1\"\n" ).arg( now.toString( Qt::ISODate ) );
1039 fcString += QStringLiteral(
" \"numberOfFeatures\": %1\n" ).arg( QString::number( numberOfFeatures ) );
1040 fcString += QLatin1Char(
'}' );
1044 if ( format == QgsWfsParameters::Format::GML2 )
1045 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1047 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1050 QString hrefString =
serviceUrl( request, project, *settings );
1052 QUrl mapUrl( hrefString );
1054 QUrlQuery query( mapUrl );
1055 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1057 if ( mWfsParameters.version().isEmpty() )
1060 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1062 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1064 for (
auto param : query.queryItems() )
1066 if ( sParamFilter.contains( param.first.toUpper() ) )
1067 query.removeAllQueryItems( param.first );
1070 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1071 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1074 if ( format == QgsWfsParameters::Format::GML2 )
1075 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1077 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1080 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1082 mapUrl.setQuery( query );
1084 hrefString = mapUrl.toString();
1087 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1088 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1090 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1093 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1097 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1098 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1100 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1101 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1102 fcString +=
"\n timeStamp=\"" + now.toString( Qt::ISODate ) +
"\"";
1103 fcString +=
"\n numberOfFeatures=\"" + QString::number( numberOfFeatures ) +
"\"";
1104 fcString += QLatin1String(
">\n" );
1105 fcString += QLatin1String(
"</wfs:FeatureCollection>" );
1108 response.
write( fcString.toUtf8() );
1117 std::unique_ptr< QgsRectangle > transformedRect;
1119 if ( format == QgsWfsParameters::Format::GeoJSON )
1121 response.
setHeader(
"Content-Type",
"application/vnd.geo+json; charset=utf-8" );
1134 rect = transformedRect.get();
1145 fcString = QStringLiteral(
"{\"type\": \"FeatureCollection\",\n" );
1147 fcString += QLatin1String(
" \"features\": [\n" );
1148 response.
write( fcString.toUtf8() );
1152 if ( format == QgsWfsParameters::Format::GML2 )
1153 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/2.1.2; charset=utf-8" );
1155 response.
setHeader(
"Content-Type",
"text/xml; subtype=gml/3.1.1; charset=utf-8" );
1158 QString hrefString =
serviceUrl( request, project, *settings );
1160 QUrl mapUrl( hrefString );
1162 QUrlQuery query( mapUrl );
1163 query.addQueryItem( QStringLiteral(
"SERVICE" ), QStringLiteral(
"WFS" ) );
1165 if ( mWfsParameters.version().isEmpty() )
1168 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.1.0" ) );
1170 query.addQueryItem( QStringLiteral(
"VERSION" ), QStringLiteral(
"1.0.0" ) );
1172 const auto queryItems {query.queryItems()};
1173 for (
auto param : std::as_const( queryItems ) )
1175 if ( sParamFilter.contains( param.first.toUpper() ) )
1176 query.removeAllQueryItems( param.first );
1179 query.addQueryItem( QStringLiteral(
"REQUEST" ), QStringLiteral(
"DescribeFeatureType" ) );
1180 query.addQueryItem( QStringLiteral(
"TYPENAME" ), typeNames.join(
',' ) );
1183 if ( format == QgsWfsParameters::Format::GML2 )
1184 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/2.1.2" ) );
1186 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"text/xml; subtype=gml/3.1.1" ) );
1189 query.addQueryItem( QStringLiteral(
"OUTPUTFORMAT" ), QStringLiteral(
"XMLSCHEMA" ) );
1191 mapUrl.setQuery( query );
1193 hrefString = mapUrl.toString();
1196 if ( mWfsParameters.version().isEmpty() || mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) )
1197 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd" );
1199 wfsSchema = QStringLiteral(
"http://schemas.opengis.net/wfs/1.0.0/wfs.xsd" );
1202 fcString = QStringLiteral(
"<wfs:FeatureCollection" );
1206 fcString += QLatin1String(
" xmlns:ows=\"http://www.opengis.net/ows\"" );
1207 fcString += QLatin1String(
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"" );
1209 fcString += QLatin1String(
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" );
1210 fcString +=
" xsi:schemaLocation=\"" +
WFS_NAMESPACE +
" " + wfsSchema +
" " +
QGS_NAMESPACE +
" " + hrefString.replace( QLatin1String(
"&" ), QLatin1String(
"&" ) ) +
"\"";
1211 fcString += QLatin1String(
">\n" );
1213 response.
write( fcString.toUtf8() );
1217 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1218 if ( format == QgsWfsParameters::Format::GML3 )
1222 const bool invertAxis { mWfsParameters.versionAsNumber() >=
QgsProjectVersion( 1, 1, 0 ) &&
1224 !
srsName.startsWith( QLatin1String(
"EPSG:" ) ) };
1242 if ( !envElem.isNull() )
1246 envElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1248 bbElem.appendChild( envElem );
1249 doc.appendChild( bbElem );
1255 if ( !boxElem.isNull() )
1259 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1261 bbElem.appendChild( boxElem );
1262 doc.appendChild( bbElem );
1265 response.
write( doc.toByteArray() );
1276 if ( format == QgsWfsParameters::Format::GeoJSON )
1280 fcString += QLatin1String(
" " );
1282 fcString += QLatin1String(
" ," );
1283 mJsonExporter.setSourceCrs( params.crs );
1284 mJsonExporter.setIncludeGeometry(
false );
1285 mJsonExporter.setIncludeAttributes( !params.attributeIndexes.isEmpty() );
1286 mJsonExporter.setAttributes( params.attributeIndexes );
1287 fcString += createFeatureGeoJSON( feature, params, pkAttributes );
1288 fcString += QLatin1String(
"\n" );
1290 response.
write( fcString.toUtf8() );
1294 QDomDocument gmlDoc;
1295 QDomElement featureElement;
1296 if ( format == QgsWfsParameters::Format::GML3 )
1298 featureElement = createFeatureGML3( feature, gmlDoc, params, project, pkAttributes );
1299 gmlDoc.appendChild( featureElement );
1303 featureElement = createFeatureGML2( feature, gmlDoc, params, project, pkAttributes );
1304 gmlDoc.appendChild( featureElement );
1306 response.
write( gmlDoc.toByteArray() );
1316 if ( format == QgsWfsParameters::Format::GeoJSON )
1318 fcString += QLatin1String(
" ]\n" );
1319 fcString += QLatin1Char(
'}' );
1323 fcString = QStringLiteral(
"</wfs:FeatureCollection>\n" );
1325 response.
write( fcString.toUtf8() );
1329 QString createFeatureGeoJSON(
const QgsFeature &feature,
const createFeatureParams ¶ms,
const QgsAttributeList &pkAttributes )
1339 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1341 mJsonExporter.setIncludeGeometry(
true );
1342 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1347 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1353 return mJsonExporter.exportFeature( f, QVariantMap(),
id );
1357 QDomElement createFeatureGML2(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1360 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1363 QDomElement typeNameElement = doc.createElement(
"qgs:" + params.typeName );
1365 typeNameElement.setAttribute( QStringLiteral(
"fid" ),
id );
1366 featureElement.appendChild( typeNameElement );
1370 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1372 int prec = params.precision;
1381 crs = params.outputCrs;
1383 prec = std::min( params.precision + 3, 6 );
1391 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1392 QDomElement gmlElem;
1394 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1398 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1409 gmlElem = abstractGeom->
asGml2( doc, prec,
"http://www.opengis.net/gml" );
1412 if ( !gmlElem.isNull() )
1415 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1420 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1421 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1424 bbElem.appendChild( boxElem );
1425 typeNameElement.appendChild( bbElem );
1427 geomElem.appendChild( gmlElem );
1428 typeNameElement.appendChild( geomElem );
1435 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1437 int idx = params.attributeIndexes[i];
1438 if ( idx >= fields.
count() )
1443 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1444 typeNameElement.appendChild( fieldElem );
1447 return featureElement;
1450 QDomElement createFeatureGML3(
const QgsFeature &feature, QDomDocument &doc,
const createFeatureParams ¶ms,
const QgsProject *project,
const QgsAttributeList &pkAttributes )
1453 QDomElement featureElement = doc.createElement( QStringLiteral(
"gml:featureMember" ) );
1456 QDomElement typeNameElement = doc.createElement( QStringLiteral(
"qgs:" ) + params.typeName );
1458 typeNameElement.setAttribute( QStringLiteral(
"gml:id" ),
id );
1459 featureElement.appendChild( typeNameElement );
1463 if ( !geom.
isNull() && params.withGeom && params.geometryName != QLatin1String(
"NONE" ) )
1465 int prec = params.precision;
1474 crs = params.outputCrs;
1476 prec = std::min( params.precision + 3, 6 );
1484 QDomElement geomElem = doc.createElement( QStringLiteral(
"qgs:geometry" ) );
1485 QDomElement gmlElem;
1487 if ( params.geometryName == QLatin1String(
"EXTENT" ) )
1491 else if ( params.geometryName == QLatin1String(
"CENTROID" ) )
1502 gmlElem = abstractGeom->
asGml3( doc, prec,
"http://www.opengis.net/gml", params.hasAxisInverted ? QgsAbstractGeometry::AxisOrder::YX : QgsAbstractGeometry::AxisOrder::XY );
1505 if ( !gmlElem.isNull() )
1508 QDomElement bbElem = doc.createElement( QStringLiteral(
"gml:boundedBy" ) );
1511 if (
crs.
isValid() && params.srsName.isEmpty() )
1513 boxElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1514 gmlElem.setAttribute( QStringLiteral(
"srsName" ),
crs.
authid() );
1516 else if ( !params.srsName.isEmpty() )
1518 gmlElem.setAttribute( QStringLiteral(
"srsName" ), params.srsName );
1521 bbElem.appendChild( boxElem );
1522 typeNameElement.appendChild( bbElem );
1524 geomElem.appendChild( gmlElem );
1525 typeNameElement.appendChild( geomElem );
1532 for (
int i = 0; i < params.attributeIndexes.count(); ++i )
1534 int idx = params.attributeIndexes[i];
1535 if ( idx >= fields.
count() )
1540 const QDomElement fieldElem = createFieldElement( fields.
at( idx ), featureAttributes[idx], doc );
1541 typeNameElement.appendChild( fieldElem );
1544 return featureElement;
1547 QDomElement createFieldElement(
const QgsField &
field,
const QVariant &value, QDomDocument &doc )
1551 QDomElement fieldElem = doc.createElement( QStringLiteral(
"qgs:" ) + attributeName );
1552 if ( value.isNull() )
1554 fieldElem.setAttribute( QStringLiteral(
"xsi:nil" ), QStringLiteral(
"true" ) );
1558 const QString fieldText = encodeValueToText( value, setup );
1560 if ( fieldText.indexOf(
'<' ) != -1 || fieldText.indexOf(
'&' ) != -1 )
1562 fieldElem.appendChild( doc.createCDATASection( fieldText ) );
1566 fieldElem.appendChild( doc.createTextNode( fieldText ) );
1574 if ( value.isNull() )
1577 if ( setup.
type() == QStringLiteral(
"DateTime" ) )
1579 const QVariantMap config = setup.
config();
1581 QDateTime date = value.toDateTime();
1583 if ( date.isValid() )
1585 return date.toString( fieldFormat );
1588 else if ( setup.
type() == QStringLiteral(
"Range" ) )
1590 const QVariantMap config = setup.
config();
1591 if ( config.contains( QStringLiteral(
"Precision" ) ) )
1595 int precision( config[ QStringLiteral(
"Precision" ) ].toInt( &ok ) );
1597 return QString::number( value.toDouble(),
'f',
precision );
1601 switch ( value.type() )
1604 case QVariant::UInt:
1605 case QVariant::LongLong:
1606 case QVariant::ULongLong:
1607 case QVariant::Double:
1608 return value.toString();
1610 case QVariant::Bool:
1611 return value.toBool() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" );
1613 case QVariant::StringList:
1614 case QVariant::List:
1619 case QVariant::String:
1620 return value.toString();