Skip to content

Commit c391b86

Browse files
committed
SLD: support LayerFeatureConstraints/FeatureTypeConstraint/Filter (#4025)
1 parent c8f813d commit c391b86

File tree

2 files changed

+203
-79
lines changed

2 files changed

+203
-79
lines changed

mapogcsld.c

+202-78
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ int msSLDApplySLD(mapObj *map, char *psSLDXML, int iLayer,
167167
int bFailedExpression=0;
168168

169169
pasLayers = msSLDParseSLD(map, psSLDXML, &nLayers);
170+
if( pasLayers == NULL ) {
171+
errorObj* psError = msGetErrorObj();
172+
if( psError && psError->code != MS_NOERR )
173+
return MS_FAILURE;
174+
}
175+
170176
/* -------------------------------------------------------------------- */
171177
/* If the same layer is given more that once, we need to */
172178
/* duplicate it. */
@@ -398,6 +404,74 @@ int msSLDApplySLD(mapObj *map, char *psSLDXML, int iLayer,
398404
goto sld_cleanup;
399405
}
400406
} else {
407+
lp = GET_LAYER(map, i);
408+
409+
/* The SLD might have a FeatureTypeConstraint */
410+
if( pasLayers[j].filter.type == MS_EXPRESSION )
411+
{
412+
char* pszFilter;
413+
if( lp->filter.string && lp->filter.type == MS_EXPRESSION )
414+
{
415+
pszFilter = msStringConcatenate(NULL, "((");
416+
pszFilter = msStringConcatenate(pszBuffer, lp->filter.string);
417+
pszFilter = msStringConcatenate(pszBuffer, ") AND (");
418+
pszFilter = msStringConcatenate(pszBuffer, pasLayers[j].filter.string);
419+
pszFilter = msStringConcatenate(pszBuffer, "))");
420+
}
421+
else
422+
{
423+
pszFilter = msStringConcatenate(NULL, "(");
424+
pszFilter = msStringConcatenate(pszFilter, pasLayers[j].filter.string);
425+
pszFilter = msStringConcatenate(pszFilter, ")");
426+
}
427+
if (lp->connectiontype == MS_POSTGIS || lp->connectiontype == MS_ORACLESPATIAL ||
428+
lp->connectiontype == MS_SDE || lp->connectiontype == MS_PLUGIN) {
429+
/* Try to make a SQL filter from the FeatureTypeConstraint. */
430+
/*But this will only work with very simple expressions. */
431+
int bManagedToBuildSQLFilter = FALSE;
432+
if( lp->filter.string == NULL ) {
433+
psExpressionNode = BuildExpressionTree(pszFilter,NULL);
434+
if (psExpressionNode) {
435+
pszSqlExpression = FLTGetSQLExpression(psExpressionNode,lp);
436+
if (pszSqlExpression) {
437+
bManagedToBuildSQLFilter = TRUE;
438+
msLoadExpressionString(&lp->filter, pszSqlExpression);
439+
msFree(pszSqlExpression);
440+
}
441+
FLTFreeFilterEncodingNode(psExpressionNode);
442+
}
443+
}
444+
/* Otherwise fallback to adding the FeatureTypeConstraint to each */
445+
/* class expression. Runtime will be slow (client-side), but no */
446+
/* other choice... */
447+
if( !bManagedToBuildSQLFilter ) {
448+
for (k=0; k<lp->numclasses; k++) {
449+
if( lp->class[k]->expression.string == NULL ) {
450+
char* pszExpr = msStringConcatenate(NULL, "(");
451+
pszExpr = msStringConcatenate(pszExpr, pasLayers[j].filter.string);
452+
pszExpr = msStringConcatenate(pszExpr, ")");
453+
msLoadExpressionString(&(lp->class[k]->expression), pszExpr);
454+
msFree(pszExpr);
455+
}
456+
else if (lp->class[k]->expression.type == MS_EXPRESSION) {
457+
char* pszExpr = msStringConcatenate(NULL, "((");
458+
pszExpr = msStringConcatenate(pszExpr, pasLayers[j].filter.string);
459+
pszExpr = msStringConcatenate(pszExpr, ") AND (");
460+
pszExpr = msStringConcatenate(pszExpr, lp->class[k]->expression.string);
461+
pszExpr = msStringConcatenate(pszExpr, "))");
462+
msLoadExpressionString(&(lp->class[k]->expression), pszExpr);
463+
msFree(pszExpr);
464+
}
465+
}
466+
}
467+
}
468+
else {
469+
msLoadExpressionString(&lp->filter, pszFilter);
470+
}
471+
472+
msFree(pszFilter);
473+
}
474+
401475
/*in some cases it would make sense to concatenate all the class
402476
expressions and use it to set the filter on the layer. This
403477
could increase performace. Will do it for db types layers #2840*/
@@ -617,7 +691,15 @@ layerObj *msSLDParseSLD(mapObj *map, char *psSLDXML, int *pnLayers)
617691
if (psName && psName->psChild && psName->psChild->pszValue)
618692
pasLayers[iLayer].name = msStrdup(psName->psChild->pszValue);
619693

620-
msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]);
694+
if( msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]) != MS_SUCCESS ) {
695+
int i;
696+
for (i=0; i<=iLayer; i++)
697+
freeLayer(&pasLayers[i]);
698+
msFree(pasLayers);
699+
nLayers = 0;
700+
pasLayers = NULL;
701+
break;
702+
}
621703

622704
psNamedLayer = psNamedLayer->psNext;
623705
iLayer++;
@@ -729,6 +811,79 @@ void _SLDApplyRuleValues(CPLXMLNode *psRule, layerObj *psLayer,
729811

730812
}
731813

814+
/************************************************************************/
815+
/* msSLDGetCommonExpressionFromFilter */
816+
/* */
817+
/* Get a commomn expression valid from the filter valid for the */
818+
/* temporary layer. */
819+
/************************************************************************/
820+
static char* msSLDGetCommonExpressionFromFilter(CPLXMLNode* psFilter,
821+
layerObj *psLayer)
822+
{
823+
char *pszExpression = NULL;
824+
CPLXMLNode *psTmpNextNode = NULL;
825+
CPLXMLNode *psTmpNode = NULL;
826+
FilterEncodingNode *psNode = NULL;
827+
char *pszTmpFilter = NULL;
828+
layerObj *psCurrentLayer = NULL;
829+
const char *pszWmsName=NULL;
830+
const char *key=NULL;
831+
832+
/* clone the tree and set the next node to null */
833+
/* so we only have the Filter node */
834+
psTmpNode = CPLCloneXMLTree(psFilter);
835+
psTmpNextNode = psTmpNode->psNext;
836+
psTmpNode->psNext = NULL;
837+
pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
838+
psTmpNode->psNext = psTmpNextNode;
839+
CPLDestroyXMLNode(psTmpNode);
840+
841+
if (pszTmpFilter) {
842+
psNode = FLTParseFilterEncoding(pszTmpFilter);
843+
844+
CPLFree(pszTmpFilter);
845+
}
846+
847+
if (psNode) {
848+
int j;
849+
850+
/*preparse the filter for possible gml aliases set on the layer's metada:
851+
"gml_NA3DESC_alias" "alias_name" and filter could be
852+
<ogc:PropertyName>alias_name</ogc:PropertyName> #3079*/
853+
for (j=0; j<psLayer->map->numlayers; j++) {
854+
psCurrentLayer = GET_LAYER(psLayer->map, j);
855+
856+
pszWmsName = msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
857+
858+
if ((psCurrentLayer->name && psLayer->name &&
859+
strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
860+
(psCurrentLayer->group && psLayer->name &&
861+
strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
862+
(psLayer->name && pszWmsName &&
863+
strcasecmp(pszWmsName, psLayer->name) == 0))
864+
break;
865+
}
866+
if (j < psLayer->map->numlayers) {
867+
/*make sure that the tmp layer has all the metadata that
868+
the orinal layer has, allowing to do parsing for
869+
such things as gml_attribute_type #3052*/
870+
while (1) {
871+
key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
872+
if (!key)
873+
break;
874+
else
875+
msInsertHashTable(&psLayer->metadata, key,
876+
msLookupHashTable(&psCurrentLayer->metadata, key));
877+
}
878+
FLTPreParseFilterForAlias(psNode, psLayer->map, j, "G");
879+
}
880+
881+
pszExpression = FLTGetCommonExpression(psNode, psLayer);
882+
FLTFreeFilterEncodingNode(psNode);
883+
}
884+
885+
return pszExpression;
886+
}
732887

733888
/************************************************************************/
734889
/* msSLDParseNamedLayer */
@@ -740,15 +895,9 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
740895
CPLXMLNode *psFeatureTypeStyle, *psRule, *psUserStyle;
741896
CPLXMLNode *psSLDName = NULL, *psNamedStyle=NULL;
742897
CPLXMLNode *psElseFilter = NULL, *psFilter=NULL;
743-
CPLXMLNode *psTmpNode = NULL;
744-
FilterEncodingNode *psNode = NULL;
898+
CPLXMLNode *psLayerFeatureConstraints = NULL;
745899
int nNewClasses=0, nClassBeforeFilter=0, nClassAfterFilter=0;
746900
int nClassAfterRule=0, nClassBeforeRule=0;
747-
char *pszTmpFilter = NULL;
748-
layerObj *psCurrentLayer = NULL;
749-
const char *pszWmsName=NULL;
750-
int j=0;
751-
const char *key=NULL;
752901

753902
if (!psRoot || !psLayer)
754903
return MS_FAILURE;
@@ -792,69 +941,11 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
792941
/* NOTE : Spatial Filter is not supported. */
793942
/* -------------------------------------------------------------------- */
794943
psFilter = CPLGetXMLNode(psRule, "Filter");
795-
if (psFilter && psFilter->psChild &&
796-
psFilter->psChild->pszValue) {
797-
CPLXMLNode *psTmpNextNode = NULL;
798-
/* clone the tree and set the next node to null */
799-
/* so we only have the Filter node */
800-
psTmpNode = CPLCloneXMLTree(psFilter);
801-
psTmpNextNode = psTmpNode->psNext;
802-
psTmpNode->psNext = NULL;
803-
pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
804-
psTmpNode->psNext = psTmpNextNode;
805-
CPLDestroyXMLNode(psTmpNode);
806-
807-
if (pszTmpFilter) {
808-
/* nTmp = strlen(psFilter->psChild->pszValue)+17; */
809-
/* pszTmpFilter = malloc(sizeof(char)*nTmp); */
810-
/* sprintf(pszTmpFilter,"<Filter>%s</Filter>", */
811-
/* psFilter->psChild->pszValue); */
812-
/* pszTmpFilter[nTmp-1]='\0'; */
813-
psNode = FLTParseFilterEncoding(pszTmpFilter);
814-
815-
CPLFree(pszTmpFilter);
816-
}
817-
818-
if (psNode) {
819-
char *pszExpression = NULL;
820-
int i;
821-
822-
/*preparse the filter for possible gml aliases set on the layer's metada:
823-
"gml_NA3DESC_alias" "alias_name" and filter could be
824-
<ogc:PropertyName>alias_name</ogc:PropertyName> #3079*/
825-
for (j=0; j<psLayer->map->numlayers; j++) {
826-
psCurrentLayer = GET_LAYER(psLayer->map, j);
827-
828-
pszWmsName = msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
829-
830-
if ((psCurrentLayer->name && psLayer->name &&
831-
strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
832-
(psCurrentLayer->group && psLayer->name &&
833-
strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
834-
(psLayer->name && pszWmsName &&
835-
strcasecmp(pszWmsName, psLayer->name) == 0))
836-
break;
837-
}
838-
if (j < psLayer->map->numlayers) {
839-
/*make sure that the tmp layer has all the metadata that
840-
the orinal layer has, allowing to do parsing for
841-
such things as gml_attribute_type #3052*/
842-
while (1) {
843-
key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
844-
if (!key)
845-
break;
846-
else
847-
msInsertHashTable(&psLayer->metadata, key,
848-
msLookupHashTable(&psCurrentLayer->metadata, key));
849-
}
850-
FLTPreParseFilterForAlias(psNode, psLayer->map, j, "G");
851-
}
852-
853-
pszExpression = FLTGetCommonExpression(psNode, psLayer);
854-
FLTFreeFilterEncodingNode(psNode);
855-
psNode = NULL;
856-
857-
if (pszExpression) {
944+
if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
945+
char* pszExpression = msSLDGetCommonExpressionFromFilter(psFilter,
946+
psLayer);
947+
if (pszExpression) {
948+
int i;
858949
nNewClasses =
859950
nClassAfterFilter - nClassBeforeFilter;
860951
for (i=0; i<nNewClasses; i++) {
@@ -864,8 +955,6 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
864955
}
865956
msFree(pszExpression);
866957
pszExpression = NULL;
867-
}
868-
869958
}
870959
}
871960
nClassAfterRule = psLayer->numclasses;
@@ -917,6 +1006,46 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
9171006
}
9181007
}
9191008

1009+
/* Deal with LayerFeatureConstraints */
1010+
psLayerFeatureConstraints = CPLGetXMLNode(psRoot, "LayerFeatureConstraints");
1011+
if( psLayerFeatureConstraints != NULL ) {
1012+
CPLXMLNode* psIter = psLayerFeatureConstraints->psChild;
1013+
CPLXMLNode* psFeatureTypeConstraint = NULL;
1014+
for(; psIter != NULL; psIter = psIter->psNext ) {
1015+
if( psIter->eType == CXT_Element &&
1016+
strcmp(psIter->pszValue, "FeatureTypeConstraint") == 0 ) {
1017+
if( psFeatureTypeConstraint == NULL ) {
1018+
psFeatureTypeConstraint = psIter;
1019+
} else {
1020+
msSetError(MS_WMSERR, "Only one single FeatureTypeConstraint element "
1021+
"per LayerFeatureConstraints is supported", "");
1022+
return MS_FAILURE;
1023+
}
1024+
}
1025+
}
1026+
if( psFeatureTypeConstraint != NULL ) {
1027+
if( CPLGetXMLNode(psFeatureTypeConstraint, "FeatureTypeName") != NULL ) {
1028+
msSetError(MS_WMSERR, "FeatureTypeName element is not "
1029+
"supported in FeatureTypeConstraint", "");
1030+
return MS_FAILURE;
1031+
}
1032+
if( CPLGetXMLNode(psFeatureTypeConstraint, "Extent") != NULL ) {
1033+
msSetError(MS_WMSERR, "Extent element is not "
1034+
"supported in FeatureTypeConstraint", "");
1035+
return MS_FAILURE;
1036+
}
1037+
psFilter = CPLGetXMLNode(psFeatureTypeConstraint, "Filter");
1038+
if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
1039+
char* pszExpression = msSLDGetCommonExpressionFromFilter(psFilter,
1040+
psLayer);
1041+
if (pszExpression) {
1042+
msLoadExpressionString(&psLayer->filter, pszExpression);
1043+
msFree(pszExpression);
1044+
}
1045+
}
1046+
}
1047+
}
1048+
9201049
return MS_SUCCESS;
9211050
}
9221051

@@ -4736,17 +4865,13 @@ FilterEncodingNode *BuildExpressionTree(char *pszExpression,
47364865
{
47374866
int nLength = 0;
47384867
int nOperators=0;
4739-
char *pszFinalExpression = NULL;
47404868
char *pszComparionValue=NULL, *pszAttibuteName=NULL;
47414869
char *pszAttibuteValue=NULL;
47424870
char *pszLeftExpression=NULL, *pszRightExpression=NULL, *pszOperator=NULL;
47434871

47444872
if (!pszExpression || (nLength = strlen(pszExpression)) <=0)
47454873
return NULL;
47464874

4747-
pszFinalExpression = (char *)malloc(sizeof(char)*(nLength+1));
4748-
pszFinalExpression[0] = '\0';
4749-
47504875
/* -------------------------------------------------------------------- */
47514876
/* First we check how many logical operators are there : */
47524877
/* - if none : It means It is a comparision operator (like =, */
@@ -4867,7 +4992,6 @@ FilterEncodingNode *BuildExpressionTree(char *pszExpression,
48674992

48684993
return psNode;
48694994
} else {
4870-
msFree(pszFinalExpression);
48714995
return NULL;
48724996
}
48734997
}

msautotest

Submodule msautotest updated from e420f0c to 1ac3d4a

0 commit comments

Comments
 (0)