/**********************************************************************
* $Id$
*
* Project: MapServer
* Purpose: OGC Filter Encoding implementation
* Author: Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
*
**********************************************************************
* Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
****************************************************************************/
#include "mapserver-config.h"
#include "cpl_minixml.h"
#include "cpl_string.h"
#include "mapogcfilter.h"
#include "mapserver.h"
#include "mapowscommon.h"
#include "maptime.h"
#include "mapows.h"
#include
#if 0
static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode);
#endif
#if !(defined(_WIN32) && !defined(__CYGWIN__))
static inline void IGUR_double(double ignored) {
(void)ignored;
} /* Ignore GCC Unused Result */
#endif
int FLTIsNumeric(const char *pszValue) {
if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
/*the regex seems to have a problem on windows when mapserver is built using
PHP regex*/
#if defined(_WIN32) && !defined(__CYGWIN__)
int i = 0, nLength = 0, bString = 0;
nLength = strlen(pszValue);
for (i = 0; i < nLength; i++) {
if (i == 0) {
if (!isdigit(pszValue[i]) && pszValue[i] != '-') {
bString = 1;
break;
}
} else if (!isdigit(pszValue[i]) && pszValue[i] != '.') {
bString = 1;
break;
}
}
if (!bString)
return MS_TRUE;
#else
char *p;
IGUR_double(strtod(pszValue, &p));
if (p != pszValue && *p == '\0')
return MS_TRUE;
#endif
}
return MS_FALSE;
}
/*
** Apply an expression to the layer's filter element.
**
*/
int FLTApplyExpressionToLayer(layerObj *lp, const char *pszExpression) {
char *pszFinalExpression = NULL, *pszBuffer = NULL;
/*char *escapedTextString=NULL;*/
int bConcatWhere = 0, bHasAWhere = 0;
if (lp && pszExpression) {
if (lp->connectiontype == MS_POSTGIS ||
lp->connectiontype == MS_ORACLESPATIAL ||
lp->connectiontype == MS_PLUGIN) {
pszFinalExpression = msStrdup("(");
pszFinalExpression =
msStringConcatenate(pszFinalExpression, pszExpression);
pszFinalExpression = msStringConcatenate(pszFinalExpression, ")");
} else if (lp->connectiontype == MS_OGR) {
pszFinalExpression = msStrdup(pszExpression);
if (lp->filter.type != MS_EXPRESSION) {
bConcatWhere = 1;
} else {
if (lp->filter.string && EQUALN(lp->filter.string, "WHERE ", 6)) {
bHasAWhere = 1;
bConcatWhere = 1;
}
}
} else
pszFinalExpression = msStrdup(pszExpression);
if (bConcatWhere)
pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
/* if the filter is set and it's an expression type, concatenate it with
this filter. If not just free it */
if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
pszBuffer = msStringConcatenate(pszBuffer, "((");
if (bHasAWhere)
pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string + 6);
else
pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
pszBuffer = msStringConcatenate(pszBuffer, ") and ");
} else if (lp->filter.string)
msFreeExpression(&lp->filter);
pszBuffer = msStringConcatenate(pszBuffer, pszFinalExpression);
if (lp->filter.string && lp->filter.type == MS_EXPRESSION)
pszBuffer = msStringConcatenate(pszBuffer, ")");
/*assuming that expression was properly escaped
escapedTextString = msStringEscape(pszBuffer);
msLoadExpressionString(&lp->filter,
(char*)CPLSPrintf("%s", escapedTextString));
msFree(escapedTextString);
*/
msLoadExpressionString(&lp->filter, pszBuffer);
msFree(pszFinalExpression);
if (pszBuffer)
msFree(pszBuffer);
return MS_TRUE;
}
return MS_FALSE;
}
char *FLTGetExpressionForValuesRanges(layerObj *lp, const char *item,
const char *value, int forcecharcter) {
int bIscharacter;
char *pszExpression = NULL, *pszTmpExpression = NULL;
char **paszElements = NULL, **papszRangeElements = NULL;
int numelements, i, nrangeelements;
/* double minval, maxval; */
if (lp && item && value) {
if (strstr(value, "/") == NULL) {
/*value(s)*/
paszElements = msStringSplit(value, ',', &numelements);
if (paszElements && numelements > 0) {
if (forcecharcter)
bIscharacter = MS_TRUE;
else
bIscharacter = !FLTIsNumeric(paszElements[0]);
pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
for (i = 0; i < numelements; i++) {
pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
{
if (bIscharacter)
pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
if (bIscharacter)
pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
}
if (bIscharacter) {
pszTmpExpression = msStringConcatenate(pszTmpExpression, " = \"");
} else
pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
{
char *pszEscapedStr = msLayerEscapeSQLParam(lp, paszElements[i]);
pszTmpExpression =
msStringConcatenate(pszTmpExpression, pszEscapedStr);
msFree(pszEscapedStr);
}
if (bIscharacter) {
pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
}
pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
if (pszExpression != NULL)
pszExpression = msStringConcatenate(pszExpression, " OR ");
pszExpression = msStringConcatenate(pszExpression, pszTmpExpression);
msFree(pszTmpExpression);
pszTmpExpression = NULL;
}
pszExpression = msStringConcatenate(pszExpression, ")");
}
msFreeCharArray(paszElements, numelements);
} else {
/*range(s)*/
paszElements = msStringSplit(value, ',', &numelements);
if (paszElements && numelements > 0) {
pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
for (i = 0; i < numelements; i++) {
papszRangeElements =
msStringSplit(paszElements[i], '/', &nrangeelements);
if (papszRangeElements && nrangeelements > 0) {
pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
if (nrangeelements == 2 || nrangeelements == 3) {
/*
minval = atof(papszRangeElements[0]);
maxval = atof(papszRangeElements[1]);
*/
{
pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
}
pszTmpExpression = msStringConcatenate(pszTmpExpression, " >= ");
{
char *pszEscapedStr =
msLayerEscapeSQLParam(lp, papszRangeElements[0]);
pszTmpExpression =
msStringConcatenate(pszTmpExpression, pszEscapedStr);
msFree(pszEscapedStr);
}
pszTmpExpression = msStringConcatenate(pszTmpExpression, " AND ");
{
pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
}
pszTmpExpression = msStringConcatenate(pszTmpExpression, " data, "VERSION");
pszTmp2 = msStringConcatenate(pszTmp2, " NONE ");
pszTmp2 = msStringConcatenate(pszTmp2, pszTmp);
lp->data = msCaseReplaceSubstring(lp->data, pszTmp, pszTmp2);
msFree(pszTmp2);
} else if (strcasestr(lp->data, "SRID")) {
lp->data = msStringConcatenate(lp->data, " NONE");
}
}
}
return msQueryByRect(map);
/* return MS_SUCCESS; */
}
/************************************************************************/
/* FLTSplitFilters */
/* */
/* Split filters separated by parentheses into an array of strings. */
/************************************************************************/
char **FLTSplitFilters(const char *pszStr, int *pnTokens) {
const char *pszTokenBegin;
char **papszRet = NULL;
int nTokens = 0;
char chStringQuote = '\0';
int nXMLIndent = 0;
int bInBracket = FALSE;
if (*pszStr != '(') {
*pnTokens = 0;
return NULL;
}
pszStr++;
pszTokenBegin = pszStr;
while (*pszStr != '\0') {
/* Ignore any character until end of quoted string */
if (chStringQuote != '\0') {
if (*pszStr == chStringQuote)
chStringQuote = 0;
}
/* Detect begin of quoted string only for an XML attribute, i.e. between <
and > */
else if (bInBracket && (*pszStr == '\'' || *pszStr == '"')) {
chStringQuote = *pszStr;
}
/* Begin of XML element */
else if (*pszStr == '') {
bInBracket = FALSE;
nXMLIndent--;
pszStr++;
}
/* End of XML element */
else if (*pszStr == '>') {
bInBracket = FALSE;
}
/* Only detect and of filter when XML indentation goes back to zero */
else if (nXMLIndent == 0 && *pszStr == ')') {
papszRet =
(char **)msSmallRealloc(papszRet, sizeof(char *) * (nTokens + 1));
papszRet[nTokens] = msStrdup(pszTokenBegin);
papszRet[nTokens][pszStr - pszTokenBegin] = '\0';
nTokens++;
if (pszStr[1] != '(') {
break;
}
pszStr++;
pszTokenBegin = pszStr + 1;
}
pszStr++;
}
*pnTokens = nTokens;
return papszRet;
}
/************************************************************************/
/* FLTIsSimpleFilter */
/* */
/* Filter encoding with only attribute queries and only one bbox. */
/************************************************************************/
int FLTIsSimpleFilter(FilterEncodingNode *psNode) {
if (FLTValidForBBoxFilter(psNode)) {
if (FLTNumberOfFilterType(psNode, "DWithin") == 0 &&
FLTNumberOfFilterType(psNode, "Intersect") == 0 &&
FLTNumberOfFilterType(psNode, "Intersects") == 0 &&
FLTNumberOfFilterType(psNode, "Equals") == 0 &&
FLTNumberOfFilterType(psNode, "Disjoint") == 0 &&
FLTNumberOfFilterType(psNode, "Touches") == 0 &&
FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
FLTNumberOfFilterType(psNode, "Within") == 0 &&
FLTNumberOfFilterType(psNode, "Contains") == 0 &&
FLTNumberOfFilterType(psNode, "Overlaps") == 0 &&
FLTNumberOfFilterType(psNode, "Beyond") == 0)
return TRUE;
}
return FALSE;
}
/************************************************************************/
/* FLTApplyFilterToLayer */
/* */
/* Use the filter encoding node to create mapserver expressions */
/* and apply it to the layer. */
/************************************************************************/
int FLTApplyFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
int iLayerIndex) {
layerObj *layer = GET_LAYER(map, iLayerIndex);
if (!layer->vtable) {
int rv = msInitializeVirtualTable(layer);
if (rv != MS_SUCCESS)
return rv;
}
if (!layer->vtable)
return MS_FAILURE;
return layer->vtable->LayerApplyFilterToLayer(psNode, map, iLayerIndex);
}
/************************************************************************/
/* FLTLayerApplyCondSQLFilterToLayer */
/* */
/* Helper function for layer virtual table architecture */
/************************************************************************/
int FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
int iLayerIndex) {
return FLTLayerApplyPlainFilterToLayer(psNode, map, iLayerIndex);
}
/************************************************************************/
/* FLTGetTopBBOX */
/* */
/* Return the "top" BBOX if there's a unique one. */
/************************************************************************/
static int FLTGetTopBBOXInternal(FilterEncodingNode *psNode,
FilterEncodingNode **ppsTopBBOX,
int *pnCount) {
if (psNode->pszValue && strcasecmp(psNode->pszValue, "BBOX") == 0) {
(*pnCount)++;
if (*pnCount == 1) {
*ppsTopBBOX = psNode;
return TRUE;
}
*ppsTopBBOX = NULL;
return FALSE;
} else if (psNode->pszValue && strcasecmp(psNode->pszValue, "AND") == 0) {
return FLTGetTopBBOXInternal(psNode->psLeftNode, ppsTopBBOX, pnCount) &&
FLTGetTopBBOXInternal(psNode->psRightNode, ppsTopBBOX, pnCount);
} else {
return TRUE;
}
}
static FilterEncodingNode *FLTGetTopBBOX(FilterEncodingNode *psNode) {
int nCount = 0;
FilterEncodingNode *psTopBBOX = NULL;
FLTGetTopBBOXInternal(psNode, &psTopBBOX, &nCount);
return psTopBBOX;
}
/************************************************************************/
/* FLTLayerSetInvalidRectIfSupported */
/* */
/* This function will set in *rect a very huge extent if the layer */
/* wfs_use_default_extent_for_getfeature metadata item is set to false */
/* and the layer supports such degenerate rectangle, as a hint that */
/* they should not issue a spatial filter. */
/************************************************************************/
int FLTLayerSetInvalidRectIfSupported(layerObj *lp, rectObj *rect,
const char *metadata_namespaces) {
const char *pszUseDefaultExtent =
msOWSLookupMetadata(&(lp->metadata), metadata_namespaces,
"use_default_extent_for_getfeature");
if (pszUseDefaultExtent && !CSLTestBoolean(pszUseDefaultExtent) &&
(lp->connectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS ||
((lp->connectiontype == MS_PLUGIN) &&
(strstr(lp->plugin_library, "msplugin_mssql2008") != NULL)))) {
const rectObj rectInvalid = MS_INIT_INVALID_RECT;
*rect = rectInvalid;
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* FLTLayerApplyPlainFilterToLayer */
/* */
/* Helper function for layer virtual table architecture */
/************************************************************************/
int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
int iLayerIndex) {
char *pszExpression = NULL;
int status = MS_FALSE;
layerObj *lp = GET_LAYER(map, iLayerIndex);
pszExpression = FLTGetCommonExpression(psNode, lp);
if (pszExpression) {
FilterEncodingNode *psTopBBOX;
rectObj rect = map->extent;
FLTLayerSetInvalidRectIfSupported(lp, &rect, "OF");
psTopBBOX = FLTGetTopBBOX(psNode);
if (psTopBBOX) {
int can_remove_expression = MS_TRUE;
const char *pszEPSG = FLTGetBBOX(psNode, &rect);
if (pszEPSG && map->projection.numargs > 0) {
projectionObj sProjTmp;
msInitProjection(&sProjTmp);
msProjectionInheritContextFrom(&sProjTmp, &map->projection);
/* Use the non EPSG variant since axis swapping is done in
* FLTDoAxisSwappingIfNecessary */
if (msLoadProjectionString(&sProjTmp, pszEPSG) == 0) {
rectObj oldRect = rect;
msProjectRect(&sProjTmp, &map->projection, &rect);
/* If reprojection is involved, do not remove the expression */
if (rect.minx != oldRect.minx || rect.miny != oldRect.miny ||
rect.maxx != oldRect.maxx || rect.maxy != oldRect.maxy) {
can_remove_expression = MS_FALSE;
}
}
msFreeProjection(&sProjTmp);
}
/* Small optimization: if the query is just a BBOX, then do a */
/* msQueryByRect() */
if (psTopBBOX == psNode && can_remove_expression) {
msFree(pszExpression);
pszExpression = NULL;
}
}
if (map->debug == MS_DEBUGLEVEL_VVV) {
if (pszExpression)
msDebug("FLTLayerApplyPlainFilterToLayer(): %s, "
"rect=%.15g,%.15g,%.15g,%.15g\n",
pszExpression, rect.minx, rect.miny, rect.maxx, rect.maxy);
else
msDebug(
"FLTLayerApplyPlainFilterToLayer(): rect=%.15g,%.15g,%.15g,%.15g\n",
rect.minx, rect.miny, rect.maxx, rect.maxy);
}
status = FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex,
pszExpression, rect);
msFree(pszExpression);
}
return status;
}
/************************************************************************/
/* FilterNode *FLTPaserFilterEncoding(char *szXMLString) */
/* */
/* Parses an Filter Encoding XML string and creates a */
/* FilterEncodingNodes corresponding to the string. */
/* Returns a pointer to the first node or NULL if */
/* unsuccessful. */
/* Calling function should use FreeFilterEncodingNode function */
/* to free memory. */
/************************************************************************/
FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString) {
CPLXMLNode *psRoot = NULL, *psChild = NULL, *psFilter = NULL;
FilterEncodingNode *psFilterNode = NULL;
if (szXMLString == NULL || strlen(szXMLString) == 0 ||
(strstr(szXMLString, "Filter") == NULL))
return NULL;
psRoot = CPLParseXMLString(szXMLString);
if (psRoot == NULL)
return NULL;
/* strip namespaces. We srtip all name spaces (#1350)*/
CPLStripXMLNamespace(psRoot, NULL, 1);
/* -------------------------------------------------------------------- */
/* get the root element (Filter). */
/* -------------------------------------------------------------------- */
psFilter = CPLGetXMLNode(psRoot, "=Filter");
if (!psFilter) {
CPLDestroyXMLNode(psRoot);
return NULL;
}
psChild = psFilter->psChild;
while (psChild) {
if (FLTIsSupportedFilterType(psChild)) {
psFilterNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psFilterNode, psChild);
break;
} else
psChild = psChild->psNext;
}
CPLDestroyXMLNode(psRoot);
/* -------------------------------------------------------------------- */
/* validate the node tree to make sure that all the nodes are valid.*/
/* -------------------------------------------------------------------- */
if (!FLTValidFilterNode(psFilterNode)) {
FLTFreeFilterEncodingNode(psFilterNode);
return NULL;
}
return psFilterNode;
}
/************************************************************************/
/* int FLTValidFilterNode(FilterEncodingNode *psFilterNode) */
/* */
/* Validate that all the nodes are filled properly. We could */
/* have parts of the nodes that are correct and part which */
/* could be incorrect if the filter string sent is corrupted */
/* (eg missing a value :) */
/************************************************************************/
int FLTValidFilterNode(FilterEncodingNode *psFilterNode) {
if (!psFilterNode)
return 0;
if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
return 0;
if (psFilterNode->psLeftNode) {
const int bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
if (bReturn == 0)
return 0;
else if (psFilterNode->psRightNode)
return FLTValidFilterNode(psFilterNode->psRightNode);
}
return 1;
}
/************************************************************************/
/* FLTIsGeometryFilterNodeType */
/************************************************************************/
static int FLTIsGeometryFilterNodeType(int eType) {
return (eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON);
}
/************************************************************************/
/* FLTFreeFilterEncodingNode */
/* */
/* recursive freeing of Filter Encoding nodes. */
/************************************************************************/
void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode) {
if (psFilterNode) {
if (psFilterNode->psLeftNode) {
FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
psFilterNode->psLeftNode = NULL;
}
if (psFilterNode->psRightNode) {
FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
psFilterNode->psRightNode = NULL;
}
if (psFilterNode->pszSRS)
free(psFilterNode->pszSRS);
if (psFilterNode->pOther) {
if (psFilterNode->pszValue != NULL &&
strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
FEPropertyIsLike *propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
if (propIsLike->pszWildCard)
free(propIsLike->pszWildCard);
if (propIsLike->pszSingleChar)
free(propIsLike->pszSingleChar);
if (propIsLike->pszEscapeChar)
free(propIsLike->pszEscapeChar);
} else if (FLTIsGeometryFilterNodeType(psFilterNode->eType)) {
msFreeShape((shapeObj *)(psFilterNode->pOther));
}
/* else */
/* TODO free pOther special fields */
free(psFilterNode->pOther);
}
/* Cannot free pszValue before, 'cause we are testing it above */
if (psFilterNode->pszValue)
free(psFilterNode->pszValue);
free(psFilterNode);
}
}
/************************************************************************/
/* FLTCreateFilterEncodingNode */
/* */
/* return a FilterEncoding node. */
/************************************************************************/
FilterEncodingNode *FLTCreateFilterEncodingNode(void) {
FilterEncodingNode *psFilterNode = NULL;
psFilterNode = (FilterEncodingNode *)malloc(sizeof(FilterEncodingNode));
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
psFilterNode->pszValue = NULL;
psFilterNode->pOther = NULL;
psFilterNode->pszSRS = NULL;
psFilterNode->psLeftNode = NULL;
psFilterNode->psRightNode = NULL;
return psFilterNode;
}
FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void) {
FilterEncodingNode *psFilterNode = NULL;
psFilterNode = FLTCreateFilterEncodingNode();
/* used to store case sensitivity flag. Default is 0 meaning the
comparing is case sensititive */
psFilterNode->pOther = (int *)malloc(sizeof(int));
(*(int *)(psFilterNode->pOther)) = 0;
return psFilterNode;
}
/************************************************************************/
/* FLTFindGeometryNode */
/* */
/************************************************************************/
static CPLXMLNode *FLTFindGeometryNode(CPLXMLNode *psXMLNode, int *pbPoint,
int *pbLine, int *pbPolygon) {
CPLXMLNode *psGMLElement = NULL;
psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
if (!psGMLElement)
psGMLElement = CPLGetXMLNode(psXMLNode, "PointType");
if (psGMLElement)
*pbPoint = 1;
else {
psGMLElement = CPLGetXMLNode(psXMLNode, "Polygon");
if (psGMLElement)
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPolygon")))
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Surface")))
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiSurface")))
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Box")))
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Envelope")))
*pbPolygon = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "LineString")))
*pbLine = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiLineString")))
*pbLine = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Curve")))
*pbLine = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiCurve")))
*pbLine = 1;
else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPoint")))
*pbPoint = 1;
}
return psGMLElement;
}
/************************************************************************/
/* FLTGetPropertyName */
/************************************************************************/
static const char *FLTGetPropertyName(CPLXMLNode *psXMLNode) {
const char *pszPropertyName;
pszPropertyName = CPLGetXMLValue(psXMLNode, "PropertyName", NULL);
if (pszPropertyName == NULL) /* FE 2.0 ? */
pszPropertyName = CPLGetXMLValue(psXMLNode, "ValueReference", NULL);
return pszPropertyName;
}
/************************************************************************/
/* FLTGetFirstChildNode */
/************************************************************************/
static CPLXMLNode *FLTGetFirstChildNode(CPLXMLNode *psXMLNode) {
if (psXMLNode == NULL)
return NULL;
psXMLNode = psXMLNode->psChild;
while (psXMLNode != NULL) {
if (psXMLNode->eType == CXT_Element)
return psXMLNode;
psXMLNode = psXMLNode->psNext;
}
return NULL;
}
/************************************************************************/
/* FLTGetNextSibblingNode */
/************************************************************************/
static CPLXMLNode *FLTGetNextSibblingNode(CPLXMLNode *psXMLNode) {
if (psXMLNode == NULL)
return NULL;
psXMLNode = psXMLNode->psNext;
while (psXMLNode != NULL) {
if (psXMLNode->eType == CXT_Element)
return psXMLNode;
psXMLNode = psXMLNode->psNext;
}
return NULL;
}
/************************************************************************/
/* FLTInsertElementInNode */
/* */
/* Utility function to parse an XML node and transfer the */
/* contents into the Filter Encoding node structure. */
/************************************************************************/
void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
CPLXMLNode *psXMLNode) {
char *pszTmp = NULL;
FilterEncodingNode *psCurFilNode = NULL;
CPLXMLNode *psCurXMLNode = NULL;
CPLXMLNode *psTmpNode = NULL;
CPLXMLNode *psFeatureIdNode = NULL;
const char *pszFeatureId = NULL;
char *pszFeatureIdList = NULL;
if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
psFilterNode->psLeftNode = NULL;
psFilterNode->psRightNode = NULL;
/* -------------------------------------------------------------------- */
/* Logical filter. AND, OR and NOT are supported. Example of */
/* filer using logical filters : */
/* */
/* */
/* */
/* Person/Age */
/* 50 */
/* */
/* */
/* Person/Address/City */
/* Toronto */
/* */
/* */
/* */
/* -------------------------------------------------------------------- */
if (FLTIsLogicalFilterType(psXMLNode->pszValue)) {
psFilterNode->eType = FILTER_NODE_TYPE_LOGICAL;
if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
strcasecmp(psFilterNode->pszValue, "OR") == 0) {
CPLXMLNode *psFirstNode = FLTGetFirstChildNode(psXMLNode);
CPLXMLNode *psSecondNode = FLTGetNextSibblingNode(psFirstNode);
if (psFirstNode && psSecondNode) {
/*2 operators */
CPLXMLNode *psNextNode = FLTGetNextSibblingNode(psSecondNode);
if (psNextNode == NULL) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psFilterNode->psRightNode, psSecondNode);
} else {
psCurXMLNode = psFirstNode;
psCurFilNode = psFilterNode;
while (psCurXMLNode) {
psNextNode = FLTGetNextSibblingNode(psCurXMLNode);
if (FLTGetNextSibblingNode(psNextNode)) {
psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
psCurFilNode->psRightNode->eType = FILTER_NODE_TYPE_LOGICAL;
psCurFilNode->psRightNode->pszValue =
msStrdup(psFilterNode->pszValue);
psCurFilNode = psCurFilNode->psRightNode;
psCurXMLNode = psNextNode;
} else { /*last 2 operators*/
psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psCurFilNode->psRightNode, psNextNode);
break;
}
}
}
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
CPLXMLNode *psFirstNode = FLTGetFirstChildNode(psXMLNode);
if (psFirstNode) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} /* end if is logical */
/* -------------------------------------------------------------------- */
/* Spatial Filter. */
/* BBOX : */
/* */
/* */
/* Geometry */
/* */
/* 13.0983,31.5899 35.5472,42.8143*/
/* */
/* */
/* */
/* */
/* DWithin */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* Geometry */
/* */
/* 13.0983,31.5899 */
/* */
/* 10 */
/* */
/* */
/* */
/* Intersect */
/* */
/* type="ogc:BinarySpatialOpType"
substitutionGroup="ogc:spatialOps"/>*/
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* -------------------------------------------------------------------- */
else if (FLTIsSpatialFilterType(psXMLNode->pszValue)) {
psFilterNode->eType = FILTER_NODE_TYPE_SPATIAL;
if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
char *pszSRS = NULL;
const char *pszPropertyName = NULL;
CPLXMLNode *psBox = NULL, *psEnvelope = NULL;
rectObj sBox = {0, 0, 0, 0};
int bCoordinatesValid = 0;
pszPropertyName = FLTGetPropertyName(psXMLNode);
psBox = CPLGetXMLNode(psXMLNode, "Box");
if (!psBox)
psBox = CPLGetXMLNode(psXMLNode, "BoxType");
/*FE 1.0 used box FE1.1 uses envelop*/
if (psBox)
bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);
if (bCoordinatesValid) {
/*set the srs if available*/
if (pszSRS)
psFilterNode->pszSRS = pszSRS;
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
/* PropertyName is optional since FE 1.1.0, in which case */
/* the BBOX must apply to all geometry fields. As we support */
/* currently only one geometry field, this doesn't make much */
/* difference to further processing. */
if (pszPropertyName != NULL) {
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
}
/* coordinates */
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
psFilterNode->psRightNode->pOther =
(rectObj *)msSmallMalloc(sizeof(rectObj));
((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
((rectObj *)psFilterNode->psRightNode->pOther)->maxy = sBox.maxy;
} else {
msFree(pszSRS);
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
} else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
strcasecmp(psXMLNode->pszValue, "Beyond") == 0)
{
shapeObj *psShape = NULL;
int bPoint = 0, bLine = 0, bPolygon = 0;
const char *pszUnits = NULL;
const char *pszDistance = NULL;
const char *pszPropertyName;
char *pszSRS = NULL;
CPLXMLNode *psGMLElement = NULL, *psDistance = NULL;
pszPropertyName = FLTGetPropertyName(psXMLNode);
psGMLElement =
FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
psDistance = CPLGetXMLNode(psXMLNode, "Distance");
if (psDistance != NULL)
pszDistance = CPLGetXMLValue(psDistance, NULL, NULL);
if (pszPropertyName != NULL && psGMLElement && psDistance != NULL) {
pszUnits = CPLGetXMLValue(psDistance, "units", NULL);
if (pszUnits == NULL) /* FE 2.0 */
pszUnits = CPLGetXMLValue(psDistance, "uom", NULL);
psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
msInitShape(psShape);
if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
/*set the srs if available*/
if (pszSRS)
psFilterNode->pszSRS = pszSRS;
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
if (bPoint)
psFilterNode->psRightNode->eType =
FILTER_NODE_TYPE_GEOMETRY_POINT;
else if (bLine)
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
else if (bPolygon)
psFilterNode->psRightNode->eType =
FILTER_NODE_TYPE_GEOMETRY_POLYGON;
psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
/*the value will be distance;units*/
psFilterNode->psRightNode->pszValue = msStrdup(pszDistance);
if (pszUnits) {
psFilterNode->psRightNode->pszValue =
msStringConcatenate(psFilterNode->psRightNode->pszValue, ";");
psFilterNode->psRightNode->pszValue = msStringConcatenate(
psFilterNode->psRightNode->pszValue, pszUnits);
}
} else {
free(psShape);
msFree(pszSRS);
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} else if (strcasecmp(psXMLNode->pszValue, "Intersect") == 0 ||
strcasecmp(psXMLNode->pszValue, "Intersects") == 0 ||
strcasecmp(psXMLNode->pszValue, "Equals") == 0 ||
strcasecmp(psXMLNode->pszValue, "Disjoint") == 0 ||
strcasecmp(psXMLNode->pszValue, "Touches") == 0 ||
strcasecmp(psXMLNode->pszValue, "Crosses") == 0 ||
strcasecmp(psXMLNode->pszValue, "Within") == 0 ||
strcasecmp(psXMLNode->pszValue, "Contains") == 0 ||
strcasecmp(psXMLNode->pszValue, "Overlaps") == 0) {
shapeObj *psShape = NULL;
int bLine = 0, bPolygon = 0, bPoint = 0;
char *pszSRS = NULL;
const char *pszPropertyName;
CPLXMLNode *psGMLElement = NULL;
pszPropertyName = FLTGetPropertyName(psXMLNode);
psGMLElement =
FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
if (pszPropertyName != NULL && psGMLElement) {
psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
msInitShape(psShape);
if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
/*set the srs if available*/
if (pszSRS)
psFilterNode->pszSRS = pszSRS;
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
if (bPoint)
psFilterNode->psRightNode->eType =
FILTER_NODE_TYPE_GEOMETRY_POINT;
else if (bLine)
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
else if (bPolygon)
psFilterNode->psRightNode->eType =
FILTER_NODE_TYPE_GEOMETRY_POLYGON;
psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
} else {
free(psShape);
msFree(pszSRS);
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
} /* end of is spatial */
/* -------------------------------------------------------------------- */
/* Comparison Filter */
/* -------------------------------------------------------------------- */
else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
/* -------------------------------------------------------------------- */
/* binary comaparison types. Example : */
/* */
/* */
/* */
/* SomeProperty */
/* 100 */
/* */
/* */
/* -------------------------------------------------------------------- */
if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
if (pszPropertyName != NULL) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psTmpNode = CPLSearchXMLNode(psXMLNode, "Literal");
if (psTmpNode) {
const char *pszLiteral = CPLGetXMLValue(psTmpNode, NULL, NULL);
psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
if (pszLiteral != NULL) {
const char *pszMatchCase;
psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
pszMatchCase = CPLGetXMLValue(psXMLNode, "matchCase", NULL);
/*check if the matchCase attribute is set*/
if (pszMatchCase != NULL &&
strcasecmp(pszMatchCase, "false") == 0) {
(*(int *)psFilterNode->psRightNode->pOther) = 1;
}
}
/* special case where the user puts an empty value */
/* for the Literal so it can end up as an empty */
/* string query in the expression */
else
psFilterNode->psRightNode->pszValue = NULL;
}
}
if (psFilterNode->psLeftNode == NULL ||
psFilterNode->psRightNode == NULL)
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
/* -------------------------------------------------------------------- */
/* PropertyIsBetween filter : extract property name and boundary */
/* values. The boundary values are stored in the right */
/* node. The values are separated by a semi-column (;) */
/* Eg of Filter : */
/* */
/* DEPTH */
/* 400 */
/* 800 */
/* */
/* */
/* Or */
/* */
/* DEPTH */
/* 400 */
/* 800 */
/* */
/* -------------------------------------------------------------------- */
else if (strcasecmp(psXMLNode->pszValue, "PropertyIsBetween") == 0) {
const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
CPLXMLNode *psLowerBoundary = CPLGetXMLNode(psXMLNode, "LowerBoundary");
CPLXMLNode *psUpperBoundary = CPLGetXMLNode(psXMLNode, "UpperBoundary");
const char *pszLowerNode = NULL;
const char *pszUpperNode = NULL;
if (psLowerBoundary != NULL) {
/* check if the is there */
if (CPLGetXMLNode(psLowerBoundary, "Literal") != NULL)
pszLowerNode = CPLGetXMLValue(psLowerBoundary, "Literal", NULL);
else
pszLowerNode = CPLGetXMLValue(psLowerBoundary, NULL, NULL);
}
if (psUpperBoundary != NULL) {
if (CPLGetXMLNode(psUpperBoundary, "Literal") != NULL)
pszUpperNode = CPLGetXMLValue(psUpperBoundary, "Literal", NULL);
else
pszUpperNode = CPLGetXMLValue(psUpperBoundary, NULL, NULL);
}
if (pszPropertyName != NULL && pszLowerNode != NULL &&
pszUpperNode != NULL) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
/* adding a ; between boundary values */
const int nStrLength =
strlen(pszLowerNode) + strlen(pszUpperNode) + 2;
psFilterNode->psRightNode->pszValue =
(char *)malloc(sizeof(char) * (nStrLength));
strcpy(psFilterNode->psRightNode->pszValue, pszLowerNode);
strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
strlcat(psFilterNode->psRightNode->pszValue, pszUpperNode,
nStrLength);
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} /* end of PropertyIsBetween */
/* -------------------------------------------------------------------- */
/* PropertyIsLike */
/* */
/* */
/* */
/* LAST_NAME */
/* JOHN* */
/* */
/* */
/* -------------------------------------------------------------------- */
else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
const char *pszLiteral = CPLGetXMLValue(psXMLNode, "Literal", NULL);
const char *pszWildCard = CPLGetXMLValue(psXMLNode, "wildCard", NULL);
const char *pszSingleChar =
CPLGetXMLValue(psXMLNode, "singleChar", NULL);
const char *pszEscapeChar = CPLGetXMLValue(psXMLNode, "escape", NULL);
if (pszEscapeChar == NULL)
pszEscapeChar = CPLGetXMLValue(psXMLNode, "escapeChar", NULL);
if (pszPropertyName != NULL && pszLiteral != NULL &&
pszWildCard != NULL && pszSingleChar != NULL &&
pszEscapeChar != NULL) {
FEPropertyIsLike *propIsLike;
propIsLike = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
psFilterNode->pOther = propIsLike;
propIsLike->bCaseInsensitive = 0;
propIsLike->pszWildCard = msStrdup(pszWildCard);
propIsLike->pszSingleChar = msStrdup(pszSingleChar);
propIsLike->pszEscapeChar = msStrdup(pszEscapeChar);
pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", NULL);
if (pszTmp && strcasecmp(pszTmp, "false") == 0) {
propIsLike->bCaseInsensitive = 1;
}
/* --------------------------------------------------------------------
*/
/* Create left and right node for the attribute and the value. */
/* --------------------------------------------------------------------
*/
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNull") == 0) {
const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
if (pszPropertyName != NULL) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNil") == 0) {
const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
if (pszPropertyName != NULL) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
}
/* -------------------------------------------------------------------- */
/* FeatureId Filter */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* Note that for FES1.1.0 the featureid has been deprecated in */
/* favor of GmlObjectId */
/* */
/* */
/* And in FES 2.0, in favor of */
/* -------------------------------------------------------------------- */
else if (FLTIsFeatureIdFilterType(psXMLNode->pszValue)) {
psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
pszFeatureIdList = NULL;
psFeatureIdNode = psXMLNode;
while (psFeatureIdNode) {
pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
if (!pszFeatureId)
pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "id", NULL);
if (!pszFeatureId)
pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "rid", NULL);
if (pszFeatureId) {
if (pszFeatureIdList)
pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");
pszFeatureIdList =
msStringConcatenate(pszFeatureIdList, pszFeatureId);
}
psFeatureIdNode = psFeatureIdNode->psNext;
}
if (pszFeatureIdList) {
msFree(psFilterNode->pszValue);
psFilterNode->pszValue = msStrdup(pszFeatureIdList);
msFree(pszFeatureIdList);
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
/* -------------------------------------------------------------------- */
/* Temporal Filter. */
/*
gml:TimeInstant
2005-05-17T00:00:00Z
2005-05-23T00:00:00Z
*/
/* -------------------------------------------------------------------- */
else if (FLTIsTemporalFilterType(psXMLNode->pszValue)) {
psFilterNode->eType = FILTER_NODE_TYPE_TEMPORAL;
if (strcasecmp(psXMLNode->pszValue, "During") == 0) {
const char *pszPropertyName = NULL;
const char *pszBeginTime;
const char *pszEndTime;
pszPropertyName = FLTGetPropertyName(psXMLNode);
pszBeginTime = CPLGetXMLValue(
psXMLNode, "TimePeriod.begin.TimeInstant.timePosition", NULL);
if (pszBeginTime == NULL)
pszBeginTime =
CPLGetXMLValue(psXMLNode, "TimePeriod.beginPosition", NULL);
pszEndTime = CPLGetXMLValue(
psXMLNode, "TimePeriod.end.TimeInstant.timePosition", NULL);
if (pszEndTime == NULL)
pszEndTime =
CPLGetXMLValue(psXMLNode, "TimePeriod.endPosition", NULL);
if (pszPropertyName && pszBeginTime && pszEndTime &&
strchr(pszBeginTime, '\'') == NULL &&
strchr(pszBeginTime, '\\') == NULL &&
strchr(pszEndTime, '\'') == NULL &&
strchr(pszEndTime, '\\') == NULL &&
msTimeGetResolution(pszBeginTime) >= 0 &&
msTimeGetResolution(pszEndTime) >= 0) {
psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_TIME_PERIOD;
const size_t nSize = strlen(pszBeginTime) + strlen(pszEndTime) + 2;
psFilterNode->psRightNode->pszValue =
static_cast(msSmallMalloc(nSize));
snprintf(psFilterNode->psRightNode->pszValue, nSize, "%s/%s",
pszBeginTime, pszEndTime);
} else
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
} else {
psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
}
} /* end of is temporal */
}
}
/************************************************************************/
/* int FLTIsLogicalFilterType((char *pszValue) */
/* */
/* return TRUE if the value of the node is of logical filter */
/* encoding type. */
/************************************************************************/
int FLTIsLogicalFilterType(const char *pszValue) {
if (pszValue) {
if (strcasecmp(pszValue, "AND") == 0 || strcasecmp(pszValue, "OR") == 0 ||
strcasecmp(pszValue, "NOT") == 0)
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsBinaryComparisonFilterType(char *pszValue) */
/* */
/* Binary comparison filter type. */
/************************************************************************/
int FLTIsBinaryComparisonFilterType(const char *pszValue) {
if (pszValue) {
if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsComparisonFilterType(char *pszValue) */
/* */
/* return TRUE if the value of the node is of comparison filter */
/* encoding type. */
/************************************************************************/
int FLTIsComparisonFilterType(const char *pszValue) {
if (pszValue) {
if (FLTIsBinaryComparisonFilterType(pszValue) ||
strcasecmp(pszValue, "PropertyIsLike") == 0 ||
strcasecmp(pszValue, "PropertyIsBetween") == 0 ||
strcasecmp(pszValue, "PropertyIsNull") == 0 ||
strcasecmp(pszValue, "PropertyIsNil") == 0)
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsFeatureIdFilterType(char *pszValue) */
/* */
/* return TRUE if the value of the node is of featureid filter */
/* encoding type. */
/************************************************************************/
int FLTIsFeatureIdFilterType(const char *pszValue) {
if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
strcasecmp(pszValue, "GmlObjectId") == 0 ||
strcasecmp(pszValue, "ResourceId") == 0))
return MS_TRUE;
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsSpatialFilterType(char *pszValue) */
/* */
/* return TRUE if the value of the node is of spatial filter */
/* encoding type. */
/************************************************************************/
int FLTIsSpatialFilterType(const char *pszValue) {
if (pszValue) {
if (strcasecmp(pszValue, "BBOX") == 0 ||
strcasecmp(pszValue, "DWithin") == 0 ||
strcasecmp(pszValue, "Intersect") == 0 ||
strcasecmp(pszValue, "Intersects") == 0 ||
strcasecmp(pszValue, "Equals") == 0 ||
strcasecmp(pszValue, "Disjoint") == 0 ||
strcasecmp(pszValue, "Touches") == 0 ||
strcasecmp(pszValue, "Crosses") == 0 ||
strcasecmp(pszValue, "Within") == 0 ||
strcasecmp(pszValue, "Contains") == 0 ||
strcasecmp(pszValue, "Overlaps") == 0 ||
strcasecmp(pszValue, "Beyond") == 0)
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsTemportalFilterType(char *pszValue) */
/* */
/* return TRUE if the value of the node is of temporal filter */
/* encoding type. */
/************************************************************************/
int FLTIsTemporalFilterType(const char *pszValue) {
if (pszValue) {
if (strcasecmp(pszValue, "During") == 0)
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode) */
/* */
/* Verify if the value of the node is one of the supported */
/* filter type. */
/************************************************************************/
int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode) {
if (psXMLNode) {
if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
FLTIsSpatialFilterType(psXMLNode->pszValue) ||
FLTIsComparisonFilterType(psXMLNode->pszValue) ||
FLTIsFeatureIdFilterType(psXMLNode->pszValue) ||
FLTIsTemporalFilterType(psXMLNode->pszValue))
return MS_TRUE;
}
return MS_FALSE;
}
/************************************************************************/
/* FLTNumberOfFilterType */
/* */
/* Loop trhough the nodes and return the number of nodes of */
/* specified value. */
/************************************************************************/
int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode,
const char *szType) {
int nCount = 0;
int nLeftNode = 0, nRightNode = 0;
if (!psFilterNode || !szType || !psFilterNode->pszValue)
return 0;
if (strcasecmp(psFilterNode->pszValue, (char *)szType) == 0)
nCount++;
if (psFilterNode->psLeftNode)
nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);
nCount += nLeftNode;
if (psFilterNode->psRightNode)
nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
nCount += nRightNode;
return nCount;
}
/************************************************************************/
/* FLTValidForBBoxFilter */
/* */
/* Validate if there is only one BBOX filter node. Here is what */
/* is supported (is valid) : */
/* - one node which is a BBOX */
/* - a logical AND with a valid BBOX */
/* */
/* eg 1: */
/* */
/* Geometry */
/* */
/* 13.0983,31.5899 35.5472,42.8143*/
/* */
/* */
/* */
/* */
/* eg 2 : */
/* */
/* */
/* Geometry */
/* */
/* 13.0983,31.5899 35.5472,42.8143*/
/* */
/* */
/* */
/* SomeProperty */
/* 100 */
/* */
/* */
/* */
/* */
/************************************************************************/
int FLTValidForBBoxFilter(FilterEncodingNode *psFilterNode) {
int nCount = 0;
if (!psFilterNode || !psFilterNode->pszValue)
return 1;
nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");
if (nCount > 1)
return 0;
else if (nCount == 0)
return 1;
/* nCount ==1 */
if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
return 1;
if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
return FLTValidForBBoxFilter(psFilterNode->psLeftNode) &&
FLTValidForBBoxFilter(psFilterNode->psRightNode);
}
return 0;
}
#if 0
static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode)
{
int nCount = 0;
if (!psFilterNode || !psFilterNode->pszValue)
return 1;
nCount = FLTNumberOfFilterType(psFilterNode, "During");
if (nCount > 1)
return 0;
else if (nCount == 0)
return 1;
/* nCount ==1 */
if (strcasecmp(psFilterNode->pszValue, "During") == 0)
return 1;
if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
return FLTHasUniqueTopLevelDuringFilter(psFilterNode->psLeftNode) &&
FLTHasUniqueTopLevelDuringFilter(psFilterNode->psRightNode);
}
return 0;
}
#endif
int FLTIsLineFilter(FilterEncodingNode *psFilterNode) {
if (!psFilterNode || !psFilterNode->pszValue)
return 0;
if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
psFilterNode->psRightNode &&
psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
return 1;
return 0;
}
int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode) {
if (!psFilterNode || !psFilterNode->pszValue)
return 0;
if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
psFilterNode->psRightNode &&
psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
return 1;
return 0;
}
int FLTIsPointFilter(FilterEncodingNode *psFilterNode) {
if (!psFilterNode || !psFilterNode->pszValue)
return 0;
if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
psFilterNode->psRightNode &&
psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
return 1;
return 0;
}
int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode) {
if (!psFilterNode || !psFilterNode->pszValue)
return 0;
if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
return 1;
return 0;
}
shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
int *pnUnit) {
char **tokens = NULL;
int nTokens = 0;
FilterEncodingNode *psNode = psFilterNode;
char *szUnitStr = NULL;
char *szUnit = NULL;
if (psNode) {
if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
psNode = psNode->psRightNode;
if (FLTIsGeometryFilterNodeType(psNode->eType)) {
if (psNode->pszValue && pdfDistance) {
/*
syntax expected is "distance;unit" or just "distance"
if unit is there syntax is "URI#unit" (eg http://..../#m)
or just "unit"
*/
tokens = msStringSplit(psNode->pszValue, ';', &nTokens);
if (tokens && nTokens >= 1) {
*pdfDistance = atof(tokens[0]);
if (nTokens == 2 && pnUnit) {
szUnitStr = msStrdup(tokens[1]);
msFreeCharArray(tokens, nTokens);
nTokens = 0;
tokens = msStringSplit(szUnitStr, '#', &nTokens);
msFree(szUnitStr);
if (tokens && nTokens >= 1) {
if (nTokens == 1)
szUnit = tokens[0];
else
szUnit = tokens[1];
if (strcasecmp(szUnit, "m") == 0 ||
strcasecmp(szUnit, "meters") == 0)
*pnUnit = MS_METERS;
else if (strcasecmp(szUnit, "km") == 0 ||
strcasecmp(szUnit, "kilometers") == 0)
*pnUnit = MS_KILOMETERS;
else if (strcasecmp(szUnit, "NM") == 0 ||
strcasecmp(szUnit, "nauticalmiles") == 0)
*pnUnit = MS_NAUTICALMILES;
else if (strcasecmp(szUnit, "mi") == 0 ||
strcasecmp(szUnit, "miles") == 0)
*pnUnit = MS_MILES;
else if (strcasecmp(szUnit, "in") == 0 ||
strcasecmp(szUnit, "inches") == 0)
*pnUnit = MS_INCHES;
else if (strcasecmp(szUnit, "ft") == 0 ||
strcasecmp(szUnit, "feet") == 0)
*pnUnit = MS_FEET;
else if (strcasecmp(szUnit, "deg") == 0 ||
strcasecmp(szUnit, "dd") == 0)
*pnUnit = MS_DD;
else if (strcasecmp(szUnit, "px") == 0)
*pnUnit = MS_PIXELS;
}
}
}
msFreeCharArray(tokens, nTokens);
}
return (shapeObj *)psNode->pOther;
}
}
return NULL;
}
/************************************************************************/
/* FLTGetBBOX */
/* */
/* Loop through the nodes are return the coordinates of the */
/* first bbox node found. The return value is the epsg code of */
/* the bbox. */
/************************************************************************/
const char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect) {
const char *pszReturn = NULL;
if (!psFilterNode || !psRect)
return NULL;
if (psFilterNode->pszValue &&
strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
rectObj *pRect = (rectObj *)psFilterNode->psRightNode->pOther;
psRect->minx = pRect->minx;
psRect->miny = pRect->miny;
psRect->maxx = pRect->maxx;
psRect->maxy = pRect->maxy;
return psFilterNode->pszSRS;
}
} else {
pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
if (pszReturn)
return pszReturn;
else
return FLTGetBBOX(psFilterNode->psRightNode, psRect);
}
return pszReturn;
}
const char *FLTGetDuring(FilterEncodingNode *psFilterNode,
const char **ppszTimeField) {
const char *pszReturn = NULL;
if (!psFilterNode || !ppszTimeField)
return NULL;
if (psFilterNode->pszValue &&
strcasecmp(psFilterNode->pszValue, "During") == 0) {
*ppszTimeField = psFilterNode->psLeftNode->pszValue;
return psFilterNode->psRightNode->pszValue;
} else {
pszReturn = FLTGetDuring(psFilterNode->psLeftNode, ppszTimeField);
if (pszReturn)
return pszReturn;
else
return FLTGetDuring(psFilterNode->psRightNode, ppszTimeField);
}
return pszReturn;
}
/************************************************************************/
/* FLTGetSQLExpression */
/* */
/* Build SQL expressions from the mapserver nodes. */
/************************************************************************/
char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) {
char *pszExpression = NULL;
if (psFilterNode == NULL || lp == NULL)
return NULL;
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
pszExpression = FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
} else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0) {
pszExpression =
FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
} else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
pszExpression = FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);
}
}
} else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
strcasecmp(psFilterNode->pszValue, "OR") == 0) {
pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
} else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
}
}
else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
/* TODO */
} else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) || \
defined(USE_SOS_SVR)
if (psFilterNode->pszValue) {
const char *pszAttribute =
msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
if (pszAttribute) {
int nTokens = 0;
char **tokens = msStringSplit(psFilterNode->pszValue, ',', &nTokens);
int bString = 0;
if (tokens && nTokens > 0) {
for (int i = 0; i < nTokens; i++) {
char *pszEscapedStr = NULL;
const char *pszId = tokens[i];
const char *pszDot = strchr(pszId, '.');
if (pszDot)
pszId = pszDot + 1;
if (strlen(pszId) connectiontype == MS_OGR ||
lp->connectiontype == MS_POSTGIS)
snprintf(szTmp, sizeof(szTmp),
"(CAST(%s AS CHARACTER(255)) = '%s')", pszAttribute,
pszEscapedStr);
else
snprintf(szTmp, sizeof(szTmp), "(%s = '%s')", pszAttribute,
pszEscapedStr);
} else
snprintf(szTmp, sizeof(szTmp), "(%s = %s)", pszAttribute,
pszEscapedStr);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
if (pszExpression != NULL)
pszExpression = msStringConcatenate(pszExpression, " OR ");
else
/*opening and closing brackets*/
pszExpression = msStringConcatenate(pszExpression, "(");
pszExpression = msStringConcatenate(pszExpression, szTmp);
}
}
msFreeCharArray(tokens, nTokens);
}
/*opening and closing brackets*/
if (pszExpression)
pszExpression = msStringConcatenate(pszExpression, ")");
}
#else
msSetError(MS_MISCERR, "OWS support is not available.",
"FLTGetSQLExpression()");
return NULL;
#endif
} else if (lp->connectiontype != MS_OGR &&
psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL)
pszExpression = msStrdup(FLTGetTimeExpression(psFilterNode, lp).c_str());
return pszExpression;
}
/************************************************************************/
/* FLTGetLogicalComparisonSQLExpresssion */
/* */
/* Return the expression for logical comparison expression. */
/************************************************************************/
char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
layerObj *lp) {
char *pszBuffer = NULL;
char *pszTmp = NULL;
if (lp == NULL)
return NULL;
/* ==================================================================== */
/* special case for BBOX node. */
/* ==================================================================== */
if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
(strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
else
pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
if (!pszTmp)
return NULL;
const size_t nSize = strlen(pszTmp) + 1;
pszBuffer = (char *)malloc(nSize);
snprintf(pszBuffer, nSize, "%s", pszTmp);
}
/* ==================================================================== */
/* special case for temporal filter node (OGR layer only) */
/* ==================================================================== */
else if (lp->connectiontype == MS_OGR && psFilterNode->psLeftNode &&
psFilterNode->psRightNode &&
(psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_TEMPORAL ||
psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_TEMPORAL)) {
if (psFilterNode->psLeftNode->eType != FILTER_NODE_TYPE_TEMPORAL)
pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
else
pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
if (!pszTmp)
return NULL;
const size_t nSize = strlen(pszTmp) + 1;
pszBuffer = (char *)malloc(nSize);
snprintf(pszBuffer, nSize, "%s", pszTmp);
}
/* -------------------------------------------------------------------- */
/* OR and AND */
/* -------------------------------------------------------------------- */
else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
if (!pszTmp)
return NULL;
pszBuffer = (char *)malloc(
sizeof(char) * (strlen(pszTmp) + strlen(psFilterNode->pszValue) + 5));
pszBuffer[0] = '\0';
strcat(pszBuffer, " (");
strcat(pszBuffer, pszTmp);
strcat(pszBuffer, " ");
strcat(pszBuffer, psFilterNode->pszValue);
strcat(pszBuffer, " ");
free(pszTmp);
const size_t nTmp = strlen(pszBuffer);
pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
if (!pszTmp) {
free(pszBuffer);
return NULL;
}
pszBuffer = (char *)msSmallRealloc(
pszBuffer, sizeof(char) * (strlen(pszTmp) + nTmp + 3));
strcat(pszBuffer, pszTmp);
strcat(pszBuffer, ") ");
}
/* -------------------------------------------------------------------- */
/* NOT */
/* -------------------------------------------------------------------- */
else if (psFilterNode->psLeftNode &&
strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
if (!pszTmp)
return NULL;
pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 9));
pszBuffer[0] = '\0';
strcat(pszBuffer, " (NOT ");
strcat(pszBuffer, pszTmp);
strcat(pszBuffer, ") ");
} else
return NULL;
/* -------------------------------------------------------------------- */
/* Cleanup. */
/* -------------------------------------------------------------------- */
if (pszTmp != NULL)
free(pszTmp);
return pszBuffer;
}
/************************************************************************/
/* FLTGetBinaryComparisonSQLExpresssion */
/* */
/* Return the expression for a binary comparison filter node. */
/************************************************************************/
char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
layerObj *lp) {
const size_t bufferSize = 1024;
char szBuffer[1024];
int bString = 0;
char szTmp[256];
char *pszEscapedStr = NULL;
szBuffer[0] = '\0';
if (!psFilterNode || !FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
return NULL;
/* -------------------------------------------------------------------- */
/* check if the value is a numeric value or alphanumeric. If it */
/* is alphanumeric, add quotes around attribute and values. */
/* -------------------------------------------------------------------- */
bString = 0;
if (psFilterNode->psRightNode->pszValue) {
const char *pszOFGType;
snprintf(szTmp, sizeof(szTmp), "%s_type",
psFilterNode->psLeftNode->pszValue);
pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
if (pszOFGType != NULL && strcasecmp(pszOFGType, "Character") == 0)
bString = 1;
else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
bString = 1;
}
/* special case to be able to have empty strings in the expression. */
if (psFilterNode->psRightNode->pszValue == NULL)
bString = 1;
/*opening bracket*/
strlcat(szBuffer, " (", bufferSize);
pszEscapedStr =
msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
/* attribute */
/*case insensitive set ? */
if (bString && strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
psFilterNode->psRightNode->pOther &&
(*(int *)psFilterNode->psRightNode->pOther) == 1) {
snprintf(szTmp, sizeof(szTmp), "lower(%s) ", pszEscapedStr);
strlcat(szBuffer, szTmp, bufferSize);
} else
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
/* logical operator */
if (strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0)
strlcat(szBuffer, "=", bufferSize);
else if (strcasecmp(psFilterNode->pszValue, "PropertyIsNotEqualTo") == 0)
strlcat(szBuffer, "", bufferSize);
else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThan") == 0)
strlcat(szBuffer, "", bufferSize);
else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThanOrEqualTo") ==
0)
strlcat(szBuffer, "=", bufferSize);
strlcat(szBuffer, " ", bufferSize);
/* value */
if (bString && psFilterNode->psRightNode->pszValue &&
strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
psFilterNode->psRightNode->pOther &&
(*(int *)psFilterNode->psRightNode->pOther) == 1) {
char *pszEscapedStr;
pszEscapedStr =
msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
msFree(pszEscapedStr);
strlcat(szBuffer, szTmp, bufferSize);
} else {
if (bString)
strlcat(szBuffer, "'", bufferSize);
if (psFilterNode->psRightNode->pszValue) {
if (bString) {
char *pszEscapedStr;
pszEscapedStr =
msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
} else
strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
}
if (bString)
strlcat(szBuffer, "'", bufferSize);
}
/*closing bracket*/
strlcat(szBuffer, ") ", bufferSize);
return msStrdup(szBuffer);
}
/************************************************************************/
/* FLTGetIsBetweenComparisonSQLExpresssion */
/* */
/* Build an SQL expression for IsBteween Filter. */
/************************************************************************/
char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
layerObj *lp) {
const size_t bufferSize = 1024;
char szBuffer[1024];
char **aszBounds = NULL;
int nBounds = 0;
int bString = 0;
char szTmp[256];
char *pszEscapedStr;
szBuffer[0] = '\0';
if (!psFilterNode ||
!(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
return NULL;
if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode)
return NULL;
/* -------------------------------------------------------------------- */
/* Get the bounds value which are stored like boundmin;boundmax */
/* -------------------------------------------------------------------- */
aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
if (nBounds != 2) {
msFreeCharArray(aszBounds, nBounds);
return NULL;
}
/* -------------------------------------------------------------------- */
/* check if the value is a numeric value or alphanumeric. If it */
/* is alphanumeric, add quotes around attribute and values. */
/* -------------------------------------------------------------------- */
bString = 0;
if (aszBounds[0]) {
const char *pszOFGType;
snprintf(szTmp, sizeof(szTmp), "%s_type",
psFilterNode->psLeftNode->pszValue);
pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
if (pszOFGType != NULL && strcasecmp(pszOFGType, "Character") == 0)
bString = 1;
else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
bString = 1;
}
if (!bString) {
if (aszBounds[1]) {
if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
bString = 1;
}
}
/* -------------------------------------------------------------------- */
/* build expression. */
/* -------------------------------------------------------------------- */
/*opening parenthesis */
strlcat(szBuffer, " (", bufferSize);
/* attribute */
pszEscapedStr =
msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
/*between*/
strlcat(szBuffer, " BETWEEN ", bufferSize);
/*bound 1*/
if (bString)
strlcat(szBuffer, "'", bufferSize);
pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[0]);
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
if (bString)
strlcat(szBuffer, "'", bufferSize);
strlcat(szBuffer, " AND ", bufferSize);
/*bound 2*/
if (bString)
strlcat(szBuffer, "'", bufferSize);
pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[1]);
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
if (bString)
strlcat(szBuffer, "'", bufferSize);
/*closing parenthesis*/
strlcat(szBuffer, ")", bufferSize);
msFreeCharArray(aszBounds, nBounds);
return msStrdup(szBuffer);
}
/************************************************************************/
/* FLTGetIsLikeComparisonSQLExpression */
/* */
/* Build an sql expression for IsLike filter. */
/************************************************************************/
char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
layerObj *lp) {
const size_t bufferSize = 1024;
char szBuffer[1024];
char *pszValue = NULL;
const char *pszWild = NULL;
const char *pszSingle = NULL;
const char *pszEscape = NULL;
char szTmp[4];
int nLength = 0, i = 0, j = 0;
int bCaseInsensitive = 0;
char *pszEscapedStr = NULL;
FEPropertyIsLike *propIsLike;
if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
!psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
return NULL;
propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
pszWild = propIsLike->pszWildCard;
pszSingle = propIsLike->pszSingleChar;
pszEscape = propIsLike->pszEscapeChar;
bCaseInsensitive = propIsLike->bCaseInsensitive;
if (!pszWild || strlen(pszWild) == 0 || !pszSingle ||
strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
return NULL;
if (pszEscape[0] == '\'') {
/* This might be valid, but the risk of SQL injection is too high */
/* and the below code is not ready for that */
/* Someone who does this has clearly suspect intentions ! */
msSetError(
MS_MISCERR,
"Single quote character is not allowed as an escaping character.",
"FLTGetIsLikeComparisonSQLExpression()");
return NULL;
}
szBuffer[0] = '\0';
/*opening bracket*/
strlcat(szBuffer, " (", bufferSize);
/* attribute name */
pszEscapedStr =
msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
pszEscapedStr = NULL;
if (lp->connectiontype == MS_POSTGIS) {
if (bCaseInsensitive == 1)
strlcat(szBuffer, "::text ilike '", bufferSize);
else
strlcat(szBuffer, "::text like '", bufferSize);
} else
strlcat(szBuffer, " like '", bufferSize);
pszValue = psFilterNode->psRightNode->pszValue;
nLength = strlen(pszValue);
pszEscapedStr = (char *)msSmallMalloc(3 * nLength + 1);
for (i = 0, j = 0; i < nLength; i++) {
char c = pszValue[i];
if (c != pszWild[0] && c != pszSingle[0] && c != pszEscape[0]) {
if (c == '\'') {
pszEscapedStr[j++] = '\'';
pszEscapedStr[j++] = '\'';
} else if (c == '\\') {
pszEscapedStr[j++] = '\\';
pszEscapedStr[j++] = '\\';
} else
pszEscapedStr[j++] = c;
} else if (c == pszSingle[0]) {
pszEscapedStr[j++] = '_';
} else if (c == pszEscape[0]) {
pszEscapedStr[j++] = pszEscape[0];
if (i + 1 < nLength) {
char nextC = pszValue[i + 1];
i++;
if (nextC == '\'') {
pszEscapedStr[j++] = '\'';
pszEscapedStr[j++] = '\'';
} else
pszEscapedStr[j++] = nextC;
}
} else if (c == pszWild[0]) {
pszEscapedStr[j++] = '%';
}
}
pszEscapedStr[j++] = 0;
strlcat(szBuffer, pszEscapedStr, bufferSize);
msFree(pszEscapedStr);
strlcat(szBuffer, "'", bufferSize);
if (lp->connectiontype != MS_OGR) {
if (lp->connectiontype == MS_POSTGIS && pszEscape[0] == '\\')
strlcat(szBuffer, " escape E'", bufferSize);
else
strlcat(szBuffer, " escape '", bufferSize);
szTmp[0] = pszEscape[0];
if (pszEscape[0] == '\\') {
szTmp[1] = '\\';
szTmp[2] = '\'';
szTmp[3] = '\0';
} else {
szTmp[1] = '\'';
szTmp[2] = '\0';
}
strlcat(szBuffer, szTmp, bufferSize);
}
strlcat(szBuffer, ") ", bufferSize);
return msStrdup(szBuffer);
}
/************************************************************************/
/* FLTHasSpatialFilter */
/* */
/* Utility function to see if a spatial filter is included in */
/* the node. */
/************************************************************************/
int FLTHasSpatialFilter(FilterEncodingNode *psNode) {
int bResult = MS_FALSE;
if (!psNode)
return MS_FALSE;
if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
if (psNode->psLeftNode)
bResult = FLTHasSpatialFilter(psNode->psLeftNode);
if (bResult)
return MS_TRUE;
if (psNode->psRightNode)
bResult = FLTHasSpatialFilter(psNode->psRightNode);
if (bResult)
return MS_TRUE;
} else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
return MS_TRUE;
return MS_FALSE;
}
/************************************************************************/
/* FLTCreateFeatureIdFilterEncoding */
/* */
/* Utility function to create a filter node of FeatureId type. */
/************************************************************************/
FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(const char *pszString) {
FilterEncodingNode *psFilterNode = NULL;
if (pszString) {
psFilterNode = FLTCreateFilterEncodingNode();
psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
psFilterNode->pszValue = msStrdup(pszString);
return psFilterNode;
}
return NULL;
}
/************************************************************************/
/* FLTParseGMLBox */
/* */
/* Parse gml box. Used for FE 1.0 */
/************************************************************************/
int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS) {
int bCoordinatesValid = 0;
CPLXMLNode *psCoordinates = NULL;
CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
char **papszCoords = NULL, **papszMin = NULL, **papszMax = NULL;
int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
const char *pszTmpCoord = NULL;
const char *pszSRS = NULL;
const char *pszTS = NULL;
const char *pszCS = NULL;
double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
if (psBox) {
pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
if (ppszSRS && pszSRS)
*ppszSRS = msStrdup(pszSRS);
psCoordinates = CPLGetXMLNode(psBox, "coordinates");
pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
if (pszTS == NULL)
pszTS = " ";
pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);
if (pszCS == NULL)
pszCS = ",";
pszTmpCoord = CPLGetXMLValue(psCoordinates, NULL, NULL);
if (pszTmpCoord) {
papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
if (papszCoords && nCoords == 2) {
papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
if (papszMin && nCoordsMin == 2) {
papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
}
if (papszMax && nCoordsMax == 2) {
bCoordinatesValid = 1;
minx = atof(papszMin[0]);
miny = atof(papszMin[1]);
maxx = atof(papszMax[0]);
maxy = atof(papszMax[1]);
}
msFreeCharArray(papszMin, nCoordsMin);
msFreeCharArray(papszMax, nCoordsMax);
}
msFreeCharArray(papszCoords, nCoords);
} else {
psCoord1 = CPLGetXMLNode(psBox, "coord");
psCoord2 = FLTGetNextSibblingNode(psCoord1);
if (psCoord1 && psCoord2 && strcmp(psCoord2->pszValue, "coord") == 0) {
const char *pszX = CPLGetXMLValue(psCoord1, "X", NULL);
const char *pszY = CPLGetXMLValue(psCoord1, "Y", NULL);
if (pszX && pszY) {
minx = atof(pszX);
miny = atof(pszY);
pszX = CPLGetXMLValue(psCoord2, "X", NULL);
pszY = CPLGetXMLValue(psCoord2, "Y", NULL);
if (pszX && pszY) {
maxx = atof(pszX);
maxy = atof(pszY);
bCoordinatesValid = 1;
}
}
}
}
}
if (bCoordinatesValid) {
psBbox->minx = minx;
psBbox->miny = miny;
psBbox->maxx = maxx;
psBbox->maxy = maxy;
}
return bCoordinatesValid;
}
/************************************************************************/
/* FLTParseGMLEnvelope */
/* */
/* Utility function to parse a gml:Envelope (used for SOS and FE1.1)*/
/************************************************************************/
int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS) {
CPLXMLNode *psUpperCorner = NULL, *psLowerCorner = NULL;
const char *pszLowerCorner = NULL, *pszUpperCorner = NULL;
int bValid = 0;
char **tokens;
int n;
if (psRoot && psBbox && psRoot->eType == CXT_Element &&
EQUAL(psRoot->pszValue, "Envelope")) {
/*Get the srs if available*/
if (ppszSRS) {
const char *pszSRS = CPLGetXMLValue(psRoot, "srsName", NULL);
if (pszSRS != NULL)
*ppszSRS = msStrdup(pszSRS);
}
psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");
if (psLowerCorner && psUpperCorner) {
pszLowerCorner = CPLGetXMLValue(psLowerCorner, NULL, NULL);
pszUpperCorner = CPLGetXMLValue(psUpperCorner, NULL, NULL);
if (pszLowerCorner && pszUpperCorner) {
tokens = msStringSplit(pszLowerCorner, ' ', &n);
if (tokens && n >= 2) {
psBbox->minx = atof(tokens[0]);
psBbox->miny = atof(tokens[1]);
msFreeCharArray(tokens, n);
tokens = msStringSplit(pszUpperCorner, ' ', &n);
if (tokens && n >= 2) {
psBbox->maxx = atof(tokens[0]);
psBbox->maxy = atof(tokens[1]);
bValid = 1;
}
}
msFreeCharArray(tokens, n);
}
}
}
return bValid;
}
/************************************************************************/
/* FLTNeedSRSSwapping */
/************************************************************************/
static int FLTNeedSRSSwapping(mapObj *map, const char *pszSRS) {
int bNeedSwapping = MS_FALSE;
projectionObj sProjTmp;
msInitProjection(&sProjTmp);
if (map) {
msProjectionInheritContextFrom(&sProjTmp, &map->projection);
}
if (msLoadProjectionStringEPSG(&sProjTmp, pszSRS) == 0) {
bNeedSwapping = msIsAxisInvertedProj(&sProjTmp);
}
msFreeProjection(&sProjTmp);
return bNeedSwapping;
}
/************************************************************************/
/* FLTDoAxisSwappingIfNecessary */
/* */
/* Explore all geometries and BBOX to do axis swapping when the */
/* SRS requires it. If no explicit SRS is attached to the geometry */
/* the bDefaultSRSNeedsAxisSwapping is taken into account. The */
/* caller will have to determine its value from a more general */
/* context. */
/************************************************************************/
void FLTDoAxisSwappingIfNecessary(mapObj *map, FilterEncodingNode *psFilterNode,
int bDefaultSRSNeedsAxisSwapping) {
if (psFilterNode == NULL)
return;
if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_BBOX) {
rectObj *rect = (rectObj *)psFilterNode->psRightNode->pOther;
const char *pszSRS = psFilterNode->pszSRS;
if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
(pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
double tmp;
tmp = rect->minx;
rect->minx = rect->miny;
rect->miny = tmp;
tmp = rect->maxx;
rect->maxx = rect->maxy;
rect->maxy = tmp;
}
} else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
FLTIsGeometryFilterNodeType(psFilterNode->psRightNode->eType)) {
shapeObj *shape = (shapeObj *)(psFilterNode->psRightNode->pOther);
const char *pszSRS = psFilterNode->pszSRS;
if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
(pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
msAxisSwapShape(shape);
}
} else {
FLTDoAxisSwappingIfNecessary(map, psFilterNode->psLeftNode,
bDefaultSRSNeedsAxisSwapping);
FLTDoAxisSwappingIfNecessary(map, psFilterNode->psRightNode,
bDefaultSRSNeedsAxisSwapping);
}
}
static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
const char *pszOldName,
const char *pszNewName) {
if (psFilterNode && pszOldName && pszNewName) {
if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
if (psFilterNode->pszValue &&
strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
msFree(psFilterNode->pszValue);
psFilterNode->pszValue = msStrdup(pszNewName);
}
}
if (psFilterNode->psLeftNode)
FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName, pszNewName);
if (psFilterNode->psRightNode)
FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName, pszNewName);
}
}
static int FLTIsGMLDefaultProperty(const char *pszName) {
return (strcmp(pszName, "gml:name") == 0 ||
strcmp(pszName, "gml:description") == 0 ||
strcmp(pszName, "gml:descriptionReference") == 0 ||
strcmp(pszName, "gml:identifier") == 0 ||
strcmp(pszName, "gml:boundedBy") == 0 ||
strcmp(pszName, "@gml:id") == 0);
}
static void
FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode) {
char **tokens = NULL;
int n = 0;
if (psFilterNode) {
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
psFilterNode->psLeftNode != NULL &&
psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
return;
}
if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
if (psFilterNode->pszValue && strstr(psFilterNode->pszValue, ":")) {
tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
if (tokens && n == 2) {
msFree(psFilterNode->pszValue);
psFilterNode->pszValue = msStrdup(tokens[1]);
}
msFreeCharArray(tokens, n);
}
}
if (psFilterNode->psLeftNode)
FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
if (psFilterNode->psRightNode)
FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
}
}
static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode,
gmlGroupListObj *groupList) {
int i;
if (psFilterNode) {
if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
if (psFilterNode->pszValue != NULL) {
const char *pszPropertyName = psFilterNode->pszValue;
const char *pszSlash = strchr(pszPropertyName, '/');
if (pszSlash != NULL) {
const char *pszColon = strchr(pszPropertyName, ':');
if (pszColon != NULL && pszColon < pszSlash)
pszPropertyName = pszColon + 1;
for (i = 0; i < groupList->numgroups; i++) {
const char *pszGroupName = groupList->groups[i].name;
size_t nGroupNameLen = strlen(pszGroupName);
if (strncasecmp(pszPropertyName, pszGroupName, nGroupNameLen) ==
0 &&
pszPropertyName[nGroupNameLen] == '/') {
char *pszTmp;
pszPropertyName = pszPropertyName + nGroupNameLen + 1;
pszColon = strchr(pszPropertyName, ':');
if (pszColon != NULL)
pszPropertyName = pszColon + 1;
pszTmp = msStrdup(pszPropertyName);
msFree(psFilterNode->pszValue);
psFilterNode->pszValue = pszTmp;
break;
}
}
}
}
}
if (psFilterNode->psLeftNode)
FLTRemoveGroupName(psFilterNode->psLeftNode, groupList);
if (psFilterNode->psRightNode)
FLTRemoveGroupName(psFilterNode->psRightNode, groupList);
}
}
/************************************************************************/
/* FLTPreParseFilterForAliasAndGroup */
/* */
/* Utility function to replace aliased' and grouped attributes */
/* with their internal name. */
/************************************************************************/
void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode,
mapObj *map, int i,
const char *namespaces) {
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) || \
defined(USE_SOS_SVR)
if (psFilterNode && map && i >= 0 && i < map->numlayers) {
/*strip name spaces before hand*/
FLTStripNameSpacesFromPropertyName(psFilterNode);
layerObj *lp = GET_LAYER(map, i);
int layerWasOpened = msLayerIsOpen(lp);
if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
/* Remove group names from property names if using groupname/itemname
* syntax */
gmlGroupListObj *groupList = msGMLGetGroups(lp, namespaces);
if (groupList && groupList->numgroups > 0)
FLTRemoveGroupName(psFilterNode, groupList);
msGMLFreeGroups(groupList);
for (i = 0; i < lp->numitems; i++) {
if (!lp->items[i] || strlen(lp->items[i]) items[i]);
const char *pszFullName =
msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
if (pszFullName) {
FLTReplacePropertyName(psFilterNode, pszFullName, lp->items[i]);
}
}
if (!layerWasOpened) /* do not close the layer if it has been opened
somewhere else (paging?) */
msLayerClose(lp);
}
}
#else
msSetError(MS_MISCERR, "OWS support is not available.",
"FLTPreParseFilterForAlias()");
#endif
}
/************************************************************************/
/* FLTCheckFeatureIdFilters */
/* */
/* Check that FeatureId filters match features in the active */
/* layer. */
/************************************************************************/
int FLTCheckFeatureIdFilters(FilterEncodingNode *psFilterNode, mapObj *map,
int i) {
int status = MS_SUCCESS;
if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
char **tokens;
int nTokens = 0;
layerObj *lp;
int j;
lp = GET_LAYER(map, i);
tokens = msStringSplit(psFilterNode->pszValue, ',', &nTokens);
for (j = 0; j < nTokens; j++) {
const char *pszId = tokens[j];
const char *pszDot = strrchr(pszId, '.');
if (pszDot) {
if (static_cast(pszDot - pszId) != strlen(lp->name) ||
strncasecmp(pszId, lp->name, strlen(lp->name)) != 0) {
msSetError(MS_MISCERR,
"Feature id %s not consistent with feature type name %s.",
"FLTPreParseFilterForAlias()", pszId, lp->name);
status = MS_FAILURE;
break;
}
}
}
msFreeCharArray(tokens, nTokens);
}
if (psFilterNode->psLeftNode) {
status = FLTCheckFeatureIdFilters(psFilterNode->psLeftNode, map, i);
if (status == MS_SUCCESS) {
if (psFilterNode->psRightNode)
status = FLTCheckFeatureIdFilters(psFilterNode->psRightNode, map, i);
}
}
return status;
}
/************************************************************************/
/* FLTCheckInvalidOperand */
/* */
/* Check that the operand of a comparison operator is valid */
/* Currently only detects use of boundedBy in a binary comparison */
/************************************************************************/
int FLTCheckInvalidOperand(FilterEncodingNode *psFilterNode) {
int status = MS_SUCCESS;
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
psFilterNode->psLeftNode != NULL &&
psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
if (strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") == 0 &&
strcmp(psFilterNode->pszValue, "PropertyIsNull") != 0 &&
strcmp(psFilterNode->pszValue, "PropertyIsNil") != 0) {
msSetError(MS_MISCERR, "Operand '%s' is invalid in comparison.",
"FLTCheckInvalidOperand()",
psFilterNode->psLeftNode->pszValue);
return MS_FAILURE;
}
}
if (psFilterNode->psLeftNode) {
status = FLTCheckInvalidOperand(psFilterNode->psLeftNode);
if (status == MS_SUCCESS) {
if (psFilterNode->psRightNode)
status = FLTCheckInvalidOperand(psFilterNode->psRightNode);
}
}
return status;
}
/************************************************************************/
/* FLTProcessPropertyIsNull */
/* */
/* HACK for PropertyIsNull processing. PostGIS & Spatialite only */
/* for now. */
/************************************************************************/
int FLTProcessPropertyIsNull(FilterEncodingNode *psFilterNode, mapObj *map,
int i) {
int status = MS_SUCCESS;
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
psFilterNode->psLeftNode != NULL &&
psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
!FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
layerObj *lp;
int layerWasOpened;
lp = GET_LAYER(map, i);
layerWasOpened = msLayerIsOpen(lp);
/* Horrible HACK to compensate for the lack of null testing in MapServer */
if (lp->connectiontype == MS_POSTGIS ||
(lp->connectiontype == MS_OGR && msOGRSupportsIsNull(lp))) {
msFree(psFilterNode->pszValue);
psFilterNode->pszValue = msStrdup("PropertyIsEqualTo");
psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
psFilterNode->psRightNode->pszValue = msStrdup("_MAPSERVER_NULL_");
}
if (!layerWasOpened) /* do not close the layer if it has been opened
somewhere else (paging?) */
msLayerClose(lp);
}
if (psFilterNode->psLeftNode) {
status = FLTProcessPropertyIsNull(psFilterNode->psLeftNode, map, i);
if (status == MS_SUCCESS) {
if (psFilterNode->psRightNode)
status = FLTProcessPropertyIsNull(psFilterNode->psRightNode, map, i);
}
}
return status;
}
/************************************************************************/
/* FLTCheckInvalidProperty */
/* */
/* Check that property names are known */
/************************************************************************/
int FLTCheckInvalidProperty(FilterEncodingNode *psFilterNode, mapObj *map,
int i) {
int status = MS_SUCCESS;
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
psFilterNode->psLeftNode != NULL &&
psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
layerObj *lp;
int layerWasOpened;
int bFound = MS_FALSE;
if ((strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
return MS_SUCCESS;
}
lp = GET_LAYER(map, i);
layerWasOpened = msLayerIsOpen(lp);
if ((layerWasOpened || msLayerOpen(lp) == MS_SUCCESS) &&
msLayerGetItems(lp) == MS_SUCCESS) {
int i;
gmlItemListObj *items = msGMLGetItems(lp, "G");
for (i = 0; i < items->numitems; i++) {
if (!items->items[i].name || strlen(items->items[i].name) == 0 ||
!items->items[i].visible)
continue;
if (strcasecmp(items->items[i].name,
psFilterNode->psLeftNode->pszValue) == 0) {
bFound = MS_TRUE;
break;
}
}
msGMLFreeItems(items);
}
if (!layerWasOpened) /* do not close the layer if it has been opened
somewhere else (paging?) */
msLayerClose(lp);
if (!bFound) {
msSetError(MS_MISCERR, "Property '%s' is unknown.",
"FLTCheckInvalidProperty()",
psFilterNode->psLeftNode->pszValue);
return MS_FAILURE;
}
}
if (psFilterNode->psLeftNode) {
status = FLTCheckInvalidProperty(psFilterNode->psLeftNode, map, i);
if (status == MS_SUCCESS) {
if (psFilterNode->psRightNode)
status = FLTCheckInvalidProperty(psFilterNode->psRightNode, map, i);
}
}
return status;
}
/************************************************************************/
/* FLTSimplify */
/* */
/* Simplify the expression by removing parts that evaluate to */
/* constants. */
/* The passed psFilterNode is potentially consumed by the function */
/* and replaced by the returned value. */
/* If the function returns NULL, *pnEvaluation = MS_FALSE means */
/* that the filter evaluates to FALSE, or MS_TRUE that it */
/* evaluates to TRUE */
/************************************************************************/
FilterEncodingNode *FLTSimplify(FilterEncodingNode *psFilterNode,
int *pnEvaluation) {
*pnEvaluation = -1;
/* There are no nullable or nillable property in WFS currently */
/* except gml:name or gml:description that are null */
if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
(strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
psFilterNode->psLeftNode != NULL &&
psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
if (strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) &&
strcmp(psFilterNode->psLeftNode->pszValue, "@gml:id") != 0 &&
strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") != 0)
*pnEvaluation = MS_TRUE;
else
*pnEvaluation = MS_FALSE;
FLTFreeFilterEncodingNode(psFilterNode);
return NULL;
}
if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
strcasecmp(psFilterNode->pszValue, "NOT") == 0 &&
psFilterNode->psLeftNode != NULL) {
int nEvaluation;
psFilterNode->psLeftNode =
FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
if (psFilterNode->psLeftNode == NULL) {
*pnEvaluation = 1 - nEvaluation;
FLTFreeFilterEncodingNode(psFilterNode);
return NULL;
}
}
if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
(strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
strcasecmp(psFilterNode->pszValue, "OR") == 0) &&
psFilterNode->psLeftNode != NULL && psFilterNode->psRightNode != NULL) {
FilterEncodingNode *psOtherNode;
int nEvaluation;
int nExpectedValForFastExit;
psFilterNode->psLeftNode =
FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
if (strcasecmp(psFilterNode->pszValue, "AND") == 0)
nExpectedValForFastExit = MS_FALSE;
else
nExpectedValForFastExit = MS_TRUE;
if (psFilterNode->psLeftNode == NULL) {
if (nEvaluation == nExpectedValForFastExit) {
*pnEvaluation = nEvaluation;
FLTFreeFilterEncodingNode(psFilterNode);
return NULL;
}
psOtherNode = psFilterNode->psRightNode;
psFilterNode->psRightNode = NULL;
FLTFreeFilterEncodingNode(psFilterNode);
return FLTSimplify(psOtherNode, pnEvaluation);
}
psFilterNode->psRightNode =
FLTSimplify(psFilterNode->psRightNode, &nEvaluation);
if (psFilterNode->psRightNode == NULL) {
if (nEvaluation == nExpectedValForFastExit) {
*pnEvaluation = nEvaluation;
FLTFreeFilterEncodingNode(psFilterNode);
return NULL;
}
psOtherNode = psFilterNode->psLeftNode;
psFilterNode->psLeftNode = NULL;
FLTFreeFilterEncodingNode(psFilterNode);
return FLTSimplify(psOtherNode, pnEvaluation);
}
}
return psFilterNode;
}
#ifdef USE_LIBXML2
xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc,
int bTemporal) {
xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL,
psSubSubNode = NULL;
psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");
psNode =
xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);
psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
BAD_CAST "gml:Point");
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
BAD_CAST "gml:LineString");
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
BAD_CAST "gml:Polygon");
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
BAD_CAST "gml:Envelope");
psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
#ifdef USE_GEOS
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
#endif
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");
if (bTemporal) {
psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities",
NULL);
psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
BAD_CAST "gml:TimePeriod");
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
BAD_CAST "gml:TimeInstant");
psSubNode =
xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
psSubSubNode =
xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
}
psNode =
xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "LessThan");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "GreaterThan");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "LessThanEqualTo");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "GreaterThanEqualTo");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "EqualTo");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "NotEqualTo");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
BAD_CAST "Between");
psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
return psRootNode;
}
#endif