Skip to content

Commit a8400fb

Browse files
committedApr 8, 2017
[Server] WFS Transaction add KVP support
1 parent 4943367 commit a8400fb

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed
 

‎src/server/services/wfs/qgswfstransaction.cpp

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ namespace QgsWfs
6767
QDomElement docElem = doc.documentElement();
6868
aRequest = parseTransactionRequestBody( docElem );
6969
}
70+
else
71+
{
72+
aRequest = parseTransactionParameters( parameters );
73+
}
7074

7175
int actionCount = aRequest.inserts.size() + aRequest.updates.size() + aRequest.deletes.size();
7276
if ( actionCount == 0 )
@@ -741,6 +745,257 @@ namespace QgsWfs
741745
return featList;
742746
}
743747

748+
transactionRequest parseTransactionParameters( QgsServerRequest::Parameters parameters )
749+
{
750+
if ( !parameters.contains( QStringLiteral( "OPERATION" ) ) )
751+
{
752+
throw QgsRequestNotWellFormedException( QStringLiteral( "OPERATION parameter is mandatory" ) );
753+
}
754+
if ( parameters.value( QStringLiteral( "OPERATION" ) ).toUpper() != QStringLiteral( "DELETE" ) )
755+
{
756+
throw QgsRequestNotWellFormedException( QStringLiteral( "Only DELETE value is defined for OPERATION parameter" ) );
757+
}
758+
759+
// Verifying parameters mutually exclusive
760+
if ( ( parameters.contains( QStringLiteral( "FEATUREID" ) )
761+
&& ( parameters.contains( QStringLiteral( "FILTER" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
762+
|| ( parameters.contains( QStringLiteral( "FILTER" ) )
763+
&& ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "BBOX" ) ) ) )
764+
|| ( parameters.contains( QStringLiteral( "BBOX" ) )
765+
&& ( parameters.contains( QStringLiteral( "FEATUREID" ) ) || parameters.contains( QStringLiteral( "FILTER" ) ) ) )
766+
)
767+
{
768+
throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID FILTER and BBOX parameters are mutually exclusive" ) );
769+
}
770+
771+
transactionRequest request;
772+
773+
QStringList typeNameList;
774+
// parse FEATUREID
775+
if ( parameters.contains( QStringLiteral( "FEATUREID" ) ) )
776+
{
777+
QStringList fidList = parameters.value( QStringLiteral( "FEATUREID" ) ).split( QStringLiteral( "," ) );
778+
779+
QMap<QString, QgsFeatureIds> fidsMap;
780+
781+
QStringList::const_iterator fidIt = fidList.constBegin();
782+
for ( ; fidIt != fidList.constEnd(); ++fidIt )
783+
{
784+
// Get FeatureID
785+
QString fid = *fidIt;
786+
fid = fid.trimmed();
787+
// testing typename in the WFS featureID
788+
if ( !fid.contains( QLatin1String( "." ) ) )
789+
{
790+
throw QgsRequestNotWellFormedException( QStringLiteral( "FEATUREID has to have TYPENAME in the values" ) );
791+
}
792+
793+
QString typeName = fid.section( QStringLiteral( "." ), 0, 0 );
794+
fid = fid.section( QStringLiteral( "." ), 1, 1 );
795+
if ( !typeNameList.contains( typeName ) )
796+
{
797+
typeNameList << typeName;
798+
}
799+
800+
QgsFeatureIds fids;
801+
if ( fidsMap.contains( typeName ) )
802+
{
803+
fids = fidsMap.value( typeName );
804+
}
805+
fids.insert( fid.toInt() );
806+
fidsMap.insert( typeName, fids );
807+
}
808+
809+
QMap<QString, QgsFeatureIds>::const_iterator fidsMapIt = fidsMap.constBegin();
810+
while ( fidsMapIt != fidsMap.constEnd() )
811+
{
812+
transactionDelete action;
813+
action.typeName = fidsMapIt.key();
814+
815+
QgsFeatureIds fids = fidsMapIt.value();
816+
action.featureRequest = QgsFeatureRequest( fids );
817+
818+
request.deletes.append( action );
819+
}
820+
return request;
821+
}
822+
823+
if ( !parameters.contains( QStringLiteral( "TYPENAME" ) ) )
824+
{
825+
throw QgsRequestNotWellFormedException( QStringLiteral( "TYPENAME is mandatory except if FEATUREID is used" ) );
826+
}
827+
828+
typeNameList = parameters.value( QStringLiteral( "TYPENAME" ) ).split( QStringLiteral( "," ) );
829+
830+
// Create actions based on TypeName
831+
QStringList::const_iterator typeNameIt = typeNameList.constBegin();
832+
for ( ; typeNameIt != typeNameList.constEnd(); ++typeNameIt )
833+
{
834+
QString typeName = *typeNameIt;
835+
typeName = typeName.trimmed();
836+
837+
transactionDelete action;
838+
action.typeName = typeName;
839+
840+
request.deletes.append( action );
841+
}
842+
843+
// Manage extra parameter exp_filter
844+
if ( parameters.contains( QStringLiteral( "EXP_FILTER" ) ) )
845+
{
846+
QString expFilterName = parameters.value( QStringLiteral( "EXP_FILTER" ) );
847+
QStringList expFilterList;
848+
QRegExp rx( "\\(([^()]+)\\)" );
849+
if ( rx.indexIn( expFilterName, 0 ) == -1 )
850+
{
851+
expFilterList << expFilterName;
852+
}
853+
else
854+
{
855+
int pos = 0;
856+
while ( ( pos = rx.indexIn( expFilterName, pos ) ) != -1 )
857+
{
858+
expFilterList << rx.cap( 1 );
859+
pos += rx.matchedLength();
860+
}
861+
}
862+
863+
// Verifying the 1:1 mapping between TYPENAME and EXP_FILTER but without exception
864+
if ( request.deletes.size() == expFilterList.size() )
865+
{
866+
// set feature request filter expression based on filter element
867+
QList<transactionDelete>::iterator dIt = request.deletes.begin();
868+
QStringList::const_iterator expFilterIt = expFilterList.constBegin();
869+
for ( ; dIt != request.deletes.end(); ++dIt )
870+
{
871+
transactionDelete &action = *dIt;
872+
// Get Filter for this typeName
873+
QString expFilter;
874+
if ( expFilterIt != expFilterList.constEnd() )
875+
{
876+
expFilter = *expFilterIt;
877+
}
878+
std::shared_ptr<QgsExpression> filter( new QgsExpression( expFilter ) );
879+
if ( filter )
880+
{
881+
if ( filter->hasParserError() )
882+
{
883+
QgsMessageLog::logMessage( filter->parserErrorString() );
884+
}
885+
else
886+
{
887+
if ( filter->needsGeometry() )
888+
{
889+
action.featureRequest.setFlags( QgsFeatureRequest::NoFlags );
890+
}
891+
action.featureRequest.setFilterExpression( filter->expression() );
892+
}
893+
}
894+
}
895+
}
896+
else
897+
{
898+
QgsMessageLog::logMessage( "There has to be a 1:1 mapping between each element in a TYPENAME and the EXP_FILTER list" );
899+
}
900+
}
901+
902+
if ( parameters.contains( QStringLiteral( "BBOX" ) ) )
903+
{
904+
// get bbox value
905+
QString bbox = parameters.value( QStringLiteral( "BBOX" ) );
906+
if ( bbox.isEmpty() )
907+
{
908+
throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX parameter is empty" ) );
909+
}
910+
911+
// get bbox corners
912+
QStringList corners = bbox.split( "," );
913+
if ( corners.size() != 4 )
914+
{
915+
throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 elements: '%1'" ).arg( bbox ) );
916+
}
917+
918+
// convert corners to double
919+
double d[4];
920+
bool ok;
921+
for ( int i = 0; i < 4; i++ )
922+
{
923+
corners[i].replace( QLatin1String( " " ), QLatin1String( "+" ) );
924+
d[i] = corners[i].toDouble( &ok );
925+
if ( !ok )
926+
{
927+
throw QgsRequestNotWellFormedException( QStringLiteral( "BBOX has to be composed of 4 double: '%1'" ).arg( bbox ) );
928+
}
929+
}
930+
// create extent
931+
QgsRectangle extent( d[0], d[1], d[2], d[3] );
932+
933+
// set feature request filter rectangle
934+
QList<transactionDelete>::iterator dIt = request.deletes.begin();
935+
for ( ; dIt != request.deletes.end(); ++dIt )
936+
{
937+
transactionDelete &action = *dIt;
938+
action.featureRequest.setFilterRect( extent );
939+
}
940+
return request;
941+
}
942+
else if ( parameters.contains( QStringLiteral( "FILTER" ) ) )
943+
{
944+
QString filterName = parameters.value( QStringLiteral( "FILTER" ) );
945+
QStringList filterList;
946+
QRegExp rx( "\\(([^()]+)\\)" );
947+
if ( rx.indexIn( filterName, 0 ) == -1 )
948+
{
949+
filterList << filterName;
950+
}
951+
else
952+
{
953+
int pos = 0;
954+
while ( ( pos = rx.indexIn( filterName, pos ) ) != -1 )
955+
{
956+
filterList << rx.cap( 1 );
957+
pos += rx.matchedLength();
958+
}
959+
}
960+
961+
// Verifying the 1:1 mapping between TYPENAME and FILTER
962+
if ( request.deletes.size() != filterList.size() )
963+
{
964+
throw QgsRequestNotWellFormedException( QStringLiteral( "There has to be a 1:1 mapping between each element in a TYPENAME and the FILTER list" ) );
965+
}
966+
967+
// set feature request filter expression based on filter element
968+
QList<transactionDelete>::iterator dIt = request.deletes.begin();
969+
QStringList::const_iterator filterIt = filterList.constBegin();
970+
for ( ; dIt != request.deletes.end(); ++dIt )
971+
{
972+
transactionDelete &action = *dIt;
973+
974+
// Get Filter for this typeName
975+
QDomDocument filter;
976+
if ( filterIt != filterList.constEnd() )
977+
{
978+
QString errorMsg;
979+
if ( !filter.setContent( *filterIt, true, &errorMsg ) )
980+
{
981+
throw QgsRequestNotWellFormedException( QStringLiteral( "error message: %1. The XML string was: %2" ).arg( errorMsg, *filterIt ) );
982+
}
983+
}
984+
985+
QDomElement filterElem = filter.firstChildElement();
986+
action.featureRequest = parseFilterElement( action.typeName, filterElem );
987+
988+
if ( filterIt != filterList.constEnd() )
989+
{
990+
++filterIt;
991+
}
992+
}
993+
return request;
994+
}
995+
996+
return request;
997+
}
998+
744999
transactionRequest parseTransactionRequestBody( QDomElement &docElem )
7451000
{
7461001
transactionRequest request;

‎src/server/services/wfs/qgswfstransaction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,14 @@ namespace QgsWfs
9494
*/
9595
transactionRequest parseTransactionRequestBody( QDomElement &docElem );
9696

97+
transactionRequest parseTransactionParameters( QgsServerRequest::Parameters parameters );
98+
99+
/** Transform GML feature nodes to features
100+
*/
97101
QgsFeatureList featuresFromGML( QDomNodeList featureNodeList, QgsVectorDataProvider *provider );
98102

103+
/** Perform the transaction
104+
*/
99105
void performTransaction( transactionRequest &aRequest, QgsServerInterface *serverIface, const QgsProject *project );
100106

101107
/**

0 commit comments

Comments
 (0)
Please sign in to comment.