Permalink
Browse files

SLD: support LayerFeatureConstraints/FeatureTypeConstraint/Filter (#4025

)
  • Loading branch information...
1 parent c8f813d commit c391b86b12ba08e52184e5d14e5d7f2b28c0260d @rouault rouault committed Sep 10, 2015
Showing with 203 additions and 79 deletions.
  1. +202 −78 mapogcsld.c
  2. +1 −1 msautotest
View
@@ -167,6 +167,12 @@ int msSLDApplySLD(mapObj *map, char *psSLDXML, int iLayer,
int bFailedExpression=0;
pasLayers = msSLDParseSLD(map, psSLDXML, &nLayers);
+ if( pasLayers == NULL ) {
+ errorObj* psError = msGetErrorObj();
+ if( psError && psError->code != MS_NOERR )
+ return MS_FAILURE;
+ }
+
/* -------------------------------------------------------------------- */
/* If the same layer is given more that once, we need to */
/* duplicate it. */
@@ -398,6 +404,74 @@ int msSLDApplySLD(mapObj *map, char *psSLDXML, int iLayer,
goto sld_cleanup;
}
} else {
+ lp = GET_LAYER(map, i);
+
+ /* The SLD might have a FeatureTypeConstraint */
+ if( pasLayers[j].filter.type == MS_EXPRESSION )
+ {
+ char* pszFilter;
+ if( lp->filter.string && lp->filter.type == MS_EXPRESSION )
+ {
+ pszFilter = msStringConcatenate(NULL, "((");
+ pszFilter = msStringConcatenate(pszBuffer, lp->filter.string);
+ pszFilter = msStringConcatenate(pszBuffer, ") AND (");
+ pszFilter = msStringConcatenate(pszBuffer, pasLayers[j].filter.string);
+ pszFilter = msStringConcatenate(pszBuffer, "))");
+ }
+ else
+ {
+ pszFilter = msStringConcatenate(NULL, "(");
+ pszFilter = msStringConcatenate(pszFilter, pasLayers[j].filter.string);
+ pszFilter = msStringConcatenate(pszFilter, ")");
+ }
+ if (lp->connectiontype == MS_POSTGIS || lp->connectiontype == MS_ORACLESPATIAL ||
+ lp->connectiontype == MS_SDE || lp->connectiontype == MS_PLUGIN) {
+ /* Try to make a SQL filter from the FeatureTypeConstraint. */
+ /*But this will only work with very simple expressions. */
+ int bManagedToBuildSQLFilter = FALSE;
+ if( lp->filter.string == NULL ) {
+ psExpressionNode = BuildExpressionTree(pszFilter,NULL);
+ if (psExpressionNode) {
+ pszSqlExpression = FLTGetSQLExpression(psExpressionNode,lp);
+ if (pszSqlExpression) {
+ bManagedToBuildSQLFilter = TRUE;
+ msLoadExpressionString(&lp->filter, pszSqlExpression);
+ msFree(pszSqlExpression);
+ }
+ FLTFreeFilterEncodingNode(psExpressionNode);
+ }
+ }
+ /* Otherwise fallback to adding the FeatureTypeConstraint to each */
+ /* class expression. Runtime will be slow (client-side), but no */
+ /* other choice... */
+ if( !bManagedToBuildSQLFilter ) {
+ for (k=0; k<lp->numclasses; k++) {
+ if( lp->class[k]->expression.string == NULL ) {
+ char* pszExpr = msStringConcatenate(NULL, "(");
+ pszExpr = msStringConcatenate(pszExpr, pasLayers[j].filter.string);
+ pszExpr = msStringConcatenate(pszExpr, ")");
+ msLoadExpressionString(&(lp->class[k]->expression), pszExpr);
+ msFree(pszExpr);
+ }
+ else if (lp->class[k]->expression.type == MS_EXPRESSION) {
+ char* pszExpr = msStringConcatenate(NULL, "((");
+ pszExpr = msStringConcatenate(pszExpr, pasLayers[j].filter.string);
+ pszExpr = msStringConcatenate(pszExpr, ") AND (");
+ pszExpr = msStringConcatenate(pszExpr, lp->class[k]->expression.string);
+ pszExpr = msStringConcatenate(pszExpr, "))");
+ msLoadExpressionString(&(lp->class[k]->expression), pszExpr);
+ msFree(pszExpr);
+ }
+ }
+ }
+ }
+ else {
+ msLoadExpressionString(&lp->filter, pszFilter);
+ }
+
+ msFree(pszFilter);
+ }
+
/*in some cases it would make sense to concatenate all the class
expressions and use it to set the filter on the layer. This
could increase performace. Will do it for db types layers #2840*/
@@ -617,7 +691,15 @@ layerObj *msSLDParseSLD(mapObj *map, char *psSLDXML, int *pnLayers)
if (psName && psName->psChild && psName->psChild->pszValue)
pasLayers[iLayer].name = msStrdup(psName->psChild->pszValue);
- msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]);
+ if( msSLDParseNamedLayer(psNamedLayer, &pasLayers[iLayer]) != MS_SUCCESS ) {
+ int i;
+ for (i=0; i<=iLayer; i++)
+ freeLayer(&pasLayers[i]);
+ msFree(pasLayers);
+ nLayers = 0;
+ pasLayers = NULL;
+ break;
+ }
psNamedLayer = psNamedLayer->psNext;
iLayer++;
@@ -729,6 +811,79 @@ void _SLDApplyRuleValues(CPLXMLNode *psRule, layerObj *psLayer,
}
+/************************************************************************/
+/* msSLDGetCommonExpressionFromFilter */
+/* */
+/* Get a commomn expression valid from the filter valid for the */
+/* temporary layer. */
+/************************************************************************/
+static char* msSLDGetCommonExpressionFromFilter(CPLXMLNode* psFilter,
+ layerObj *psLayer)
+{
+ char *pszExpression = NULL;
+ CPLXMLNode *psTmpNextNode = NULL;
+ CPLXMLNode *psTmpNode = NULL;
+ FilterEncodingNode *psNode = NULL;
+ char *pszTmpFilter = NULL;
+ layerObj *psCurrentLayer = NULL;
+ const char *pszWmsName=NULL;
+ const char *key=NULL;
+
+ /* clone the tree and set the next node to null */
+ /* so we only have the Filter node */
+ psTmpNode = CPLCloneXMLTree(psFilter);
+ psTmpNextNode = psTmpNode->psNext;
+ psTmpNode->psNext = NULL;
+ pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
+ psTmpNode->psNext = psTmpNextNode;
+ CPLDestroyXMLNode(psTmpNode);
+
+ if (pszTmpFilter) {
+ psNode = FLTParseFilterEncoding(pszTmpFilter);
+
+ CPLFree(pszTmpFilter);
+ }
+
+ if (psNode) {
+ int j;
+
+ /*preparse the filter for possible gml aliases set on the layer's metada:
+ "gml_NA3DESC_alias" "alias_name" and filter could be
+ <ogc:PropertyName>alias_name</ogc:PropertyName> #3079*/
+ for (j=0; j<psLayer->map->numlayers; j++) {
+ psCurrentLayer = GET_LAYER(psLayer->map, j);
+
+ pszWmsName = msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
+
+ if ((psCurrentLayer->name && psLayer->name &&
+ strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
+ (psCurrentLayer->group && psLayer->name &&
+ strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
+ (psLayer->name && pszWmsName &&
+ strcasecmp(pszWmsName, psLayer->name) == 0))
+ break;
+ }
+ if (j < psLayer->map->numlayers) {
+ /*make sure that the tmp layer has all the metadata that
+ the orinal layer has, allowing to do parsing for
+ such things as gml_attribute_type #3052*/
+ while (1) {
+ key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
+ if (!key)
+ break;
+ else
+ msInsertHashTable(&psLayer->metadata, key,
+ msLookupHashTable(&psCurrentLayer->metadata, key));
+ }
+ FLTPreParseFilterForAlias(psNode, psLayer->map, j, "G");
+ }
+
+ pszExpression = FLTGetCommonExpression(psNode, psLayer);
+ FLTFreeFilterEncodingNode(psNode);
+ }
+
+ return pszExpression;
+}
/************************************************************************/
/* msSLDParseNamedLayer */
@@ -740,15 +895,9 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
CPLXMLNode *psFeatureTypeStyle, *psRule, *psUserStyle;
CPLXMLNode *psSLDName = NULL, *psNamedStyle=NULL;
CPLXMLNode *psElseFilter = NULL, *psFilter=NULL;
- CPLXMLNode *psTmpNode = NULL;
- FilterEncodingNode *psNode = NULL;
+ CPLXMLNode *psLayerFeatureConstraints = NULL;
int nNewClasses=0, nClassBeforeFilter=0, nClassAfterFilter=0;
int nClassAfterRule=0, nClassBeforeRule=0;
- char *pszTmpFilter = NULL;
- layerObj *psCurrentLayer = NULL;
- const char *pszWmsName=NULL;
- int j=0;
- const char *key=NULL;
if (!psRoot || !psLayer)
return MS_FAILURE;
@@ -792,69 +941,11 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
/* NOTE : Spatial Filter is not supported. */
/* -------------------------------------------------------------------- */
psFilter = CPLGetXMLNode(psRule, "Filter");
- if (psFilter && psFilter->psChild &&
- psFilter->psChild->pszValue) {
- CPLXMLNode *psTmpNextNode = NULL;
- /* clone the tree and set the next node to null */
- /* so we only have the Filter node */
- psTmpNode = CPLCloneXMLTree(psFilter);
- psTmpNextNode = psTmpNode->psNext;
- psTmpNode->psNext = NULL;
- pszTmpFilter = CPLSerializeXMLTree(psTmpNode);
- psTmpNode->psNext = psTmpNextNode;
- CPLDestroyXMLNode(psTmpNode);
-
- if (pszTmpFilter) {
- /* nTmp = strlen(psFilter->psChild->pszValue)+17; */
- /* pszTmpFilter = malloc(sizeof(char)*nTmp); */
- /* sprintf(pszTmpFilter,"<Filter>%s</Filter>", */
- /* psFilter->psChild->pszValue); */
- /* pszTmpFilter[nTmp-1]='\0'; */
- psNode = FLTParseFilterEncoding(pszTmpFilter);
-
- CPLFree(pszTmpFilter);
- }
-
- if (psNode) {
- char *pszExpression = NULL;
- int i;
-
- /*preparse the filter for possible gml aliases set on the layer's metada:
- "gml_NA3DESC_alias" "alias_name" and filter could be
- <ogc:PropertyName>alias_name</ogc:PropertyName> #3079*/
- for (j=0; j<psLayer->map->numlayers; j++) {
- psCurrentLayer = GET_LAYER(psLayer->map, j);
-
- pszWmsName = msOWSLookupMetadata(&(psCurrentLayer->metadata), "MO", "name");
-
- if ((psCurrentLayer->name && psLayer->name &&
- strcasecmp(psCurrentLayer->name, psLayer->name) == 0) ||
- (psCurrentLayer->group && psLayer->name &&
- strcasecmp(psCurrentLayer->group, psLayer->name) == 0) ||
- (psLayer->name && pszWmsName &&
- strcasecmp(pszWmsName, psLayer->name) == 0))
- break;
- }
- if (j < psLayer->map->numlayers) {
- /*make sure that the tmp layer has all the metadata that
- the orinal layer has, allowing to do parsing for
- such things as gml_attribute_type #3052*/
- while (1) {
- key = msNextKeyFromHashTable(&psCurrentLayer->metadata, key);
- if (!key)
- break;
- else
- msInsertHashTable(&psLayer->metadata, key,
- msLookupHashTable(&psCurrentLayer->metadata, key));
- }
- FLTPreParseFilterForAlias(psNode, psLayer->map, j, "G");
- }
-
- pszExpression = FLTGetCommonExpression(psNode, psLayer);
- FLTFreeFilterEncodingNode(psNode);
- psNode = NULL;
-
- if (pszExpression) {
+ if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
+ char* pszExpression = msSLDGetCommonExpressionFromFilter(psFilter,
+ psLayer);
+ if (pszExpression) {
+ int i;
nNewClasses =
nClassAfterFilter - nClassBeforeFilter;
for (i=0; i<nNewClasses; i++) {
@@ -864,8 +955,6 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
}
msFree(pszExpression);
pszExpression = NULL;
- }
-
}
}
nClassAfterRule = psLayer->numclasses;
@@ -917,6 +1006,46 @@ int msSLDParseNamedLayer(CPLXMLNode *psRoot, layerObj *psLayer)
}
}
+ /* Deal with LayerFeatureConstraints */
+ psLayerFeatureConstraints = CPLGetXMLNode(psRoot, "LayerFeatureConstraints");
+ if( psLayerFeatureConstraints != NULL ) {
+ CPLXMLNode* psIter = psLayerFeatureConstraints->psChild;
+ CPLXMLNode* psFeatureTypeConstraint = NULL;
+ for(; psIter != NULL; psIter = psIter->psNext ) {
+ if( psIter->eType == CXT_Element &&
+ strcmp(psIter->pszValue, "FeatureTypeConstraint") == 0 ) {
+ if( psFeatureTypeConstraint == NULL ) {
+ psFeatureTypeConstraint = psIter;
+ } else {
+ msSetError(MS_WMSERR, "Only one single FeatureTypeConstraint element "
+ "per LayerFeatureConstraints is supported", "");
+ return MS_FAILURE;
+ }
+ }
+ }
+ if( psFeatureTypeConstraint != NULL ) {
+ if( CPLGetXMLNode(psFeatureTypeConstraint, "FeatureTypeName") != NULL ) {
+ msSetError(MS_WMSERR, "FeatureTypeName element is not "
+ "supported in FeatureTypeConstraint", "");
+ return MS_FAILURE;
+ }
+ if( CPLGetXMLNode(psFeatureTypeConstraint, "Extent") != NULL ) {
+ msSetError(MS_WMSERR, "Extent element is not "
+ "supported in FeatureTypeConstraint", "");
+ return MS_FAILURE;
+ }
+ psFilter = CPLGetXMLNode(psFeatureTypeConstraint, "Filter");
+ if (psFilter && psFilter->psChild && psFilter->psChild->pszValue) {
+ char* pszExpression = msSLDGetCommonExpressionFromFilter(psFilter,
+ psLayer);
+ if (pszExpression) {
+ msLoadExpressionString(&psLayer->filter, pszExpression);
+ msFree(pszExpression);
+ }
+ }
+ }
+ }
+
return MS_SUCCESS;
}
@@ -4736,17 +4865,13 @@ FilterEncodingNode *BuildExpressionTree(char *pszExpression,
{
int nLength = 0;
int nOperators=0;
- char *pszFinalExpression = NULL;
char *pszComparionValue=NULL, *pszAttibuteName=NULL;
char *pszAttibuteValue=NULL;
char *pszLeftExpression=NULL, *pszRightExpression=NULL, *pszOperator=NULL;
if (!pszExpression || (nLength = strlen(pszExpression)) <=0)
return NULL;
- pszFinalExpression = (char *)malloc(sizeof(char)*(nLength+1));
- pszFinalExpression[0] = '\0';
-
/* -------------------------------------------------------------------- */
/* First we check how many logical operators are there : */
/* - if none : It means It is a comparision operator (like =, */
@@ -4867,7 +4992,6 @@ FilterEncodingNode *BuildExpressionTree(char *pszExpression,
return psNode;
} else {
- msFree(pszFinalExpression);
return NULL;
}
}

3 comments on commit c391b86

@tigerfoot
Contributor

Would be cool to not commit on msautotest, so patches apply directly on tar release.
Helpful for packagers ;-)

@tbonfort
Member

sure! let's remove all tests :) more seriously, we've changed how tests get run on branch-7 onwards and don't need a git subproject and such commits anymore, but they are here to stay on branch-6-4 unless someone wants to backport those changes.

@tigerfoot
Contributor

@tbonfort good to know it was changed on 7.0
I understand your wink for the backport :-)))

Please sign in to comment.