• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 22953070987

11 Mar 2026 12:41PM UTC coverage: 42.422% (+0.4%) from 42.009%
22953070987

push

github

geographika
Switch result to ASCII

64590 of 152256 relevant lines covered (42.42%)

27315.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.23
/src/mapogcfiltercommon.cpp
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
#include "mapogcfilter.h"
30
#include "mapserver.h"
31
#include "mapows.h"
32
#include "mapowscommon.h"
33
#include "cpl_minixml.h"
34

35
#include <string>
36

37
static std::string FLTEscapePropertyName(const char *pszStr,
392✔
38
                                         char chEscapeChar) {
39
  std::string ret;
40
  for (; *pszStr; ++pszStr) {
2,700✔
41
    if (*pszStr == chEscapeChar) {
2,308✔
42
      ret += chEscapeChar;
43
      ret += chEscapeChar;
44
    } else {
45
      ret += *pszStr;
46
    }
47
  }
48
  return ret;
392✔
49
}
50

51
std::string msGetLikePatternAsRegex(const FEPropertyIsLike *propIsLike,
42✔
52
                                    const char *pszValue) {
53
  /* From
54
   * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04
55
   */
56
  /* also add double quote because we are within a string */
57
  const char *pszRegexSpecialCharsAndDoubleQuote = "\\^${[().*+?|\"";
58

59
  const char *pszWild = propIsLike->pszWildCard;
42✔
60
  const char *pszSingle = propIsLike->pszSingleChar;
42✔
61
  const char *pszEscape = propIsLike->pszEscapeChar;
42✔
62

63
  std::string expr;
64
  const size_t nLength = strlen(pszValue);
42✔
65

66
  if (nLength > 0) {
42✔
67
    expr += '^';
68
  }
69
  for (size_t i = 0; i < nLength; i++) {
252✔
70
    if (pszValue[i] == pszSingle[0]) {
210✔
71
      expr += '.';
72
      /* The Filter escape character is supposed to only escape the single,
73
       * wildcard and escape character */
74
    } else if (pszValue[i] == pszEscape[0] &&
203✔
75
               (pszValue[i + 1] == pszSingle[0] ||
6✔
76
                pszValue[i + 1] == pszWild[0] ||
4✔
77
                pszValue[i + 1] == pszEscape[0])) {
78
      if (pszValue[i + 1] == '\\') {
6✔
79
        /* Tricky case: \ must be escaped ncce in the regular expression context
80
           so that the regexp matches it as an ordinary character.
81
           But as \ is also the escape character for MapServer string, we
82
           must escape it again. */
83
        expr += "\\"
84
                "\\"
85
                "\\"
86
                "\\";
87
      } else {
88
        /* If the escaped character is itself a regular expression special
89
         * character */
90
        /* we need to regular-expression-escape-it ! */
91
        if (strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i + 1])) {
5✔
92
          expr += '\\';
93
        }
94
        expr += pszValue[i + 1];
5✔
95
      }
96
      i++;
97
    } else if (pszValue[i] == pszWild[0]) {
197✔
98
      expr += ".*";
99
    }
100
    /* Escape regular expressions special characters and double quote */
101
    else if (strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i])) {
173✔
102
      if (pszValue[i] == '\\') {
23✔
103
        /* See above explantation */
104
        expr += "\\"
105
                "\\"
106
                "\\"
107
                "\\";
108
      } else {
109
        expr += '\\';
110
        expr += pszValue[i];
21✔
111
      }
112
    } else {
113
      expr += pszValue[i];
114
    }
115
  }
116
  if (nLength > 0) {
42✔
117
    expr += '$';
118
  }
119
  return expr;
42✔
120
}
121

122
static std::string
123
FLTGetIsLikeComparisonCommonExpression(const FilterEncodingNode *psFilterNode) {
30✔
124

125
  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
30✔
126
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
30✔
127
    return std::string();
128

129
  const FEPropertyIsLike *propIsLike =
130
      (const FEPropertyIsLike *)psFilterNode->pOther;
131
  const char *pszWild = propIsLike->pszWildCard;
30✔
132
  const char *pszSingle = propIsLike->pszSingleChar;
30✔
133
  const char *pszEscape = propIsLike->pszEscapeChar;
30✔
134
  const bool bCaseInsensitive = propIsLike->bCaseInsensitive != 0;
30✔
135

136
  if (!pszWild || strlen(pszWild) == 0 || !pszSingle ||
30✔
137
      strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
30✔
138
    return std::string();
139

140
  /* -------------------------------------------------------------------- */
141
  /*      Use operand with regular expressions.                           */
142
  /* -------------------------------------------------------------------- */
143
  std::string expr("(\"[");
30✔
144

145
  /* attribute */
146
  expr += FLTEscapePropertyName(psFilterNode->psLeftNode->pszValue, '"');
30✔
147

148
  /* #3521 */
149
  if (bCaseInsensitive)
30✔
150
    expr += "]\" ~* \"";
151
  else
152
    expr += "]\" ~ \"";
153

154
  expr +=
155
      msGetLikePatternAsRegex(propIsLike, psFilterNode->psRightNode->pszValue);
60✔
156

157
  expr += "\")";
158
  return expr;
30✔
159
}
160

161
static std::string
162
FLTGetIsBetweenComparisonCommonExpresssion(FilterEncodingNode *psFilterNode,
17✔
163
                                           layerObj *lp) {
164
  if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL)
17✔
165
    return std::string();
166

167
  /* -------------------------------------------------------------------- */
168
  /*      Get the bounds value which are stored like boundmin;boundmax    */
169
  /* -------------------------------------------------------------------- */
170
  const auto bounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';');
17✔
171
  if (bounds.size() != 2) {
17✔
172
    return std::string();
173
  }
174

175
  /* -------------------------------------------------------------------- */
176
  /*      check if the value is a numeric value or alphanumeric. If it    */
177
  /*      is alphanumeric, add quotes around attribute and values.        */
178
  /* -------------------------------------------------------------------- */
179
  bool bString = false;
180
  bool bDateTime = false;
181

182
  const char *pszType = msOWSLookupMetadata(
17✔
183
      &(lp->metadata), "OFG",
17✔
184
      (std::string(psFilterNode->psLeftNode->pszValue) + "_type").c_str());
17✔
185
  if (pszType != NULL && (strcasecmp(pszType, "Character") == 0))
17✔
186
    bString = true;
187
  else if (pszType != NULL && (strcasecmp(pszType, "Date") == 0))
3✔
188
    bDateTime = true;
189
  else if (FLTIsNumeric(bounds[0].c_str()) == MS_FALSE)
17✔
190
    bString = true;
191

192
  if (!bString && !bDateTime) {
17✔
193
    if (FLTIsNumeric(bounds[1].c_str()) == MS_FALSE)
16✔
194
      bString = true;
195
  }
196

197
  std::string expr;
198
  /* -------------------------------------------------------------------- */
199
  /*      build expression.                                              */
200
  /* -------------------------------------------------------------------- */
201
  /* attribute */
202
  if (bString)
17✔
203
    expr += "(\"[";
204
  else
205
    expr += "([";
206

207
  expr += FLTEscapePropertyName(psFilterNode->psLeftNode->pszValue,
33✔
208
                                bString ? '"' : ']');
209

210
  if (bString)
17✔
211
    expr += "]\" ";
212
  else
213
    expr += "] ";
214

215
  expr += " >= ";
216

217
  if (bString) {
17✔
218
    expr += '\"';
219
  } else if (bDateTime) {
16✔
220
    expr += '`';
221
  }
222

223
  expr += msStdStringEscape(bounds[0].c_str());
17✔
224

225
  if (bString) {
17✔
226
    expr += '\"';
227
  } else if (bDateTime) {
16✔
228
    expr += '`';
229
  }
230

231
  expr += " AND ";
232

233
  if (bString)
17✔
234
    expr += " \"[";
235
  else
236
    expr += " [";
237

238
  /* attribute */
239
  expr += psFilterNode->psLeftNode->pszValue;
17✔
240

241
  if (bString)
17✔
242
    expr += "]\" ";
243
  else
244
    expr += "] ";
245

246
  expr += " <= ";
247

248
  if (bString) {
17✔
249
    expr += '\"';
250
  } else if (bDateTime) {
16✔
251
    expr += '`';
252
  }
253

254
  expr += msStdStringEscape(bounds[1].c_str());
17✔
255

256
  if (bString) {
17✔
257
    expr += '\"';
258
  } else if (bDateTime) {
16✔
259
    expr += '`';
260
  }
261
  expr += ')';
262

263
  return expr;
17✔
264
}
17✔
265

266
std::string FLTGetBinaryComparisonCommonExpression(layerObj *lp,
278✔
267
                                                   const char *pszPropertyName,
268
                                                   bool bForceString,
269
                                                   const char *pszOp,
270
                                                   const char *pszValue) {
271
  assert(pszPropertyName);
272
  assert(pszOp);
273

274
  /* -------------------------------------------------------------------- */
275
  /*      check if the value is a numeric value or alphanumeric. If it    */
276
  /*      is alphanumeric, add quotes around attribute and values.        */
277
  /* -------------------------------------------------------------------- */
278
  bool bString = false;
279
  bool bDateTime = false;
280
  if (pszValue) {
278✔
281
    const char *pszType =
282
        msOWSLookupMetadata(&(lp->metadata), "OFG",
275✔
283
                            (std::string(pszPropertyName) + "_type").c_str());
275✔
284
    if (pszType != NULL && (strcasecmp(pszType, "Character") == 0))
275✔
285
      bString = true;
286
    else if (pszType != NULL && (strcasecmp(pszType, "Date") == 0))
16✔
287
      bDateTime = true;
288
    else if (FLTIsNumeric(pszValue) == MS_FALSE)
242✔
289
      bString = true;
290
  }
291

292
  /* special case to be able to have empty strings in the expression. */
293
  /* propertyislike is always treated as string */
294
  if (!pszValue || bForceString)
278✔
295
    bString = true;
296

297
  /* attribute */
298
  std::string expr;
299
  if (bString)
278✔
300
    expr = "(\"[";
301
  else
302
    expr = "([";
303

304
  expr += FLTEscapePropertyName(pszPropertyName, bString ? '"' : ']');
406✔
305

306
  if (bString)
278✔
307
    expr += "]\" ";
308
  else
309
    expr += "] ";
310

311
  expr += pszOp;
312
  expr += ' ';
313

314
  /* value */
315
  if (bString) {
278✔
316
    expr += "\"";
317
  } else if (bDateTime) {
128✔
318
    expr += "`";
319
  }
320

321
  if (pszValue) {
278✔
322
    expr += msStdStringEscape(pszValue);
550✔
323
  }
324

325
  if (bString) {
278✔
326
    expr += "\"";
327
  } else if (bDateTime) {
128✔
328
    expr += "`";
329
  }
330

331
  expr += ")";
332

333
  return expr;
278✔
334
}
335

336
static std::string
337
FLTGetBinaryComparisonCommonExpression(FilterEncodingNode *psFilterNode,
270✔
338
                                       layerObj *lp) {
339

340
  const char *pszPropertyName = psFilterNode->psLeftNode->pszValue;
270✔
341
  const char *pszValue = psFilterNode->psRightNode->pszValue;
270✔
342
  const char *pszXMLOp = psFilterNode->pszValue;
270✔
343
  if (pszPropertyName && pszXMLOp) {
270✔
344
    /* special case to be able to have empty strings in the expression. */
345
    /* propertyislike is always treated as string */
346
    const bool bForceString = (strcasecmp(pszXMLOp, "PropertyIsLike") == 0);
270✔
347

348
    const char *pszOp = nullptr;
349
    if (strcasecmp(pszXMLOp, "PropertyIsEqualTo") == 0) {
270✔
350
      /* case insensitive set ? */
351
      if (psFilterNode->psRightNode->pOther &&
189✔
352
          (*(int *)psFilterNode->psRightNode->pOther) == 1)
189✔
353
        pszOp = "=*";
354
      else
355
        pszOp = "=";
356
    } else if (strcasecmp(pszXMLOp, "PropertyIsNotEqualTo") == 0)
81✔
357
      pszOp = "!=";
358
    else if (strcasecmp(pszXMLOp, "PropertyIsLessThan") == 0)
69✔
359
      pszOp = "<";
360
    else if (strcasecmp(pszXMLOp, "PropertyIsGreaterThan") == 0)
39✔
361
      pszOp = ">";
362
    else if (strcasecmp(pszXMLOp, "PropertyIsLessThanOrEqualTo") == 0)
28✔
363
      pszOp = "<=";
364
    else if (strcasecmp(pszXMLOp, "PropertyIsGreaterThanOrEqualTo") == 0)
19✔
365
      pszOp = ">=";
366
    else if (strcasecmp(pszXMLOp, "PropertyIsLike") == 0)
×
367
      pszOp = "~";
368
    else
369
      return std::string();
370

371
    return FLTGetBinaryComparisonCommonExpression(
372
        lp, pszPropertyName, bForceString, pszOp, pszValue);
270✔
373
  }
374

375
  return std::string();
376
}
377

378
static std::string
379
FLTGetLogicalComparisonCommonExpression(FilterEncodingNode *psFilterNode,
88✔
380
                                        layerObj *lp) {
381
  std::string expr;
382
  /* -------------------------------------------------------------------- */
383
  /*      OR and AND                                                      */
384
  /* -------------------------------------------------------------------- */
385
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
88✔
386
    char *pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
77✔
387
    if (!pszTmp)
77✔
388
      return std::string();
389

390
    expr = '(';
391
    expr += pszTmp;
392
    msFree(pszTmp);
77✔
393
    expr += ' ';
394
    expr += psFilterNode->pszValue;
77✔
395
    expr += ' ';
396

397
    pszTmp = FLTGetCommonExpression(psFilterNode->psRightNode, lp);
77✔
398
    if (!pszTmp) {
77✔
399
      return std::string();
400
    }
401

402
    expr += pszTmp;
403
    msFree(pszTmp);
77✔
404
    expr += ')';
405
  }
406
  /* -------------------------------------------------------------------- */
407
  /*      NOT                                                             */
408
  /* -------------------------------------------------------------------- */
409
  else if (psFilterNode->psLeftNode &&
11✔
410
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
11✔
411
    char *pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
11✔
412
    if (!pszTmp)
11✔
413
      return std::string();
414

415
    expr = "(NOT ";
416
    expr += pszTmp;
417
    msFree(pszTmp);
11✔
418
    expr += ')';
419
  }
420

421
  return expr;
88✔
422
}
423

424
static std::string
425
FLTGetSpatialComparisonCommonExpression(FilterEncodingNode *psNode,
132✔
426
                                        layerObj *lp) {
427
  std::string expr;
428
  double dfDistance = -1;
132✔
429
  shapeObj *psTmpShape = NULL;
430
  bool bBBoxQuery = false;
431
  bool bAlreadyReprojected = false;
432

433
  if (lp == NULL)
132✔
434
    return std::string();
435

436
  /* get the shape */
437
  if (FLTIsBBoxFilter(psNode)) {
132✔
438
    rectObj sQueryRect;
439
    FLTGetBBOX(psNode, &sQueryRect);
42✔
440

441
    char szPolygon[512];
442
    snprintf(szPolygon, sizeof(szPolygon),
42✔
443
             "POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f "
444
             "%.18f))",
445
             sQueryRect.minx, sQueryRect.miny, sQueryRect.minx, sQueryRect.maxy,
446
             sQueryRect.maxx, sQueryRect.maxy, sQueryRect.maxx, sQueryRect.miny,
447
             sQueryRect.minx, sQueryRect.miny);
448

449
    psTmpShape = msShapeFromWKT(szPolygon);
42✔
450

451
    /*
452
    ** This is a horrible hack to deal with world-extent requests and
453
    ** reprojection. msProjectRect() detects if reprojection from longlat to
454
    ** projected SRS, and in that case it transforms the bbox to
455
    *-1e-15,-1e-15,1e15,1e15
456
    ** to ensure that all features are returned.
457
    **
458
    ** Make wfs_200_cite_filter_bbox_world.xml and
459
    *wfs_200_cite_postgis_bbox_world.xml pass
460
    */
461
    if (fabs(sQueryRect.minx - -180.0) < 1e-5 &&
42✔
462
        fabs(sQueryRect.miny - -90.0) < 1e-5 &&
2✔
463
        fabs(sQueryRect.maxx - 180.0) < 1e-5 &&
2✔
464
        fabs(sQueryRect.maxy - 90.0) < 1e-5) {
2✔
465
      if (lp->projection.numargs > 0) {
2✔
466
        projectionObj sProjTmp;
467
        if (psNode->pszSRS) {
2✔
468
          msInitProjection(&sProjTmp);
2✔
469
          msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
2✔
470
        }
471
        if (psNode->pszSRS) {
2✔
472
          /* Use the non EPSG variant since axis swapping is done in
473
           * FLTDoAxisSwappingIfNecessary */
474
          if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
2✔
475
            msProjectRect(&sProjTmp, &lp->projection, &sQueryRect);
2✔
476
          }
477
        } else if (lp->map->projection.numargs > 0)
×
478
          msProjectRect(&lp->map->projection, &lp->projection, &sQueryRect);
×
479
        if (psNode->pszSRS)
2✔
480
          msFreeProjection(&sProjTmp);
2✔
481
      }
482
      if (sQueryRect.minx <= -1e14) {
2✔
483
        msFreeShape(psTmpShape);
2✔
484
        msFree(psTmpShape);
2✔
485
        psTmpShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
2✔
486
        msInitShape(psTmpShape);
2✔
487
        msRectToPolygon(sQueryRect, psTmpShape);
2✔
488
        bAlreadyReprojected = true;
489
      }
490
    }
491

492
    bBBoxQuery = true;
493
  } else {
494
    /* other geos type operations */
495

496
    /* project shape to layer projection. If the proj is not part of the filter
497
      query, assume that the cooredinates are in the map projection */
498

499
    int nUnit = -1;
90✔
500
    shapeObj *psQueryShape = FLTGetShape(psNode, &dfDistance, &nUnit);
90✔
501

502
    if ((strcasecmp(psNode->pszValue, "DWithin") == 0 ||
90✔
503
         strcasecmp(psNode->pszValue, "Beyond") == 0) &&
73✔
504
        dfDistance > 0) {
23✔
505
      int nLayerUnit = lp->units;
23✔
506
      if (nLayerUnit == -1)
23✔
507
        nLayerUnit = GetMapserverUnitUsingProj(&lp->projection);
×
508
      if (nLayerUnit == -1)
×
509
        nLayerUnit = lp->map->units;
×
510
      if (nLayerUnit == -1)
23✔
511
        nLayerUnit = GetMapserverUnitUsingProj(&lp->map->projection);
×
512

513
      if (nUnit >= 0 && nUnit != nLayerUnit)
23✔
514
        dfDistance *=
18✔
515
            msInchesPerUnit(nUnit, 0) /
36✔
516
            msInchesPerUnit(nLayerUnit, 0); /* target is layer units */
18✔
517
    }
518

519
    psTmpShape = psQueryShape;
520
  }
521

522
  if (psTmpShape) {
132✔
523

524
    /*
525
    ** target is layer projection
526
    */
527
    if (!bAlreadyReprojected && lp->projection.numargs > 0) {
132✔
528
      projectionObj sProjTmp;
529
      if (psNode->pszSRS) {
127✔
530
        msInitProjection(&sProjTmp);
58✔
531
        msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
58✔
532
      }
533
      if (psNode->pszSRS) {
127✔
534
        /* Use the non EPSG variant since axis swapping is done in
535
         * FLTDoAxisSwappingIfNecessary */
536
        if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
58✔
537
          msProjectShape(&sProjTmp, &lp->projection, psTmpShape);
58✔
538
        }
539
      } else if (lp->map->projection.numargs > 0)
69✔
540
        msProjectShape(&lp->map->projection, &lp->projection, psTmpShape);
69✔
541
      if (psNode->pszSRS)
127✔
542
        msFreeProjection(&sProjTmp);
58✔
543
    }
544

545
    /* function name */
546
    if (bBBoxQuery) {
132✔
547
      expr = "intersects";
548
    } else {
549
      if (strncasecmp(psNode->pszValue, "intersect", 9) == 0)
90✔
550
        expr = "intersects";
551
      else {
552
        expr = msStringToLower(std::string(psNode->pszValue));
142✔
553
      }
554
    }
555
    /* geometry binding */
556
    expr += "([shape],fromText('";
557

558
    /* filter geometry */
559
    char *pszWktText = msGEOSShapeToWKT(psTmpShape);
132✔
560
    expr += pszWktText ? pszWktText : "Cannot translate shape to WKT";
132✔
561
    expr += "')";
562
    msGEOSFreeWKT(pszWktText);
132✔
563

564
    /* (optional) beyond/dwithin distance, always 0.0 since we apply the
565
     * distance as a buffer earlier */
566
    if ((strcasecmp(psNode->pszValue, "DWithin") == 0 ||
132✔
567
         strcasecmp(psNode->pszValue, "Beyond") == 0)) {
115✔
568
      char szBuffer[32];
569
      snprintf(szBuffer, sizeof(szBuffer), ",%g", dfDistance);
23✔
570
      expr += szBuffer;
571
    }
572

573
    /* terminate the function */
574
    expr += ") = TRUE";
575
  }
576

577
  /*
578
  ** Cleanup
579
  */
580
  if (bBBoxQuery) {
132✔
581
    msFreeShape(psTmpShape);
42✔
582
    msFree(psTmpShape);
42✔
583
  }
584

585
  return expr;
132✔
586
}
587

588
static std::string
589
FLTGetFeatureIdCommonExpression(FilterEncodingNode *psFilterNode,
64✔
590
                                layerObj *lp) {
591
  std::string expr;
592

593
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
594
    defined(USE_SOS_SVR)
595
  if (psFilterNode->pszValue) {
64✔
596
    const char *pszAttribute =
597
        msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
64✔
598
    if (pszAttribute) {
64✔
599
      const auto tokens = msStringSplit(psFilterNode->pszValue, ',');
64✔
600
      if (!tokens.empty()) {
64✔
601
        bool bString = false;
602
        for (size_t i = 0; i < tokens.size(); i++) {
131✔
603
          const char *pszId = tokens[i].c_str();
604
          const char *pszDot = strrchr(pszId, '.');
605
          if (pszDot)
67✔
606
            pszId = pszDot + 1;
29✔
607

608
          if (i == 0) {
67✔
609
            if (FLTIsNumeric(pszId) == MS_FALSE)
64✔
610
              bString = true;
611
          }
612

613
          if (!expr.empty())
67✔
614
            expr += " OR ";
615
          else
616
            expr = '(';
617

618
          if (bString) {
67✔
619
            expr += "(\"[";
620
            expr += FLTEscapePropertyName(pszAttribute, '"');
28✔
621
            expr += "]\" == \"";
622
            expr += pszId;
623
            expr += "\")";
624
          } else {
625
            expr += "([";
626
            expr += FLTEscapePropertyName(pszAttribute, ']');
106✔
627
            expr += "] == ";
628
            expr += pszId;
629
            expr += ")";
630
          }
631
        }
632
      }
633
    }
64✔
634

635
    /* opening and closing brackets are needed for mapserver expressions */
636
    if (!expr.empty())
64✔
637
      expr += ')';
638
  }
639
#endif
640

641
  return expr;
64✔
642
}
643

644
std::string FLTGetTimeExpression(FilterEncodingNode *psFilterNode,
19✔
645
                                 layerObj *lp) {
646
  if (lp == NULL)
19✔
647
    return std::string();
648

649
  std::string expr;
650
  const char *pszTimeField = nullptr;
19✔
651
  const char *pszTimeValue = FLTGetDuring(psFilterNode, &pszTimeField);
19✔
652
  if (pszTimeField && pszTimeValue) {
19✔
653
    expressionObj old_filter;
654
    msInitExpression(&old_filter);
19✔
655
    msCopyExpression(&old_filter, &lp->filter); /* save existing filter */
19✔
656
    msFreeExpression(&lp->filter);
19✔
657
    if (msLayerSetTimeFilter(lp, pszTimeValue, pszTimeField) == MS_TRUE &&
19✔
658
        lp->filter.string) {
19✔
659
      expr = lp->filter.string;
660
    }
661
    msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
19✔
662
    msFreeExpression(&old_filter);
19✔
663
  }
664
  return expr;
19✔
665
}
666

667
char *FLTGetCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp) {
620✔
668
  char *pszExpression = NULL;
669

670
  if (!psFilterNode)
620✔
671
    return NULL;
672

673
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
620✔
674
    if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
317✔
675
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
317✔
676
        pszExpression = msStrdup(
270✔
677
            FLTGetBinaryComparisonCommonExpression(psFilterNode, lp).c_str());
540✔
678
      else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
47✔
679
        pszExpression = msStrdup(
30✔
680
            FLTGetIsLikeComparisonCommonExpression(psFilterNode).c_str());
60✔
681
      else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0)
17✔
682
        pszExpression = msStrdup(
17✔
683
            FLTGetIsBetweenComparisonCommonExpresssion(psFilterNode, lp)
34✔
684
                .c_str());
685
    }
686
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
687
    pszExpression = msStrdup(
88✔
688
        FLTGetLogicalComparisonCommonExpression(psFilterNode, lp).c_str());
176✔
689
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
690
    pszExpression = msStrdup(
132✔
691
        FLTGetSpatialComparisonCommonExpression(psFilterNode, lp).c_str());
264✔
692
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
693
    pszExpression =
694
        msStrdup(FLTGetFeatureIdCommonExpression(psFilterNode, lp).c_str());
128✔
695
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL) {
696
    pszExpression = msStrdup(FLTGetTimeExpression(psFilterNode, lp).c_str());
38✔
697
  }
698

699
  return pszExpression;
700
}
701

702
int FLTApplyFilterToLayerCommonExpression(mapObj *map, int iLayerIndex,
×
703
                                          const char *pszExpression) {
704
  return FLTApplyFilterToLayerCommonExpressionWithRect(
×
705
      map, iLayerIndex, pszExpression, map->extent);
×
706
}
707

708
/* rect must be in map->projection */
709
int FLTApplyFilterToLayerCommonExpressionWithRect(mapObj *map, int iLayerIndex,
390✔
710
                                                  const char *pszExpression,
711
                                                  rectObj rect) {
712
  int retval;
713

714
  const int save_startindex = map->query.startindex;
390✔
715
  const int save_maxfeatures = map->query.maxfeatures;
390✔
716
  const int save_only_cache_result_count = map->query.only_cache_result_count;
390✔
717
  const int save_cache_shapes = map->query.cache_shapes;
390✔
718
  const int save_max_cached_shape_count = map->query.max_cached_shape_count;
390✔
719
  const int save_max_cached_shape_ram_amount =
390✔
720
      map->query.max_cached_shape_ram_amount;
721
  msInitQuery(&(map->query));
390✔
722
  map->query.startindex = save_startindex;
390✔
723
  map->query.maxfeatures = save_maxfeatures;
390✔
724
  map->query.only_cache_result_count = save_only_cache_result_count;
390✔
725
  map->query.cache_shapes = save_cache_shapes;
390✔
726
  map->query.max_cached_shape_count = save_max_cached_shape_count;
390✔
727
  map->query.max_cached_shape_ram_amount = save_max_cached_shape_ram_amount;
390✔
728

729
  map->query.mode = MS_QUERY_MULTIPLE;
390✔
730
  map->query.layer = iLayerIndex;
390✔
731

732
  map->query.rect = rect;
390✔
733

734
  if (pszExpression) {
390✔
735
    map->query.type = MS_QUERY_BY_FILTER;
365✔
736
    msInitExpression(&map->query.filter);
365✔
737
    map->query.filter.string = msStrdup(pszExpression);
365✔
738
    map->query.filter.type = MS_EXPRESSION; /* a logical expression */
365✔
739

740
    retval = msQueryByFilter(map);
365✔
741
  } else {
742
    map->query.type = MS_QUERY_BY_RECT;
25✔
743
    retval = msQueryByRect(map);
25✔
744
  }
745

746
  return retval;
390✔
747
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc