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

geographika / mapserver / 17854294711

19 Sep 2025 09:25AM UTC coverage: 41.548% (+0.007%) from 41.541%
17854294711

push

github

geographika
Switch to plain template for testing

62277 of 149890 relevant lines covered (41.55%)

25038.1 hits per line

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

96.05
/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,
384✔
38
                                         char chEscapeChar) {
39
  std::string ret;
40
  for (; *pszStr; ++pszStr) {
2,628✔
41
    if (*pszStr == chEscapeChar) {
2,244✔
42
      ret += chEscapeChar;
43
      ret += chEscapeChar;
44
    } else {
45
      ret += *pszStr;
46
    }
47
  }
48
  return ret;
384✔
49
}
50

51
static std::string
52
FLTGetIsLikeComparisonCommonExpression(FilterEncodingNode *psFilterNode) {
30✔
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
  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
30✔
60
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
30✔
61
    return std::string();
62

63
  const FEPropertyIsLike *propIsLike =
64
      (const FEPropertyIsLike *)psFilterNode->pOther;
65
  const char *pszWild = propIsLike->pszWildCard;
30✔
66
  const char *pszSingle = propIsLike->pszSingleChar;
30✔
67
  const char *pszEscape = propIsLike->pszEscapeChar;
30✔
68
  const bool bCaseInsensitive = propIsLike->bCaseInsensitive != 0;
30✔
69

70
  if (!pszWild || strlen(pszWild) == 0 || !pszSingle ||
30✔
71
      strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
30✔
72
    return std::string();
73

74
  /* -------------------------------------------------------------------- */
75
  /*      Use operand with regular expressions.                           */
76
  /* -------------------------------------------------------------------- */
77
  std::string expr("(\"[");
30✔
78

79
  /* attribute */
80
  expr += FLTEscapePropertyName(psFilterNode->psLeftNode->pszValue, '"');
30✔
81

82
  /* #3521 */
83
  if (bCaseInsensitive)
30✔
84
    expr += "]\" ~* \"";
85
  else
86
    expr += "]\" ~ \"";
87

88
  const char *pszValue = psFilterNode->psRightNode->pszValue;
30✔
89
  const size_t nLength = strlen(pszValue);
30✔
90

91
  if (nLength > 0) {
30✔
92
    expr += '^';
93
  }
94
  for (size_t i = 0; i < nLength; i++) {
193✔
95
    if (pszValue[i] == pszSingle[0]) {
163✔
96
      expr += '.';
97
      /* The Filter escape character is supposed to only escape the single,
98
       * wildcard and escape character */
99
    } else if (pszValue[i] == pszEscape[0] &&
159✔
100
               (pszValue[i + 1] == pszSingle[0] ||
6✔
101
                pszValue[i + 1] == pszWild[0] ||
4✔
102
                pszValue[i + 1] == pszEscape[0])) {
103
      if (pszValue[i + 1] == '\\') {
6✔
104
        /* Tricky case: \ must be escaped ncce in the regular expression context
105
           so that the regexp matches it as an ordinary character.
106
           But as \ is also the escape character for MapServer string, we
107
           must escape it again. */
108
        expr += "\\"
109
                "\\"
110
                "\\"
111
                "\\";
112
      } else {
113
        /* If the escaped character is itself a regular expression special
114
         * character */
115
        /* we need to regular-expression-escape-it ! */
116
        if (strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i + 1])) {
5✔
117
          expr += '\\';
118
        }
119
        expr += pszValue[i + 1];
5✔
120
      }
121
      i++;
122
    } else if (pszValue[i] == pszWild[0]) {
153✔
123
      expr += ".*";
124
    }
125
    /* Escape regular expressions special characters and double quote */
126
    else if (strchr(pszRegexSpecialCharsAndDoubleQuote, pszValue[i])) {
130✔
127
      if (pszValue[i] == '\\') {
23✔
128
        /* See above explantation */
129
        expr += "\\"
130
                "\\"
131
                "\\"
132
                "\\";
133
      } else {
134
        expr += '\\';
135
        expr += pszValue[i];
21✔
136
      }
137
    } else {
138
      expr += pszValue[i];
139
    }
140
  }
141
  if (nLength > 0) {
30✔
142
    expr += '$';
143
  }
144
  expr += "\")";
145
  return expr;
30✔
146
}
147

148
static std::string
149
FLTGetIsBetweenComparisonCommonExpresssion(FilterEncodingNode *psFilterNode,
17✔
150
                                           layerObj *lp) {
151
  if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL)
17✔
152
    return std::string();
153

154
  /* -------------------------------------------------------------------- */
155
  /*      Get the bounds value which are stored like boundmin;boundmax    */
156
  /* -------------------------------------------------------------------- */
157
  const auto bounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';');
17✔
158
  if (bounds.size() != 2) {
17✔
159
    return std::string();
160
  }
161

162
  /* -------------------------------------------------------------------- */
163
  /*      check if the value is a numeric value or alphanumeric. If it    */
164
  /*      is alphanumeric, add quotes around attribute and values.        */
165
  /* -------------------------------------------------------------------- */
166
  bool bString = false;
167
  bool bDateTime = false;
168

169
  const char *pszType = msOWSLookupMetadata(
17✔
170
      &(lp->metadata), "OFG",
171
      (std::string(psFilterNode->psLeftNode->pszValue) + "_type").c_str());
17✔
172
  if (pszType != NULL && (strcasecmp(pszType, "Character") == 0))
17✔
173
    bString = true;
174
  else if (pszType != NULL && (strcasecmp(pszType, "Date") == 0))
3✔
175
    bDateTime = true;
176
  else if (FLTIsNumeric(bounds[0].c_str()) == MS_FALSE)
17✔
177
    bString = true;
178

179
  if (!bString && !bDateTime) {
17✔
180
    if (FLTIsNumeric(bounds[1].c_str()) == MS_FALSE)
16✔
181
      bString = true;
182
  }
183

184
  std::string expr;
185
  /* -------------------------------------------------------------------- */
186
  /*      build expression.                                              */
187
  /* -------------------------------------------------------------------- */
188
  /* attribute */
189
  if (bString)
17✔
190
    expr += "(\"[";
191
  else
192
    expr += "([";
193

194
  expr += FLTEscapePropertyName(psFilterNode->psLeftNode->pszValue,
33✔
195
                                bString ? '"' : ']');
196

197
  if (bString)
17✔
198
    expr += "]\" ";
199
  else
200
    expr += "] ";
201

202
  expr += " >= ";
203

204
  if (bString) {
17✔
205
    expr += '\"';
206
  } else if (bDateTime) {
16✔
207
    expr += '`';
208
  }
209

210
  expr += msStdStringEscape(bounds[0].c_str());
17✔
211

212
  if (bString) {
17✔
213
    expr += '\"';
214
  } else if (bDateTime) {
16✔
215
    expr += '`';
216
  }
217

218
  expr += " AND ";
219

220
  if (bString)
17✔
221
    expr += " \"[";
222
  else
223
    expr += " [";
224

225
  /* attribute */
226
  expr += psFilterNode->psLeftNode->pszValue;
17✔
227

228
  if (bString)
17✔
229
    expr += "]\" ";
230
  else
231
    expr += "] ";
232

233
  expr += " <= ";
234

235
  if (bString) {
17✔
236
    expr += '\"';
237
  } else if (bDateTime) {
16✔
238
    expr += '`';
239
  }
240

241
  expr += msStdStringEscape(bounds[1].c_str());
17✔
242

243
  if (bString) {
17✔
244
    expr += '\"';
245
  } else if (bDateTime) {
16✔
246
    expr += '`';
247
  }
248
  expr += ')';
249

250
  return expr;
17✔
251
}
17✔
252

253
static std::string
254
FLTGetBinaryComparisonCommonExpression(FilterEncodingNode *psFilterNode,
270✔
255
                                       layerObj *lp) {
256
  /* -------------------------------------------------------------------- */
257
  /*      check if the value is a numeric value or alphanumeric. If it    */
258
  /*      is alphanumeric, add quotes around attribute and values.        */
259
  /* -------------------------------------------------------------------- */
260
  bool bString = false;
261
  bool bDateTime = false;
262
  if (psFilterNode->psRightNode->pszValue) {
270✔
263
    const char *pszType = msOWSLookupMetadata(
267✔
264
        &(lp->metadata), "OFG",
265
        (std::string(psFilterNode->psLeftNode->pszValue) + "_type").c_str());
267✔
266
    if (pszType != NULL && (strcasecmp(pszType, "Character") == 0))
267✔
267
      bString = true;
268
    else if (pszType != NULL && (strcasecmp(pszType, "Date") == 0))
16✔
269
      bDateTime = true;
270
    else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
234✔
271
      bString = true;
272
  }
273

274
  /* special case to be able to have empty strings in the expression. */
275
  /* propertyislike is always treated as string */
276
  if (psFilterNode->psRightNode->pszValue == NULL ||
270✔
277
      strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
267✔
278
    bString = true;
279

280
  /* attribute */
281
  std::string expr;
282
  if (bString)
270✔
283
    expr = "(\"[";
284
  else
285
    expr = "([";
286

287
  expr += FLTEscapePropertyName(psFilterNode->psLeftNode->pszValue,
398✔
288
                                bString ? '"' : ']');
289

290
  if (bString)
270✔
291
    expr += "]\" ";
292
  else
293
    expr += "] ";
294

295
  if (strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0) {
270✔
296
    /* case insensitive set ? */
297
    if (psFilterNode->psRightNode->pOther &&
189✔
298
        (*(int *)psFilterNode->psRightNode->pOther) == 1)
189✔
299
      expr += "=*";
300
    else
301
      expr += "=";
302
  } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsNotEqualTo") == 0)
81✔
303
    expr += "!=";
304
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThan") == 0)
69✔
305
    expr += "<";
306
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsGreaterThan") == 0)
39✔
307
    expr += ">";
308
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThanOrEqualTo") ==
28✔
309
           0)
310
    expr += "<=";
311
  else if (strcasecmp(psFilterNode->pszValue,
19✔
312
                      "PropertyIsGreaterThanOrEqualTo") == 0)
313
    expr += ">=";
314
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
×
315
    expr += "~";
316
  expr += ' ';
317

318
  /* value */
319
  if (bString) {
270✔
320
    expr += "\"";
321
  } else if (bDateTime) {
128✔
322
    expr += "`";
323
  }
324

325
  if (psFilterNode->psRightNode->pszValue) {
270✔
326
    expr += msStdStringEscape(psFilterNode->psRightNode->pszValue);
534✔
327
  }
328

329
  if (bString) {
270✔
330
    expr += "\"";
331
  } else if (bDateTime) {
128✔
332
    expr += "`";
333
  }
334

335
  expr += ")";
336

337
  return expr;
270✔
338
}
339

340
static std::string
341
FLTGetLogicalComparisonCommonExpression(FilterEncodingNode *psFilterNode,
88✔
342
                                        layerObj *lp) {
343
  std::string expr;
344
  /* -------------------------------------------------------------------- */
345
  /*      OR and AND                                                      */
346
  /* -------------------------------------------------------------------- */
347
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
88✔
348
    char *pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
77✔
349
    if (!pszTmp)
77✔
350
      return std::string();
351

352
    expr = '(';
353
    expr += pszTmp;
354
    msFree(pszTmp);
77✔
355
    expr += ' ';
356
    expr += psFilterNode->pszValue;
77✔
357
    expr += ' ';
358

359
    pszTmp = FLTGetCommonExpression(psFilterNode->psRightNode, lp);
77✔
360
    if (!pszTmp) {
77✔
361
      return std::string();
362
    }
363

364
    expr += pszTmp;
365
    msFree(pszTmp);
77✔
366
    expr += ')';
367
  }
368
  /* -------------------------------------------------------------------- */
369
  /*      NOT                                                             */
370
  /* -------------------------------------------------------------------- */
371
  else if (psFilterNode->psLeftNode &&
11✔
372
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
11✔
373
    char *pszTmp = FLTGetCommonExpression(psFilterNode->psLeftNode, lp);
11✔
374
    if (!pszTmp)
11✔
375
      return std::string();
376

377
    expr = "(NOT ";
378
    expr += pszTmp;
379
    msFree(pszTmp);
11✔
380
    expr += ')';
381
  }
382

383
  return expr;
88✔
384
}
385

386
static std::string
387
FLTGetSpatialComparisonCommonExpression(FilterEncodingNode *psNode,
132✔
388
                                        layerObj *lp) {
389
  std::string expr;
390
  double dfDistance = -1;
132✔
391
  shapeObj *psTmpShape = NULL;
392
  bool bBBoxQuery = false;
393
  bool bAlreadyReprojected = false;
394

395
  if (lp == NULL)
132✔
396
    return std::string();
397

398
  /* get the shape */
399
  if (FLTIsBBoxFilter(psNode)) {
132✔
400
    rectObj sQueryRect;
401
    FLTGetBBOX(psNode, &sQueryRect);
42✔
402

403
    char szPolygon[512];
404
    snprintf(szPolygon, sizeof(szPolygon),
42✔
405
             "POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f "
406
             "%.18f))",
407
             sQueryRect.minx, sQueryRect.miny, sQueryRect.minx, sQueryRect.maxy,
408
             sQueryRect.maxx, sQueryRect.maxy, sQueryRect.maxx, sQueryRect.miny,
409
             sQueryRect.minx, sQueryRect.miny);
410

411
    psTmpShape = msShapeFromWKT(szPolygon);
42✔
412

413
    /*
414
    ** This is a horrible hack to deal with world-extent requests and
415
    ** reprojection. msProjectRect() detects if reprojection from longlat to
416
    ** projected SRS, and in that case it transforms the bbox to
417
    *-1e-15,-1e-15,1e15,1e15
418
    ** to ensure that all features are returned.
419
    **
420
    ** Make wfs_200_cite_filter_bbox_world.xml and
421
    *wfs_200_cite_postgis_bbox_world.xml pass
422
    */
423
    if (fabs(sQueryRect.minx - -180.0) < 1e-5 &&
42✔
424
        fabs(sQueryRect.miny - -90.0) < 1e-5 &&
2✔
425
        fabs(sQueryRect.maxx - 180.0) < 1e-5 &&
2✔
426
        fabs(sQueryRect.maxy - 90.0) < 1e-5) {
2✔
427
      if (lp->projection.numargs > 0) {
2✔
428
        projectionObj sProjTmp;
429
        if (psNode->pszSRS) {
2✔
430
          msInitProjection(&sProjTmp);
2✔
431
          msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
2✔
432
        }
433
        if (psNode->pszSRS) {
2✔
434
          /* Use the non EPSG variant since axis swapping is done in
435
           * FLTDoAxisSwappingIfNecessary */
436
          if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
2✔
437
            msProjectRect(&sProjTmp, &lp->projection, &sQueryRect);
2✔
438
          }
439
        } else if (lp->map->projection.numargs > 0)
×
440
          msProjectRect(&lp->map->projection, &lp->projection, &sQueryRect);
×
441
        if (psNode->pszSRS)
2✔
442
          msFreeProjection(&sProjTmp);
2✔
443
      }
444
      if (sQueryRect.minx <= -1e14) {
2✔
445
        msFreeShape(psTmpShape);
2✔
446
        msFree(psTmpShape);
2✔
447
        psTmpShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
2✔
448
        msInitShape(psTmpShape);
2✔
449
        msRectToPolygon(sQueryRect, psTmpShape);
2✔
450
        bAlreadyReprojected = true;
451
      }
452
    }
453

454
    bBBoxQuery = true;
455
  } else {
456
    /* other geos type operations */
457

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

461
    int nUnit = -1;
90✔
462
    shapeObj *psQueryShape = FLTGetShape(psNode, &dfDistance, &nUnit);
90✔
463

464
    if ((strcasecmp(psNode->pszValue, "DWithin") == 0 ||
90✔
465
         strcasecmp(psNode->pszValue, "Beyond") == 0) &&
73✔
466
        dfDistance > 0) {
23✔
467
      int nLayerUnit = lp->units;
23✔
468
      if (nLayerUnit == -1)
23✔
469
        nLayerUnit = GetMapserverUnitUsingProj(&lp->projection);
×
470
      if (nLayerUnit == -1)
×
471
        nLayerUnit = lp->map->units;
×
472
      if (nLayerUnit == -1)
23✔
473
        nLayerUnit = GetMapserverUnitUsingProj(&lp->map->projection);
×
474

475
      if (nUnit >= 0 && nUnit != nLayerUnit)
23✔
476
        dfDistance *=
18✔
477
            msInchesPerUnit(nUnit, 0) /
36✔
478
            msInchesPerUnit(nLayerUnit, 0); /* target is layer units */
18✔
479
    }
480

481
    psTmpShape = psQueryShape;
482
  }
483

484
  if (psTmpShape) {
132✔
485

486
    /*
487
    ** target is layer projection
488
    */
489
    if (!bAlreadyReprojected && lp->projection.numargs > 0) {
132✔
490
      projectionObj sProjTmp;
491
      if (psNode->pszSRS) {
127✔
492
        msInitProjection(&sProjTmp);
58✔
493
        msProjectionInheritContextFrom(&sProjTmp, &lp->projection);
58✔
494
      }
495
      if (psNode->pszSRS) {
127✔
496
        /* Use the non EPSG variant since axis swapping is done in
497
         * FLTDoAxisSwappingIfNecessary */
498
        if (msLoadProjectionString(&sProjTmp, psNode->pszSRS) == 0) {
58✔
499
          msProjectShape(&sProjTmp, &lp->projection, psTmpShape);
58✔
500
        }
501
      } else if (lp->map->projection.numargs > 0)
69✔
502
        msProjectShape(&lp->map->projection, &lp->projection, psTmpShape);
69✔
503
      if (psNode->pszSRS)
127✔
504
        msFreeProjection(&sProjTmp);
58✔
505
    }
506

507
    /* function name */
508
    if (bBBoxQuery) {
132✔
509
      expr = "intersects";
510
    } else {
511
      if (strncasecmp(psNode->pszValue, "intersect", 9) == 0)
90✔
512
        expr = "intersects";
513
      else {
514
        expr = msStringToLower(std::string(psNode->pszValue));
142✔
515
      }
516
    }
517
    /* geometry binding */
518
    expr += "([shape],fromText('";
519

520
    /* filter geometry */
521
    char *pszWktText = msGEOSShapeToWKT(psTmpShape);
132✔
522
    expr += pszWktText ? pszWktText : "Cannot translate shape to WKT";
132✔
523
    expr += "')";
524
    msGEOSFreeWKT(pszWktText);
132✔
525

526
    /* (optional) beyond/dwithin distance, always 0.0 since we apply the
527
     * distance as a buffer earlier */
528
    if ((strcasecmp(psNode->pszValue, "DWithin") == 0 ||
132✔
529
         strcasecmp(psNode->pszValue, "Beyond") == 0)) {
115✔
530
      char szBuffer[32];
531
      snprintf(szBuffer, sizeof(szBuffer), ",%g", dfDistance);
23✔
532
      expr += szBuffer;
533
    }
534

535
    /* terminate the function */
536
    expr += ") = TRUE";
537
  }
538

539
  /*
540
  ** Cleanup
541
  */
542
  if (bBBoxQuery) {
132✔
543
    msFreeShape(psTmpShape);
42✔
544
    msFree(psTmpShape);
42✔
545
  }
546

547
  return expr;
132✔
548
}
549

550
static std::string
551
FLTGetFeatureIdCommonExpression(FilterEncodingNode *psFilterNode,
64✔
552
                                layerObj *lp) {
553
  std::string expr;
554

555
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
556
    defined(USE_SOS_SVR)
557
  if (psFilterNode->pszValue) {
64✔
558
    const char *pszAttribute =
559
        msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
64✔
560
    if (pszAttribute) {
64✔
561
      const auto tokens = msStringSplit(psFilterNode->pszValue, ',');
64✔
562
      if (!tokens.empty()) {
64✔
563
        bool bString = false;
564
        for (size_t i = 0; i < tokens.size(); i++) {
131✔
565
          const char *pszId = tokens[i].c_str();
566
          const char *pszDot = strrchr(pszId, '.');
567
          if (pszDot)
67✔
568
            pszId = pszDot + 1;
29✔
569

570
          if (i == 0) {
67✔
571
            if (FLTIsNumeric(pszId) == MS_FALSE)
64✔
572
              bString = true;
573
          }
574

575
          if (!expr.empty())
67✔
576
            expr += " OR ";
577
          else
578
            expr = '(';
579

580
          if (bString) {
67✔
581
            expr += "(\"[";
582
            expr += FLTEscapePropertyName(pszAttribute, '"');
28✔
583
            expr += "]\" == \"";
584
            expr += pszId;
585
            expr += "\")";
586
          } else {
587
            expr += "([";
588
            expr += FLTEscapePropertyName(pszAttribute, ']');
106✔
589
            expr += "] == ";
590
            expr += pszId;
591
            expr += ")";
592
          }
593
        }
594
      }
595
    }
64✔
596

597
    /* opening and closing brackets are needed for mapserver expressions */
598
    if (!expr.empty())
64✔
599
      expr += ')';
600
  }
601
#endif
602

603
  return expr;
64✔
604
}
605

606
std::string FLTGetTimeExpression(FilterEncodingNode *psFilterNode,
19✔
607
                                 layerObj *lp) {
608
  if (lp == NULL)
19✔
609
    return std::string();
610

611
  std::string expr;
612
  const char *pszTimeField = nullptr;
19✔
613
  const char *pszTimeValue = FLTGetDuring(psFilterNode, &pszTimeField);
19✔
614
  if (pszTimeField && pszTimeValue) {
19✔
615
    expressionObj old_filter;
616
    msInitExpression(&old_filter);
19✔
617
    msCopyExpression(&old_filter, &lp->filter); /* save existing filter */
19✔
618
    msFreeExpression(&lp->filter);
19✔
619
    if (msLayerSetTimeFilter(lp, pszTimeValue, pszTimeField) == MS_TRUE &&
19✔
620
        lp->filter.string) {
19✔
621
      expr = lp->filter.string;
622
    }
623
    msCopyExpression(&lp->filter, &old_filter); /* restore old filter */
19✔
624
    msFreeExpression(&old_filter);
19✔
625
  }
626
  return expr;
19✔
627
}
628

629
char *FLTGetCommonExpression(FilterEncodingNode *psFilterNode, layerObj *lp) {
620✔
630
  char *pszExpression = NULL;
631

632
  if (!psFilterNode)
620✔
633
    return NULL;
634

635
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
620✔
636
    if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
317✔
637
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
317✔
638
        pszExpression = msStrdup(
270✔
639
            FLTGetBinaryComparisonCommonExpression(psFilterNode, lp).c_str());
540✔
640
      else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
47✔
641
        pszExpression = msStrdup(
30✔
642
            FLTGetIsLikeComparisonCommonExpression(psFilterNode).c_str());
60✔
643
      else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0)
17✔
644
        pszExpression = msStrdup(
17✔
645
            FLTGetIsBetweenComparisonCommonExpresssion(psFilterNode, lp)
34✔
646
                .c_str());
647
    }
648
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
649
    pszExpression = msStrdup(
88✔
650
        FLTGetLogicalComparisonCommonExpression(psFilterNode, lp).c_str());
176✔
651
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
652
    pszExpression = msStrdup(
132✔
653
        FLTGetSpatialComparisonCommonExpression(psFilterNode, lp).c_str());
264✔
654
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
655
    pszExpression =
656
        msStrdup(FLTGetFeatureIdCommonExpression(psFilterNode, lp).c_str());
128✔
657
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL) {
658
    pszExpression = msStrdup(FLTGetTimeExpression(psFilterNode, lp).c_str());
38✔
659
  }
660

661
  return pszExpression;
662
}
663

664
int FLTApplyFilterToLayerCommonExpression(mapObj *map, int iLayerIndex,
×
665
                                          const char *pszExpression) {
666
  return FLTApplyFilterToLayerCommonExpressionWithRect(
×
667
      map, iLayerIndex, pszExpression, map->extent);
×
668
}
669

670
/* rect must be in map->projection */
671
int FLTApplyFilterToLayerCommonExpressionWithRect(mapObj *map, int iLayerIndex,
390✔
672
                                                  const char *pszExpression,
673
                                                  rectObj rect) {
674
  int retval;
675

676
  const int save_startindex = map->query.startindex;
390✔
677
  const int save_maxfeatures = map->query.maxfeatures;
390✔
678
  const int save_only_cache_result_count = map->query.only_cache_result_count;
390✔
679
  const int save_cache_shapes = map->query.cache_shapes;
390✔
680
  const int save_max_cached_shape_count = map->query.max_cached_shape_count;
390✔
681
  const int save_max_cached_shape_ram_amount =
390✔
682
      map->query.max_cached_shape_ram_amount;
683
  msInitQuery(&(map->query));
390✔
684
  map->query.startindex = save_startindex;
390✔
685
  map->query.maxfeatures = save_maxfeatures;
390✔
686
  map->query.only_cache_result_count = save_only_cache_result_count;
390✔
687
  map->query.cache_shapes = save_cache_shapes;
390✔
688
  map->query.max_cached_shape_count = save_max_cached_shape_count;
390✔
689
  map->query.max_cached_shape_ram_amount = save_max_cached_shape_ram_amount;
390✔
690

691
  map->query.mode = MS_QUERY_MULTIPLE;
390✔
692
  map->query.layer = iLayerIndex;
390✔
693

694
  map->query.rect = rect;
390✔
695

696
  if (pszExpression) {
390✔
697
    map->query.type = MS_QUERY_BY_FILTER;
365✔
698
    msInitExpression(&map->query.filter);
365✔
699
    map->query.filter.string = msStrdup(pszExpression);
365✔
700
    map->query.filter.type = MS_EXPRESSION; /* a logical expression */
365✔
701

702
    retval = msQueryByFilter(map);
365✔
703
  } else {
704
    map->query.type = MS_QUERY_BY_RECT;
25✔
705
    retval = msQueryByRect(map);
25✔
706
  }
707

708
  return retval;
390✔
709
}
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