1 /**********************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: OGC Filter Encoding implementation
6 * Author: Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
7 *
8 **********************************************************************
9 * Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 ****************************************************************************/
28
29
30 #define _GNU_SOURCE
31 #include "mapserver-config.h"
32
33 #include "cpl_minixml.h"
34 #include "cpl_string.h"
35
36 #include "mapogcfilter.h"
37 #include "mapserver.h"
38 #include "mapowscommon.h"
39 #include "maptime.h"
40 #include "mapows.h"
41 #include <ctype.h>
42
43 #if 0
44 static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode);
45 #endif
46
47 #if !(defined(_WIN32) && !defined(__CYGWIN__))
IGUR_double(double ignored)48 static inline void IGUR_double(double ignored) { (void)ignored; } /* Ignore GCC Unused Result */
49 #endif
50
FLTIsNumeric(const char * pszValue)51 int FLTIsNumeric(const char *pszValue)
52 {
53 if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
54 /*the regex seems to have a problem on windows when mapserver is built using
55 PHP regex*/
56 #if defined(_WIN32) && !defined(__CYGWIN__)
57 int i = 0, nLength=0, bString=0;
58
59 nLength = strlen(pszValue);
60 for (i=0; i<nLength; i++) {
61 if (i == 0) {
62 if (!isdigit(pszValue[i]) && pszValue[i] != '-') {
63 bString = 1;
64 break;
65 }
66 } else if (!isdigit(pszValue[i]) && pszValue[i] != '.') {
67 bString = 1;
68 break;
69 }
70 }
71 if (!bString)
72 return MS_TRUE;
73 #else
74 char * p;
75 IGUR_double(strtod(pszValue, &p));
76 if ( p != pszValue && *p == '\0') return MS_TRUE;
77 #endif
78 }
79
80 return MS_FALSE;
81 }
82
83 /*
84 ** Apply an expression to the layer's filter element.
85 **
86 */
FLTApplyExpressionToLayer(layerObj * lp,const char * pszExpression)87 int FLTApplyExpressionToLayer(layerObj *lp, const char *pszExpression)
88 {
89 char *pszFinalExpression=NULL, *pszBuffer = NULL;
90 /*char *escapedTextString=NULL;*/
91 int bConcatWhere=0, bHasAWhere=0;
92
93 if (lp && pszExpression) {
94 if (lp->connectiontype == MS_POSTGIS || lp->connectiontype == MS_ORACLESPATIAL ||
95 lp->connectiontype == MS_PLUGIN) {
96 pszFinalExpression = msStrdup("(");
97 pszFinalExpression = msStringConcatenate(pszFinalExpression, pszExpression);
98 pszFinalExpression = msStringConcatenate(pszFinalExpression, ")");
99 } else if (lp->connectiontype == MS_OGR) {
100 pszFinalExpression = msStrdup(pszExpression);
101 if (lp->filter.type != MS_EXPRESSION) {
102 bConcatWhere = 1;
103 } else {
104 if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
105 bHasAWhere = 1;
106 bConcatWhere =1;
107 }
108 }
109
110 } else
111 pszFinalExpression = msStrdup(pszExpression);
112
113 if (bConcatWhere)
114 pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
115 /* if the filter is set and it's an expression type, concatenate it with
116 this filter. If not just free it */
117 if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
118 pszBuffer = msStringConcatenate(pszBuffer, "((");
119 if (bHasAWhere)
120 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
121 else
122 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
123 pszBuffer = msStringConcatenate(pszBuffer, ") and ");
124 } else if (lp->filter.string)
125 msFreeExpression(&lp->filter);
126
127 pszBuffer = msStringConcatenate(pszBuffer, pszFinalExpression);
128
129 if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
130 pszBuffer = msStringConcatenate(pszBuffer, ")");
131
132 /*assuming that expression was properly escaped
133 escapedTextString = msStringEscape(pszBuffer);
134 msLoadExpressionString(&lp->filter,
135 (char*)CPLSPrintf("%s", escapedTextString));
136 msFree(escapedTextString);
137 */
138 msLoadExpressionString(&lp->filter, pszBuffer);
139
140
141 msFree(pszFinalExpression);
142
143 if (pszBuffer)
144 msFree(pszBuffer);
145
146 return MS_TRUE;
147 }
148
149 return MS_FALSE;
150 }
151
FLTGetExpressionForValuesRanges(layerObj * lp,const char * item,const char * value,int forcecharcter)152 char *FLTGetExpressionForValuesRanges(layerObj *lp, const char *item, const char *value, int forcecharcter)
153 {
154 int bIscharacter, bSqlLayer=MS_FALSE;
155 char *pszExpression = NULL, *pszEscapedStr=NULL, *pszTmpExpression=NULL;
156 char **paszElements = NULL, **papszRangeElements=NULL;
157 int numelements,i,nrangeelements;
158
159 /* TODO: remove the bSqlLayer checks since we want to write MapServer expressions only. */
160
161 /* double minval, maxval; */
162 if (lp && item && value) {
163 if (strstr(value, "/") == NULL) {
164 /*value(s)*/
165 paszElements = msStringSplit (value, ',', &numelements);
166 if (paszElements && numelements > 0) {
167 if (forcecharcter)
168 bIscharacter = MS_TRUE;
169 else
170 bIscharacter= !FLTIsNumeric(paszElements[0]);
171
172 pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
173 for (i=0; i<numelements; i++) {
174 pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
175 if (bSqlLayer)
176 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
177 else {
178 if (bIscharacter)
179 pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
180 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
181 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
182 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
183 if (bIscharacter)
184 pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
185 }
186 if (bIscharacter) {
187 if (bSqlLayer)
188 pszTmpExpression = msStringConcatenate(pszTmpExpression, " = '");
189 else
190 pszTmpExpression = msStringConcatenate(pszTmpExpression, " = \"");
191 } else
192 pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
193
194 pszEscapedStr = msLayerEscapeSQLParam(lp, paszElements[i]);
195 pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
196
197 if (bIscharacter) {
198 if (bSqlLayer)
199 pszTmpExpression = msStringConcatenate(pszTmpExpression, "'");
200 else
201 pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
202 }
203 pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
204
205 msFree(pszEscapedStr);
206 pszEscapedStr=NULL;
207
208 if (pszExpression != NULL)
209 pszExpression = msStringConcatenate(pszExpression, " OR ");
210
211 pszExpression = msStringConcatenate(pszExpression, pszTmpExpression);
212
213 msFree(pszTmpExpression);
214 pszTmpExpression = NULL;
215 }
216 pszExpression = msStringConcatenate(pszExpression, ")");
217 }
218 msFreeCharArray(paszElements, numelements);
219 } else {
220 /*range(s)*/
221 paszElements = msStringSplit (value, ',', &numelements);
222 if (paszElements && numelements > 0) {
223 pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
224 for (i=0; i<numelements; i++) {
225 papszRangeElements = msStringSplit (paszElements[i], '/', &nrangeelements);
226 if (papszRangeElements && nrangeelements > 0) {
227 pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
228 if (nrangeelements == 2 || nrangeelements == 3) {
229 /*
230 minval = atof(papszRangeElements[0]);
231 maxval = atof(papszRangeElements[1]);
232 */
233 if (bSqlLayer)
234 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
235 else {
236 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
237 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
238 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
239 }
240
241 pszTmpExpression = msStringConcatenate(pszTmpExpression, " >= ");
242
243 pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
244 pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
245 msFree(pszEscapedStr);
246 pszEscapedStr=NULL;
247
248 pszTmpExpression = msStringConcatenate(pszTmpExpression, " AND ");
249
250 if (bSqlLayer)
251 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
252 else {
253 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
254 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
255 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
256 }
257
258 pszTmpExpression = msStringConcatenate(pszTmpExpression, " <= ");
259
260 pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[1]);
261 pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
262 msFree(pszEscapedStr);
263 pszEscapedStr=NULL;
264
265 pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
266 } else if (nrangeelements == 1) {
267 pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
268 if (bSqlLayer)
269 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
270 else {
271 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
272 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
273 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
274 }
275
276 pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
277
278 pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
279 pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
280 msFree(pszEscapedStr);
281 pszEscapedStr=NULL;
282
283 pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
284 }
285
286 if (pszExpression != NULL)
287 pszExpression = msStringConcatenate(pszExpression, " OR ");
288
289 pszExpression = msStringConcatenate(pszExpression, pszTmpExpression);
290 msFree(pszTmpExpression);
291 pszTmpExpression = NULL;
292
293 }
294 msFreeCharArray(papszRangeElements, nrangeelements);
295 }
296 pszExpression = msStringConcatenate(pszExpression, ")");
297 }
298 msFreeCharArray(paszElements, numelements);
299 }
300 }
301 msFree(pszTmpExpression);
302 return pszExpression;
303 }
304
FLTogrConvertGeometry(OGRGeometryH hGeometry,shapeObj * psShape,OGRwkbGeometryType nType)305 int FLTogrConvertGeometry(OGRGeometryH hGeometry, shapeObj *psShape,
306 OGRwkbGeometryType nType)
307 {
308 return msOGRGeometryToShape(hGeometry, psShape, nType);
309 }
310
311 static
FLTShapeFromGMLTree(CPLXMLNode * psTree,shapeObj * psShape,char ** ppszSRS)312 int FLTShapeFromGMLTree(CPLXMLNode *psTree, shapeObj *psShape , char **ppszSRS)
313 {
314 const char *pszSRS = NULL;
315 if (psTree && psShape) {
316 CPLXMLNode *psNext = psTree->psNext;
317 OGRGeometryH hGeometry = NULL;
318
319 psTree->psNext = NULL;
320 hGeometry = OGR_G_CreateFromGMLTree(psTree );
321 psTree->psNext = psNext;
322
323 if (hGeometry) {
324 OGRwkbGeometryType nType;
325 nType = OGR_G_GetGeometryType(hGeometry);
326 if (nType == wkbPolygon25D || nType == wkbMultiPolygon25D)
327 nType = wkbPolygon;
328 else if (nType == wkbLineString25D || nType == wkbMultiLineString25D)
329 nType = wkbLineString;
330 else if (nType == wkbPoint25D || nType == wkbMultiPoint25D)
331 nType = wkbPoint;
332 FLTogrConvertGeometry(hGeometry, psShape, nType);
333
334 OGR_G_DestroyGeometry(hGeometry);
335
336 pszSRS = CPLGetXMLValue(psTree, "srsName", NULL);
337 if (ppszSRS && pszSRS)
338 *ppszSRS = msStrdup(pszSRS);
339
340 return MS_TRUE;
341 }
342 }
343
344 return MS_FALSE;
345 }
346
FLTGetGeosOperator(char * pszValue)347 int FLTGetGeosOperator(char *pszValue)
348 {
349 if (!pszValue)
350 return -1;
351
352 if (strcasecmp(pszValue, "Equals") == 0)
353 return MS_GEOS_EQUALS;
354 else if (strcasecmp(pszValue, "Intersect") == 0 ||
355 strcasecmp(pszValue, "Intersects") == 0)
356 return MS_GEOS_INTERSECTS;
357 else if (strcasecmp(pszValue, "Disjoint") == 0)
358 return MS_GEOS_DISJOINT;
359 else if (strcasecmp(pszValue, "Touches") == 0)
360 return MS_GEOS_TOUCHES;
361 else if (strcasecmp(pszValue, "Crosses") == 0)
362 return MS_GEOS_CROSSES;
363 else if (strcasecmp(pszValue, "Within") == 0)
364 return MS_GEOS_WITHIN;
365 else if (strcasecmp(pszValue, "Contains") == 0)
366 return MS_GEOS_CONTAINS;
367 else if (strcasecmp(pszValue, "Overlaps") == 0)
368 return MS_GEOS_OVERLAPS;
369 else if (strcasecmp(pszValue, "Beyond") == 0)
370 return MS_GEOS_BEYOND;
371 else if (strcasecmp(pszValue, "DWithin") == 0)
372 return MS_GEOS_DWITHIN;
373
374 return -1;
375 }
376
FLTIsGeosNode(char * pszValue)377 int FLTIsGeosNode(char *pszValue)
378 {
379 if (FLTGetGeosOperator(pszValue) == -1)
380 return MS_FALSE;
381
382 return MS_TRUE;
383 }
384
385 /************************************************************************/
386 /* FLTIsSimpleFilterNoSpatial */
387 /* */
388 /* Filter encoding with only attribute queries */
389 /************************************************************************/
FLTIsSimpleFilterNoSpatial(FilterEncodingNode * psNode)390 int FLTIsSimpleFilterNoSpatial(FilterEncodingNode *psNode)
391 {
392 if (FLTIsSimpleFilter(psNode) && FLTNumberOfFilterType(psNode, "BBOX") == 0)
393 return MS_TRUE;
394
395 return MS_FALSE;
396 }
397
398 /************************************************************************/
399 /* FLTApplySimpleSQLFilter() */
400 /************************************************************************/
401
FLTApplySimpleSQLFilter(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)402 int FLTApplySimpleSQLFilter(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
403 {
404 layerObj *lp = NULL;
405 char *szExpression = NULL;
406 rectObj sQueryRect = map->extent;
407 const char *szEPSG = NULL;
408 projectionObj sProjTmp;
409 char *pszBuffer = NULL;
410 int bConcatWhere = 0;
411 int bHasAWhere =0;
412 char *pszTmp = NULL, *pszTmp2 = NULL;
413 char *tmpfilename = NULL;
414 const char* pszTimeField = NULL;
415 const char* pszTimeValue = NULL;
416
417 lp = (GET_LAYER(map, iLayerIndex));
418
419 /* if there is a bbox use it */
420 szEPSG = FLTGetBBOX(psNode, &sQueryRect);
421 if(szEPSG && map->projection.numargs > 0) {
422 msInitProjection(&sProjTmp);
423 msProjectionInheritContextFrom(&sProjTmp, &map->projection);
424 /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
425 if (msLoadProjectionString(&sProjTmp, szEPSG) == 0) {
426 msProjectRect(&sProjTmp, &map->projection, &sQueryRect);
427 }
428 msFreeProjection(&sProjTmp);
429 }
430
431 if( lp->connectiontype == MS_OGR ) {
432 pszTimeValue = FLTGetDuring(psNode, &pszTimeField);
433 }
434
435 /* make sure that the layer can be queried*/
436 if (!lp->template) lp->template = msStrdup("ttt.html");
437
438 /* if there is no class, create at least one, so that query by rect would work */
439 if (lp->numclasses == 0) {
440 if (msGrowLayerClasses(lp) == NULL)
441 return MS_FAILURE;
442 initClass(lp->class[0]);
443 }
444
445 bConcatWhere = 0;
446 bHasAWhere = 0;
447 if (lp->connectiontype == MS_POSTGIS || lp->connectiontype == MS_ORACLESPATIAL ||
448 lp->connectiontype == MS_PLUGIN) {
449 szExpression = FLTGetSQLExpression(psNode, lp);
450 if (szExpression) {
451 pszTmp = msStrdup("(");
452 pszTmp = msStringConcatenate(pszTmp, szExpression);
453 pszTmp = msStringConcatenate(pszTmp, ")");
454 msFree(szExpression);
455 szExpression = pszTmp;
456 }
457 }
458 /* concatenates the WHERE clause for OGR layers. This only applies if
459 the expression was empty or not of an expression string. If there
460 is an sql type expression, it is assumed to have the WHERE clause.
461 If it is an expression and does not have a WHERE it is assumed to be a mapserver
462 type expression*/
463 else if (lp->connectiontype == MS_OGR) {
464 if (lp->filter.type != MS_EXPRESSION) {
465 szExpression = FLTGetSQLExpression(psNode, lp);
466 bConcatWhere = 1;
467 } else {
468 if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
469 szExpression = FLTGetSQLExpression(psNode, lp);
470 bHasAWhere = 1;
471 bConcatWhere =1;
472 } else {
473 szExpression = FLTGetCommonExpression(psNode, lp);
474 }
475 }
476 } else {
477 szExpression = FLTGetCommonExpression(psNode, lp);
478
479 }
480
481 if (szExpression) {
482 if (bConcatWhere)
483 pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
484
485 /* if the filter is set and it's an expression type, concatenate it with
486 this filter. If not just free it */
487 if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
488 pszBuffer = msStringConcatenate(pszBuffer, "((");
489 if (bHasAWhere)
490 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
491 else
492 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
493 pszBuffer = msStringConcatenate(pszBuffer, ") and ");
494 } else if (lp->filter.string)
495 msFreeExpression(&lp->filter);
496
497 pszBuffer = msStringConcatenate(pszBuffer, szExpression);
498
499 if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
500 pszBuffer = msStringConcatenate(pszBuffer, ")");
501
502 msLoadExpressionString(&lp->filter, pszBuffer);
503 free(szExpression);
504 }
505
506 if (pszTimeField && pszTimeValue)
507 msLayerSetTimeFilter(lp, pszTimeValue, pszTimeField);
508
509 if (pszBuffer)
510 free(pszBuffer);
511
512 map->query.type = MS_QUERY_BY_RECT;
513 map->query.mode = MS_QUERY_MULTIPLE;
514 map->query.layer = lp->index;
515 map->query.rect = sQueryRect;
516
517 if(map->debug == MS_DEBUGLEVEL_VVV) {
518 tmpfilename = msTmpFile(map, map->mappath, NULL, "_filter.map");
519 if (tmpfilename == NULL) {
520 tmpfilename = msTmpFile(map, NULL, NULL, "_filter.map" );
521 }
522 if (tmpfilename) {
523 msSaveMap(map,tmpfilename);
524 msDebug("FLTApplySimpleSQLFilter(): Map file after Filter was applied %s\n", tmpfilename);
525 msFree(tmpfilename);
526 }
527 }
528
529 /*for oracle connection, if we have a simple filter with no spatial constraints
530 we should set the connection function to NONE to have a better performance
531 (#2725)*/
532
533 if (lp->connectiontype == MS_ORACLESPATIAL && FLTIsSimpleFilterNoSpatial(psNode)) {
534 if (strcasestr(lp->data, "USING") == 0)
535 lp->data = msStringConcatenate(lp->data, " USING NONE");
536 else if (strcasestr(lp->data, "NONE") == 0) {
537 /*if one of the functions is used, just replace it with NONE*/
538 if (strcasestr(lp->data, "FILTER"))
539 lp->data = msCaseReplaceSubstring(lp->data, "FILTER", "NONE");
540 else if (strcasestr(lp->data, "GEOMRELATE"))
541 lp->data = msCaseReplaceSubstring(lp->data, "GEOMRELATE", "NONE");
542 else if (strcasestr(lp->data, "RELATE"))
543 lp->data = msCaseReplaceSubstring(lp->data, "RELATE", "NONE");
544 else if (strcasestr(lp->data, "VERSION")) {
545 /*should add NONE just before the VERSION. Cases are:
546 DATA "ORA_GEOMETRY FROM data USING VERSION 10g
547 DATA "ORA_GEOMETRY FROM data USING UNIQUE FID VERSION 10g"
548 */
549 pszTmp = (char *)strcasestr(lp->data, "VERSION");
550 pszTmp2 = msStringConcatenate(pszTmp2, " NONE ");
551 pszTmp2 = msStringConcatenate(pszTmp2, pszTmp);
552
553 lp->data = msCaseReplaceSubstring(lp->data, pszTmp, pszTmp2);
554
555 msFree(pszTmp2);
556
557 } else if (strcasestr(lp->data, "SRID")) {
558 lp->data = msStringConcatenate(lp->data, " NONE");
559 }
560 }
561 }
562
563 return msQueryByRect(map);
564
565 /* return MS_SUCCESS; */
566 }
567
568 /************************************************************************/
569 /* FLTSplitFilters */
570 /* */
571 /* Split filters separated by parentheses into an array of strings. */
572 /************************************************************************/
FLTSplitFilters(const char * pszStr,int * pnTokens)573 char** FLTSplitFilters(const char* pszStr, int* pnTokens)
574 {
575 const char* pszTokenBegin;
576 char** papszRet = NULL;
577 int nTokens = 0;
578 char chStringQuote = '\0';
579 int nXMLIndent = 0;
580 int bInBracket = FALSE;
581
582 if( *pszStr != '(' )
583 {
584 *pnTokens = 0;
585 return NULL;
586 }
587 pszStr ++;
588 pszTokenBegin = pszStr;
589 while( *pszStr != '\0' )
590 {
591 /* Ignore any character until end of quoted string */
592 if( chStringQuote != '\0' )
593 {
594 if( *pszStr == chStringQuote )
595 chStringQuote = 0;
596 }
597 /* Detect begin of quoted string only for an XML attribute, i.e. between < and > */
598 else if( bInBracket && (*pszStr == '\'' || *pszStr == '"') )
599 {
600 chStringQuote = *pszStr;
601 }
602 /* Begin of XML element */
603 else if( *pszStr == '<' )
604 {
605 bInBracket = TRUE;
606 if( pszStr[1] == '/' )
607 nXMLIndent --;
608 else if( pszStr[1] != '!' )
609 nXMLIndent ++;
610 }
611 /* <something /> case */
612 else if (*pszStr == '/' && pszStr[1] == '>' )
613 {
614 bInBracket = FALSE;
615 nXMLIndent --;
616 pszStr ++;
617 }
618 /* End of XML element */
619 else if( *pszStr == '>' )
620 {
621 bInBracket = FALSE;
622 }
623 /* Only detect and of filter when XML indentation goes back to zero */
624 else if( nXMLIndent == 0 && *pszStr == ')' )
625 {
626 papszRet = (char**) msSmallRealloc(papszRet, sizeof(char*) * (nTokens + 1));
627 papszRet[nTokens] = msStrdup(pszTokenBegin);
628 papszRet[nTokens][pszStr - pszTokenBegin] = '\0';
629 nTokens ++;
630 if( pszStr[1] != '(' )
631 {
632 break;
633 }
634 pszStr ++;
635 pszTokenBegin = pszStr + 1;
636 }
637 pszStr ++;
638 }
639 *pnTokens = nTokens;
640 return papszRet;
641 }
642
643 /************************************************************************/
644 /* FLTIsSimpleFilter */
645 /* */
646 /* Filter encoding with only attribute queries and only one bbox. */
647 /************************************************************************/
FLTIsSimpleFilter(FilterEncodingNode * psNode)648 int FLTIsSimpleFilter(FilterEncodingNode *psNode)
649 {
650 if (FLTValidForBBoxFilter(psNode)) {
651 if (FLTNumberOfFilterType(psNode, "DWithin") == 0 &&
652 FLTNumberOfFilterType(psNode, "Intersect") == 0 &&
653 FLTNumberOfFilterType(psNode, "Intersects") == 0 &&
654 FLTNumberOfFilterType(psNode, "Equals") == 0 &&
655 FLTNumberOfFilterType(psNode, "Disjoint") == 0 &&
656 FLTNumberOfFilterType(psNode, "Touches") == 0 &&
657 FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
658 FLTNumberOfFilterType(psNode, "Within") == 0 &&
659 FLTNumberOfFilterType(psNode, "Contains") == 0 &&
660 FLTNumberOfFilterType(psNode, "Overlaps") == 0 &&
661 FLTNumberOfFilterType(psNode, "Beyond") == 0)
662 return TRUE;
663 }
664
665 return FALSE;
666 }
667
668 /************************************************************************/
669 /* FLTApplyFilterToLayer */
670 /* */
671 /* Use the filter encoding node to create mapserver expressions */
672 /* and apply it to the layer. */
673 /************************************************************************/
FLTApplyFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)674 int FLTApplyFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
675 {
676 layerObj *layer = GET_LAYER(map, iLayerIndex);
677
678 if ( ! layer->vtable) {
679 int rv = msInitializeVirtualTable(layer);
680 if (rv != MS_SUCCESS)
681 return rv;
682 }
683 return layer->vtable->LayerApplyFilterToLayer(psNode, map, iLayerIndex);
684 }
685
686 /************************************************************************/
687 /* FLTLayerApplyCondSQLFilterToLayer */
688 /* */
689 /* Helper function for layer virtual table architecture */
690 /************************************************************************/
FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)691 int FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
692 {
693 return FLTLayerApplyPlainFilterToLayer(psNode, map, iLayerIndex);
694 }
695
696
697 /************************************************************************/
698 /* FLTGetTopBBOX */
699 /* */
700 /* Return the "top" BBOX if there's a unique one. */
701 /************************************************************************/
FLTGetTopBBOXInternal(FilterEncodingNode * psNode,FilterEncodingNode ** ppsTopBBOX,int * pnCount)702 static int FLTGetTopBBOXInternal(FilterEncodingNode *psNode, FilterEncodingNode** ppsTopBBOX, int *pnCount)
703 {
704 if (psNode->pszValue && strcasecmp(psNode->pszValue, "BBOX") == 0) {
705 (*pnCount) ++;
706 if( *pnCount == 1 )
707 {
708 *ppsTopBBOX = psNode;
709 return TRUE;
710 }
711 *ppsTopBBOX = NULL;
712 return FALSE;
713 }
714 else if (psNode->pszValue && strcasecmp(psNode->pszValue, "AND") == 0) {
715 return FLTGetTopBBOXInternal(psNode->psLeftNode, ppsTopBBOX, pnCount) &&
716 FLTGetTopBBOXInternal(psNode->psRightNode, ppsTopBBOX, pnCount);
717 }
718 else
719 {
720 return TRUE;
721 }
722 }
723
FLTGetTopBBOX(FilterEncodingNode * psNode)724 static FilterEncodingNode* FLTGetTopBBOX(FilterEncodingNode *psNode)
725 {
726 int nCount = 0;
727 FilterEncodingNode* psTopBBOX = NULL;
728 FLTGetTopBBOXInternal(psNode, &psTopBBOX, &nCount);
729 return psTopBBOX;
730 }
731
732 /************************************************************************/
733 /* FLTLayerSetInvalidRectIfSupported */
734 /* */
735 /* This function will set in *rect a very huge extent if the layer */
736 /* wfs_use_default_extent_for_getfeature metadata item is set to false */
737 /* and the layer supports such degenerate rectangle, as a hint that */
738 /* they should not issue a spatial filter. */
739 /************************************************************************/
740
FLTLayerSetInvalidRectIfSupported(layerObj * lp,rectObj * rect)741 int FLTLayerSetInvalidRectIfSupported(layerObj* lp,
742 rectObj* rect)
743 {
744 const char* pszUseDefaultExtent = msOWSLookupMetadata(&(lp->metadata), "F",
745 "use_default_extent_for_getfeature");
746 if( pszUseDefaultExtent && !CSLTestBoolean(pszUseDefaultExtent) &&
747 (lp->connectiontype == MS_OGR ||
748 ((lp->connectiontype == MS_PLUGIN) && (strstr(lp->plugin_library,"msplugin_mssql2008") != NULL))) )
749 {
750 const rectObj rectInvalid = MS_INIT_INVALID_RECT;
751 *rect = rectInvalid;
752 return MS_TRUE;
753 }
754 return MS_FALSE;
755 }
756
757 /************************************************************************/
758 /* FLTLayerApplyPlainFilterToLayer */
759 /* */
760 /* Helper function for layer virtual table architecture */
761 /************************************************************************/
FLTLayerApplyPlainFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)762 int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
763 int iLayerIndex)
764 {
765 char *pszExpression =NULL;
766 int status =MS_FALSE;
767 layerObj* lp = GET_LAYER(map, iLayerIndex);
768
769 pszExpression = FLTGetCommonExpression(psNode, lp);
770 if (pszExpression) {
771 FilterEncodingNode* psTopBBOX;
772 rectObj rect = map->extent;
773
774 FLTLayerSetInvalidRectIfSupported(lp, &rect);
775
776 psTopBBOX = FLTGetTopBBOX(psNode);
777 if( psTopBBOX )
778 {
779 int can_remove_expression = MS_TRUE;
780 const char* pszEPSG = FLTGetBBOX(psNode, &rect);
781 if(pszEPSG && map->projection.numargs > 0) {
782 projectionObj sProjTmp;
783 msInitProjection(&sProjTmp);
784 msProjectionInheritContextFrom(&sProjTmp, &map->projection);
785 /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
786 if (msLoadProjectionString(&sProjTmp, pszEPSG) == 0) {
787 rectObj oldRect = rect;
788 msProjectRect(&sProjTmp, &map->projection, &rect);
789 /* If reprojection is involved, do not remove the expression */
790 if( rect.minx != oldRect.minx ||
791 rect.miny != oldRect.miny ||
792 rect.maxx != oldRect.maxx ||
793 rect.maxy != oldRect.maxy )
794 {
795 can_remove_expression = MS_FALSE;
796 }
797 }
798 msFreeProjection(&sProjTmp);
799 }
800
801 /* Small optimization: if the query is just a BBOX, then do a */
802 /* msQueryByRect() */
803 if( psTopBBOX == psNode && can_remove_expression )
804 {
805 msFree(pszExpression);
806 pszExpression = NULL;
807 }
808 }
809
810 if(map->debug == MS_DEBUGLEVEL_VVV)
811 {
812 if( pszExpression )
813 msDebug("FLTLayerApplyPlainFilterToLayer(): %s, rect=%.15g,%.15g,%.15g,%.15g\n", pszExpression, rect.minx, rect.miny, rect.maxx, rect.maxy);
814 else
815 msDebug("FLTLayerApplyPlainFilterToLayer(): rect=%.15g,%.15g,%.15g,%.15g\n", rect.minx, rect.miny, rect.maxx, rect.maxy);
816 }
817
818 status = FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex,
819 pszExpression, rect);
820 msFree(pszExpression);
821 }
822
823 return status;
824 }
825
826
827
828 /************************************************************************/
829 /* FilterNode *FLTPaserFilterEncoding(char *szXMLString) */
830 /* */
831 /* Parses an Filter Encoding XML string and creates a */
832 /* FilterEncodingNodes corresponding to the string. */
833 /* Returns a pointer to the first node or NULL if */
834 /* unsuccessfull. */
835 /* Calling function should use FreeFilterEncodingNode function */
836 /* to free memeory. */
837 /************************************************************************/
FLTParseFilterEncoding(const char * szXMLString)838 FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString)
839 {
840 CPLXMLNode *psRoot = NULL, *psChild=NULL, *psFilter=NULL;
841 FilterEncodingNode *psFilterNode = NULL;
842
843 if (szXMLString == NULL || strlen(szXMLString) <= 0 ||
844 (strstr(szXMLString, "Filter") == NULL))
845 return NULL;
846
847 psRoot = CPLParseXMLString(szXMLString);
848
849 if( psRoot == NULL)
850 return NULL;
851
852 /* strip namespaces. We srtip all name spaces (#1350)*/
853 CPLStripXMLNamespace(psRoot, NULL, 1);
854
855 /* -------------------------------------------------------------------- */
856 /* get the root element (Filter). */
857 /* -------------------------------------------------------------------- */
858 psFilter = CPLGetXMLNode(psRoot, "=Filter");
859 if (!psFilter)
860 {
861 CPLDestroyXMLNode( psRoot );
862 return NULL;
863 }
864
865 psChild = psFilter->psChild;
866 while (psChild) {
867 if (FLTIsSupportedFilterType(psChild)) {
868 psFilterNode = FLTCreateFilterEncodingNode();
869 FLTInsertElementInNode(psFilterNode, psChild);
870 break;
871 } else
872 psChild = psChild->psNext;
873 }
874
875 CPLDestroyXMLNode( psRoot );
876
877 /* -------------------------------------------------------------------- */
878 /* validate the node tree to make sure that all the nodes are valid.*/
879 /* -------------------------------------------------------------------- */
880 if (!FLTValidFilterNode(psFilterNode)) {
881 FLTFreeFilterEncodingNode(psFilterNode);
882 return NULL;
883 }
884
885
886 return psFilterNode;
887 }
888
889
890 /************************************************************************/
891 /* int FLTValidFilterNode(FilterEncodingNode *psFilterNode) */
892 /* */
893 /* Validate that all the nodes are filled properly. We could */
894 /* have parts of the nodes that are correct and part which */
895 /* could be incorrect if the filter string sent is corrupted */
896 /* (eg missing a value :<PropertyName><PropertyName>) */
897 /************************************************************************/
FLTValidFilterNode(FilterEncodingNode * psFilterNode)898 int FLTValidFilterNode(FilterEncodingNode *psFilterNode)
899 {
900 int bReturn = 0;
901
902 if (!psFilterNode)
903 return 0;
904
905 if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
906 return 0;
907
908 if (psFilterNode->psLeftNode) {
909 bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
910 if (bReturn == 0)
911 return 0;
912 else if (psFilterNode->psRightNode)
913 return FLTValidFilterNode(psFilterNode->psRightNode);
914 }
915
916 return 1;
917 }
918
919 /************************************************************************/
920 /* FLTIsGeometryFilterNodeType */
921 /************************************************************************/
922
FLTIsGeometryFilterNodeType(int eType)923 static int FLTIsGeometryFilterNodeType(int eType)
924 {
925 return (eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
926 eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
927 eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON);
928 }
929
930 /************************************************************************/
931 /* FLTFreeFilterEncodingNode */
932 /* */
933 /* recursive freeing of Filter Encoding nodes. */
934 /************************************************************************/
FLTFreeFilterEncodingNode(FilterEncodingNode * psFilterNode)935 void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode)
936 {
937 if (psFilterNode) {
938 if (psFilterNode->psLeftNode) {
939 FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
940 psFilterNode->psLeftNode = NULL;
941 }
942 if (psFilterNode->psRightNode) {
943 FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
944 psFilterNode->psRightNode = NULL;
945 }
946
947 if (psFilterNode->pszSRS)
948 free( psFilterNode->pszSRS);
949
950 if( psFilterNode->pOther ) {
951 if (psFilterNode->pszValue != NULL &&
952 strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
953 FEPropertyIsLike* propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
954 if( propIsLike->pszWildCard )
955 free( propIsLike->pszWildCard );
956 if( propIsLike->pszSingleChar )
957 free( propIsLike->pszSingleChar );
958 if( propIsLike->pszEscapeChar )
959 free( propIsLike->pszEscapeChar );
960 } else if (FLTIsGeometryFilterNodeType(psFilterNode->eType)) {
961 msFreeShape((shapeObj *)(psFilterNode->pOther));
962 }
963 /* else */
964 /* TODO free pOther special fields */
965 free( psFilterNode->pOther );
966 }
967
968 /* Cannot free pszValue before, 'cause we are testing it above */
969 if( psFilterNode->pszValue )
970 free( psFilterNode->pszValue );
971
972 free(psFilterNode);
973 }
974 }
975
976
977 /************************************************************************/
978 /* FLTCreateFilterEncodingNode */
979 /* */
980 /* return a FilterEncoding node. */
981 /************************************************************************/
FLTCreateFilterEncodingNode(void)982 FilterEncodingNode *FLTCreateFilterEncodingNode(void)
983 {
984 FilterEncodingNode *psFilterNode = NULL;
985
986 psFilterNode =
987 (FilterEncodingNode *)malloc(sizeof (FilterEncodingNode));
988 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
989 psFilterNode->pszValue = NULL;
990 psFilterNode->pOther = NULL;
991 psFilterNode->pszSRS = NULL;
992 psFilterNode->psLeftNode = NULL;
993 psFilterNode->psRightNode = NULL;
994
995 return psFilterNode;
996 }
997
FLTCreateBinaryCompFilterEncodingNode(void)998 FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void)
999 {
1000 FilterEncodingNode *psFilterNode = NULL;
1001
1002 psFilterNode = FLTCreateFilterEncodingNode();
1003 /* used to store case sensitivity flag. Default is 0 meaning the
1004 comparing is case sensititive */
1005 psFilterNode->pOther = (int *)malloc(sizeof(int));
1006 (*(int *)(psFilterNode->pOther)) = 0;
1007
1008 return psFilterNode;
1009 }
1010
1011
1012 /************************************************************************/
1013 /* FLTFindGeometryNode */
1014 /* */
1015 /************************************************************************/
1016
FLTFindGeometryNode(CPLXMLNode * psXMLNode,int * pbPoint,int * pbLine,int * pbPolygon)1017 static CPLXMLNode* FLTFindGeometryNode(CPLXMLNode* psXMLNode,
1018 int* pbPoint,
1019 int* pbLine,
1020 int* pbPolygon)
1021 {
1022 CPLXMLNode *psGMLElement = NULL;
1023
1024 psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
1025 if (!psGMLElement)
1026 psGMLElement = CPLGetXMLNode(psXMLNode, "PointType");
1027 if (psGMLElement)
1028 *pbPoint =1;
1029 else {
1030 psGMLElement= CPLGetXMLNode(psXMLNode, "Polygon");
1031 if (psGMLElement)
1032 *pbPolygon = 1;
1033 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPolygon")))
1034 *pbPolygon = 1;
1035 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Surface")))
1036 *pbPolygon = 1;
1037 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiSurface")))
1038 *pbPolygon = 1;
1039 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Box")))
1040 *pbPolygon = 1;
1041 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Envelope")))
1042 *pbPolygon = 1;
1043 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "LineString")))
1044 *pbLine = 1;
1045 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiLineString")))
1046 *pbLine = 1;
1047 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Curve")))
1048 *pbLine = 1;
1049 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiCurve")))
1050 *pbLine = 1;
1051 else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPoint")))
1052 *pbPoint = 1;
1053 }
1054 return psGMLElement;
1055 }
1056
1057 /************************************************************************/
1058 /* FLTGetPropertyName */
1059 /************************************************************************/
FLTGetPropertyName(CPLXMLNode * psXMLNode)1060 static const char* FLTGetPropertyName(CPLXMLNode* psXMLNode)
1061 {
1062 const char* pszPropertyName;
1063
1064 pszPropertyName = CPLGetXMLValue(psXMLNode, "PropertyName", NULL);
1065 if( pszPropertyName == NULL ) /* FE 2.0 ? */
1066 pszPropertyName = CPLGetXMLValue(psXMLNode, "ValueReference", NULL);
1067 return pszPropertyName;
1068 }
1069
1070 /************************************************************************/
1071 /* FLTGetFirstChildNode */
1072 /************************************************************************/
FLTGetFirstChildNode(CPLXMLNode * psXMLNode)1073 static CPLXMLNode* FLTGetFirstChildNode(CPLXMLNode* psXMLNode)
1074 {
1075 if( psXMLNode == NULL )
1076 return NULL;
1077 psXMLNode = psXMLNode->psChild;
1078 while( psXMLNode != NULL )
1079 {
1080 if( psXMLNode->eType == CXT_Element )
1081 return psXMLNode;
1082 psXMLNode = psXMLNode->psNext;
1083 }
1084 return NULL;
1085 }
1086
1087 /************************************************************************/
1088 /* FLTGetNextSibblingNode */
1089 /************************************************************************/
FLTGetNextSibblingNode(CPLXMLNode * psXMLNode)1090 static CPLXMLNode* FLTGetNextSibblingNode(CPLXMLNode* psXMLNode)
1091 {
1092 if( psXMLNode == NULL )
1093 return NULL;
1094 psXMLNode = psXMLNode->psNext;
1095 while( psXMLNode != NULL )
1096 {
1097 if( psXMLNode->eType == CXT_Element )
1098 return psXMLNode;
1099 psXMLNode = psXMLNode->psNext;
1100 }
1101 return NULL;
1102 }
1103
1104 /************************************************************************/
1105 /* FLTInsertElementInNode */
1106 /* */
1107 /* Utility function to parse an XML node and transfer the */
1108 /* contents into the Filter Encoding node structure. */
1109 /************************************************************************/
FLTInsertElementInNode(FilterEncodingNode * psFilterNode,CPLXMLNode * psXMLNode)1110 void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
1111 CPLXMLNode *psXMLNode)
1112 {
1113 int nStrLength = 0;
1114 char *pszTmp = NULL;
1115 FilterEncodingNode *psCurFilNode= NULL;
1116 CPLXMLNode *psCurXMLNode = NULL;
1117 CPLXMLNode *psTmpNode = NULL;
1118 CPLXMLNode *psFeatureIdNode = NULL;
1119 const char *pszFeatureId=NULL;
1120 char *pszFeatureIdList=NULL;
1121
1122 if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
1123 psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
1124 psFilterNode->psLeftNode = NULL;
1125 psFilterNode->psRightNode = NULL;
1126
1127 /* -------------------------------------------------------------------- */
1128 /* Logical filter. AND, OR and NOT are supported. Example of */
1129 /* filer using logical filters : */
1130 /* <Filter> */
1131 /* <And> */
1132 /* <PropertyIsGreaterThan> */
1133 /* <PropertyName>Person/Age</PropertyName> */
1134 /* <Literal>50</Literal> */
1135 /* </PropertyIsGreaterThan> */
1136 /* <PropertyIsEqualTo> */
1137 /* <PropertyName>Person/Address/City</PropertyName> */
1138 /* <Literal>Toronto</Literal> */
1139 /* </PropertyIsEqualTo> */
1140 /* </And> */
1141 /* </Filter> */
1142 /* -------------------------------------------------------------------- */
1143 if (FLTIsLogicalFilterType(psXMLNode->pszValue)) {
1144 psFilterNode->eType = FILTER_NODE_TYPE_LOGICAL;
1145 if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
1146 strcasecmp(psFilterNode->pszValue, "OR") == 0) {
1147 CPLXMLNode* psFirstNode = FLTGetFirstChildNode(psXMLNode);
1148 CPLXMLNode* psSecondNode = FLTGetNextSibblingNode(psFirstNode);
1149 if (psFirstNode && psSecondNode) {
1150 /*2 operators */
1151 CPLXMLNode* psNextNode = FLTGetNextSibblingNode(psSecondNode);
1152 if (psNextNode == NULL) {
1153 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1154 FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
1155 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1156 FLTInsertElementInNode(psFilterNode->psRightNode, psSecondNode);
1157 } else {
1158 psCurXMLNode = psFirstNode;
1159 psCurFilNode = psFilterNode;
1160 while(psCurXMLNode) {
1161 psNextNode = FLTGetNextSibblingNode(psCurXMLNode);
1162 if (FLTGetNextSibblingNode(psNextNode)) {
1163 psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1164 FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1165 psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1166 psCurFilNode->psRightNode->eType = FILTER_NODE_TYPE_LOGICAL;
1167 psCurFilNode->psRightNode->pszValue = msStrdup(psFilterNode->pszValue);
1168
1169 psCurFilNode = psCurFilNode->psRightNode;
1170 psCurXMLNode = psNextNode;
1171 } else { /*last 2 operators*/
1172 psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1173 FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1174
1175 psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1176 FLTInsertElementInNode(psCurFilNode->psRightNode, psNextNode);
1177 break;
1178 }
1179 }
1180 }
1181 }
1182 else
1183 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1184 } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
1185 CPLXMLNode* psFirstNode = FLTGetFirstChildNode(psXMLNode);
1186 if (psFirstNode) {
1187 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1188 FLTInsertElementInNode(psFilterNode->psLeftNode,
1189 psFirstNode);
1190 }
1191 else
1192 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1193 } else
1194 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1195 }/* end if is logical */
1196 /* -------------------------------------------------------------------- */
1197 /* Spatial Filter. */
1198 /* BBOX : */
1199 /* <Filter> */
1200 /* <BBOX> */
1201 /* <PropertyName>Geometry</PropertyName> */
1202 /* <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1203 /* <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1204 /* </gml:Box> */
1205 /* </BBOX> */
1206 /* </Filter> */
1207 /* */
1208 /* DWithin */
1209 /* */
1210 /* <xsd:element name="DWithin" */
1211 /* type="ogc:DistanceBufferType" */
1212 /* substitutionGroup="ogc:spatialOps"/> */
1213 /* */
1214 /* <xsd:complexType name="DistanceBufferType"> */
1215 /* <xsd:complexContent> */
1216 /* <xsd:extension base="ogc:SpatialOpsType"> */
1217 /* <xsd:sequence> */
1218 /* <xsd:element ref="ogc:PropertyName"/> */
1219 /* <xsd:element ref="gml:_Geometry"/> */
1220 /* <xsd:element name="Distance" type="ogc:DistanceType"/>*/
1221 /* </xsd:sequence> */
1222 /* </xsd:extension> */
1223 /* </xsd:complexContent> */
1224 /* </xsd:complexType> */
1225 /* */
1226 /* */
1227 /* <Filter> */
1228 /* <DWithin> */
1229 /* <PropertyName>Geometry</PropertyName> */
1230 /* <gml:Point> */
1231 /* <gml:coordinates>13.0983,31.5899</gml:coordinates> */
1232 /* </gml:Point> */
1233 /* <Distance units="url#m">10</Distance> */
1234 /* </DWithin> */
1235 /* </Filter> */
1236 /* */
1237 /* Intersect */
1238 /* */
1239 /* type="ogc:BinarySpatialOpType" substitutionGroup="ogc:spatialOps"/>*/
1240 /* <xsd:element name="Intersects" */
1241 /* type="ogc:BinarySpatialOpType" */
1242 /* substitutionGroup="ogc:spatialOps"/> */
1243 /* */
1244 /* <xsd:complexType name="BinarySpatialOpType"> */
1245 /* <xsd:complexContent> */
1246 /* <xsd:extension base="ogc:SpatialOpsType"> */
1247 /* <xsd:sequence> */
1248 /* <xsd:element ref="ogc:PropertyName"/> */
1249 /* <xsd:choice> */
1250 /* <xsd:element ref="gml:_Geometry"/> */
1251 /* <xsd:element ref="gml:Box"/> */
1252 /* </xsd:sequence> */
1253 /* </xsd:extension> */
1254 /* </xsd:complexContent> */
1255 /* </xsd:complexType> */
1256 /* -------------------------------------------------------------------- */
1257 else if (FLTIsSpatialFilterType(psXMLNode->pszValue)) {
1258 psFilterNode->eType = FILTER_NODE_TYPE_SPATIAL;
1259
1260 if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
1261 char *pszSRS = NULL;
1262 const char* pszPropertyName = NULL;
1263 CPLXMLNode *psBox = NULL, *psEnvelope=NULL;
1264 rectObj sBox = {0};
1265
1266 int bCoordinatesValid = 0;
1267
1268 pszPropertyName = FLTGetPropertyName(psXMLNode);
1269 psBox = CPLGetXMLNode(psXMLNode, "Box");
1270 if (!psBox)
1271 psBox = CPLGetXMLNode(psXMLNode, "BoxType");
1272
1273 /*FE 1.0 used box FE1.1 uses envelop*/
1274 if (psBox)
1275 bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
1276 else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
1277 bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);
1278
1279 if (bCoordinatesValid) {
1280 /*set the srs if available*/
1281 if (pszSRS)
1282 psFilterNode->pszSRS = pszSRS;
1283
1284 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1285 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1286 /* PropertyName is optional since FE 1.1.0, in which case */
1287 /* the BBOX must apply to all geometry fields. As we support */
1288 /* currently only one geometry field, this doesn't make much */
1289 /* difference to further processing. */
1290 if( pszPropertyName != NULL ) {
1291 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1292 }
1293
1294 /* coordinates */
1295 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1296 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
1297 psFilterNode->psRightNode->pOther =
1298 (rectObj *)msSmallMalloc(sizeof(rectObj));
1299 ((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
1300 ((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
1301 ((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
1302 ((rectObj *)psFilterNode->psRightNode->pOther)->maxy = sBox.maxy;
1303 } else {
1304 msFree(pszSRS);
1305 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1306 }
1307 } else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
1308 strcasecmp(psXMLNode->pszValue, "Beyond") == 0)
1309
1310 {
1311 shapeObj *psShape = NULL;
1312 int bPoint = 0, bLine = 0, bPolygon = 0;
1313 const char *pszUnits = NULL;
1314 const char* pszDistance = NULL;
1315 const char* pszPropertyName;
1316 char *pszSRS = NULL;
1317
1318 CPLXMLNode *psGMLElement = NULL, *psDistance=NULL;
1319
1320 pszPropertyName = FLTGetPropertyName(psXMLNode);
1321
1322 psGMLElement = FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
1323
1324 psDistance = CPLGetXMLNode(psXMLNode, "Distance");
1325 if( psDistance != NULL )
1326 pszDistance = CPLGetXMLValue(psDistance, NULL, NULL );
1327 if (pszPropertyName != NULL && psGMLElement && psDistance != NULL ) {
1328 pszUnits = CPLGetXMLValue(psDistance, "units", NULL);
1329 if( pszUnits == NULL ) /* FE 2.0 */
1330 pszUnits = CPLGetXMLValue(psDistance, "uom", NULL);
1331 psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
1332 msInitShape(psShape);
1333 if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
1334 {
1335 /*set the srs if available*/
1336 if (pszSRS)
1337 psFilterNode->pszSRS = pszSRS;
1338
1339 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1340 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1341 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1342
1343 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1344 if (bPoint)
1345 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
1346 else if (bLine)
1347 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
1348 else if (bPolygon)
1349 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1350 psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
1351 /*the value will be distance;units*/
1352 psFilterNode->psRightNode->pszValue = msStrdup(pszDistance);
1353 if (pszUnits) {
1354 psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, ";");
1355 psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, pszUnits);
1356 }
1357 }
1358 else
1359 {
1360 free(psShape);
1361 msFree(pszSRS);
1362 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1363 }
1364 } else
1365 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1366 } else if (strcasecmp(psXMLNode->pszValue, "Intersect") == 0 ||
1367 strcasecmp(psXMLNode->pszValue, "Intersects") == 0 ||
1368 strcasecmp(psXMLNode->pszValue, "Equals") == 0 ||
1369 strcasecmp(psXMLNode->pszValue, "Disjoint") == 0 ||
1370 strcasecmp(psXMLNode->pszValue, "Touches") == 0 ||
1371 strcasecmp(psXMLNode->pszValue, "Crosses") == 0 ||
1372 strcasecmp(psXMLNode->pszValue, "Within") == 0 ||
1373 strcasecmp(psXMLNode->pszValue, "Contains") == 0 ||
1374 strcasecmp(psXMLNode->pszValue, "Overlaps") == 0) {
1375 shapeObj *psShape = NULL;
1376 int bLine = 0, bPolygon = 0, bPoint=0;
1377 char *pszSRS = NULL;
1378 const char* pszPropertyName;
1379
1380 CPLXMLNode *psGMLElement = NULL;
1381
1382 pszPropertyName = FLTGetPropertyName(psXMLNode);
1383
1384 psGMLElement = FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
1385
1386 if (pszPropertyName != NULL && psGMLElement) {
1387 psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
1388 msInitShape(psShape);
1389 if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
1390 {
1391 /*set the srs if available*/
1392 if (pszSRS)
1393 psFilterNode->pszSRS = pszSRS;
1394
1395 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1396 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1397 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1398
1399 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1400 if (bPoint)
1401 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
1402 else if (bLine)
1403 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
1404 else if (bPolygon)
1405 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1406 psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
1407
1408 }
1409 else
1410 {
1411 free(psShape);
1412 msFree(pszSRS);
1413 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1414 }
1415 } else
1416 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1417 }
1418
1419
1420 }/* end of is spatial */
1421
1422
1423 /* -------------------------------------------------------------------- */
1424 /* Comparison Filter */
1425 /* -------------------------------------------------------------------- */
1426 else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
1427 psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
1428 /* -------------------------------------------------------------------- */
1429 /* binary comaparison types. Example : */
1430 /* */
1431 /* <Filter> */
1432 /* <PropertyIsEqualTo> */
1433 /* <PropertyName>SomeProperty</PropertyName> */
1434 /* <Literal>100</Literal> */
1435 /* </PropertyIsEqualTo> */
1436 /* </Filter> */
1437 /* -------------------------------------------------------------------- */
1438 if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
1439 const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1440 if (pszPropertyName != NULL ) {
1441
1442 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1443 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1444 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1445
1446 psTmpNode = CPLSearchXMLNode(psXMLNode, "Literal");
1447 if (psTmpNode) {
1448 const char* pszLiteral = CPLGetXMLValue(psTmpNode, NULL, NULL);
1449
1450 psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
1451 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
1452
1453 if (pszLiteral != NULL) {
1454 const char* pszMatchCase;
1455
1456 psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
1457
1458 pszMatchCase = CPLGetXMLValue(psXMLNode, "matchCase", NULL);
1459
1460 /*check if the matchCase attribute is set*/
1461 if( pszMatchCase != NULL && strcasecmp( pszMatchCase, "false") == 0) {
1462 (*(int *)psFilterNode->psRightNode->pOther) = 1;
1463 }
1464
1465 }
1466 /* special case where the user puts an empty value */
1467 /* for the Literal so it can end up as an empty */
1468 /* string query in the expression */
1469 else
1470 psFilterNode->psRightNode->pszValue = NULL;
1471 }
1472 }
1473 if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL)
1474 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1475 }
1476
1477 /* -------------------------------------------------------------------- */
1478 /* PropertyIsBetween filter : extract property name and boudary */
1479 /* values. The boundary values are stored in the right */
1480 /* node. The values are separated by a semi-column (;) */
1481 /* Eg of Filter : */
1482 /* <PropertyIsBetween> */
1483 /* <PropertyName>DEPTH</PropertyName> */
1484 /* <LowerBoundary><Literal>400</Literal></LowerBoundary> */
1485 /* <UpperBoundary><Literal>800</Literal></UpperBoundary> */
1486 /* </PropertyIsBetween> */
1487 /* */
1488 /* Or */
1489 /* <PropertyIsBetween> */
1490 /* <PropertyName>DEPTH</PropertyName> */
1491 /* <LowerBoundary>400</LowerBoundary> */
1492 /* <UpperBoundary>800</UpperBoundary> */
1493 /* </PropertyIsBetween> */
1494 /* -------------------------------------------------------------------- */
1495 else if (strcasecmp(psXMLNode->pszValue, "PropertyIsBetween") == 0) {
1496 const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1497 CPLXMLNode* psLowerBoundary = CPLGetXMLNode(psXMLNode, "LowerBoundary");
1498 CPLXMLNode* psUpperBoundary = CPLGetXMLNode(psXMLNode, "UpperBoundary");
1499 const char* pszLowerNode = NULL;
1500 const char* pszUpperNode = NULL;
1501 if( psLowerBoundary != NULL )
1502 {
1503 /* check if the <Literal> is there */
1504 if (CPLGetXMLNode(psLowerBoundary, "Literal") != NULL)
1505 pszLowerNode = CPLGetXMLValue(psLowerBoundary, "Literal", NULL);
1506 else
1507 pszLowerNode = CPLGetXMLValue(psLowerBoundary, NULL, NULL);
1508 }
1509 if( psUpperBoundary != NULL )
1510 {
1511 if (CPLGetXMLNode(psUpperBoundary, "Literal") != NULL)
1512 pszUpperNode = CPLGetXMLValue(psUpperBoundary, "Literal", NULL);
1513 else
1514 pszUpperNode = CPLGetXMLValue(psUpperBoundary, NULL, NULL);
1515 }
1516 if (pszPropertyName != NULL && pszLowerNode != NULL && pszUpperNode != NULL) {
1517 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1518
1519 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1520 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1521
1522 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1523 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
1524
1525 /* adding a ; between bounary values */
1526 nStrLength = strlen(pszLowerNode) + strlen(pszUpperNode) + 2;
1527
1528 psFilterNode->psRightNode->pszValue =
1529 (char *)malloc(sizeof(char)*(nStrLength));
1530 strcpy( psFilterNode->psRightNode->pszValue, pszLowerNode);
1531 strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
1532 strlcat(psFilterNode->psRightNode->pszValue, pszUpperNode, nStrLength);
1533
1534
1535 } else
1536 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1537
1538 }/* end of PropertyIsBetween */
1539 /* -------------------------------------------------------------------- */
1540 /* PropertyIsLike */
1541 /* */
1542 /* <Filter> */
1543 /* <PropertyIsLike wildCard="*" singleChar="#" escape="!"> */
1544 /* <PropertyName>LAST_NAME</PropertyName> */
1545 /* <Literal>JOHN*</Literal> */
1546 /* </PropertyIsLike> */
1547 /* </Filter> */
1548 /* -------------------------------------------------------------------- */
1549 else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
1550 const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1551 const char* pszLiteral = CPLGetXMLValue(psXMLNode, "Literal", NULL);
1552 const char* pszWildCard = CPLGetXMLValue(psXMLNode, "wildCard", NULL);
1553 const char* pszSingleChar = CPLGetXMLValue(psXMLNode, "singleChar", NULL);
1554 const char* pszEscapeChar = CPLGetXMLValue(psXMLNode, "escape", NULL);
1555 if( pszEscapeChar == NULL )
1556 pszEscapeChar = CPLGetXMLValue(psXMLNode, "escapeChar", NULL);
1557 if (pszPropertyName != NULL && pszLiteral != NULL &&
1558 pszWildCard != NULL && pszSingleChar != NULL && pszEscapeChar != NULL)
1559 {
1560 FEPropertyIsLike* propIsLike;
1561
1562 propIsLike = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
1563
1564 psFilterNode->pOther = propIsLike;
1565 propIsLike->bCaseInsensitive = 0;
1566 propIsLike->pszWildCard = msStrdup(pszWildCard);
1567 propIsLike->pszSingleChar = msStrdup(pszSingleChar);
1568 propIsLike->pszEscapeChar = msStrdup(pszEscapeChar);
1569
1570 pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", NULL);
1571 if (pszTmp && strcasecmp(pszTmp, "false") == 0) {
1572 propIsLike->bCaseInsensitive =1;
1573 }
1574 /* -------------------------------------------------------------------- */
1575 /* Create left and right node for the attribute and the value. */
1576 /* -------------------------------------------------------------------- */
1577 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1578
1579 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1580 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1581
1582 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1583
1584 psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
1585
1586 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
1587 } else
1588 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1589
1590 }
1591
1592 else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNull") == 0) {
1593 const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1594 if( pszPropertyName != NULL )
1595 {
1596 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1597 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1598 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1599 } else
1600 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1601 }
1602
1603 else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNil") == 0) {
1604 const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1605 if( pszPropertyName != NULL )
1606 {
1607 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1608 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1609 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1610 } else
1611 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1612 }
1613 }
1614 /* -------------------------------------------------------------------- */
1615 /* FeatureId Filter */
1616 /* */
1617 /* <ogc:Filter> */
1618 /* <ogc:FeatureId fid="INWATERA_1M.1013"/> */
1619 /* <ogc:FeatureId fid="INWATERA_1M.10"/> */
1620 /* <ogc:FeatureId fid="INWATERA_1M.13"/> */
1621 /* <ogc:FeatureId fid="INWATERA_1M.140"/> */
1622 /* <ogc:FeatureId fid="INWATERA_1M.5001"/> */
1623 /* <ogc:FeatureId fid="INWATERA_1M.2001"/> */
1624 /* </ogc:Filter> */
1625 /* */
1626 /* */
1627 /* Note that for FES1.1.0 the featureid has been depricated in */
1628 /* favor of GmlObjectId */
1629 /* <GmlObjectId gml:id="TREESA_1M.1234"/> */
1630 /* */
1631 /* And in FES 2.0, in favor of <fes:ResourceId rid="foo.1234"/> */
1632 /* -------------------------------------------------------------------- */
1633 else if (FLTIsFeatureIdFilterType(psXMLNode->pszValue)) {
1634 psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
1635 pszFeatureId = CPLGetXMLValue(psXMLNode, "fid", NULL);
1636 /*for FE 1.1.0 GmlObjectId */
1637 if (pszFeatureId == NULL)
1638 pszFeatureId = CPLGetXMLValue(psXMLNode, "id", NULL);
1639 /*for FE 2.0 ResourceId */
1640 if (pszFeatureId == NULL)
1641 pszFeatureId = CPLGetXMLValue(psXMLNode, "rid", NULL);
1642 pszFeatureIdList = NULL;
1643
1644 psFeatureIdNode = psXMLNode;
1645 while (psFeatureIdNode) {
1646 pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
1647 if (!pszFeatureId)
1648 pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "id", NULL);
1649 if (!pszFeatureId)
1650 pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "rid", NULL);
1651
1652 if (pszFeatureId) {
1653 if (pszFeatureIdList)
1654 pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");
1655
1656 pszFeatureIdList = msStringConcatenate(pszFeatureIdList, pszFeatureId);
1657 }
1658 psFeatureIdNode = psFeatureIdNode->psNext;
1659 }
1660
1661 if (pszFeatureIdList) {
1662 msFree(psFilterNode->pszValue);
1663 psFilterNode->pszValue = msStrdup(pszFeatureIdList);
1664 msFree(pszFeatureIdList);
1665 } else
1666 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1667 }
1668
1669 /* -------------------------------------------------------------------- */
1670 /* Temporal Filter. */
1671 /*
1672 <fes:During>
1673 <fes:ValueReference>gml:TimeInstant</fes:ValueReference>
1674 <gml:TimePeriod gml:id="TP1">
1675 <gml:begin>
1676 <gml:TimeInstant gml:id="TI1">
1677 <gml:timePosition>2005-05-17T00:00:00Z</gml:timePosition>
1678 </gml:TimeInstant>
1679 </gml:begin>
1680 <gml:end>
1681 <gml:TimeInstant gml:id="TI2">
1682 <gml:timePosition>2005-05-23T00:00:00Z</gml:timePosition>
1683 </gml:TimeInstant>
1684 </gml:end>
1685 </gml:TimePeriod>
1686 </fes:During>
1687 */
1688 /* -------------------------------------------------------------------- */
1689 else if (FLTIsTemporalFilterType(psXMLNode->pszValue)) {
1690 psFilterNode->eType = FILTER_NODE_TYPE_TEMPORAL;
1691
1692 if (strcasecmp(psXMLNode->pszValue, "During") == 0) {
1693 const char* pszPropertyName = NULL;
1694 const char* pszBeginTime;
1695 const char* pszEndTime;
1696
1697 pszPropertyName = FLTGetPropertyName(psXMLNode);
1698 pszBeginTime = CPLGetXMLValue(psXMLNode, "TimePeriod.begin.TimeInstant.timePosition", NULL);
1699 if( pszBeginTime == NULL )
1700 pszBeginTime = CPLGetXMLValue(psXMLNode, "TimePeriod.beginPosition", NULL);
1701 pszEndTime = CPLGetXMLValue(psXMLNode, "TimePeriod.end.TimeInstant.timePosition", NULL);
1702 if( pszEndTime == NULL )
1703 pszEndTime = CPLGetXMLValue(psXMLNode, "TimePeriod.endPosition", NULL);
1704
1705 if (pszPropertyName && pszBeginTime && pszEndTime &&
1706 strchr(pszBeginTime, '\'') == NULL && strchr(pszBeginTime, '\\') == NULL &&
1707 strchr(pszEndTime, '\'') == NULL && strchr(pszEndTime, '\\') == NULL &&
1708 msTimeGetResolution(pszBeginTime) >= 0 &&
1709 msTimeGetResolution(pszEndTime) >= 0) {
1710
1711 psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1712 psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1713 psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1714
1715 psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1716 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_TIME_PERIOD;
1717 psFilterNode->psRightNode->pszValue = msSmallMalloc( strlen(pszBeginTime) + strlen(pszEndTime) + 2 );
1718 sprintf(psFilterNode->psRightNode->pszValue, "%s/%s", pszBeginTime, pszEndTime);
1719 }
1720 else
1721 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1722 } else {
1723 psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1724 }
1725
1726 }/* end of is temporal */
1727
1728
1729
1730 }
1731 }
1732
1733
1734 /************************************************************************/
1735 /* int FLTIsLogicalFilterType((char *pszValue) */
1736 /* */
1737 /* return TRUE if the value of the node is of logical filter */
1738 /* encoding type. */
1739 /************************************************************************/
FLTIsLogicalFilterType(const char * pszValue)1740 int FLTIsLogicalFilterType(const char *pszValue)
1741 {
1742 if (pszValue) {
1743 if (strcasecmp(pszValue, "AND") == 0 ||
1744 strcasecmp(pszValue, "OR") == 0 ||
1745 strcasecmp(pszValue, "NOT") == 0)
1746 return MS_TRUE;
1747 }
1748
1749 return MS_FALSE;
1750 }
1751
1752 /************************************************************************/
1753 /* int FLTIsBinaryComparisonFilterType(char *pszValue) */
1754 /* */
1755 /* Binary comparison filter type. */
1756 /************************************************************************/
FLTIsBinaryComparisonFilterType(const char * pszValue)1757 int FLTIsBinaryComparisonFilterType(const char *pszValue)
1758 {
1759 if (pszValue) {
1760 if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
1761 strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
1762 strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
1763 strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
1764 strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
1765 strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
1766 return MS_TRUE;
1767 }
1768
1769 return MS_FALSE;
1770 }
1771
1772 /************************************************************************/
1773 /* int FLTIsComparisonFilterType(char *pszValue) */
1774 /* */
1775 /* return TRUE if the value of the node is of comparison filter */
1776 /* encoding type. */
1777 /************************************************************************/
FLTIsComparisonFilterType(const char * pszValue)1778 int FLTIsComparisonFilterType(const char *pszValue)
1779 {
1780 if (pszValue) {
1781 if (FLTIsBinaryComparisonFilterType(pszValue) ||
1782 strcasecmp(pszValue, "PropertyIsLike") == 0 ||
1783 strcasecmp(pszValue, "PropertyIsBetween") == 0 ||
1784 strcasecmp(pszValue, "PropertyIsNull") == 0 ||
1785 strcasecmp(pszValue, "PropertyIsNil") == 0)
1786 return MS_TRUE;
1787 }
1788
1789 return MS_FALSE;
1790 }
1791
1792 /************************************************************************/
1793 /* int FLTIsFeatureIdFilterType(char *pszValue) */
1794 /* */
1795 /* return TRUE if the value of the node is of featureid filter */
1796 /* encoding type. */
1797 /************************************************************************/
FLTIsFeatureIdFilterType(const char * pszValue)1798 int FLTIsFeatureIdFilterType(const char *pszValue)
1799 {
1800 if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
1801 strcasecmp(pszValue, "GmlObjectId") == 0 ||
1802 strcasecmp(pszValue, "ResourceId") == 0))
1803
1804 return MS_TRUE;
1805
1806 return MS_FALSE;
1807 }
1808
1809 /************************************************************************/
1810 /* int FLTIsSpatialFilterType(char *pszValue) */
1811 /* */
1812 /* return TRUE if the value of the node is of spatial filter */
1813 /* encoding type. */
1814 /************************************************************************/
FLTIsSpatialFilterType(const char * pszValue)1815 int FLTIsSpatialFilterType(const char *pszValue)
1816 {
1817 if (pszValue) {
1818 if ( strcasecmp(pszValue, "BBOX") == 0 ||
1819 strcasecmp(pszValue, "DWithin") == 0 ||
1820 strcasecmp(pszValue, "Intersect") == 0 ||
1821 strcasecmp(pszValue, "Intersects") == 0 ||
1822 strcasecmp(pszValue, "Equals") == 0 ||
1823 strcasecmp(pszValue, "Disjoint") == 0 ||
1824 strcasecmp(pszValue, "Touches") == 0 ||
1825 strcasecmp(pszValue, "Crosses") == 0 ||
1826 strcasecmp(pszValue, "Within") == 0 ||
1827 strcasecmp(pszValue, "Contains") == 0 ||
1828 strcasecmp(pszValue, "Overlaps") == 0 ||
1829 strcasecmp(pszValue, "Beyond") == 0)
1830 return MS_TRUE;
1831 }
1832
1833 return MS_FALSE;
1834 }
1835
1836 /************************************************************************/
1837 /* int FLTIsTemportalFilterType(char *pszValue) */
1838 /* */
1839 /* return TRUE if the value of the node is of temporal filter */
1840 /* encoding type. */
1841 /************************************************************************/
FLTIsTemporalFilterType(const char * pszValue)1842 int FLTIsTemporalFilterType(const char *pszValue)
1843 {
1844 if (pszValue) {
1845 if ( strcasecmp(pszValue, "During") == 0 )
1846 return MS_TRUE;
1847 }
1848
1849 return MS_FALSE;
1850 }
1851
1852 /************************************************************************/
1853 /* int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode) */
1854 /* */
1855 /* Verfify if the value of the node is one of the supported */
1856 /* filter type. */
1857 /************************************************************************/
FLTIsSupportedFilterType(CPLXMLNode * psXMLNode)1858 int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)
1859 {
1860 if (psXMLNode) {
1861 if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
1862 FLTIsSpatialFilterType(psXMLNode->pszValue) ||
1863 FLTIsComparisonFilterType(psXMLNode->pszValue) ||
1864 FLTIsFeatureIdFilterType(psXMLNode->pszValue) ||
1865 FLTIsTemporalFilterType(psXMLNode->pszValue))
1866 return MS_TRUE;
1867 }
1868
1869 return MS_FALSE;
1870 }
1871
1872 /************************************************************************/
1873 /* FLTNumberOfFilterType */
1874 /* */
1875 /* Loop trhough the nodes and return the number of nodes of */
1876 /* specified value. */
1877 /************************************************************************/
FLTNumberOfFilterType(FilterEncodingNode * psFilterNode,const char * szType)1878 int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode, const char *szType)
1879 {
1880 int nCount = 0;
1881 int nLeftNode=0 , nRightNode = 0;
1882
1883 if (!psFilterNode || !szType || !psFilterNode->pszValue)
1884 return 0;
1885
1886 if (strcasecmp(psFilterNode->pszValue, (char*)szType) == 0)
1887 nCount++;
1888
1889 if (psFilterNode->psLeftNode)
1890 nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);
1891
1892 nCount += nLeftNode;
1893
1894 if (psFilterNode->psRightNode)
1895 nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
1896 nCount += nRightNode;
1897
1898 return nCount;
1899 }
1900
1901
1902
1903
1904 /************************************************************************/
1905 /* FLTValidForBBoxFilter */
1906 /* */
1907 /* Validate if there is only one BBOX filter node. Here is waht */
1908 /* is supported (is valid) : */
1909 /* - one node which is a BBOX */
1910 /* - a logical AND with a valid BBOX */
1911 /* */
1912 /* eg 1: <Filter> */
1913 /* <BBOX> */
1914 /* <PropertyName>Geometry</PropertyName> */
1915 /* <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1916 /* <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1917 /* </gml:Box> */
1918 /* </BBOX> */
1919 /* </Filter> */
1920 /* */
1921 /* eg 2 :<Filter> */
1922 /* <AND> */
1923 /* <BBOX> */
1924 /* <PropertyName>Geometry</PropertyName> */
1925 /* <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1926 /* <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1927 /* </gml:Box> */
1928 /* </BBOX> */
1929 /* <PropertyIsEqualTo> */
1930 /* <PropertyName>SomeProperty</PropertyName> */
1931 /* <Literal>100</Literal> */
1932 /* </PropertyIsEqualTo> */
1933 /* </AND> */
1934 /* </Filter> */
1935 /* */
1936 /************************************************************************/
FLTValidForBBoxFilter(FilterEncodingNode * psFilterNode)1937 int FLTValidForBBoxFilter(FilterEncodingNode *psFilterNode)
1938 {
1939 int nCount = 0;
1940
1941 if (!psFilterNode || !psFilterNode->pszValue)
1942 return 1;
1943
1944 nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");
1945
1946 if (nCount > 1)
1947 return 0;
1948 else if (nCount == 0)
1949 return 1;
1950
1951 /* nCount ==1 */
1952 if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
1953 return 1;
1954
1955 if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1956 return FLTValidForBBoxFilter(psFilterNode->psLeftNode) &&
1957 FLTValidForBBoxFilter(psFilterNode->psRightNode);
1958 }
1959
1960 return 0;
1961 }
1962
1963 #if 0
1964 static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode)
1965 {
1966 int nCount = 0;
1967
1968 if (!psFilterNode || !psFilterNode->pszValue)
1969 return 1;
1970
1971 nCount = FLTNumberOfFilterType(psFilterNode, "During");
1972
1973 if (nCount > 1)
1974 return 0;
1975 else if (nCount == 0)
1976 return 1;
1977
1978 /* nCount ==1 */
1979 if (strcasecmp(psFilterNode->pszValue, "During") == 0)
1980 return 1;
1981
1982 if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1983 return FLTHasUniqueTopLevelDuringFilter(psFilterNode->psLeftNode) &&
1984 FLTHasUniqueTopLevelDuringFilter(psFilterNode->psRightNode);
1985 }
1986
1987 return 0;
1988 }
1989 #endif
1990
FLTIsLineFilter(FilterEncodingNode * psFilterNode)1991 int FLTIsLineFilter(FilterEncodingNode *psFilterNode)
1992 {
1993 if (!psFilterNode || !psFilterNode->pszValue)
1994 return 0;
1995
1996 if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
1997 psFilterNode->psRightNode &&
1998 psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
1999 return 1;
2000
2001 return 0;
2002 }
2003
FLTIsPolygonFilter(FilterEncodingNode * psFilterNode)2004 int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode)
2005 {
2006 if (!psFilterNode || !psFilterNode->pszValue)
2007 return 0;
2008
2009 if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2010 psFilterNode->psRightNode &&
2011 psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
2012 return 1;
2013
2014 return 0;
2015 }
2016
FLTIsPointFilter(FilterEncodingNode * psFilterNode)2017 int FLTIsPointFilter(FilterEncodingNode *psFilterNode)
2018 {
2019 if (!psFilterNode || !psFilterNode->pszValue)
2020 return 0;
2021
2022 if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2023 psFilterNode->psRightNode &&
2024 psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
2025 return 1;
2026
2027 return 0;
2028 }
2029
FLTIsBBoxFilter(FilterEncodingNode * psFilterNode)2030 int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode)
2031 {
2032 if (!psFilterNode || !psFilterNode->pszValue)
2033 return 0;
2034
2035 if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
2036 return 1;
2037
2038 return 0;
2039 }
2040
FLTGetShape(FilterEncodingNode * psFilterNode,double * pdfDistance,int * pnUnit)2041 shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
2042 int *pnUnit)
2043 {
2044 char **tokens = NULL;
2045 int nTokens = 0;
2046 FilterEncodingNode *psNode = psFilterNode;
2047 char *szUnitStr = NULL;
2048 char *szUnit = NULL;
2049
2050 if (psNode) {
2051 if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
2052 psNode = psNode->psRightNode;
2053
2054 if (FLTIsGeometryFilterNodeType(psNode->eType)) {
2055
2056 if (psNode->pszValue && pdfDistance) {
2057 /*
2058 sytnax expected is "distance;unit" or just "distance"
2059 if unit is there syntax is "URI#unit" (eg http://..../#m)
2060 or just "unit"
2061 */
2062 tokens = msStringSplit(psNode->pszValue,';', &nTokens);
2063 if (tokens && nTokens >= 1) {
2064 *pdfDistance = atof(tokens[0]);
2065
2066 if (nTokens == 2 && pnUnit) {
2067 szUnitStr = msStrdup(tokens[1]);
2068 msFreeCharArray(tokens, nTokens);
2069 nTokens = 0;
2070 tokens = msStringSplit(szUnitStr,'#', &nTokens);
2071 msFree(szUnitStr);
2072 if (tokens && nTokens >= 1) {
2073 if (nTokens ==1)
2074 szUnit = tokens[0];
2075 else
2076 szUnit = tokens[1];
2077
2078 if (strcasecmp(szUnit,"m") == 0 ||
2079 strcasecmp(szUnit,"meters") == 0 )
2080 *pnUnit = MS_METERS;
2081 else if (strcasecmp(szUnit,"km") == 0 ||
2082 strcasecmp(szUnit,"kilometers") == 0)
2083 *pnUnit = MS_KILOMETERS;
2084 else if (strcasecmp(szUnit,"NM") == 0 ||
2085 strcasecmp(szUnit,"nauticalmiles") == 0)
2086 *pnUnit = MS_NAUTICALMILES;
2087 else if (strcasecmp(szUnit,"mi") == 0 ||
2088 strcasecmp(szUnit,"miles") == 0)
2089 *pnUnit = MS_MILES;
2090 else if (strcasecmp(szUnit,"in") == 0 ||
2091 strcasecmp(szUnit,"inches") == 0)
2092 *pnUnit = MS_INCHES;
2093 else if (strcasecmp(szUnit,"ft") == 0 ||
2094 strcasecmp(szUnit,"feet") == 0)
2095 *pnUnit = MS_FEET;
2096 else if (strcasecmp(szUnit,"deg") == 0 ||
2097 strcasecmp(szUnit,"dd") == 0)
2098 *pnUnit = MS_DD;
2099 else if (strcasecmp(szUnit,"px") == 0)
2100 *pnUnit = MS_PIXELS;
2101
2102 }
2103 }
2104 }
2105 msFreeCharArray(tokens, nTokens);
2106 }
2107
2108 return (shapeObj *)psNode->pOther;
2109 }
2110 }
2111 return NULL;
2112 }
2113
2114 /************************************************************************/
2115 /* FLTGetBBOX */
2116 /* */
2117 /* Loop through the nodes are return the coordinates of the */
2118 /* first bbox node found. The retrun value is the epsg code of */
2119 /* the bbox. */
2120 /************************************************************************/
FLTGetBBOX(FilterEncodingNode * psFilterNode,rectObj * psRect)2121 const char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect)
2122 {
2123 const char *pszReturn = NULL;
2124
2125 if (!psFilterNode || !psRect)
2126 return NULL;
2127
2128 if (psFilterNode->pszValue && strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
2129 if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
2130 rectObj* pRect= (rectObj *)psFilterNode->psRightNode->pOther;
2131 psRect->minx = pRect->minx;
2132 psRect->miny = pRect->miny;
2133 psRect->maxx = pRect->maxx;
2134 psRect->maxy = pRect->maxy;
2135
2136 return psFilterNode->pszSRS;
2137
2138 }
2139 } else {
2140 pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
2141 if (pszReturn)
2142 return pszReturn;
2143 else
2144 return FLTGetBBOX(psFilterNode->psRightNode, psRect);
2145 }
2146
2147 return pszReturn;
2148 }
2149
FLTGetDuring(FilterEncodingNode * psFilterNode,const char ** ppszTimeField)2150 const char* FLTGetDuring(FilterEncodingNode *psFilterNode, const char** ppszTimeField)
2151 {
2152 const char *pszReturn = NULL;
2153
2154 if (!psFilterNode || !ppszTimeField)
2155 return NULL;
2156
2157 if (psFilterNode->pszValue && strcasecmp(psFilterNode->pszValue, "During") == 0) {
2158 *ppszTimeField = psFilterNode->psLeftNode->pszValue;
2159 return psFilterNode->psRightNode->pszValue;
2160 } else {
2161 pszReturn = FLTGetDuring(psFilterNode->psLeftNode, ppszTimeField);
2162 if (pszReturn)
2163 return pszReturn;
2164 else
2165 return FLTGetDuring(psFilterNode->psRightNode, ppszTimeField);
2166 }
2167
2168 return pszReturn;
2169 }
2170
2171 /************************************************************************/
2172 /* FLTGetSQLExpression */
2173 /* */
2174 /* Build SQL expressions from the mapserver nodes. */
2175 /************************************************************************/
FLTGetSQLExpression(FilterEncodingNode * psFilterNode,layerObj * lp)2176 char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
2177 {
2178 char *pszExpression = NULL;
2179 const char *pszAttribute = NULL;
2180 char szTmp[256];
2181 char **tokens = NULL;
2182 int nTokens = 0, i=0, bString=0;
2183
2184 if (psFilterNode == NULL || lp == NULL)
2185 return NULL;
2186
2187 if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
2188 if ( psFilterNode->psLeftNode && psFilterNode->psRightNode) {
2189 if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
2190 pszExpression =
2191 FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
2192 } else if (strcasecmp(psFilterNode->pszValue,
2193 "PropertyIsBetween") == 0) {
2194 pszExpression =
2195 FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
2196 } else if (strcasecmp(psFilterNode->pszValue,
2197 "PropertyIsLike") == 0) {
2198 pszExpression =
2199 FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);
2200
2201 }
2202 }
2203 } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
2204 if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
2205 strcasecmp(psFilterNode->pszValue, "OR") == 0) {
2206 pszExpression =
2207 FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
2208
2209 } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
2210 pszExpression =
2211 FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
2212
2213 }
2214 }
2215
2216 else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
2217 /* TODO */
2218 } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
2219 #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
2220 if (psFilterNode->pszValue) {
2221 pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
2222 if (pszAttribute) {
2223 tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
2224 bString = 0;
2225 if (tokens && nTokens > 0) {
2226 for (i=0; i<nTokens; i++) {
2227 char *pszEscapedStr = NULL;
2228 const char* pszId = tokens[i];
2229 const char* pszDot = strchr(pszId, '.');
2230 if( pszDot )
2231 pszId = pszDot + 1;
2232
2233 if (strlen(pszId) <= 0)
2234 continue;
2235
2236 if (FLTIsNumeric(pszId) == MS_FALSE)
2237 bString = 1;
2238
2239 pszEscapedStr = msLayerEscapeSQLParam(lp, pszId);
2240 if (bString)
2241 {
2242 if( lp->connectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS )
2243 snprintf(szTmp, sizeof(szTmp), "(CAST(%s AS CHARACTER(255)) = '%s')" , pszAttribute, pszEscapedStr);
2244 else
2245 snprintf(szTmp, sizeof(szTmp), "(%s = '%s')" , pszAttribute, pszEscapedStr);
2246 }
2247 else
2248 snprintf(szTmp, sizeof(szTmp), "(%s = %s)" , pszAttribute, pszEscapedStr);
2249
2250 msFree(pszEscapedStr);
2251 pszEscapedStr=NULL;
2252
2253 if (pszExpression != NULL)
2254 pszExpression = msStringConcatenate(pszExpression, " OR ");
2255 else
2256 /*opening and closing brackets*/
2257 pszExpression = msStringConcatenate(pszExpression, "(");
2258
2259 pszExpression = msStringConcatenate(pszExpression, szTmp);
2260 }
2261
2262 msFreeCharArray(tokens, nTokens);
2263 }
2264 }
2265 /*opening and closing brackets*/
2266 if (pszExpression)
2267 pszExpression = msStringConcatenate(pszExpression, ")");
2268 }
2269 #else
2270 msSetError(MS_MISCERR, "OWS support is not available.",
2271 "FLTGetSQLExpression()");
2272 return NULL;
2273 #endif
2274
2275 }
2276 else if ( lp->connectiontype != MS_OGR &&
2277 psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL )
2278 pszExpression = FLTGetTimeExpression(psFilterNode, lp);
2279
2280 return pszExpression;
2281 }
2282
2283 /************************************************************************/
2284 /* FLTGetLogicalComparisonSQLExpresssion */
2285 /* */
2286 /* Return the expression for logical comparison expression. */
2287 /************************************************************************/
FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2288 char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2289 layerObj *lp)
2290 {
2291 char *pszBuffer = NULL;
2292 char *pszTmp = NULL;
2293 int nTmp = 0;
2294
2295 if (lp == NULL)
2296 return NULL;
2297
2298 /* ==================================================================== */
2299 /* special case for BBOX node. */
2300 /* ==================================================================== */
2301 if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
2302 ((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
2303 (strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
2304 if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
2305 pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2306 else
2307 pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2308
2309 if (!pszTmp)
2310 return NULL;
2311
2312 pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 1));
2313 sprintf(pszBuffer, "%s", pszTmp);
2314 }
2315
2316 /* ==================================================================== */
2317 /* special case for temporal filter node (OGR layer only) */
2318 /* ==================================================================== */
2319 else if (lp->connectiontype == MS_OGR &&
2320 psFilterNode->psLeftNode && psFilterNode->psRightNode &&
2321 (psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_TEMPORAL ||
2322 psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_TEMPORAL) ) {
2323 if (psFilterNode->psLeftNode->eType != FILTER_NODE_TYPE_TEMPORAL)
2324 pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2325 else
2326 pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2327
2328 if (!pszTmp)
2329 return NULL;
2330
2331 pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 1));
2332 sprintf(pszBuffer, "%s", pszTmp);
2333 }
2334
2335 /* -------------------------------------------------------------------- */
2336 /* OR and AND */
2337 /* -------------------------------------------------------------------- */
2338 else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
2339 pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2340 if (!pszTmp)
2341 return NULL;
2342
2343 pszBuffer = (char *)malloc(sizeof(char) *
2344 (strlen(pszTmp) +
2345 strlen(psFilterNode->pszValue) + 5));
2346 pszBuffer[0] = '\0';
2347 strcat(pszBuffer, " (");
2348 strcat(pszBuffer, pszTmp);
2349 strcat(pszBuffer, " ");
2350 strcat(pszBuffer, psFilterNode->pszValue);
2351 strcat(pszBuffer, " ");
2352
2353 free( pszTmp );
2354
2355 nTmp = strlen(pszBuffer);
2356 pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2357 if (!pszTmp) {
2358 free(pszBuffer);
2359 return NULL;
2360 }
2361
2362 pszBuffer = (char *)realloc(pszBuffer,
2363 sizeof(char) * (strlen(pszTmp) + nTmp +3));
2364 strcat(pszBuffer, pszTmp);
2365 strcat(pszBuffer, ") ");
2366 }
2367 /* -------------------------------------------------------------------- */
2368 /* NOT */
2369 /* -------------------------------------------------------------------- */
2370 else if (psFilterNode->psLeftNode &&
2371 strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
2372 pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2373 if (!pszTmp)
2374 return NULL;
2375
2376 pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 9));
2377 pszBuffer[0] = '\0';
2378
2379 strcat(pszBuffer, " (NOT ");
2380 strcat(pszBuffer, pszTmp);
2381 strcat(pszBuffer, ") ");
2382 } else
2383 return NULL;
2384
2385 /* -------------------------------------------------------------------- */
2386 /* Cleanup. */
2387 /* -------------------------------------------------------------------- */
2388 if( pszTmp != NULL )
2389 free( pszTmp );
2390 return pszBuffer;
2391
2392 }
2393
2394
2395 /************************************************************************/
2396 /* FLTGetBinaryComparisonSQLExpresssion */
2397 /* */
2398 /* Return the expression for a binary comparison filter node. */
2399 /************************************************************************/
FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2400 char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2401 layerObj *lp)
2402 {
2403 const size_t bufferSize = 1024;
2404 char szBuffer[1024];
2405 int bString=0;
2406 char szTmp[256];
2407 char* pszEscapedStr = NULL;
2408
2409 szBuffer[0] = '\0';
2410 if (!psFilterNode || !
2411 FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
2412 return NULL;
2413
2414 /* -------------------------------------------------------------------- */
2415 /* check if the value is a numeric value or alphanumeric. If it */
2416 /* is alphanumeric, add quotes around attribute and values. */
2417 /* -------------------------------------------------------------------- */
2418 bString = 0;
2419 if (psFilterNode->psRightNode->pszValue) {
2420 const char* pszOFGType;
2421 snprintf(szTmp, sizeof(szTmp), "%s_type", psFilterNode->psLeftNode->pszValue);
2422 pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
2423 if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
2424 bString = 1;
2425
2426 else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
2427 bString = 1;
2428 }
2429
2430 /* specical case to be able to have empty strings in the expression. */
2431 if (psFilterNode->psRightNode->pszValue == NULL)
2432 bString = 1;
2433
2434
2435 /*opening bracket*/
2436 strlcat(szBuffer, " (", bufferSize);
2437
2438 pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2439
2440
2441 /* attribute */
2442 /*case insensitive set ? */
2443 if (bString &&
2444 strcasecmp(psFilterNode->pszValue,
2445 "PropertyIsEqualTo") == 0 &&
2446 psFilterNode->psRightNode->pOther &&
2447 (*(int *)psFilterNode->psRightNode->pOther) == 1) {
2448 snprintf(szTmp, sizeof(szTmp), "lower(%s) ", pszEscapedStr);
2449 strlcat(szBuffer, szTmp, bufferSize);
2450 } else
2451 strlcat(szBuffer, pszEscapedStr, bufferSize);
2452
2453 msFree(pszEscapedStr);
2454 pszEscapedStr = NULL;
2455
2456
2457 /* logical operator */
2458 if (strcasecmp(psFilterNode->pszValue,
2459 "PropertyIsEqualTo") == 0)
2460 strlcat(szBuffer, "=", bufferSize);
2461 else if (strcasecmp(psFilterNode->pszValue,
2462 "PropertyIsNotEqualTo") == 0)
2463 strlcat(szBuffer, "<>", bufferSize);
2464 else if (strcasecmp(psFilterNode->pszValue,
2465 "PropertyIsLessThan") == 0)
2466 strlcat(szBuffer, "<", bufferSize);
2467 else if (strcasecmp(psFilterNode->pszValue,
2468 "PropertyIsGreaterThan") == 0)
2469 strlcat(szBuffer, ">", bufferSize);
2470 else if (strcasecmp(psFilterNode->pszValue,
2471 "PropertyIsLessThanOrEqualTo") == 0)
2472 strlcat(szBuffer, "<=", bufferSize);
2473 else if (strcasecmp(psFilterNode->pszValue,
2474 "PropertyIsGreaterThanOrEqualTo") == 0)
2475 strlcat(szBuffer, ">=", bufferSize);
2476
2477 strlcat(szBuffer, " ", bufferSize);
2478
2479 /* value */
2480
2481 if (bString &&
2482 psFilterNode->psRightNode->pszValue &&
2483 strcasecmp(psFilterNode->pszValue,
2484 "PropertyIsEqualTo") == 0 &&
2485 psFilterNode->psRightNode->pOther &&
2486 (*(int *)psFilterNode->psRightNode->pOther) == 1) {
2487 char* pszEscapedStr;
2488 pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
2489 snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
2490 msFree(pszEscapedStr);
2491 strlcat(szBuffer, szTmp, bufferSize);
2492 } else {
2493 if (bString)
2494 strlcat(szBuffer, "'", bufferSize);
2495
2496 if (psFilterNode->psRightNode->pszValue) {
2497 if (bString) {
2498 char* pszEscapedStr;
2499 pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
2500 strlcat(szBuffer, pszEscapedStr, bufferSize);
2501 msFree(pszEscapedStr);
2502 pszEscapedStr=NULL;
2503 } else
2504 strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
2505 }
2506
2507 if (bString)
2508 strlcat(szBuffer, "'", bufferSize);
2509
2510 }
2511 /*closing bracket*/
2512 strlcat(szBuffer, ") ", bufferSize);
2513
2514 return msStrdup(szBuffer);
2515 }
2516
2517
2518 /************************************************************************/
2519 /* FLTGetIsBetweenComparisonSQLExpresssion */
2520 /* */
2521 /* Build an SQL expresssion for IsBteween Filter. */
2522 /************************************************************************/
FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2523 char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2524 layerObj *lp)
2525 {
2526 const size_t bufferSize = 1024;
2527 char szBuffer[1024];
2528 char **aszBounds = NULL;
2529 int nBounds = 0;
2530 int bString=0;
2531 char szTmp[256];
2532 char* pszEscapedStr;
2533
2534 szBuffer[0] = '\0';
2535 if (!psFilterNode ||
2536 !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
2537 return NULL;
2538
2539 if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode )
2540 return NULL;
2541
2542 /* -------------------------------------------------------------------- */
2543 /* Get the bounds value which are stored like boundmin;boundmax */
2544 /* -------------------------------------------------------------------- */
2545 aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
2546 if (nBounds != 2) {
2547 msFreeCharArray(aszBounds, nBounds);
2548 return NULL;
2549 }
2550 /* -------------------------------------------------------------------- */
2551 /* check if the value is a numeric value or alphanumeric. If it */
2552 /* is alphanumeric, add quotes around attribute and values. */
2553 /* -------------------------------------------------------------------- */
2554 bString = 0;
2555 if (aszBounds[0]) {
2556 const char* pszOFGType;
2557 snprintf(szTmp, sizeof(szTmp), "%s_type", psFilterNode->psLeftNode->pszValue);
2558 pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
2559 if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
2560 bString = 1;
2561 else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
2562 bString = 1;
2563 }
2564 if (!bString) {
2565 if (aszBounds[1]) {
2566 if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
2567 bString = 1;
2568 }
2569 }
2570
2571
2572 /* -------------------------------------------------------------------- */
2573 /* build expresssion. */
2574 /* -------------------------------------------------------------------- */
2575 /*opening paranthesis */
2576 strlcat(szBuffer, " (", bufferSize);
2577
2578 /* attribute */
2579 pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2580
2581 strlcat(szBuffer, pszEscapedStr, bufferSize);
2582 msFree(pszEscapedStr);
2583 pszEscapedStr = NULL;
2584
2585 /*between*/
2586 strlcat(szBuffer, " BETWEEN ", bufferSize);
2587
2588 /*bound 1*/
2589 if (bString)
2590 strlcat(szBuffer,"'", bufferSize);
2591 pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[0]);
2592 strlcat(szBuffer, pszEscapedStr, bufferSize);
2593 msFree(pszEscapedStr);
2594 pszEscapedStr=NULL;
2595
2596 if (bString)
2597 strlcat(szBuffer,"'", bufferSize);
2598
2599 strlcat(szBuffer, " AND ", bufferSize);
2600
2601 /*bound 2*/
2602 if (bString)
2603 strlcat(szBuffer, "'", bufferSize);
2604 pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[1]);
2605 strlcat(szBuffer, pszEscapedStr, bufferSize);
2606 msFree(pszEscapedStr);
2607 pszEscapedStr=NULL;
2608
2609 if (bString)
2610 strlcat(szBuffer,"'", bufferSize);
2611
2612 /*closing paranthesis*/
2613 strlcat(szBuffer, ")", bufferSize);
2614
2615 msFreeCharArray(aszBounds, nBounds);
2616
2617 return msStrdup(szBuffer);
2618 }
2619
2620 /************************************************************************/
2621 /* FLTGetIsLikeComparisonSQLExpression */
2622 /* */
2623 /* Build an sql expression for IsLike filter. */
2624 /************************************************************************/
FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode * psFilterNode,layerObj * lp)2625 char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
2626 layerObj *lp)
2627 {
2628 const size_t bufferSize = 1024;
2629 char szBuffer[1024];
2630 char *pszValue = NULL;
2631
2632 const char *pszWild = NULL;
2633 const char *pszSingle = NULL;
2634 const char *pszEscape = NULL;
2635 char szTmp[4];
2636
2637 int nLength=0, i=0, j=0;
2638 int bCaseInsensitive = 0;
2639
2640 char *pszEscapedStr = NULL;
2641 FEPropertyIsLike* propIsLike;
2642
2643 if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
2644 !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
2645 return NULL;
2646
2647 propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
2648 pszWild = propIsLike->pszWildCard;
2649 pszSingle = propIsLike->pszSingleChar;
2650 pszEscape = propIsLike->pszEscapeChar;
2651 bCaseInsensitive = propIsLike->bCaseInsensitive;
2652
2653 if (!pszWild || strlen(pszWild) == 0 ||
2654 !pszSingle || strlen(pszSingle) == 0 ||
2655 !pszEscape || strlen(pszEscape) == 0)
2656 return NULL;
2657
2658 if (pszEscape[0] == '\'') {
2659 /* This might be valid, but the risk of SQL injection is too high */
2660 /* and the below code is not ready for that */
2661 /* Someone who does this has clearly suspect intentions ! */
2662 msSetError(MS_MISCERR, "Single quote character is not allowed as an escaping character.",
2663 "FLTGetIsLikeComparisonSQLExpression()");
2664 return NULL;
2665 }
2666
2667
2668 szBuffer[0] = '\0';
2669 /*opening bracket*/
2670 strlcat(szBuffer, " (", bufferSize);
2671
2672 /* attribute name */
2673 pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2674
2675 strlcat(szBuffer, pszEscapedStr, bufferSize);
2676 msFree(pszEscapedStr);
2677 pszEscapedStr = NULL;
2678
2679 if (lp->connectiontype == MS_POSTGIS) {
2680 if (bCaseInsensitive == 1)
2681 strlcat(szBuffer, "::text ilike '", bufferSize);
2682 else
2683 strlcat(szBuffer, "::text like '", bufferSize);
2684 } else
2685 strlcat(szBuffer, " like '", bufferSize);
2686
2687 pszValue = psFilterNode->psRightNode->pszValue;
2688 nLength = strlen(pszValue);
2689
2690 pszEscapedStr = (char*) msSmallMalloc( 3 * nLength + 1);
2691
2692 for (i=0, j=0; i<nLength; i++) {
2693 char c = pszValue[i];
2694 if (c != pszWild[0] &&
2695 c != pszSingle[0] &&
2696 c != pszEscape[0]) {
2697 if (c == '\'') {
2698 pszEscapedStr[j++] = '\'';
2699 pszEscapedStr[j++] = '\'';
2700 } else if (c == '\\') {
2701 pszEscapedStr[j++] = '\\';
2702 pszEscapedStr[j++] = '\\';
2703 } else
2704 pszEscapedStr[j++] = c;
2705 } else if (c == pszSingle[0]) {
2706 pszEscapedStr[j++] = '_';
2707 } else if (c == pszEscape[0]) {
2708 pszEscapedStr[j++] = pszEscape[0];
2709 if (i+1<nLength) {
2710 char nextC = pszValue[i+1];
2711 i++;
2712 if (nextC == '\'') {
2713 pszEscapedStr[j++] = '\'';
2714 pszEscapedStr[j++] = '\'';
2715 } else
2716 pszEscapedStr[j++] = nextC;
2717 }
2718 } else if (c == pszWild[0]) {
2719 pszEscapedStr[j++] = '%';
2720 }
2721 }
2722 pszEscapedStr[j++] = 0;
2723 strlcat(szBuffer, pszEscapedStr, bufferSize);
2724 msFree(pszEscapedStr);
2725
2726 strlcat(szBuffer, "'", bufferSize);
2727 if (lp->connectiontype != MS_OGR) {
2728 if (lp->connectiontype == MS_POSTGIS && pszEscape[0] == '\\')
2729 strlcat(szBuffer, " escape E'", bufferSize);
2730 else
2731 strlcat(szBuffer, " escape '", bufferSize);
2732 szTmp[0] = pszEscape[0];
2733 if (pszEscape[0] == '\\') {
2734 szTmp[1] = '\\';
2735 szTmp[2] = '\'';
2736 szTmp[3] = '\0';
2737 } else {
2738 szTmp[1] = '\'';
2739 szTmp[2] = '\0';
2740 }
2741
2742 strlcat(szBuffer, szTmp, bufferSize);
2743 }
2744 strlcat(szBuffer, ") ", bufferSize);
2745
2746 return msStrdup(szBuffer);
2747 }
2748
2749 /************************************************************************/
2750 /* FLTHasSpatialFilter */
2751 /* */
2752 /* Utility function to see if a spatial filter is included in */
2753 /* the node. */
2754 /************************************************************************/
FLTHasSpatialFilter(FilterEncodingNode * psNode)2755 int FLTHasSpatialFilter(FilterEncodingNode *psNode)
2756 {
2757 int bResult = MS_FALSE;
2758
2759 if (!psNode)
2760 return MS_FALSE;
2761
2762 if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
2763 if (psNode->psLeftNode)
2764 bResult = FLTHasSpatialFilter(psNode->psLeftNode);
2765
2766 if (bResult)
2767 return MS_TRUE;
2768
2769 if (psNode->psRightNode)
2770 bResult = FLTHasSpatialFilter(psNode->psRightNode);
2771
2772 if (bResult)
2773 return MS_TRUE;
2774 } else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
2775 FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
2776 return MS_TRUE;
2777
2778
2779 return MS_FALSE;
2780 }
2781
2782
2783 /************************************************************************/
2784 /* FLTCreateFeatureIdFilterEncoding */
2785 /* */
2786 /* Utility function to create a filter node of FeatureId type. */
2787 /************************************************************************/
FLTCreateFeatureIdFilterEncoding(const char * pszString)2788 FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(const char *pszString)
2789 {
2790 FilterEncodingNode *psFilterNode = NULL;
2791
2792 if (pszString) {
2793 psFilterNode = FLTCreateFilterEncodingNode();
2794 psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
2795 psFilterNode->pszValue = msStrdup(pszString);
2796 return psFilterNode;
2797 }
2798 return NULL;
2799 }
2800
2801
2802 /************************************************************************/
2803 /* FLTParseGMLBox */
2804 /* */
2805 /* Parse gml box. Used for FE 1.0 */
2806 /************************************************************************/
FLTParseGMLBox(CPLXMLNode * psBox,rectObj * psBbox,char ** ppszSRS)2807 int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS)
2808 {
2809 int bCoordinatesValid = 0;
2810 CPLXMLNode *psCoordinates = NULL;
2811 CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
2812 char **papszCoords=NULL, **papszMin=NULL, **papszMax = NULL;
2813 int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
2814 const char *pszTmpCoord = NULL;
2815 const char *pszSRS = NULL;
2816 const char *pszTS = NULL;
2817 const char *pszCS = NULL;
2818 double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
2819
2820 if (psBox) {
2821 pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
2822 if (ppszSRS && pszSRS)
2823 *ppszSRS = msStrdup(pszSRS);
2824
2825 psCoordinates = CPLGetXMLNode(psBox, "coordinates");
2826 pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
2827 if( pszTS == NULL )
2828 pszTS = " ";
2829 pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);
2830 if( pszCS == NULL )
2831 pszCS = ",";
2832 pszTmpCoord = CPLGetXMLValue(psCoordinates, NULL, NULL);
2833
2834 if (pszTmpCoord) {
2835 papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
2836 if (papszCoords && nCoords == 2) {
2837 papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
2838 if (papszMin && nCoordsMin == 2) {
2839 papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
2840 }
2841 if (papszMax && nCoordsMax == 2) {
2842 bCoordinatesValid =1;
2843 minx = atof(papszMin[0]);
2844 miny = atof(papszMin[1]);
2845 maxx = atof(papszMax[0]);
2846 maxy = atof(papszMax[1]);
2847 }
2848
2849 msFreeCharArray(papszMin, nCoordsMin);
2850 msFreeCharArray(papszMax, nCoordsMax);
2851 }
2852
2853 msFreeCharArray(papszCoords, nCoords);
2854 } else {
2855 psCoord1 = CPLGetXMLNode(psBox, "coord");
2856 psCoord2 = FLTGetNextSibblingNode(psCoord1);
2857 if (psCoord1 && psCoord2 && strcmp(psCoord2->pszValue, "coord") == 0) {
2858 const char* pszX = CPLGetXMLValue(psCoord1, "X", NULL);
2859 const char* pszY = CPLGetXMLValue(psCoord1, "Y", NULL);
2860 if (pszX && pszY) {
2861 minx = atof(pszX);
2862 miny = atof(pszY);
2863
2864 pszX = CPLGetXMLValue(psCoord2, "X", NULL);
2865 pszY = CPLGetXMLValue(psCoord2, "Y", NULL);
2866 if (pszX && pszY) {
2867 maxx = atof(pszX);
2868 maxy = atof(pszY);
2869 bCoordinatesValid = 1;
2870 }
2871 }
2872 }
2873
2874 }
2875 }
2876
2877 if (bCoordinatesValid) {
2878 psBbox->minx = minx;
2879 psBbox->miny = miny;
2880
2881 psBbox->maxx = maxx;
2882 psBbox->maxy = maxy;
2883 }
2884
2885 return bCoordinatesValid;
2886 }
2887 /************************************************************************/
2888 /* FLTParseGMLEnvelope */
2889 /* */
2890 /* Utility function to parse a gml:Envelope (used for SOS and FE1.1)*/
2891 /************************************************************************/
FLTParseGMLEnvelope(CPLXMLNode * psRoot,rectObj * psBbox,char ** ppszSRS)2892 int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS)
2893 {
2894 CPLXMLNode *psUpperCorner=NULL, *psLowerCorner=NULL;
2895 const char *pszLowerCorner=NULL, *pszUpperCorner=NULL;
2896 int bValid = 0;
2897 char **tokens;
2898 int n;
2899
2900 if (psRoot && psBbox && psRoot->eType == CXT_Element &&
2901 EQUAL(psRoot->pszValue,"Envelope")) {
2902 /*Get the srs if available*/
2903 if (ppszSRS) {
2904 const char* pszSRS = CPLGetXMLValue(psRoot, "srsName", NULL);
2905 if( pszSRS != NULL )
2906 *ppszSRS = msStrdup(pszSRS);
2907 }
2908 psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
2909 psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");
2910
2911 if (psLowerCorner && psUpperCorner) {
2912 pszLowerCorner = CPLGetXMLValue(psLowerCorner, NULL, NULL);
2913 pszUpperCorner = CPLGetXMLValue(psUpperCorner, NULL, NULL);
2914
2915 if (pszLowerCorner && pszUpperCorner) {
2916 tokens = msStringSplit(pszLowerCorner, ' ', &n);
2917 if (tokens && n >= 2) {
2918 psBbox->minx = atof(tokens[0]);
2919 psBbox->miny = atof(tokens[1]);
2920
2921 msFreeCharArray(tokens, n);
2922
2923 tokens = msStringSplit(pszUpperCorner, ' ', &n);
2924 if (tokens && n >= 2) {
2925 psBbox->maxx = atof(tokens[0]);
2926 psBbox->maxy = atof(tokens[1]);
2927 bValid = 1;
2928 }
2929 }
2930 msFreeCharArray(tokens, n);
2931 }
2932 }
2933 }
2934
2935 return bValid;
2936 }
2937
2938 /************************************************************************/
2939 /* FLTNeedSRSSwapping */
2940 /************************************************************************/
2941
FLTNeedSRSSwapping(mapObj * map,const char * pszSRS)2942 static int FLTNeedSRSSwapping( mapObj *map, const char* pszSRS )
2943 {
2944 int bNeedSwapping = MS_FALSE;
2945 projectionObj sProjTmp;
2946 msInitProjection(&sProjTmp);
2947 if( map )
2948 {
2949 msProjectionInheritContextFrom(&sProjTmp, &map->projection);
2950 }
2951 if (msLoadProjectionStringEPSG(&sProjTmp, pszSRS) == 0) {
2952 bNeedSwapping = msIsAxisInvertedProj(&sProjTmp);
2953 }
2954 msFreeProjection(&sProjTmp);
2955 return bNeedSwapping;
2956 }
2957
2958 /************************************************************************/
2959 /* FLTDoAxisSwappingIfNecessary */
2960 /* */
2961 /* Explore all geometries and BBOX to do axis swapping when the */
2962 /* SRS requires it. If no explicit SRS is attached to the geometry */
2963 /* the bDefaultSRSNeedsAxisSwapping is taken into account. The */
2964 /* caller will have to determine its value from a more general */
2965 /* context. */
2966 /************************************************************************/
FLTDoAxisSwappingIfNecessary(mapObj * map,FilterEncodingNode * psFilterNode,int bDefaultSRSNeedsAxisSwapping)2967 void FLTDoAxisSwappingIfNecessary(mapObj *map,
2968 FilterEncodingNode *psFilterNode,
2969 int bDefaultSRSNeedsAxisSwapping)
2970 {
2971 if( psFilterNode == NULL )
2972 return;
2973
2974 if( psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2975 psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_BBOX )
2976 {
2977 rectObj* rect = (rectObj *)psFilterNode->psRightNode->pOther;
2978 const char* pszSRS = psFilterNode->pszSRS;
2979 if( (pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
2980 (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping) )
2981 {
2982 double tmp;
2983
2984 tmp = rect->minx;
2985 rect->minx = rect->miny;
2986 rect->miny = tmp;
2987
2988 tmp = rect->maxx;
2989 rect->maxx = rect->maxy;
2990 rect->maxy = tmp;
2991 }
2992 }
2993 else if( psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2994 FLTIsGeometryFilterNodeType(psFilterNode->psRightNode->eType) )
2995 {
2996 shapeObj* shape = (shapeObj *)(psFilterNode->psRightNode->pOther);
2997 const char* pszSRS = psFilterNode->pszSRS;
2998 if( (pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
2999 (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping) )
3000 {
3001 msAxisSwapShape(shape);
3002 }
3003 }
3004 else
3005 {
3006 FLTDoAxisSwappingIfNecessary(map, psFilterNode->psLeftNode, bDefaultSRSNeedsAxisSwapping);
3007 FLTDoAxisSwappingIfNecessary(map, psFilterNode->psRightNode, bDefaultSRSNeedsAxisSwapping);
3008 }
3009 }
3010
3011
FLTReplacePropertyName(FilterEncodingNode * psFilterNode,const char * pszOldName,const char * pszNewName)3012 static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
3013 const char *pszOldName,
3014 const char *pszNewName)
3015 {
3016 if (psFilterNode && pszOldName && pszNewName) {
3017 if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3018 if (psFilterNode->pszValue &&
3019 strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
3020 msFree(psFilterNode->pszValue);
3021 psFilterNode->pszValue = msStrdup(pszNewName);
3022 }
3023 }
3024 if (psFilterNode->psLeftNode)
3025 FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName,
3026 pszNewName);
3027 if (psFilterNode->psRightNode)
3028 FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName,
3029 pszNewName);
3030 }
3031 }
3032
3033
FLTIsGMLDefaultProperty(const char * pszName)3034 static int FLTIsGMLDefaultProperty(const char* pszName)
3035 {
3036 return (strcmp(pszName, "gml:name") == 0 ||
3037 strcmp(pszName, "gml:description") == 0 ||
3038 strcmp(pszName, "gml:descriptionReference") == 0 ||
3039 strcmp(pszName, "gml:identifier") == 0 ||
3040 strcmp(pszName, "gml:boundedBy") == 0 ||
3041 strcmp(pszName, "@gml:id") == 0);
3042 }
3043
FLTStripNameSpacesFromPropertyName(FilterEncodingNode * psFilterNode)3044 static void FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode)
3045 {
3046 char **tokens=NULL;
3047 int n=0;
3048
3049 if (psFilterNode) {
3050
3051 if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3052 psFilterNode->psLeftNode != NULL &&
3053 psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
3054 FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3055 {
3056 return;
3057 }
3058
3059 if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3060 if (psFilterNode->pszValue &&
3061 strstr(psFilterNode->pszValue, ":")) {
3062 tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
3063 if (tokens && n==2) {
3064 msFree(psFilterNode->pszValue);
3065 psFilterNode->pszValue = msStrdup(tokens[1]);
3066 }
3067 msFreeCharArray(tokens, n);
3068 }
3069 }
3070 if (psFilterNode->psLeftNode)
3071 FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
3072 if (psFilterNode->psRightNode)
3073 FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
3074 }
3075
3076 }
3077
FLTRemoveGroupName(FilterEncodingNode * psFilterNode,gmlGroupListObj * groupList)3078 static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode,
3079 gmlGroupListObj* groupList)
3080 {
3081 int i;
3082
3083 if (psFilterNode) {
3084
3085 if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3086 if( psFilterNode->pszValue != NULL )
3087 {
3088 const char* pszPropertyName = psFilterNode->pszValue;
3089 const char* pszSlash = strchr(pszPropertyName, '/');
3090 if( pszSlash != NULL ) {
3091 const char* pszColon = strchr(pszPropertyName, ':');
3092 if( pszColon != NULL && pszColon < pszSlash )
3093 pszPropertyName = pszColon + 1;
3094 for(i=0;i<groupList->numgroups;i++) {
3095 const char* pszGroupName = groupList->groups[i].name;
3096 size_t nGroupNameLen = strlen(pszGroupName);
3097 if(strncasecmp(pszPropertyName, pszGroupName, nGroupNameLen) == 0 &&
3098 pszPropertyName[nGroupNameLen] == '/') {
3099 char* pszTmp;
3100 pszPropertyName = pszPropertyName + nGroupNameLen + 1;
3101 pszColon = strchr(pszPropertyName, ':');
3102 if( pszColon != NULL )
3103 pszPropertyName = pszColon + 1;
3104 pszTmp = msStrdup(pszPropertyName);
3105 msFree(psFilterNode->pszValue);
3106 psFilterNode->pszValue = pszTmp;
3107 break;
3108 }
3109 }
3110 }
3111 }
3112 }
3113
3114 if (psFilterNode->psLeftNode)
3115 FLTRemoveGroupName(psFilterNode->psLeftNode, groupList);
3116 if (psFilterNode->psRightNode)
3117 FLTRemoveGroupName(psFilterNode->psRightNode, groupList);
3118 }
3119
3120 }
3121
3122 /************************************************************************/
3123 /* FLTPreParseFilterForAliasAndGroup */
3124 /* */
3125 /* Utility function to replace aliased' and grouped attributes */
3126 /* with their internal name. */
3127 /************************************************************************/
FLTPreParseFilterForAliasAndGroup(FilterEncodingNode * psFilterNode,mapObj * map,int i,const char * namespaces)3128 void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode,
3129 mapObj *map, int i, const char *namespaces)
3130 {
3131 layerObj *lp=NULL;
3132 char szTmp[256];
3133 const char *pszFullName = NULL;
3134 int layerWasOpened = MS_FALSE;
3135
3136 #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
3137
3138 if (psFilterNode && map && i>=0 && i<map->numlayers) {
3139 /*strip name spaces before hand*/
3140 FLTStripNameSpacesFromPropertyName(psFilterNode);
3141
3142 lp = GET_LAYER(map, i);
3143 layerWasOpened = msLayerIsOpen(lp);
3144 if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
3145
3146 /* Remove group names from property names if using groupname/itemname syntax */
3147 gmlGroupListObj* groupList = msGMLGetGroups(lp, namespaces);
3148 if( groupList && groupList->numgroups > 0 )
3149 FLTRemoveGroupName(psFilterNode, groupList);
3150 msGMLFreeGroups(groupList);
3151
3152 for(i=0; i<lp->numitems; i++) {
3153 if (!lp->items[i] || strlen(lp->items[i]) <= 0)
3154 continue;
3155 snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[i]);
3156 pszFullName = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
3157 if (pszFullName) {
3158 FLTReplacePropertyName(psFilterNode, pszFullName,
3159 lp->items[i]);
3160 }
3161 }
3162 if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3163 msLayerClose(lp);
3164 }
3165 }
3166 #else
3167 msSetError(MS_MISCERR, "OWS support is not available.",
3168 "FLTPreParseFilterForAlias()");
3169
3170 #endif
3171 }
3172
3173 /************************************************************************/
3174 /* FLTCheckFeatureIdFilters */
3175 /* */
3176 /* Check that FeatureId filters match features in the active */
3177 /* layer. */
3178 /************************************************************************/
FLTCheckFeatureIdFilters(FilterEncodingNode * psFilterNode,mapObj * map,int i)3179 int FLTCheckFeatureIdFilters(FilterEncodingNode *psFilterNode,
3180 mapObj *map, int i)
3181 {
3182 int status = MS_SUCCESS;
3183
3184 if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID)
3185 {
3186 char** tokens;
3187 int nTokens = 0;
3188 layerObj* lp;
3189 int j;
3190
3191 lp = GET_LAYER(map, i);
3192 tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
3193 for (j=0; j<nTokens; j++) {
3194 const char* pszId = tokens[j];
3195 const char* pszDot = strrchr(pszId, '.');
3196 if( pszDot )
3197 {
3198 if( pszDot - pszId != strlen(lp->name) ||
3199 strncasecmp(pszId, lp->name, strlen(lp->name)) != 0 )
3200 {
3201 msSetError(MS_MISCERR, "Feature id %s not consistent with feature type name %s.",
3202 "FLTPreParseFilterForAlias()", pszId, lp->name);
3203 status = MS_FAILURE;
3204 break;
3205 }
3206 }
3207 }
3208 msFreeCharArray(tokens, nTokens);
3209 }
3210
3211 if (psFilterNode->psLeftNode)
3212 {
3213 status = FLTCheckFeatureIdFilters(psFilterNode->psLeftNode, map, i);
3214 if( status == MS_SUCCESS )
3215 {
3216 if (psFilterNode->psRightNode)
3217 status = FLTCheckFeatureIdFilters(psFilterNode->psRightNode, map, i);
3218 }
3219 }
3220 return status;
3221 }
3222
3223 /************************************************************************/
3224 /* FLTCheckInvalidOperand */
3225 /* */
3226 /* Check that the operand of a comparison operator is valid */
3227 /* Currently only detects use of boundedBy in a binary comparison */
3228 /************************************************************************/
FLTCheckInvalidOperand(FilterEncodingNode * psFilterNode)3229 int FLTCheckInvalidOperand(FilterEncodingNode *psFilterNode)
3230 {
3231 int status = MS_SUCCESS;
3232
3233 if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3234 psFilterNode->psLeftNode != NULL &&
3235 psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME)
3236 {
3237 if( strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") == 0 &&
3238 strcmp(psFilterNode->pszValue, "PropertyIsNull") != 0 &&
3239 strcmp(psFilterNode->pszValue, "PropertyIsNil") != 0 )
3240 {
3241 msSetError(MS_MISCERR, "Operand '%s' is invalid in comparison.",
3242 "FLTCheckInvalidOperand()", psFilterNode->psLeftNode->pszValue);
3243 return MS_FAILURE;
3244 }
3245 }
3246 if (psFilterNode->psLeftNode)
3247 {
3248 status = FLTCheckInvalidOperand(psFilterNode->psLeftNode);
3249 if( status == MS_SUCCESS )
3250 {
3251 if (psFilterNode->psRightNode)
3252 status = FLTCheckInvalidOperand(psFilterNode->psRightNode);
3253 }
3254 }
3255 return status;
3256 }
3257
3258 /************************************************************************/
3259 /* FLTProcessPropertyIsNull */
3260 /* */
3261 /* HACK for PropertyIsNull processing. PostGIS & Spatialite only */
3262 /* for now. */
3263 /************************************************************************/
FLTProcessPropertyIsNull(FilterEncodingNode * psFilterNode,mapObj * map,int i)3264 int FLTProcessPropertyIsNull(FilterEncodingNode *psFilterNode,
3265 mapObj *map, int i)
3266 {
3267 int status = MS_SUCCESS;
3268
3269 if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3270 psFilterNode->psLeftNode != NULL &&
3271 psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
3272 strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
3273 !FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3274 {
3275 layerObj* lp;
3276 int layerWasOpened;
3277
3278 lp = GET_LAYER(map, i);
3279 layerWasOpened = msLayerIsOpen(lp);
3280
3281 /* Horrible HACK to compensate for the lack of null testing in MapServer */
3282 if( (lp->connectiontype == MS_POSTGIS ||
3283 (lp->connectiontype == MS_OGR && msOGRIsSpatialite(lp))) &&
3284 strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 )
3285 {
3286 msFree(psFilterNode->pszValue);
3287 psFilterNode->pszValue = msStrdup("PropertyIsEqualTo");
3288 psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
3289 psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
3290 psFilterNode->psRightNode->pszValue = msStrdup("_MAPSERVER_NULL_");
3291 }
3292
3293 if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3294 msLayerClose(lp);
3295 }
3296
3297 if (psFilterNode->psLeftNode)
3298 {
3299 status = FLTProcessPropertyIsNull(psFilterNode->psLeftNode, map, i);
3300 if( status == MS_SUCCESS )
3301 {
3302 if (psFilterNode->psRightNode)
3303 status = FLTProcessPropertyIsNull(psFilterNode->psRightNode, map, i);
3304 }
3305 }
3306 return status;
3307 }
3308
3309 /************************************************************************/
3310 /* FLTCheckInvalidProperty */
3311 /* */
3312 /* Check that property names are known */
3313 /************************************************************************/
FLTCheckInvalidProperty(FilterEncodingNode * psFilterNode,mapObj * map,int i)3314 int FLTCheckInvalidProperty(FilterEncodingNode *psFilterNode,
3315 mapObj *map, int i)
3316 {
3317 int status = MS_SUCCESS;
3318
3319 if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3320 psFilterNode->psLeftNode != NULL &&
3321 psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME)
3322 {
3323 layerObj* lp;
3324 int layerWasOpened;
3325 int bFound = MS_FALSE;
3326
3327 if ((strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
3328 strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
3329 FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3330 {
3331 return MS_SUCCESS;
3332 }
3333
3334 lp = GET_LAYER(map, i);
3335 layerWasOpened = msLayerIsOpen(lp);
3336 if ((layerWasOpened || msLayerOpen(lp) == MS_SUCCESS)
3337 && msLayerGetItems(lp) == MS_SUCCESS) {
3338 int i;
3339 gmlItemListObj* items = msGMLGetItems(lp, "G");
3340 for(i=0; i<items->numitems; i++) {
3341 if (!items->items[i].name || strlen(items->items[i].name) <= 0 ||
3342 !items->items[i].visible)
3343 continue;
3344 if (strcasecmp(items->items[i].name, psFilterNode->psLeftNode->pszValue) == 0) {
3345 bFound = MS_TRUE;
3346 break;
3347 }
3348 }
3349 msGMLFreeItems(items);
3350 }
3351
3352 if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3353 msLayerClose(lp);
3354
3355 if( !bFound )
3356 {
3357 msSetError(MS_MISCERR, "Property '%s' is unknown.",
3358 "FLTCheckInvalidProperty()", psFilterNode->psLeftNode->pszValue);
3359 return MS_FAILURE;
3360 }
3361 }
3362
3363 if (psFilterNode->psLeftNode)
3364 {
3365 status = FLTCheckInvalidProperty(psFilterNode->psLeftNode, map, i);
3366 if( status == MS_SUCCESS )
3367 {
3368 if (psFilterNode->psRightNode)
3369 status = FLTCheckInvalidProperty(psFilterNode->psRightNode, map, i);
3370 }
3371 }
3372 return status;
3373 }
3374
3375 /************************************************************************/
3376 /* FLTSimplify */
3377 /* */
3378 /* Simplify the expression by removing parts that evaluate to */
3379 /* constants. */
3380 /* The passed psFilterNode is potentially consumed by the function */
3381 /* and replaced by the returned value. */
3382 /* If the function returns NULL, *pnEvaluation = MS_FALSE means */
3383 /* that the filter evaluates to FALSE, or MS_TRUE that it */
3384 /* evaluates to TRUE */
3385 /************************************************************************/
FLTSimplify(FilterEncodingNode * psFilterNode,int * pnEvaluation)3386 FilterEncodingNode* FLTSimplify(FilterEncodingNode *psFilterNode,
3387 int* pnEvaluation)
3388 {
3389 *pnEvaluation = -1;
3390
3391 /* There are no nullable or nillable property in WFS currently */
3392 /* except gml:name or gml:description that are null */
3393 if( psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3394 (strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
3395 strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0 ) &&
3396 psFilterNode->psLeftNode != NULL &&
3397 psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME )
3398 {
3399 if( strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
3400 FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) &&
3401 strcmp(psFilterNode->psLeftNode->pszValue, "@gml:id") != 0 &&
3402 strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") != 0)
3403 *pnEvaluation = MS_TRUE;
3404 else
3405 *pnEvaluation = MS_FALSE;
3406 FLTFreeFilterEncodingNode(psFilterNode);
3407 return NULL;
3408 }
3409
3410 if( psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
3411 strcasecmp(psFilterNode->pszValue, "NOT") == 0 &&
3412 psFilterNode->psLeftNode != NULL )
3413 {
3414 int nEvaluation;
3415 psFilterNode->psLeftNode = FLTSimplify(psFilterNode->psLeftNode,
3416 &nEvaluation);
3417 if( psFilterNode->psLeftNode == NULL )
3418 {
3419 *pnEvaluation = 1 - nEvaluation;
3420 FLTFreeFilterEncodingNode(psFilterNode);
3421 return NULL;
3422 }
3423 }
3424
3425 if( psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
3426 (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
3427 strcasecmp(psFilterNode->pszValue, "OR") == 0) &&
3428 psFilterNode->psLeftNode != NULL &&
3429 psFilterNode->psRightNode != NULL )
3430 {
3431 FilterEncodingNode* psOtherNode;
3432 int nEvaluation;
3433 int nExpectedValForFastExit;
3434 psFilterNode->psLeftNode = FLTSimplify(psFilterNode->psLeftNode,
3435 &nEvaluation);
3436
3437 if( strcasecmp(psFilterNode->pszValue, "AND") == 0 )
3438 nExpectedValForFastExit = MS_FALSE;
3439 else
3440 nExpectedValForFastExit = MS_TRUE;
3441
3442 if( psFilterNode->psLeftNode == NULL )
3443 {
3444 if( nEvaluation == nExpectedValForFastExit )
3445 {
3446 *pnEvaluation = nEvaluation;
3447 FLTFreeFilterEncodingNode(psFilterNode);
3448 return NULL;
3449 }
3450 psOtherNode = psFilterNode->psRightNode;
3451 psFilterNode->psRightNode = NULL;
3452 FLTFreeFilterEncodingNode(psFilterNode);
3453 return FLTSimplify(psOtherNode, pnEvaluation);
3454 }
3455
3456 psFilterNode->psRightNode = FLTSimplify(psFilterNode->psRightNode,
3457 &nEvaluation);
3458 if( psFilterNode->psRightNode == NULL )
3459 {
3460 if( nEvaluation == nExpectedValForFastExit )
3461 {
3462 *pnEvaluation = nEvaluation;
3463 FLTFreeFilterEncodingNode(psFilterNode);
3464 return NULL;
3465 }
3466 psOtherNode = psFilterNode->psLeftNode;
3467 psFilterNode->psLeftNode = NULL;
3468 FLTFreeFilterEncodingNode(psFilterNode);
3469 return FLTSimplify(psOtherNode, pnEvaluation);
3470 }
3471 }
3472
3473 return psFilterNode;
3474 }
3475
3476
3477 #ifdef USE_LIBXML2
3478
FLTGetCapabilities(xmlNsPtr psNsParent,xmlNsPtr psNsOgc,int bTemporal)3479 xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc, int bTemporal)
3480 {
3481 xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL, psSubSubNode = NULL;
3482
3483 psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");
3484
3485 psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);
3486
3487 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
3488 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Point");
3489 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:LineString");
3490 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Polygon");
3491 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Envelope");
3492
3493 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
3494 #ifdef USE_GEOS
3495 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3496 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
3497 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3498 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
3499 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3500 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
3501 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3502 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
3503 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3504 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
3505 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3506 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
3507 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3508 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
3509 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3510 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
3511 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3512 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
3513 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3514 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
3515 #endif
3516 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3517 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");
3518
3519 if (bTemporal) {
3520 psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities", NULL);
3521 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
3522 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimePeriod");
3523 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimeInstant");
3524
3525 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
3526 psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
3527 xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
3528 }
3529 psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
3530 xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
3531 psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
3532 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThan");
3533 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThan");
3534 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThanEqualTo");
3535 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThanEqualTo");
3536 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "EqualTo");
3537 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "NotEqualTo");
3538 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
3539 psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Between");
3540
3541 psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
3542 xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
3543 xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
3544 return psRootNode;
3545 }
3546 #endif
3547