• 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

70.8
/src/mapogr.cpp
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGR Link
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *           Frank Warmerdam (warmerdam@pobox.com)
8
 *
9
 **********************************************************************
10
 * Copyright (c) 2000-2005, Daniel Morissette, DM Solutions Group Inc
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in
20
 * all copies of this Software or works derived from this Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 **********************************************************************/
30

31
#include <assert.h>
32
#include "mapserver.h"
33
#include "mapproject.h"
34
#include "mapthread.h"
35
#include "mapows.h"
36
#include <string>
37
#include <vector>
38

39
#include "gdal.h"
40
#include "cpl_conv.h"
41
#include "cpl_string.h"
42
#include "ogr_srs_api.h"
43

44
#include <memory>
45

46
#define ACQUIRE_OGR_LOCK msAcquireLock(TLOCK_OGR)
47
#define RELEASE_OGR_LOCK msReleaseLock(TLOCK_OGR)
48

49
// GDAL 1.x API
50
#include "ogr_api.h"
51

52
typedef struct ms_ogr_file_info_t {
53
  char *pszFname;
54
  char *pszLayerDef;
55
  int nLayerIndex;
56
  OGRDataSourceH hDS;
57
  OGRLayerH hLayer;
58
  OGRFeatureH hLastFeature;
59

60
  int nTileId;             /* applies on the tiles themselves. */
61
  projectionObj sTileProj; /* applies on the tiles themselves. */
62

63
  struct ms_ogr_file_info_t *poCurTile; /* exists on tile index, -> tiles */
64
  bool rect_is_defined;
65
  rectObj rect; /* set by TranslateMsExpression (possibly) and WhichShapes */
66

67
  int last_record_index_read;
68

69
  const char *dialect; /* NULL, Spatialite or PostgreSQL */
70
  char *pszSelect;
71
  char *pszSpatialFilterTableName;
72
  char *pszSpatialFilterGeometryColumn;
73
  char *pszMainTableName;
74
  char *pszRowId;
75
  int bIsOKForSQLCompose;
76
  bool bHasSpatialIndex; // used only for spatialite for now
77
  char *pszTablePrefix;  // prefix to qualify field names. used only for
78
                         // spatialite & gpkg for now when a join is done for
79
                         // spatial filtering.
80

81
  int bPaging;
82

83
  char *pszWHERE;
84

85
} msOGRFileInfo;
86

87
static int msOGRLayerIsOpen(layerObj *layer);
88
static int msOGRLayerInitItemInfo(layerObj *layer);
89
static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
90
                                  shapeObj *shape);
91
static void msOGRCloseConnection(void *conn_handle);
92

93
/* ==================================================================
94
 * Geometry conversion functions
95
 * ================================================================== */
96

97
/**********************************************************************
98
 *                     ogrPointsAddPoint()
99
 *
100
 * NOTE: This function assumes the line->point array already has been
101
 * allocated large enough for the point to be added, but that numpoints
102
 * does not include this new point.
103
 **********************************************************************/
104
static void ogrPointsAddPoint(lineObj *line, double dX, double dY, double dZ,
15,720✔
105
                              int lineindex, rectObj *bounds) {
106
  /* Keep track of shape bounds */
107
  if (line->numpoints == 0 && lineindex == 0) {
15,720✔
108
    bounds->minx = bounds->maxx = dX;
15,012✔
109
    bounds->miny = bounds->maxy = dY;
15,012✔
110
  } else {
111
    if (dX < bounds->minx)
708✔
112
      bounds->minx = dX;
55✔
113
    if (dX > bounds->maxx)
708✔
114
      bounds->maxx = dX;
21✔
115
    if (dY < bounds->miny)
708✔
116
      bounds->miny = dY;
19✔
117
    if (dY > bounds->maxy)
708✔
118
      bounds->maxy = dY;
21✔
119
  }
120

121
  line->point[line->numpoints].x = dX;
15,720✔
122
  line->point[line->numpoints].y = dY;
15,720✔
123
  line->point[line->numpoints].z = dZ;
15,720✔
124
  line->point[line->numpoints].m = 0.0;
15,720✔
125
  line->numpoints++;
15,720✔
126
}
15,720✔
127

128
/**********************************************************************
129
 *                     ogrGeomPoints()
130
 **********************************************************************/
131
static int ogrGeomPoints(OGRGeometryH hGeom, shapeObj *outshp) {
15,012✔
132
  int i;
133
  int numpoints;
134

135
  if (hGeom == NULL)
15,012✔
136
    return 0;
137

138
  OGRwkbGeometryType eGType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
15,012✔
139

140
  /* -------------------------------------------------------------------- */
141
  /*      Container types result in recursive invocation on each          */
142
  /*      subobject to add a set of points to the current list.           */
143
  /* -------------------------------------------------------------------- */
144
  switch (eGType) {
15,012✔
145
  case wkbGeometryCollection:
146
  case wkbMultiLineString:
147
  case wkbMultiPolygon:
148
  case wkbPolygon: {
149
    /* Treat it as GeometryCollection */
150
    for (int iGeom = 0; iGeom < OGR_G_GetGeometryCount(hGeom); iGeom++) {
×
151
      if (ogrGeomPoints(OGR_G_GetGeometryRef(hGeom, iGeom), outshp) == -1)
×
152
        return -1;
153
    }
154

155
    return 0;
156
  } break;
157

158
  case wkbPoint:
159
  case wkbMultiPoint:
160
  case wkbLineString:
161
  case wkbLinearRing:
162
    /* We will handle these directly */
163
    break;
164

165
  default:
×
166
    /* There shouldn't be any more cases should there? */
167
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported yet.",
×
168
               "ogrGeomPoints()", OGR_G_GetGeometryName(hGeom));
169
    return (-1);
×
170
  }
171

172
  /* ------------------------------------------------------------------
173
   * Count total number of points
174
   * ------------------------------------------------------------------ */
175
  if (eGType == wkbPoint) {
15,012✔
176
    numpoints = 1;
177
  } else if (eGType == wkbLineString || eGType == wkbLinearRing) {
9✔
178
    numpoints = OGR_G_GetPointCount(hGeom);
×
179
  } else if (eGType == wkbMultiPoint) {
180
    numpoints = OGR_G_GetGeometryCount(hGeom);
9✔
181
  } else {
182
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported yet.",
183
               "ogrGeomPoints()", OGR_G_GetGeometryName(hGeom));
184
    return (-1);
185
  }
186

187
  /* ------------------------------------------------------------------
188
   * Do we need to allocate a line object to contain all our points?
189
   * ------------------------------------------------------------------ */
190
  if (outshp->numlines == 0) {
15,012✔
191
    lineObj newline;
192

193
    newline.numpoints = 0;
15,012✔
194
    newline.point = NULL;
15,012✔
195
    msAddLine(outshp, &newline);
15,012✔
196
  }
197

198
  /* ------------------------------------------------------------------
199
   * Extend the point array for the new of points to add from the
200
   * current geometry.
201
   * ------------------------------------------------------------------ */
202
  lineObj *line = outshp->line + outshp->numlines - 1;
15,012✔
203

204
  if (line->point == NULL)
15,012✔
205
    line->point = (pointObj *)malloc(sizeof(pointObj) * numpoints);
×
206
  else
207
    line->point = (pointObj *)realloc(
15,012✔
208
        line->point, sizeof(pointObj) * (numpoints + line->numpoints));
15,012✔
209

210
  if (!line->point) {
15,012✔
211
    msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
×
212
               "ogrGeomPoints()");
213
    return (-1);
×
214
  }
215

216
  /* ------------------------------------------------------------------
217
   * alloc buffer and filter/transform points
218
   * ------------------------------------------------------------------ */
219
  if (eGType == wkbPoint) {
15,012✔
220
    ogrPointsAddPoint(line, OGR_G_GetX(hGeom, 0), OGR_G_GetY(hGeom, 0),
15,003✔
221
                      OGR_G_GetZ(hGeom, 0), outshp->numlines - 1,
15,003✔
222
                      &(outshp->bounds));
223
  } else if (eGType == wkbLineString || eGType == wkbLinearRing) {
9✔
224
    for (i = 0; i < numpoints; i++)
×
225
      ogrPointsAddPoint(line, OGR_G_GetX(hGeom, i), OGR_G_GetY(hGeom, i),
×
226
                        OGR_G_GetZ(hGeom, i), outshp->numlines - 1,
×
227
                        &(outshp->bounds));
228
  } else if (eGType == wkbMultiPoint) {
229
    for (i = 0; i < numpoints; i++) {
726✔
230
      OGRGeometryH hPoint = OGR_G_GetGeometryRef(hGeom, i);
717✔
231
      ogrPointsAddPoint(line, OGR_G_GetX(hPoint, 0), OGR_G_GetY(hPoint, 0),
717✔
232
                        OGR_G_GetZ(hPoint, 0), outshp->numlines - 1,
717✔
233
                        &(outshp->bounds));
234
    }
235
  }
236

237
  outshp->type = MS_SHAPE_POINT;
15,012✔
238

239
  return (0);
15,012✔
240
}
241

242
/**********************************************************************
243
 *                     ogrGeomLine()
244
 *
245
 * Recursively convert any OGRGeometry into a shapeObj.  Each part becomes
246
 * a line in the overall shapeObj.
247
 **********************************************************************/
248
static int ogrGeomLine(OGRGeometryH hGeom, shapeObj *outshp, int bCloseRings) {
2,922✔
249
  if (hGeom == NULL)
2,922✔
250
    return 0;
251

252
  /* ------------------------------------------------------------------
253
   * Use recursive calls for complex geometries
254
   * ------------------------------------------------------------------ */
255
  OGRwkbGeometryType eGType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
2,922✔
256

257
  if (eGType == wkbPolygon || eGType == wkbGeometryCollection ||
2,922✔
258
      eGType == wkbMultiLineString || eGType == wkbMultiPolygon) {
259
    if (eGType == wkbPolygon && outshp->type == MS_SHAPE_NULL)
1,378✔
260
      outshp->type = MS_SHAPE_POLYGON;
1,368✔
261

262
    /* Treat it as GeometryCollection */
263
    for (int iGeom = 0; iGeom < OGR_G_GetGeometryCount(hGeom); iGeom++) {
2,827✔
264
      if (ogrGeomLine(OGR_G_GetGeometryRef(hGeom, iGeom), outshp,
1,430✔
265
                      bCloseRings) == -1)
266
        return -1;
267
    }
268
  }
269
  /* ------------------------------------------------------------------
270
   * OGRPoint and OGRMultiPoint
271
   * ------------------------------------------------------------------ */
272
  else if (eGType == wkbPoint || eGType == wkbMultiPoint) {
273
    /* Hummmm a point when we're drawing lines/polygons... just drop it! */
274
  }
275
  /* ------------------------------------------------------------------
276
   * OGRLinearRing/OGRLineString ... both are of type wkbLineString
277
   * ------------------------------------------------------------------ */
278
  else if (eGType == wkbLineString) {
279
    int j, numpoints;
280
    lineObj line = {0, NULL};
1,521✔
281
    double dX, dY;
282

283
    if ((numpoints = OGR_G_GetPointCount(hGeom)) < 2)
1,521✔
284
      return 0;
×
285

286
    if (outshp->type == MS_SHAPE_NULL)
1,521✔
287
      outshp->type = MS_SHAPE_LINE;
119✔
288

289
    line.numpoints = 0;
1,521✔
290
    line.point = (pointObj *)malloc(sizeof(pointObj) * (numpoints + 1));
1,521✔
291
    if (!line.point) {
1,521✔
292
      msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
×
293
                 "ogrGeomLine");
294
      return (-1);
×
295
    }
296

297
    OGR_G_GetPoints(hGeom, &(line.point[0].x), sizeof(pointObj),
1,521✔
298
                    &(line.point[0].y), sizeof(pointObj), &(line.point[0].z),
1,521✔
299
                    sizeof(pointObj));
300

301
    for (j = 0; j < numpoints; j++) {
262,248✔
302
      dX = line.point[j].x = OGR_G_GetX(hGeom, j);
260,727✔
303
      dY = line.point[j].y = OGR_G_GetY(hGeom, j);
260,727✔
304

305
      /* Keep track of shape bounds */
306
      if (j == 0 && outshp->numlines == 0) {
260,727✔
307
        outshp->bounds.minx = outshp->bounds.maxx = dX;
1,487✔
308
        outshp->bounds.miny = outshp->bounds.maxy = dY;
1,487✔
309
      } else {
310
        if (dX < outshp->bounds.minx)
259,240✔
311
          outshp->bounds.minx = dX;
21,193✔
312
        if (dX > outshp->bounds.maxx)
259,240✔
313
          outshp->bounds.maxx = dX;
59,904✔
314
        if (dY < outshp->bounds.miny)
259,240✔
315
          outshp->bounds.miny = dY;
87,963✔
316
        if (dY > outshp->bounds.maxy)
259,240✔
317
          outshp->bounds.maxy = dY;
20,608✔
318
      }
319
    }
320
    line.numpoints = numpoints;
1,521✔
321

322
    if (bCloseRings && (line.point[line.numpoints - 1].x != line.point[0].x ||
1,521✔
323
                        line.point[line.numpoints - 1].y != line.point[0].y)) {
1,375✔
324
      line.point[line.numpoints].x = line.point[0].x;
38✔
325
      line.point[line.numpoints].y = line.point[0].y;
38✔
326
      line.point[line.numpoints].z = line.point[0].z;
38✔
327
      line.numpoints++;
38✔
328
    }
329

330
    msAddLineDirectly(outshp, &line);
1,521✔
331
  } else {
332
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported.",
×
333
               "ogrGeomLine()", OGR_G_GetGeometryName(hGeom));
334
    return (-1);
×
335
  }
336

337
  return (0);
338
}
339

340
/**********************************************************************
341
 *                     ogrGetLinearGeometry()
342
 *
343
 * Fetch geometry from OGR feature. If using GDAL 2.0 or later, the geometry
344
 * might be of curve type, so linearize it.
345
 **********************************************************************/
346
static OGRGeometryH ogrGetLinearGeometry(OGRFeatureH hFeature) {
16,414✔
347
  /* Convert in place and reassign to the feature */
348
  OGRGeometryH hGeom = OGR_F_StealGeometry(hFeature);
16,414✔
349
  if (hGeom != NULL) {
16,414✔
350
    hGeom = OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)),
16,414✔
351
                          NULL);
352
    OGR_F_SetGeometryDirectly(hFeature, hGeom);
16,414✔
353
  }
354
  return hGeom;
16,414✔
355
}
356

357
/**********************************************************************
358
 *                     ogrConvertGeometry()
359
 *
360
 * Convert OGR geometry into a shape object doing the best possible
361
 * job to match OGR Geometry type and layer type.
362
 *
363
 * If layer type is incompatible with geometry, then shape is returned with
364
 * shape->type = MS_SHAPE_NULL
365
 **********************************************************************/
366
static int ogrConvertGeometry(OGRGeometryH hGeom, shapeObj *outshp,
16,504✔
367
                              enum MS_LAYER_TYPE layertype) {
368
  /* ------------------------------------------------------------------
369
   * Process geometry according to layer type
370
   * ------------------------------------------------------------------ */
371
  int nStatus = MS_SUCCESS;
372

373
  if (hGeom == NULL) {
16,504✔
374
    // Empty geometry... this is not an error... we'll just skip it
375
    return MS_SUCCESS;
376
  }
377

378
  switch (layertype) {
16,504✔
379
    /* ------------------------------------------------------------------
380
     *      POINT layer - Any geometry can be converted to point/multipoint
381
     * ------------------------------------------------------------------ */
382
  case MS_LAYER_POINT:
15,012✔
383
    if (ogrGeomPoints(hGeom, outshp) == -1) {
15,012✔
384
      nStatus = MS_FAILURE; // Error message already produced.
385
    }
386
    break;
387
    /* ------------------------------------------------------------------
388
     *      LINE layer
389
     * ------------------------------------------------------------------ */
390
  case MS_LAYER_LINE:
129✔
391
    if (ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
129✔
392
      nStatus = MS_FAILURE; // Error message already produced.
393
    }
394
    if (outshp->type != MS_SHAPE_LINE && outshp->type != MS_SHAPE_POLYGON)
129✔
395
      outshp->type = MS_SHAPE_NULL; // Incompatible type for this layer
×
396
    break;
397
    /* ------------------------------------------------------------------
398
     *      POLYGON layer
399
     * ------------------------------------------------------------------ */
400
  case MS_LAYER_POLYGON:
1,363✔
401
    if (ogrGeomLine(hGeom, outshp, MS_TRUE) == -1) {
1,363✔
402
      nStatus = MS_FAILURE; // Error message already produced.
403
    }
404
    if (outshp->type != MS_SHAPE_POLYGON)
1,363✔
405
      outshp->type = MS_SHAPE_NULL; // Incompatible type for this layer
7✔
406
    break;
407
    /* ------------------------------------------------------------------
408
     *      Chart or Query layers - return real feature type
409
     * ------------------------------------------------------------------ */
410
  case MS_LAYER_CHART:
×
411
  case MS_LAYER_QUERY:
412
    switch (OGR_G_GetGeometryType(hGeom)) {
×
413
    case wkbPoint:
×
414
    case wkbPoint25D:
415
    case wkbMultiPoint:
416
    case wkbMultiPoint25D:
417
      if (ogrGeomPoints(hGeom, outshp) == -1) {
×
418
        nStatus = MS_FAILURE; // Error message already produced.
419
      }
420
      break;
421
    default:
×
422
      // Handle any non-point types as lines/polygons ... ogrGeomLine()
423
      // will decide the shape type
424
      if (ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
×
425
        nStatus = MS_FAILURE; // Error message already produced.
426
      }
427
    }
428
    break;
429

430
  default:
×
431
    msSetError(MS_MISCERR, "Unknown or unsupported layer type.",
×
432
               "msOGRLayerNextShape()");
433
    nStatus = MS_FAILURE;
434
  } /* switch layertype */
435

436
  return nStatus;
437
}
438

439
/**********************************************************************
440
 *                     msOGRGeometryToShape()
441
 *
442
 * Utility function to convert from OGR geometry to a mapserver shape
443
 * object.
444
 **********************************************************************/
445
int msOGRGeometryToShape(OGRGeometryH hGeometry, shapeObj *psShape,
90✔
446
                         OGRwkbGeometryType nType) {
447
  if (hGeometry && psShape && nType > 0) {
90✔
448
    if (nType == wkbPoint || nType == wkbMultiPoint)
449
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_POINT);
30✔
450
    else if (nType == wkbLineString || nType == wkbMultiLineString)
451
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_LINE);
4✔
452
    else if (nType == wkbPolygon || nType == wkbMultiPolygon)
453
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_POLYGON);
56✔
454
    else
455
      return MS_FAILURE;
456
  } else
457
    return MS_FAILURE;
458
}
459

460
/* ==================================================================
461
 * Attributes handling functions
462
 * ================================================================== */
463

464
// Special field index codes for handling text string and angle coming from
465
// OGR style strings.
466

467
#define MSOGR_FID_INDEX -99
468

469
#define MSOGR_LABELNUMITEMS 21
470
#define MSOGR_LABELFONTNAMENAME "OGR:LabelFont"
471
#define MSOGR_LABELFONTNAMEINDEX -100
472
#define MSOGR_LABELSIZENAME "OGR:LabelSize"
473
#define MSOGR_LABELSIZEINDEX -101
474
#define MSOGR_LABELTEXTNAME "OGR:LabelText"
475
#define MSOGR_LABELTEXTINDEX -102
476
#define MSOGR_LABELANGLENAME "OGR:LabelAngle"
477
#define MSOGR_LABELANGLEINDEX -103
478
#define MSOGR_LABELFCOLORNAME "OGR:LabelFColor"
479
#define MSOGR_LABELFCOLORINDEX -104
480
#define MSOGR_LABELBCOLORNAME "OGR:LabelBColor"
481
#define MSOGR_LABELBCOLORINDEX -105
482
#define MSOGR_LABELPLACEMENTNAME "OGR:LabelPlacement"
483
#define MSOGR_LABELPLACEMENTINDEX -106
484
#define MSOGR_LABELANCHORNAME "OGR:LabelAnchor"
485
#define MSOGR_LABELANCHORINDEX -107
486
#define MSOGR_LABELDXNAME "OGR:LabelDx"
487
#define MSOGR_LABELDXINDEX -108
488
#define MSOGR_LABELDYNAME "OGR:LabelDy"
489
#define MSOGR_LABELDYINDEX -109
490
#define MSOGR_LABELPERPNAME "OGR:LabelPerp"
491
#define MSOGR_LABELPERPINDEX -110
492
#define MSOGR_LABELBOLDNAME "OGR:LabelBold"
493
#define MSOGR_LABELBOLDINDEX -111
494
#define MSOGR_LABELITALICNAME "OGR:LabelItalic"
495
#define MSOGR_LABELITALICINDEX -112
496
#define MSOGR_LABELUNDERLINENAME "OGR:LabelUnderline"
497
#define MSOGR_LABELUNDERLINEINDEX -113
498
#define MSOGR_LABELPRIORITYNAME "OGR:LabelPriority"
499
#define MSOGR_LABELPRIORITYINDEX -114
500
#define MSOGR_LABELSTRIKEOUTNAME "OGR:LabelStrikeout"
501
#define MSOGR_LABELSTRIKEOUTINDEX -115
502
#define MSOGR_LABELSTRETCHNAME "OGR:LabelStretch"
503
#define MSOGR_LABELSTRETCHINDEX -116
504
#define MSOGR_LABELADJHORNAME "OGR:LabelAdjHor"
505
#define MSOGR_LABELADJHORINDEX -117
506
#define MSOGR_LABELADJVERTNAME "OGR:LabelAdjVert"
507
#define MSOGR_LABELADJVERTINDEX -118
508
#define MSOGR_LABELHCOLORNAME "OGR:LabelHColor"
509
#define MSOGR_LABELHCOLORINDEX -119
510
#define MSOGR_LABELOCOLORNAME "OGR:LabelOColor"
511
#define MSOGR_LABELOCOLORINDEX -120
512
// Special codes for the OGR style parameters
513
#define MSOGR_LABELPARAMNAME "OGR:LabelParam"
514
#define MSOGR_LABELPARAMNAMELEN 14
515
#define MSOGR_LABELPARAMINDEX -500
516
#define MSOGR_BRUSHPARAMNAME "OGR:BrushParam"
517
#define MSOGR_BRUSHPARAMNAMELEN 14
518
#define MSOGR_BRUSHPARAMINDEX -600
519
#define MSOGR_PENPARAMNAME "OGR:PenParam"
520
#define MSOGR_PENPARAMNAMELEN 12
521
#define MSOGR_PENPARAMINDEX -700
522
#define MSOGR_SYMBOLPARAMNAME "OGR:SymbolParam"
523
#define MSOGR_SYMBOLPARAMNAMELEN 15
524
#define MSOGR_SYMBOLPARAMINDEX -800
525

526
/**********************************************************************
527
 *                     msOGRGetValues()
528
 *
529
 * Load selected item (i.e. field) values into a char array
530
 *
531
 * Some special attribute names are used to return some OGRFeature params
532
 * like for instance stuff encoded in the OGRStyleString.
533
 * For now the following pseudo-attribute names are supported:
534
 *  "OGR:TextString"  OGRFeatureStyle's text string if present
535
 *  "OGR:TextAngle"   OGRFeatureStyle's text angle, or 0 if not set
536
 **********************************************************************/
537
static char **msOGRGetValues(layerObj *layer, OGRFeatureH hFeature) {
8,587✔
538
  char **values;
539
  const char *pszValue = NULL;
540
  int i;
541

542
  if (layer->numitems == 0)
8,587✔
543
    return (NULL);
544

545
  if (!layer->iteminfo) // Should not happen... but just in case!
8,587✔
546
    if (msOGRLayerInitItemInfo(layer) != MS_SUCCESS)
×
547
      return NULL;
548

549
  if ((values = (char **)malloc(sizeof(char *) * layer->numitems)) == NULL) {
8,587✔
550
    msSetError(MS_MEMERR, NULL, "msOGRGetValues()");
×
551
    return (NULL);
×
552
  }
553

554
  OGRStyleMgrH hStyleMgr = NULL;
555
  OGRStyleToolH hLabelStyle = NULL;
556
  OGRStyleToolH hPenStyle = NULL;
557
  OGRStyleToolH hBrushStyle = NULL;
558
  OGRStyleToolH hSymbolStyle = NULL;
559

560
  int *itemindexes = (int *)layer->iteminfo;
8,587✔
561

562
  int nYear;
563
  int nMonth;
564
  int nDay;
565
  int nHour;
566
  int nMinute;
567
  int nSecond;
568
  int nTZFlag;
569

570
  for (i = 0; i < layer->numitems; i++) {
64,001✔
571
    if (itemindexes[i] >= 0) {
55,414✔
572
      // Extract regular attributes
573
      const char *pszValue = OGR_F_GetFieldAsString(hFeature, itemindexes[i]);
55,414✔
574
      if (pszValue[0] == 0) {
55,414✔
575
        values[i] = msStrdup("");
5,344✔
576
      } else {
577
        OGRFieldDefnH hFieldDefnRef =
578
            OGR_F_GetFieldDefnRef(hFeature, itemindexes[i]);
50,070✔
579
        switch (OGR_Fld_GetType(hFieldDefnRef)) {
50,070✔
580
        case OFTTime:
4✔
581
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
4✔
582
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
583
          switch (nTZFlag) {
4✔
584
          case 0: // Unknown time zone
4✔
585
          case 1: // Local time zone (not specified)
586
            values[i] =
8✔
587
                msStrdup(CPLSPrintf("%02d:%02d:%02d", nHour, nMinute, nSecond));
4✔
588
            break;
4✔
589
          case 100: // GMT
×
590
            values[i] = msStrdup(
×
591
                CPLSPrintf("%02d:%02d:%02dZ", nHour, nMinute, nSecond));
592
            break;
×
593
          default: // Offset (in quarter-hour units) from GMT
×
594
            const int TZOffset = std::abs(nTZFlag - 100) * 15;
×
595
            const int TZHour = TZOffset / 60;
×
596
            const int TZMinute = TZOffset % 60;
×
597
            const char TZSign = (nTZFlag > 100) ? '+' : '-';
×
598
            values[i] =
×
599
                msStrdup(CPLSPrintf("%02d:%02d:%02d%c%02d:%02d", nHour, nMinute,
×
600
                                    nSecond, TZSign, TZHour, TZMinute));
601
          }
602
          break;
603
        case OFTDate:
12✔
604
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
12✔
605
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
606
          values[i] =
24✔
607
              msStrdup(CPLSPrintf("%04d-%02d-%02d", nYear, nMonth, nDay));
12✔
608
          break;
12✔
609
        case OFTDateTime:
24✔
610
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
24✔
611
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
612
          switch (nTZFlag) {
24✔
613
          case 0: // Unknown time zone
8✔
614
          case 1: // Local time zone (not specified)
615
            values[i] =
16✔
616
                msStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d", nYear,
8✔
617
                                    nMonth, nDay, nHour, nMinute, nSecond));
618
            break;
8✔
619
          case 100: // GMT
12✔
620
            values[i] =
24✔
621
                msStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
12✔
622
                                    nMonth, nDay, nHour, nMinute, nSecond));
623
            break;
12✔
624
          default: // Offset (in quarter-hour units) from GMT
4✔
625
            const int TZOffset = std::abs(nTZFlag - 100) * 15;
4✔
626
            const int TZHour = TZOffset / 60;
4✔
627
            const int TZMinute = TZOffset % 60;
4✔
628
            const char TZSign = (nTZFlag > 100) ? '+' : '-';
4✔
629
            values[i] = msStrdup(CPLSPrintf(
4✔
630
                "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", nYear, nMonth, nDay,
631
                nHour, nMinute, nSecond, TZSign, TZHour, TZMinute));
632
          }
633
          break;
634
        default:
50,030✔
635
          values[i] = msStrdup(pszValue);
50,030✔
636
          break;
50,030✔
637
        }
638
      }
639
    } else if (itemindexes[i] == MSOGR_FID_INDEX) {
×
640
      values[i] =
×
641
          msStrdup(CPLSPrintf(CPL_FRMT_GIB, (GIntBig)OGR_F_GetFID(hFeature)));
×
642
    } else {
643
      // Handle special OGR attributes coming from StyleString
644
      if (!hStyleMgr) {
×
645
        hStyleMgr = OGR_SM_Create(NULL);
×
646
        OGR_SM_InitFromFeature(hStyleMgr, hFeature);
×
647
        int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
×
648
        for (int i = 0; i < numParts; i++) {
×
649
          OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
×
650
          if (hStylePart) {
×
651
            if (OGR_ST_GetType(hStylePart) == OGRSTCLabel && !hLabelStyle)
×
652
              hLabelStyle = hStylePart;
653
            else if (OGR_ST_GetType(hStylePart) == OGRSTCPen && !hPenStyle)
×
654
              hPenStyle = hStylePart;
655
            else if (OGR_ST_GetType(hStylePart) == OGRSTCBrush && !hBrushStyle)
×
656
              hBrushStyle = hStylePart;
657
            else if (OGR_ST_GetType(hStylePart) == OGRSTCSymbol &&
×
658
                     !hSymbolStyle)
659
              hSymbolStyle = hStylePart;
660
            else {
661
              OGR_ST_Destroy(hStylePart);
×
662
              hStylePart = NULL;
663
            }
664
          }
665
          /* Setting up the size units according to msOGRLayerGetAutoStyle*/
666
          if (hStylePart && layer->map)
×
667
            OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
×
668
                           layer->map->cellsize * layer->map->resolution /
×
669
                               layer->map->defresolution * 72.0 * 39.37);
×
670
        }
671
      }
672
      int bDefault;
673
      if (itemindexes[i] == MSOGR_LABELTEXTINDEX) {
×
674
        if (hLabelStyle == NULL ||
×
675
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelTextString,
×
676
                                            &bDefault)) == NULL))
677
          values[i] = msStrdup("");
×
678
        else
679
          values[i] = msStrdup(pszValue);
×
680

681
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
682
          msDebug(MSOGR_LABELTEXTNAME " = \"%s\"\n", values[i]);
×
683
      } else if (itemindexes[i] == MSOGR_LABELANGLEINDEX) {
684
        if (hLabelStyle == NULL ||
×
685
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAngle,
×
686
                                            &bDefault)) == NULL))
687
          values[i] = msStrdup("0");
×
688
        else
689
          values[i] = msStrdup(pszValue);
×
690

691
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
692
          msDebug(MSOGR_LABELANGLENAME " = \"%s\"\n", values[i]);
×
693
      } else if (itemindexes[i] == MSOGR_LABELSIZEINDEX) {
694
        if (hLabelStyle == NULL ||
×
695
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelSize,
×
696
                                            &bDefault)) == NULL))
697
          values[i] = msStrdup("0");
×
698
        else
699
          values[i] = msStrdup(pszValue);
×
700

701
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
702
          msDebug(MSOGR_LABELSIZENAME " = \"%s\"\n", values[i]);
×
703
      } else if (itemindexes[i] == MSOGR_LABELFCOLORINDEX) {
704
        if (hLabelStyle == NULL ||
×
705
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFColor,
×
706
                                            &bDefault)) == NULL))
707
          values[i] = msStrdup("#000000");
×
708
        else
709
          values[i] = msStrdup(pszValue);
×
710

711
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
712
          msDebug(MSOGR_LABELFCOLORNAME " = \"%s\"\n", values[i]);
×
713
      } else if (itemindexes[i] == MSOGR_LABELFONTNAMEINDEX) {
714
        if (hLabelStyle == NULL ||
×
715
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFontName,
×
716
                                            &bDefault)) == NULL))
717
          values[i] = msStrdup("Arial");
×
718
        else
719
          values[i] = msStrdup(pszValue);
×
720

721
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
722
          msDebug(MSOGR_LABELFONTNAMENAME " =       \"%s\"\n", values[i]);
×
723
      } else if (itemindexes[i] == MSOGR_LABELBCOLORINDEX) {
724
        if (hLabelStyle == NULL ||
×
725
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelBColor,
×
726
                                            &bDefault)) == NULL))
727
          values[i] = msStrdup("#000000");
×
728
        else
729
          values[i] = msStrdup(pszValue);
×
730

731
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
732
          msDebug(MSOGR_LABELBCOLORNAME " = \"%s\"\n", values[i]);
×
733
      } else if (itemindexes[i] == MSOGR_LABELPLACEMENTINDEX) {
734
        if (hLabelStyle == NULL ||
×
735
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPlacement,
×
736
                                            &bDefault)) == NULL))
737
          values[i] = msStrdup("");
×
738
        else
739
          values[i] = msStrdup(pszValue);
×
740

741
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
742
          msDebug(MSOGR_LABELPLACEMENTNAME " = \"%s\"\n", values[i]);
×
743
      } else if (itemindexes[i] == MSOGR_LABELANCHORINDEX) {
744
        if (hLabelStyle == NULL ||
×
745
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAnchor,
×
746
                                            &bDefault)) == NULL))
747
          values[i] = msStrdup("0");
×
748
        else
749
          values[i] = msStrdup(pszValue);
×
750

751
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
752
          msDebug(MSOGR_LABELANCHORNAME " = \"%s\"\n", values[i]);
×
753
      } else if (itemindexes[i] == MSOGR_LABELDXINDEX) {
754
        if (hLabelStyle == NULL ||
×
755
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelDx,
×
756
                                            &bDefault)) == NULL))
757
          values[i] = msStrdup("0");
×
758
        else
759
          values[i] = msStrdup(pszValue);
×
760

761
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
762
          msDebug(MSOGR_LABELDXNAME " = \"%s\"\n", values[i]);
×
763
      } else if (itemindexes[i] == MSOGR_LABELDYINDEX) {
764
        if (hLabelStyle == NULL ||
×
765
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelDy,
×
766
                                            &bDefault)) == NULL))
767
          values[i] = msStrdup("0");
×
768
        else
769
          values[i] = msStrdup(pszValue);
×
770

771
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
772
          msDebug(MSOGR_LABELDYNAME " = \"%s\"\n", values[i]);
×
773
      } else if (itemindexes[i] == MSOGR_LABELPERPINDEX) {
774
        if (hLabelStyle == NULL ||
×
775
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPerp,
×
776
                                            &bDefault)) == NULL))
777
          values[i] = msStrdup("0");
×
778
        else
779
          values[i] = msStrdup(pszValue);
×
780

781
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
782
          msDebug(MSOGR_LABELPERPNAME " = \"%s\"\n", values[i]);
×
783
      } else if (itemindexes[i] == MSOGR_LABELBOLDINDEX) {
784
        if (hLabelStyle == NULL ||
×
785
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelBold,
×
786
                                            &bDefault)) == NULL))
787
          values[i] = msStrdup("0");
×
788
        else
789
          values[i] = msStrdup(pszValue);
×
790

791
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
792
          msDebug(MSOGR_LABELBOLDNAME " = \"%s\"\n", values[i]);
×
793
      } else if (itemindexes[i] == MSOGR_LABELITALICINDEX) {
794
        if (hLabelStyle == NULL ||
×
795
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelItalic,
×
796
                                            &bDefault)) == NULL))
797
          values[i] = msStrdup("0");
×
798
        else
799
          values[i] = msStrdup(pszValue);
×
800

801
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
802
          msDebug(MSOGR_LABELITALICNAME " = \"%s\"\n", values[i]);
×
803
      } else if (itemindexes[i] == MSOGR_LABELUNDERLINEINDEX) {
804
        if (hLabelStyle == NULL ||
×
805
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelUnderline,
×
806
                                            &bDefault)) == NULL))
807
          values[i] = msStrdup("0");
×
808
        else
809
          values[i] = msStrdup(pszValue);
×
810

811
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
812
          msDebug(MSOGR_LABELUNDERLINENAME " = \"%s\"\n", values[i]);
×
813
      } else if (itemindexes[i] == MSOGR_LABELPRIORITYINDEX) {
814
        if (hLabelStyle == NULL ||
×
815
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPriority,
×
816
                                            &bDefault)) == NULL))
817
          values[i] = msStrdup("0");
×
818
        else
819
          values[i] = msStrdup(pszValue);
×
820

821
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
822
          msDebug(MSOGR_LABELPRIORITYNAME " = \"%s\"\n", values[i]);
×
823
      } else if (itemindexes[i] == MSOGR_LABELSTRIKEOUTINDEX) {
824
        if (hLabelStyle == NULL ||
×
825
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelStrikeout,
×
826
                                            &bDefault)) == NULL))
827
          values[i] = msStrdup("0");
×
828
        else
829
          values[i] = msStrdup(pszValue);
×
830

831
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
832
          msDebug(MSOGR_LABELSTRIKEOUTNAME " = \"%s\"\n", values[i]);
×
833
      } else if (itemindexes[i] == MSOGR_LABELSTRETCHINDEX) {
834
        if (hLabelStyle == NULL ||
×
835
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelStretch,
×
836
                                            &bDefault)) == NULL))
837
          values[i] = msStrdup("0");
×
838
        else
839
          values[i] = msStrdup(pszValue);
×
840

841
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
842
          msDebug(MSOGR_LABELSTRETCHNAME " = \"%s\"\n", values[i]);
×
843
      } else if (itemindexes[i] == MSOGR_LABELADJHORINDEX) {
844
        if (hLabelStyle == NULL ||
×
845
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAdjHor,
×
846
                                            &bDefault)) == NULL))
847
          values[i] = msStrdup("");
×
848
        else
849
          values[i] = msStrdup(pszValue);
×
850

851
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
852
          msDebug(MSOGR_LABELADJHORNAME " = \"%s\"\n", values[i]);
×
853
      } else if (itemindexes[i] == MSOGR_LABELADJVERTINDEX) {
854
        if (hLabelStyle == NULL ||
×
855
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAdjVert,
×
856
                                            &bDefault)) == NULL))
857
          values[i] = msStrdup("");
×
858
        else
859
          values[i] = msStrdup(pszValue);
×
860

861
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
862
          msDebug(MSOGR_LABELADJVERTNAME " = \"%s\"\n", values[i]);
×
863
      } else if (itemindexes[i] == MSOGR_LABELHCOLORINDEX) {
864
        if (hLabelStyle == NULL ||
×
865
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelHColor,
×
866
                                            &bDefault)) == NULL))
867
          values[i] = msStrdup("");
×
868
        else
869
          values[i] = msStrdup(pszValue);
×
870

871
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
872
          msDebug(MSOGR_LABELHCOLORNAME " = \"%s\"\n", values[i]);
×
873
      } else if (itemindexes[i] == MSOGR_LABELOCOLORINDEX) {
874
        if (hLabelStyle == NULL ||
×
875
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelOColor,
×
876
                                            &bDefault)) == NULL))
877
          values[i] = msStrdup("");
×
878
        else
879
          values[i] = msStrdup(pszValue);
×
880

881
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
882
          msDebug(MSOGR_LABELOCOLORNAME " = \"%s\"\n", values[i]);
×
883
      } else if (itemindexes[i] >= MSOGR_LABELPARAMINDEX) {
×
884
        if (hLabelStyle == NULL ||
×
885
            ((pszValue = OGR_ST_GetParamStr(
×
886
                  hLabelStyle, itemindexes[i] - MSOGR_LABELPARAMINDEX,
887
                  &bDefault)) == NULL))
888
          values[i] = msStrdup("");
×
889
        else
890
          values[i] = msStrdup(pszValue);
×
891

892
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
893
          msDebug(MSOGR_LABELPARAMNAME " = \"%s\"\n", values[i]);
×
894
      } else if (itemindexes[i] >= MSOGR_BRUSHPARAMINDEX) {
×
895
        if (hBrushStyle == NULL ||
×
896
            ((pszValue = OGR_ST_GetParamStr(
×
897
                  hBrushStyle, itemindexes[i] - MSOGR_BRUSHPARAMINDEX,
898
                  &bDefault)) == NULL))
899
          values[i] = msStrdup("");
×
900
        else
901
          values[i] = msStrdup(pszValue);
×
902

903
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
904
          msDebug(MSOGR_BRUSHPARAMNAME " = \"%s\"\n", values[i]);
×
905
      } else if (itemindexes[i] >= MSOGR_PENPARAMINDEX) {
×
906
        if (hPenStyle == NULL ||
×
907
            ((pszValue = OGR_ST_GetParamStr(
×
908
                  hPenStyle, itemindexes[i] - MSOGR_PENPARAMINDEX,
909
                  &bDefault)) == NULL))
910
          values[i] = msStrdup("");
×
911
        else
912
          values[i] = msStrdup(pszValue);
×
913

914
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
915
          msDebug(MSOGR_PENPARAMNAME " = \"%s\"\n", values[i]);
×
916
      } else if (itemindexes[i] >= MSOGR_SYMBOLPARAMINDEX) {
×
917
        if (hSymbolStyle == NULL ||
×
918
            ((pszValue = OGR_ST_GetParamStr(
×
919
                  hSymbolStyle, itemindexes[i] - MSOGR_SYMBOLPARAMINDEX,
920
                  &bDefault)) == NULL))
921
          values[i] = msStrdup("");
×
922
        else
923
          values[i] = msStrdup(pszValue);
×
924

925
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
926
          msDebug(MSOGR_SYMBOLPARAMNAME " = \"%s\"\n", values[i]);
×
927
      } else {
928
        msFreeCharArray(values, i);
×
929

930
        OGR_SM_Destroy(hStyleMgr);
×
931
        OGR_ST_Destroy(hLabelStyle);
×
932
        OGR_ST_Destroy(hPenStyle);
×
933
        OGR_ST_Destroy(hBrushStyle);
×
934
        OGR_ST_Destroy(hSymbolStyle);
×
935

936
        msSetError(MS_OGRERR, "Invalid field index!?!", "msOGRGetValues()");
×
937
        return (NULL);
×
938
      }
939
    }
940
  }
941

942
  OGR_SM_Destroy(hStyleMgr);
8,587✔
943
  OGR_ST_Destroy(hLabelStyle);
8,587✔
944
  OGR_ST_Destroy(hPenStyle);
8,587✔
945
  OGR_ST_Destroy(hBrushStyle);
8,587✔
946
  OGR_ST_Destroy(hSymbolStyle);
8,587✔
947

948
  return (values);
949
}
950

951
/**********************************************************************
952
 *                     msOGRSpatialRef2ProjectionObj()
953
 *
954
 * Init a MapServer projectionObj using an OGRSpatialRef
955
 * Works only with PROJECTION AUTO
956
 *
957
 * Returns MS_SUCCESS/MS_FAILURE
958
 **********************************************************************/
959
static int msOGRSpatialRef2ProjectionObj(OGRSpatialReferenceH hSRS,
50✔
960
                                         projectionObj *proj, int debug_flag) {
961
  // First flush the "auto" name from the projargs[]...
962
  msFreeProjectionExceptContext(proj);
50✔
963

964
  if (hSRS == NULL || OSRIsLocal(hSRS)) {
50✔
965
    // Dataset had no set projection or is NonEarth (LOCAL_CS)...
966
    // Nothing else to do. Leave proj empty and no reprojection will happen!
967
    return MS_SUCCESS;
×
968
  }
969

970
  // This helps avoiding going through potentially lossy PROJ4 strings
971
  const char *pszAuthName = OSRGetAuthorityName(hSRS, NULL);
50✔
972
  if (pszAuthName && EQUAL(pszAuthName, "EPSG")) {
50✔
973
    const char *pszAuthCode = OSRGetAuthorityCode(hSRS, NULL);
44✔
974
    if (pszAuthCode) {
44✔
975
      char szInitStr[32];
976
      snprintf(szInitStr, sizeof(szInitStr), "init=epsg:%d", atoi(pszAuthCode));
977

978
      if (debug_flag)
44✔
979
        msDebug("AUTO = %s\n", szInitStr);
×
980

981
      return msLoadProjectionString(proj, szInitStr) == 0 ? MS_SUCCESS
44✔
982
                                                          : MS_FAILURE;
44✔
983
    }
984
  }
985

986
  // Export OGR SRS to a PROJ4 string
987
  char *pszProj = NULL;
6✔
988

989
  if (OSRExportToProj4(hSRS, &pszProj) != OGRERR_NONE || pszProj == NULL ||
6✔
990
      strlen(pszProj) == 0) {
6✔
991
    msSetError(MS_OGRERR, "Conversion from OGR SRS to PROJ4 failed.",
×
992
               "msOGRSpatialRef2ProjectionObj()");
993
    CPLFree(pszProj);
×
994
    return (MS_FAILURE);
×
995
  }
996

997
  if (debug_flag)
6✔
998
    msDebug("AUTO = %s\n", pszProj);
×
999

1000
  if (msLoadProjectionString(proj, pszProj) != 0)
6✔
1001
    return MS_FAILURE;
1002

1003
  CPLFree(pszProj);
6✔
1004

1005
  return MS_SUCCESS;
1006
}
1007

1008
/**********************************************************************
1009
 *                     msOGCWKT2ProjectionObj()
1010
 *
1011
 * Init a MapServer projectionObj using an OGC WKT definition.
1012
 * Works only with PROJECTION AUTO
1013
 *
1014
 * Returns MS_SUCCESS/MS_FAILURE
1015
 **********************************************************************/
1016

1017
int msOGCWKT2ProjectionObj(const char *pszWKT, projectionObj *proj,
50✔
1018
                           int debug_flag)
1019

1020
{
1021
  OGRSpatialReferenceH hSRS;
1022
  char *pszAltWKT = (char *)pszWKT;
50✔
1023
  OGRErr eErr;
1024
  int ms_result;
1025

1026
  hSRS = OSRNewSpatialReference(NULL);
50✔
1027

1028
  if (!EQUALN(pszWKT, "GEOGCS", 6) && !EQUALN(pszWKT, "PROJCS", 6) &&
50✔
1029
      !EQUALN(pszWKT, "LOCAL_CS", 8))
40✔
1030
    eErr = OSRSetFromUserInput(hSRS, pszWKT);
40✔
1031
  else
1032
    eErr = OSRImportFromWkt(hSRS, &pszAltWKT);
10✔
1033

1034
  if (eErr != OGRERR_NONE) {
50✔
1035
    OSRDestroySpatialReference(hSRS);
1✔
1036
    msSetError(MS_OGRERR, "Ingestion of WKT string '%s' failed.",
1✔
1037
               "msOGCWKT2ProjectionObj()", pszWKT);
1038
    return MS_FAILURE;
1✔
1039
  }
1040

1041
  ms_result = msOGRSpatialRef2ProjectionObj(hSRS, proj, debug_flag);
49✔
1042

1043
  OSRDestroySpatialReference(hSRS);
49✔
1044
  return ms_result;
1045
}
1046

1047
/**********************************************************************
1048
 *                     msOGRFileOpen()
1049
 *
1050
 * Open an OGR connection, and initialize a msOGRFileInfo.
1051
 **********************************************************************/
1052

1053
static int bOGRDriversRegistered = MS_FALSE;
1054

1055
void msOGRInitialize(void)
1,290✔
1056

1057
{
1058
  /* ------------------------------------------------------------------
1059
   * Register OGR Drivers, only once per execution
1060
   * ------------------------------------------------------------------ */
1061
  if (!bOGRDriversRegistered) {
1,290✔
1062
    ACQUIRE_OGR_LOCK;
343✔
1063

1064
    OGRRegisterAll();
343✔
1065
    CPLPushErrorHandler(CPLQuietErrorHandler);
343✔
1066

1067
    /* ------------------------------------------------------------------
1068
     * Pass config option GML_FIELDTYPES=ALWAYS_STRING to OGR so that all
1069
     * GML attributes are returned as strings to MapServer. This is most
1070
     * efficient and prevents problems with autodetection of some attribute
1071
     * types.
1072
     * ------------------------------------------------------------------ */
1073
    CPLSetConfigOption("GML_FIELDTYPES", "ALWAYS_STRING");
343✔
1074

1075
    bOGRDriversRegistered = MS_TRUE;
343✔
1076

1077
    RELEASE_OGR_LOCK;
343✔
1078
  }
1079
}
1,290✔
1080

1081
/* ==================================================================
1082
 * The following functions closely relate to the API called from
1083
 * maplayer.c, but are intended to be used for the tileindex or direct
1084
 * layer access.
1085
 * ================================================================== */
1086

1087
static void msOGRFileOpenSpatialite(layerObj *layer, const char *pszLayerDef,
1088
                                    msOGRFileInfo *psInfo);
1089

1090
/**********************************************************************
1091
 *                     msOGRFileOpen()
1092
 *
1093
 * Open an OGR connection, and initialize a msOGRFileInfo.
1094
 **********************************************************************/
1095

1096
static msOGRFileInfo *msOGRFileOpen(layerObj *layer, const char *connection)
872✔
1097

1098
{
1099
  char *conn_decrypted = NULL;
1100

1101
  msOGRInitialize();
872✔
1102

1103
  /* ------------------------------------------------------------------
1104
   * Make sure any encrypted token in the connection string are decrypted
1105
   * ------------------------------------------------------------------ */
1106
  if (connection) {
872✔
1107
    conn_decrypted = msDecryptStringTokens(layer->map, connection);
872✔
1108
    if (conn_decrypted == NULL)
872✔
1109
      return NULL; /* An error should already have been reported */
1110
  }
1111

1112
  /* ------------------------------------------------------------------
1113
   * Parse connection string into dataset name, and layer name.
1114
   * ------------------------------------------------------------------ */
1115
  char *pszDSName = NULL, *pszLayerDef = NULL;
1116

1117
  if (conn_decrypted == NULL) {
1118
    /* we don't have anything */
1119
  } else if (layer->data != NULL) {
872✔
1120
    pszDSName = CPLStrdup(conn_decrypted);
390✔
1121
    pszLayerDef = CPLStrdup(layer->data);
390✔
1122
  } else {
1123
    char **papszTokens = NULL;
1124

1125
    papszTokens = CSLTokenizeStringComplex(conn_decrypted, ",", TRUE, FALSE);
482✔
1126

1127
    if (CSLCount(papszTokens) > 0)
482✔
1128
      pszDSName = CPLStrdup(papszTokens[0]);
482✔
1129
    if (CSLCount(papszTokens) > 1)
482✔
1130
      pszLayerDef = CPLStrdup(papszTokens[1]);
11✔
1131

1132
    CSLDestroy(papszTokens);
482✔
1133
  }
1134

1135
  /* Get rid of decrypted connection string. We'll use the original (not
1136
   * decrypted) string for debug and error messages in the rest of the code.
1137
   */
1138
  msFree(conn_decrypted);
872✔
1139
  conn_decrypted = NULL;
1140

1141
  if (pszDSName == NULL) {
872✔
1142
    msSetError(MS_OGRERR,
×
1143
               "Error parsing OGR connection information in layer `%s'",
1144
               "msOGRFileOpen()", layer->name ? layer->name : "(null)");
×
1145
    return NULL;
×
1146
  }
1147

1148
  if (pszLayerDef == NULL)
872✔
1149
    pszLayerDef = CPLStrdup("0");
471✔
1150

1151
  /* -------------------------------------------------------------------- */
1152
  /*      Can we get an existing connection for this layer?               */
1153
  /* -------------------------------------------------------------------- */
1154
  OGRDataSourceH hDS;
1155

1156
  hDS = (OGRDataSourceH)msConnPoolRequest(layer);
872✔
1157

1158
  /* -------------------------------------------------------------------- */
1159
  /*      If not, open now, and register this connection with the         */
1160
  /*      pool.                                                           */
1161
  /* -------------------------------------------------------------------- */
1162
  if (hDS == NULL) {
872✔
1163
    char szPath[MS_MAXPATHLEN] = "";
846✔
1164
    const char *pszDSSelectedName = pszDSName;
1165

1166
    if (layer->debug >= MS_DEBUGLEVEL_V) {
846✔
1167
      msDebug("msOGRFileOpen(%s)...\n", connection);
3✔
1168
    }
1169

1170
    CPLErrorReset();
846✔
1171
    if (msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
846✔
1172
                        pszDSName) != NULL ||
1,060✔
1173
        msTryBuildPath(szPath, layer->map->mappath, pszDSName) != NULL) {
214✔
1174
      /* Use relative path */
1175
      pszDSSelectedName = szPath;
1176
    }
1177

1178
    if (layer->debug >= MS_DEBUGLEVEL_V) {
846✔
1179
      msDebug("GDALOpenEx(%s)\n", pszDSSelectedName);
3✔
1180
    }
1181

1182
    ACQUIRE_OGR_LOCK;
846✔
1183
    char **connectionoptions =
1184
        msGetStringListFromHashTable(&(layer->connectionoptions));
846✔
1185
    hDS = (OGRDataSourceH)GDALOpenEx(pszDSSelectedName, GDAL_OF_VECTOR, NULL,
846✔
1186
                                     (const char *const *)connectionoptions,
1187
                                     NULL);
1188
    CSLDestroy(connectionoptions);
846✔
1189
    RELEASE_OGR_LOCK;
846✔
1190

1191
    if (hDS == NULL) {
846✔
1192
      msSetError(MS_OGRERR,
×
1193
                 "Open failed for OGR connection in layer `%s'.  "
1194
                 "File not found or unsupported format. Check server logs.",
1195
                 "msOGRFileOpen()", layer->name ? layer->name : "(null)");
×
1196
      if (strlen(CPLGetLastErrorMsg()) == 0)
×
1197
        msDebug("Open failed for OGR connection in layer `%s'.\n%s\n",
×
1198
                layer->name ? layer->name : "(null)", CPLGetLastErrorMsg());
×
1199
      CPLFree(pszDSName);
×
1200
      CPLFree(pszLayerDef);
×
1201
      return NULL;
×
1202
    }
1203

1204
    msConnPoolRegister(layer, hDS, msOGRCloseConnection);
846✔
1205
  }
1206

1207
  CPLFree(pszDSName);
872✔
1208
  pszDSName = NULL;
1209

1210
  /* ------------------------------------------------------------------
1211
   * Find the layer selected.
1212
   * ------------------------------------------------------------------ */
1213

1214
  int nLayerIndex = 0;
1215
  OGRLayerH hLayer = NULL;
1216

1217
  int iLayer;
1218

1219
  if (EQUALN(pszLayerDef, "SELECT ", 7)) {
872✔
1220
    ACQUIRE_OGR_LOCK;
21✔
1221
    hLayer = OGR_DS_ExecuteSQL(hDS, pszLayerDef, NULL, NULL);
21✔
1222
    if (hLayer == NULL) {
21✔
1223
      msSetError(MS_OGRERR, "ExecuteSQL() failed. Check server logs.",
×
1224
                 "msOGRFileOpen()");
1225
      if (strlen(CPLGetLastErrorMsg()) == 0)
×
1226
        msDebug("ExecuteSQL(%s) failed.\n%s\n", pszLayerDef,
×
1227
                CPLGetLastErrorMsg());
1228
      RELEASE_OGR_LOCK;
×
1229
      msConnPoolRelease(layer, hDS);
×
1230
      CPLFree(pszLayerDef);
×
1231
      return NULL;
×
1232
    }
1233
    RELEASE_OGR_LOCK;
21✔
1234
    nLayerIndex = -1;
1235
  }
1236

1237
  for (iLayer = 0; hLayer == NULL && iLayer < OGR_DS_GetLayerCount(hDS);
1,399✔
1238
       iLayer++) {
1239
    hLayer = OGR_DS_GetLayer(hDS, iLayer);
896✔
1240
    if (hLayer != NULL && EQUAL(OGR_L_GetName(hLayer), pszLayerDef)) {
896✔
1241
      nLayerIndex = iLayer;
1242
      break;
1243
    } else
1244
      hLayer = NULL;
1245
  }
1246

1247
  if (hLayer == NULL && (atoi(pszLayerDef) > 0 || EQUAL(pszLayerDef, "0"))) {
1,354✔
1248
    nLayerIndex = atoi(pszLayerDef);
1249
    if (nLayerIndex < OGR_DS_GetLayerCount(hDS))
482✔
1250
      hLayer = OGR_DS_GetLayer(hDS, nLayerIndex);
482✔
1251
  }
1252

1253
  if (hLayer == NULL) {
482✔
1254
    msSetError(MS_OGRERR, "GetLayer(%s) failed for OGR connection. Check logs.",
×
1255
               "msOGRFileOpen()", pszLayerDef);
1256
    msDebug("GetLayer(%s) failed for OGR connection `%s'.\n", pszLayerDef,
×
1257
            connection);
1258
    CPLFree(pszLayerDef);
×
1259
    msConnPoolRelease(layer, hDS);
×
1260
    return NULL;
×
1261
  }
1262

1263
  /* ------------------------------------------------------------------
1264
   * OK... open succeeded... alloc and fill msOGRFileInfo inside layer obj
1265
   * ------------------------------------------------------------------ */
1266
  msOGRFileInfo *psInfo = (msOGRFileInfo *)CPLCalloc(1, sizeof(msOGRFileInfo));
872✔
1267

1268
  psInfo->pszFname = CPLStrdup(OGR_DS_GetName(hDS));
872✔
1269
  psInfo->pszLayerDef = pszLayerDef;
872✔
1270
  psInfo->nLayerIndex = nLayerIndex;
872✔
1271
  psInfo->hDS = hDS;
872✔
1272
  psInfo->hLayer = hLayer;
872✔
1273

1274
  psInfo->nTileId = 0;
872✔
1275
  msInitProjection(&(psInfo->sTileProj));
872✔
1276
  msProjectionInheritContextFrom(&(psInfo->sTileProj), &(layer->projection));
872✔
1277
  psInfo->poCurTile = NULL;
872✔
1278
  psInfo->rect_is_defined = false;
872✔
1279
  psInfo->rect.minx = psInfo->rect.maxx = 0;
872✔
1280
  psInfo->rect.miny = psInfo->rect.maxy = 0;
872✔
1281
  psInfo->last_record_index_read = -1;
872✔
1282
  psInfo->dialect = NULL;
872✔
1283

1284
  psInfo->pszSelect = NULL;
872✔
1285
  psInfo->pszSpatialFilterTableName = NULL;
872✔
1286
  psInfo->pszSpatialFilterGeometryColumn = NULL;
872✔
1287
  psInfo->pszMainTableName = NULL;
872✔
1288
  psInfo->pszRowId = NULL;
872✔
1289
  psInfo->bIsOKForSQLCompose = true;
872✔
1290
  psInfo->bPaging = false;
872✔
1291
  psInfo->bHasSpatialIndex = false;
872✔
1292
  psInfo->pszTablePrefix = NULL;
872✔
1293
  psInfo->pszWHERE = NULL;
872✔
1294

1295
  // GDAL 1.x API
1296
  OGRSFDriverH dr = OGR_DS_GetDriver(hDS);
872✔
1297
  const char *name = OGR_Dr_GetName(dr);
872✔
1298
  if (strcmp(name, "SQLite") == 0) {
872✔
1299
    bool have_spatialite = false;
1300

1301
    CPLPushErrorHandler(CPLQuietErrorHandler);
257✔
1302

1303
    // test for Spatialite support in driver
1304
    const char *test_spatialite = "SELECT spatialite_version()";
1305
    OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
257✔
1306
    if (l) {
257✔
1307
      OGR_DS_ReleaseResultSet(hDS, l);
257✔
1308
      have_spatialite = true;
1309
    }
1310

1311
    // test for Spatialite enabled db
1312
    if (have_spatialite) {
1313
      have_spatialite = false;
1314
      const char *test_sql =
1315
          "select 1 from sqlite_master where name = 'geometry_columns' and sql "
1316
          "LIKE '%spatial_index_enabled%'";
1317
      OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_sql, NULL, NULL);
257✔
1318
      if (l) {
257✔
1319
        if (OGR_L_GetFeatureCount(l, TRUE) == 1)
257✔
1320
          have_spatialite = true;
1321
        OGR_DS_ReleaseResultSet(hDS, l);
257✔
1322
      }
1323
    }
1324

1325
    CPLPopErrorHandler();
257✔
1326
    CPLErrorReset();
257✔
1327

1328
    if (have_spatialite)
257✔
1329
      psInfo->dialect = "Spatialite";
245✔
1330
    else {
1331
      if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
12✔
1332
        msDebug(
×
1333
            "msOGRFileOpen: Native SQL not available, no Spatialite support "
1334
            "and/or not a Spatialite enabled db\n");
1335
      }
1336
    }
1337
  } else if (strcmp(name, "PostgreSQL") == 0) {
615✔
1338
    psInfo->dialect = "PostgreSQL";
×
1339
    // todo: PostgreSQL not yet tested
1340

1341
  } else if (strcmp(name, "GPKG") == 0 && nLayerIndex >= 0 &&
654✔
1342
             atoi(GDALVersionInfo("VERSION_NUM")) >= 2000000) {
39✔
1343

1344
    bool has_rtree = false;
1345
    const char *test_rtree =
1346
        CPLSPrintf("SELECT 1 FROM sqlite_master WHERE name = 'rtree_%s_%s'",
39✔
1347
                   OGR_L_GetName(hLayer), OGR_L_GetGeometryColumn(hLayer));
1348
    OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_rtree, NULL, NULL);
39✔
1349
    if (l) {
39✔
1350
      if (OGR_L_GetFeatureCount(l, TRUE) == 1) {
39✔
1351
        has_rtree = true;
1352
      }
1353
      OGR_DS_ReleaseResultSet(hDS, l);
39✔
1354
    }
1355
    if (has_rtree) {
39✔
1356
      bool have_gpkg_spatialite = false;
1357

1358
      CPLPushErrorHandler(CPLQuietErrorHandler);
39✔
1359

1360
      // test for Spatialite >= 4.3 support in driver
1361
      const char *test_spatialite = "SELECT spatialite_version()";
1362
      l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
39✔
1363
      if (l) {
39✔
1364
        OGRFeatureH hFeat = OGR_L_GetNextFeature(l);
39✔
1365
        if (hFeat) {
39✔
1366
          const char *pszVersion = OGR_F_GetFieldAsString(hFeat, 0);
39✔
1367
          have_gpkg_spatialite = atof(pszVersion) >= 4.3;
39✔
1368
          OGR_F_Destroy(hFeat);
39✔
1369
        }
1370
        OGR_DS_ReleaseResultSet(hDS, l);
39✔
1371
      }
1372
      CPLPopErrorHandler();
39✔
1373
      CPLErrorReset();
39✔
1374

1375
      if (have_gpkg_spatialite) {
39✔
1376
        psInfo->pszMainTableName = msStrdup(OGR_L_GetName(hLayer));
39✔
1377
        psInfo->pszTablePrefix = msStrdup(psInfo->pszMainTableName);
39✔
1378
        psInfo->pszSpatialFilterTableName = msStrdup(OGR_L_GetName(hLayer));
39✔
1379
        psInfo->pszSpatialFilterGeometryColumn =
39✔
1380
            msStrdup(OGR_L_GetGeometryColumn(hLayer));
39✔
1381
        psInfo->dialect = "GPKG";
39✔
1382
        psInfo->bPaging = true;
39✔
1383
        psInfo->bHasSpatialIndex = true;
39✔
1384
      } else if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
×
1385
        msDebug("msOGRFileOpen: Spatialite support in GPKG not enabled\n");
×
1386
      }
1387
    } else {
1388
      if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
×
1389
        msDebug("msOGRFileOpen: RTree index not available\n");
×
1390
      }
1391
    }
1392
  }
1393

1394
  if (psInfo->dialect != NULL && EQUAL(psInfo->dialect, "Spatialite"))
872✔
1395
    msOGRFileOpenSpatialite(layer, pszLayerDef, psInfo);
245✔
1396

1397
  return psInfo;
1398
}
1399

1400
/************************************************************************/
1401
/*                      msOGRFileOpenSpatialite()                       */
1402
/************************************************************************/
1403

1404
static void msOGRFileOpenSpatialite(layerObj *layer, const char *pszLayerDef,
245✔
1405
                                    msOGRFileInfo *psInfo) {
1406
  // In the case of a SQLite DB, check that we can identify the
1407
  // underlying table
1408
  if (psInfo->nLayerIndex == -1) {
245✔
1409
    psInfo->bIsOKForSQLCompose = false;
13✔
1410

1411
    const char *from = strstr(psInfo->pszLayerDef, " from ");
13✔
1412
    if (from == NULL)
13✔
1413
      from = strstr(psInfo->pszLayerDef, " FROM ");
1414
    if (from) {
13✔
1415
      const char *pszBeginningOfTable = from + strlen(" FROM ");
13✔
1416
      const char *pszIter = pszBeginningOfTable;
1417
      while (*pszIter && *pszIter != ' ')
81✔
1418
        pszIter++;
68✔
1419
      if (strchr(pszIter, ',') == NULL && strstr(pszIter, " where ") == NULL &&
13✔
1420
          strstr(pszIter, " WHERE ") == NULL &&
7✔
1421
          strstr(pszIter, " join ") == NULL &&
7✔
1422
          strstr(pszIter, " JOIN ") == NULL &&
7✔
1423
          strstr(pszIter, " order by ") == NULL &&
7✔
1424
          strstr(pszIter, " ORDER BY ") == NULL) {
1425
        psInfo->bIsOKForSQLCompose = true;
7✔
1426
        psInfo->pszMainTableName = msStrdup(pszBeginningOfTable);
7✔
1427
        psInfo->pszMainTableName[pszIter - pszBeginningOfTable] = '\0';
7✔
1428
        psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
7✔
1429
        psInfo->pszSpatialFilterGeometryColumn =
7✔
1430
            msStrdup(OGR_L_GetGeometryColumn(psInfo->hLayer));
7✔
1431

1432
        char *pszRequest = NULL;
1433
        pszRequest = msStringConcatenate(pszRequest,
7✔
1434
                                         "SELECT name FROM sqlite_master WHERE "
1435
                                         "type = 'table' AND name = lower('");
1436
        pszRequest = msStringConcatenate(pszRequest, psInfo->pszMainTableName);
7✔
1437
        pszRequest = msStringConcatenate(pszRequest, "')");
7✔
1438
        OGRLayerH hLayer =
1439
            OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
7✔
1440
        msFree(pszRequest);
7✔
1441

1442
        if (hLayer) {
7✔
1443
          OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
7✔
1444
          psInfo->bIsOKForSQLCompose = (hFeature != NULL);
7✔
1445
          if (hFeature) {
7✔
1446
            msFree(psInfo->pszMainTableName);
4✔
1447
            msFree(psInfo->pszSpatialFilterTableName);
4✔
1448
            psInfo->pszMainTableName =
4✔
1449
                msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
4✔
1450
            psInfo->pszSpatialFilterTableName =
4✔
1451
                msStrdup(psInfo->pszMainTableName);
4✔
1452
            OGR_F_Destroy(hFeature);
4✔
1453
          }
1454
          OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
7✔
1455
        }
1456
        if (psInfo->bIsOKForSQLCompose) {
7✔
1457
          psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
4✔
1458
        } else {
1459
          // Test if it is a spatial view
1460
          pszRequest = msStringConcatenate(
3✔
1461
              NULL, "SELECT f_table_name, f_geometry_column, view_rowid FROM "
1462
                    "views_geometry_columns WHERE view_name = lower('");
1463
          pszRequest =
1464
              msStringConcatenate(pszRequest, psInfo->pszMainTableName);
3✔
1465
          pszRequest = msStringConcatenate(pszRequest, "')");
3✔
1466
          CPLPushErrorHandler(CPLQuietErrorHandler);
3✔
1467
          OGRLayerH hLayer =
1468
              OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
3✔
1469
          CPLPopErrorHandler();
3✔
1470
          msFree(pszRequest);
3✔
1471

1472
          if (hLayer) {
3✔
1473
            OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
3✔
1474
            psInfo->bIsOKForSQLCompose = (hFeature != NULL);
3✔
1475
            if (hFeature) {
3✔
1476
              psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
3✔
1477
              msFree(psInfo->pszSpatialFilterTableName);
3✔
1478
              psInfo->pszSpatialFilterTableName =
3✔
1479
                  msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
3✔
1480
              CPLFree(psInfo->pszSpatialFilterGeometryColumn);
3✔
1481
              psInfo->pszSpatialFilterGeometryColumn =
3✔
1482
                  msStrdup(OGR_F_GetFieldAsString(hFeature, 1));
3✔
1483
              psInfo->pszRowId = msStrdup(OGR_F_GetFieldAsString(hFeature, 2));
3✔
1484
              OGR_F_Destroy(hFeature);
3✔
1485
            }
1486
            OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
3✔
1487
          }
1488
        }
1489
      }
1490
    }
1491
  } else {
1492
    psInfo->bIsOKForSQLCompose = false;
232✔
1493

1494
    char *pszRequest = NULL;
1495
    pszRequest = msStringConcatenate(
232✔
1496
        pszRequest,
1497
        "SELECT * FROM sqlite_master WHERE type = 'table' AND name = lower('");
1498
    pszRequest = msStringConcatenate(
232✔
1499
        pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1500
    pszRequest = msStringConcatenate(pszRequest, "')");
232✔
1501
    OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
232✔
1502
    msFree(pszRequest);
232✔
1503

1504
    if (hLayer) {
232✔
1505
      OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
232✔
1506
      psInfo->bIsOKForSQLCompose = (hFeature != NULL);
232✔
1507
      if (hFeature)
232✔
1508
        OGR_F_Destroy(hFeature);
229✔
1509
      OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
232✔
1510
    }
1511
    if (psInfo->bIsOKForSQLCompose) {
232✔
1512
      psInfo->pszMainTableName =
229✔
1513
          msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
229✔
1514
      psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
229✔
1515
      psInfo->pszSpatialFilterGeometryColumn =
229✔
1516
          msStrdup(OGR_L_GetGeometryColumn(psInfo->hLayer));
229✔
1517
    } else {
1518
      // Test if it is a spatial view
1519
      pszRequest = msStringConcatenate(
3✔
1520
          NULL, "SELECT f_table_name, f_geometry_column, view_rowid FROM "
1521
                "views_geometry_columns WHERE view_name = lower('");
1522
      pszRequest = msStringConcatenate(
3✔
1523
          pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1524
      pszRequest = msStringConcatenate(pszRequest, "')");
3✔
1525
      CPLPushErrorHandler(CPLQuietErrorHandler);
3✔
1526
      OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
3✔
1527
      CPLPopErrorHandler();
3✔
1528
      msFree(pszRequest);
3✔
1529

1530
      if (hLayer) {
3✔
1531
        OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
3✔
1532
        psInfo->bIsOKForSQLCompose = (hFeature != NULL);
3✔
1533
        if (hFeature) {
3✔
1534
          psInfo->pszMainTableName =
3✔
1535
              msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
3✔
1536
          psInfo->pszSpatialFilterTableName =
3✔
1537
              msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
3✔
1538
          psInfo->pszSpatialFilterGeometryColumn =
3✔
1539
              msStrdup(OGR_F_GetFieldAsString(hFeature, 1));
3✔
1540
          psInfo->pszRowId = msStrdup(OGR_F_GetFieldAsString(hFeature, 2));
3✔
1541
          OGR_F_Destroy(hFeature);
3✔
1542
        }
1543
        OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
3✔
1544
      }
1545
    }
1546
  }
1547

1548
  // in the case we cannot handle the native string, go back to the client
1549
  // side evaluation by unsetting it.
1550
  if (!psInfo->bIsOKForSQLCompose && psInfo->dialect != NULL) {
245✔
1551
    if (layer->debug >= MS_DEBUGLEVEL_VVV) {
6✔
1552
      msDebug("msOGRFileOpen(): Falling back to MapServer only evaluation\n");
×
1553
    }
1554
    psInfo->dialect = NULL;
6✔
1555
  }
1556

1557
  // Check if spatial index has been disabled (testing purposes)
1558
  if (msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX") != NULL &&
248✔
1559
      !CSLTestBoolean(msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX"))) {
3✔
1560
    if (layer->debug >= MS_DEBUGLEVEL_VVV) {
3✔
1561
      msDebug("msOGRFileOpen(): Layer %s has spatial index disabled by "
×
1562
              "processing option\n",
1563
              pszLayerDef);
1564
    }
1565
  }
1566
  // Test if spatial index is available
1567
  else if (psInfo->dialect != NULL) {
242✔
1568
    char *pszRequest = NULL;
1569
    pszRequest = msStringConcatenate(
236✔
1570
        pszRequest,
1571
        "SELECT * FROM sqlite_master WHERE type = 'table' AND name = 'idx_");
1572
    pszRequest =
1573
        msStringConcatenate(pszRequest, psInfo->pszSpatialFilterTableName);
236✔
1574
    pszRequest = msStringConcatenate(pszRequest, "_");
236✔
1575
    pszRequest = msStringConcatenate(pszRequest,
236✔
1576
                                     OGR_L_GetGeometryColumn(psInfo->hLayer));
1577
    pszRequest = msStringConcatenate(pszRequest, "'");
236✔
1578

1579
    psInfo->bHasSpatialIndex = false;
236✔
1580
    // msDebug("msOGRFileOpen(): %s", pszRequest);
1581

1582
    OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
236✔
1583
    if (hLayer) {
236✔
1584
      OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
236✔
1585
      if (hFeature) {
236✔
1586
        psInfo->bHasSpatialIndex = true;
236✔
1587
        OGR_F_Destroy(hFeature);
236✔
1588
      }
1589
      OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
236✔
1590
    }
1591
    msFree(pszRequest);
236✔
1592
    pszRequest = NULL;
1593

1594
    if (!psInfo->bHasSpatialIndex) {
236✔
1595
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
×
1596
        msDebug("msOGRFileOpen(): Layer %s has no spatial index table\n",
×
1597
                pszLayerDef);
1598
      }
1599
    } else {
1600
      pszRequest = msStringConcatenate(
236✔
1601
          pszRequest,
1602
          "SELECT * FROM geometry_columns WHERE f_table_name = lower('");
1603
      pszRequest =
1604
          msStringConcatenate(pszRequest, psInfo->pszSpatialFilterTableName);
236✔
1605
      pszRequest =
1606
          msStringConcatenate(pszRequest, "') AND f_geometry_column = lower('");
236✔
1607
      pszRequest = msStringConcatenate(pszRequest,
236✔
1608
                                       psInfo->pszSpatialFilterGeometryColumn);
236✔
1609
      pszRequest =
1610
          msStringConcatenate(pszRequest, "') AND spatial_index_enabled = 1");
236✔
1611

1612
      psInfo->bHasSpatialIndex = false;
236✔
1613

1614
      OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
236✔
1615
      if (hLayer) {
236✔
1616
        OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
236✔
1617
        if (hFeature) {
236✔
1618
          psInfo->bHasSpatialIndex = true;
236✔
1619
          OGR_F_Destroy(hFeature);
236✔
1620
        }
1621
        OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
236✔
1622
      }
1623
      msFree(pszRequest);
236✔
1624
      pszRequest = NULL;
1625

1626
      if (!psInfo->bHasSpatialIndex) {
236✔
1627
        if (layer->debug >= MS_DEBUGLEVEL_VVV) {
×
1628
          msDebug("msOGRFileOpen(): Layer %s has spatial index disabled\n",
×
1629
                  pszLayerDef);
1630
        }
1631
      } else {
1632
        if (layer->debug >= MS_DEBUGLEVEL_VVV) {
236✔
1633
          msDebug("msOGRFileOpen(): Layer %s has spatial index enabled\n",
×
1634
                  pszLayerDef);
1635
        }
1636

1637
        psInfo->pszTablePrefix = msStrdup(psInfo->pszMainTableName);
236✔
1638
      }
1639
    }
1640
  }
1641

1642
  psInfo->bPaging = (psInfo->dialect != NULL);
245✔
1643
}
245✔
1644

1645
/************************************************************************/
1646
/*                        msOGRCloseConnection()                        */
1647
/*                                                                      */
1648
/*      Callback for thread pool to actually release an OGR             */
1649
/*      connection.                                                     */
1650
/************************************************************************/
1651

1652
static void msOGRCloseConnection(void *conn_handle)
829✔
1653

1654
{
1655
  OGRDataSourceH hDS = (OGRDataSourceH)conn_handle;
1656

1657
  ACQUIRE_OGR_LOCK;
829✔
1658
  OGR_DS_Destroy(hDS);
829✔
1659
  RELEASE_OGR_LOCK;
829✔
1660
}
829✔
1661

1662
/**********************************************************************
1663
 *                     msOGRFileClose()
1664
 **********************************************************************/
1665
static int msOGRFileClose(layerObj *layer, msOGRFileInfo *psInfo) {
872✔
1666
  if (!psInfo)
872✔
1667
    return MS_SUCCESS;
1668

1669
  if (layer->debug)
872✔
1670
    msDebug("msOGRFileClose(%s,%d).\n", psInfo->pszFname, psInfo->nLayerIndex);
5✔
1671

1672
  CPLFree(psInfo->pszFname);
872✔
1673
  CPLFree(psInfo->pszLayerDef);
872✔
1674

1675
  ACQUIRE_OGR_LOCK;
872✔
1676
  if (psInfo->hLastFeature)
872✔
1677
    OGR_F_Destroy(psInfo->hLastFeature);
292✔
1678

1679
  /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
1680
  if (psInfo->nLayerIndex == -1)
872✔
1681
    OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
74✔
1682

1683
  // Release (potentially close) the datasource connection.
1684
  // Make sure we aren't holding the lock when the callback may need it.
1685
  RELEASE_OGR_LOCK;
872✔
1686
  msConnPoolRelease(layer, psInfo->hDS);
872✔
1687

1688
  // Free current tile if there is one.
1689
  if (psInfo->poCurTile != NULL)
872✔
1690
    msOGRFileClose(layer, psInfo->poCurTile);
5✔
1691

1692
  msFreeProjection(&(psInfo->sTileProj));
872✔
1693
  msFree(psInfo->pszSelect);
872✔
1694
  msFree(psInfo->pszSpatialFilterTableName);
872✔
1695
  msFree(psInfo->pszSpatialFilterGeometryColumn);
872✔
1696
  msFree(psInfo->pszMainTableName);
872✔
1697
  msFree(psInfo->pszRowId);
872✔
1698
  msFree(psInfo->pszTablePrefix);
872✔
1699
  msFree(psInfo->pszWHERE);
872✔
1700

1701
  CPLFree(psInfo);
872✔
1702

1703
  return MS_SUCCESS;
872✔
1704
}
1705

1706
/************************************************************************/
1707
/*                           msOGREscapeSQLParam                        */
1708
/************************************************************************/
1709
static char *msOGREscapeSQLParam(layerObj *layer, const char *pszString) {
49✔
1710
  char *pszEscapedStr = NULL;
1711
  if (layer && pszString) {
49✔
1712
    char *pszEscapedOGRStr =
1713
        CPLEscapeString(pszString, strlen(pszString), CPLES_SQL);
49✔
1714
    pszEscapedStr = msStrdup(pszEscapedOGRStr);
49✔
1715
    CPLFree(pszEscapedOGRStr);
49✔
1716
  }
1717
  return pszEscapedStr;
49✔
1718
}
1719

1720
// http://www.sqlite.org/lang_expr.html
1721
// http://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html
1722

1723
static char *msOGRGetQuotedItem(layerObj *layer, const char *pszItem) {
383✔
1724
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
383✔
1725
  char *ret = NULL;
1726
  char *escapedItem = msLayerEscapePropertyName(layer, pszItem);
383✔
1727
  if (psInfo->pszTablePrefix) {
383✔
1728
    char *escapedTable =
1729
        msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
281✔
1730
    ret = msStringConcatenate(ret, "\"");
281✔
1731
    ret = msStringConcatenate(ret, escapedTable);
281✔
1732
    ret = msStringConcatenate(ret, "\".\"");
281✔
1733
    ret = msStringConcatenate(ret, escapedItem);
281✔
1734
    ret = msStringConcatenate(ret, "\"");
281✔
1735
    msFree(escapedTable);
281✔
1736
  } else {
1737
    ret = msStringConcatenate(ret, "\"");
102✔
1738
    ret = msStringConcatenate(ret, escapedItem);
102✔
1739
    ret = msStringConcatenate(ret, "\"");
102✔
1740
  }
1741
  msFree(escapedItem);
383✔
1742
  return ret;
383✔
1743
}
1744

1745
static char *msOGRGetToken(layerObj *layer, tokenListNodeObjPtr *node) {
257✔
1746
  msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
257✔
1747
  tokenListNodeObjPtr n = *node;
257✔
1748
  if (!n)
257✔
1749
    return NULL;
1750
  char *out = NULL;
1751
  size_t nOutSize;
1752

1753
  switch (n->token) {
257✔
1754
  case MS_TOKEN_LOGICAL_AND:
6✔
1755
    out = msStrdup(" AND ");
6✔
1756
    break;
6✔
1757
  case MS_TOKEN_LOGICAL_OR:
1✔
1758
    out = msStrdup(" OR ");
1✔
1759
    break;
1✔
1760
  case MS_TOKEN_LOGICAL_NOT:
1✔
1761
    out = msStrdup(" NOT ");
1✔
1762
    break;
1✔
1763
  case MS_TOKEN_LITERAL_NUMBER:
9✔
1764
    nOutSize = 32;
1765
    out = (char *)msSmallMalloc(nOutSize);
9✔
1766
    snprintf(out, nOutSize, "%.18g", n->tokenval.dblval);
9✔
1767
    break;
1768
  case MS_TOKEN_LITERAL_STRING: {
17✔
1769
    char *stresc = msOGREscapeSQLParam(layer, n->tokenval.strval);
17✔
1770
    nOutSize = strlen(stresc) + 3;
17✔
1771
    out = (char *)msSmallMalloc(nOutSize);
17✔
1772
    snprintf(out, nOutSize, "'%s'", stresc);
1773
    msFree(stresc);
17✔
1774
    break;
17✔
1775
  }
1776
  case MS_TOKEN_LITERAL_TIME:
×
1777
    // seems to require METADATA gml_types => auto
1778
    nOutSize = 80;
1779
    out = (char *)msSmallMalloc(nOutSize);
×
1780
#if 0
1781
        // FIXME? or perhaps just remove me. tm_zone is not supported on Windows, and not used anywhere else in the code base
1782
        if (n->tokenval.tmval.tm_zone)
1783
            snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d%s'",
1784
                     n->tokenval.tmval.tm_year+1900, n->tokenval.tmval.tm_mon+1, n->tokenval.tmval.tm_mday,
1785
                     n->tokenval.tmval.tm_hour, n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec,
1786
                     n->tokenval.tmval.tm_zone);
1787
        else
1788
#endif
1789
    snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d'",
×
1790
             n->tokenval.tmval.tm_year + 1900, n->tokenval.tmval.tm_mon + 1,
×
1791
             n->tokenval.tmval.tm_mday, n->tokenval.tmval.tm_hour,
1792
             n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec);
1793
    break;
1794
  case MS_TOKEN_LITERAL_SHAPE: {
17✔
1795
    // assumed to be in right srs after FLTGetSpatialComparisonCommonExpression
1796
    char *wkt = msShapeToWKT(n->tokenval.shpval);
17✔
1797
    char *stresc = msOGRGetQuotedItem(
17✔
1798
        layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
1799
    nOutSize = strlen(wkt) + strlen(stresc) + 35;
17✔
1800
    out = (char *)msSmallMalloc(nOutSize);
17✔
1801
    snprintf(out, nOutSize, "ST_GeomFromText('%s',ST_SRID(%s))", wkt, stresc);
1802
    msFree(wkt);
17✔
1803
    msFree(stresc);
17✔
1804
    break;
17✔
1805
  }
1806
  case MS_TOKEN_LITERAL_BOOLEAN:
17✔
1807
    out = msStrdup(n->tokenval.dblval == 0 ? "FALSE" : "TRUE");
34✔
1808
    break;
17✔
1809
  case MS_TOKEN_COMPARISON_EQ:
28✔
1810
    if (n->next != NULL && n->next->token == MS_TOKEN_LITERAL_STRING &&
28✔
1811
        strcmp(n->next->tokenval.strval, "_MAPSERVER_NULL_") == 0) {
10✔
1812
      out = msStrdup(" IS NULL");
2✔
1813
      n = n->next;
2✔
1814
      break;
2✔
1815
    }
1816

1817
    out = msStrdup(" = ");
26✔
1818
    break;
26✔
1819
  case MS_TOKEN_COMPARISON_NE:
3✔
1820
    out = msStrdup(" != ");
3✔
1821
    break;
3✔
1822
  case MS_TOKEN_COMPARISON_GT:
2✔
1823
    out = msStrdup(" > ");
2✔
1824
    break;
2✔
1825
  case MS_TOKEN_COMPARISON_LT:
2✔
1826
    out = msStrdup(" < ");
2✔
1827
    break;
2✔
1828
  case MS_TOKEN_COMPARISON_LE:
3✔
1829
    out = msStrdup(" <= ");
3✔
1830
    break;
3✔
1831
  case MS_TOKEN_COMPARISON_GE:
3✔
1832
    out = msStrdup(" >= ");
3✔
1833
    break;
3✔
1834
  case MS_TOKEN_COMPARISON_IEQ:
×
1835
    out = msStrdup(" = ");
×
1836
    break;
×
1837
  case MS_TOKEN_COMPARISON_IN:
×
1838
    out = msStrdup(" IN ");
×
1839
    break;
×
1840
  // the origin may be mapfile (complex regexes, layer->map.query.filter.string
1841
  // == NULL, regex may have //) or OGC Filter (simple patterns only,
1842
  // layer->map.query.filter.string != NULL)
1843
  case MS_TOKEN_COMPARISON_RE:
6✔
1844
  case MS_TOKEN_COMPARISON_IRE: {
1845
    int case_sensitive = n->token == MS_TOKEN_COMPARISON_RE;
1846
    // in PostgreSQL and OGR: LIKE (case sensitive) and ILIKE (case insensitive)
1847
    // in SQLite: LIKE (case insensitive) and GLOB (case sensitive)
1848
    const char *op = case_sensitive ? "LIKE" : "ILIKE";
6✔
1849
    char wild_any = '%';
1850
    char wild_one = '_';
1851

1852
    if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG")) {
6✔
1853
      if (case_sensitive) {
6✔
1854
        op = "GLOB";
1855
        wild_any = '*';
1856
        wild_one = '?';
1857
      } else {
1858
        op = "LIKE";
1859
      }
1860
    }
1861

1862
    n = n->next;
6✔
1863
    if (n->token != MS_TOKEN_LITERAL_STRING)
6✔
1864
      return NULL;
1865

1866
    char *regex = msStrdup(n->tokenval.strval);
6✔
1867
    int complex_regex = *n->tokenval.strval ==
6✔
1868
                        '/'; // could be non-complex but that is soo corner case
1869

1870
    // PostgreSQL has POSIX regexes, SQLite does not by default, OGR does not
1871
    if (complex_regex) {
6✔
1872
      if (!EQUAL(info->dialect, "PostgreSQL")) {
×
1873
        msFree(regex);
×
1874
        return NULL;
×
1875
      }
1876
      // remove //
1877
      regex++;
×
1878
      regex[strlen(regex) - 1] = '\0';
×
1879
      if (case_sensitive)
×
1880
        op = "~";
1881
      else
1882
        op = "~*";
1883
    }
1884

1885
    const size_t regex_len = strlen(regex);
6✔
1886
    char *re = (char *)msSmallMalloc(2 * regex_len + 3);
6✔
1887
    size_t i = 0, j = 0;
1888
    re[j++] = '\'';
6✔
1889
    while (i < regex_len) {
55✔
1890
      char c = regex[i];
55✔
1891
      char c_next = regex[i + 1];
55✔
1892

1893
      if (c == '.' && c_next == '*') {
55✔
1894
        i++;
1895
        c = wild_any;
1896
      } else if (c == '.')
49✔
1897
        c = wild_one;
1898
      else if (i == 0 && c == '^') {
48✔
1899
        i++;
1900
        continue;
6✔
1901
      } else if (c == '$' && c_next == 0) {
42✔
1902
        break;
1903
      } else if (c == '\'') {
36✔
1904
        re[j++] = '\'';
1✔
1905
      } else if (c == '\\') {
35✔
1906
        if (c_next == 0) {
3✔
1907
          break;
1908
        }
1909
        i++;
1910
        c = c_next;
1911
      }
1912

1913
      re[j++] = c;
43✔
1914
      i++;
43✔
1915
    }
1916
    re[j++] = '\'';
6✔
1917
    re[j] = '\0';
6✔
1918

1919
    nOutSize = 1 + strlen(op) + 1 + strlen(re) + 1;
6✔
1920
    out = (char *)msSmallMalloc(nOutSize);
6✔
1921
    snprintf(out, nOutSize, " %s %s", op, re);
1922
    msFree(re);
6✔
1923
    msFree(regex);
6✔
1924
    break;
6✔
1925
  }
1926
  case MS_TOKEN_COMPARISON_INTERSECTS:
8✔
1927
    out = msStrdup("ST_Intersects");
8✔
1928
    break;
8✔
1929
  case MS_TOKEN_COMPARISON_DISJOINT:
1✔
1930
    out = msStrdup("ST_Disjoint");
1✔
1931
    break;
1✔
1932
  case MS_TOKEN_COMPARISON_TOUCHES:
1✔
1933
    out = msStrdup("ST_Touches");
1✔
1934
    break;
1✔
1935
  case MS_TOKEN_COMPARISON_OVERLAPS:
1✔
1936
    out = msStrdup("ST_Overlaps");
1✔
1937
    break;
1✔
1938
  case MS_TOKEN_COMPARISON_CROSSES:
1✔
1939
    out = msStrdup("ST_Crosses");
1✔
1940
    break;
1✔
1941
  case MS_TOKEN_COMPARISON_WITHIN:
1✔
1942
    out = msStrdup("ST_Within");
1✔
1943
    break;
1✔
1944
  case MS_TOKEN_COMPARISON_DWITHIN:
1✔
1945
    out = msStrdup("ST_Distance");
1✔
1946
    break;
1✔
1947
  case MS_TOKEN_COMPARISON_BEYOND:
1✔
1948
    out = msStrdup("ST_Distance");
1✔
1949
    break;
1✔
1950
  case MS_TOKEN_COMPARISON_CONTAINS:
1✔
1951
    out = msStrdup("ST_Contains");
1✔
1952
    break;
1✔
1953
  case MS_TOKEN_COMPARISON_EQUALS:
1✔
1954
    out = msStrdup("ST_Equals");
1✔
1955
    break;
1✔
1956
  case MS_TOKEN_FUNCTION_LENGTH:
×
1957
    out = msStrdup("ST_Length");
×
1958
    break;
×
1959
  case MS_TOKEN_FUNCTION_AREA:
×
1960
    out = msStrdup("ST_Area");
×
1961
    break;
×
1962
  case MS_TOKEN_BINDING_DOUBLE: {
7✔
1963
    char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
7✔
1964
    nOutSize = strlen(stresc) + +30;
7✔
1965
    out = (char *)msSmallMalloc(nOutSize);
7✔
1966

1967
    bool bIsNumeric = msLayerPropertyIsNumeric(layer, n->tokenval.bindval.item);
7✔
1968
    // Do not cast if the variable is of the appropriate type as it can
1969
    // prevent using database indexes, such as for SQlite
1970
    if (bIsNumeric) {
7✔
1971
      snprintf(out, nOutSize, "%s", stresc);
1972
    } else {
1973
      const char *SQLtype = "float(16)";
1974
      if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG"))
×
1975
        SQLtype = "REAL";
1976
      else if (EQUAL(info->dialect, "PostgreSQL"))
×
1977
        SQLtype = "double precision";
1978
      snprintf(out, nOutSize, "CAST(%s AS %s)", stresc, SQLtype);
1979
    }
1980
    msFree(stresc);
7✔
1981
    break;
7✔
1982
  }
1983
  case MS_TOKEN_BINDING_INTEGER: {
×
1984
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
×
1985
    nOutSize = strlen(stresc) + 20;
×
1986
    out = (char *)msSmallMalloc(nOutSize);
×
1987

1988
    bool bIsNumeric = msLayerPropertyIsNumeric(layer, n->tokenval.bindval.item);
×
1989
    // Do not cast if the variable is of the appropriate type as it can
1990
    // prevent using database indexes, such as for SQlite
1991
    if (bIsNumeric) {
×
1992
      snprintf(out, nOutSize, "\"%s\"", stresc);
1993
    } else {
1994
      snprintf(out, nOutSize, "CAST(\"%s\" AS integer)", stresc);
1995
    }
1996
    msFree(stresc);
×
1997
    break;
×
1998
  }
1999
  case MS_TOKEN_BINDING_STRING: {
25✔
2000
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
25✔
2001
    nOutSize = strlen(stresc) + 30;
25✔
2002
    out = (char *)msSmallMalloc(nOutSize);
25✔
2003

2004
    bool bIsCharacter =
2005
        msLayerPropertyIsCharacter(layer, n->tokenval.bindval.item);
25✔
2006
    // Do not cast if the variable is of the appropriate type as it can
2007
    // prevent using database indexes, such as for SQlite
2008
    if (bIsCharacter) {
25✔
2009
      snprintf(out, nOutSize, "\"%s\"", stresc);
2010
    } else {
2011
      snprintf(out, nOutSize, "CAST(\"%s\" AS text)", stresc);
2012
    }
2013
    msFree(stresc);
25✔
2014
    break;
25✔
2015
  }
2016
  case MS_TOKEN_BINDING_TIME: {
×
2017
    // won't get here unless col is parsed as time and they are not
2018
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
×
2019
    nOutSize = strlen(stresc) + 10;
×
2020
    out = (char *)msSmallMalloc(nOutSize);
×
2021
    snprintf(out, nOutSize, "\"%s\"", stresc);
2022
    msFree(stresc);
×
2023
    break;
×
2024
  }
2025
  case MS_TOKEN_BINDING_SHAPE: {
17✔
2026
    char *stresc = msLayerEscapePropertyName(
17✔
2027
        layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
2028
    nOutSize = strlen(stresc) + 10;
17✔
2029
    out = (char *)msSmallMalloc(nOutSize);
17✔
2030
    snprintf(out, nOutSize, "%s", stresc);
2031
    msFree(stresc);
17✔
2032
    break;
17✔
2033
  }
2034

2035
    // unhandled below until default
2036

2037
  case MS_TOKEN_FUNCTION_TOSTRING:
2038
  case MS_TOKEN_FUNCTION_COMMIFY:
2039
  case MS_TOKEN_FUNCTION_ROUND:
2040
  case MS_TOKEN_FUNCTION_FROMTEXT:
2041
  case MS_TOKEN_FUNCTION_BUFFER:
2042
  case MS_TOKEN_FUNCTION_DIFFERENCE:
2043
  case MS_TOKEN_FUNCTION_SIMPLIFY:
2044
  case MS_TOKEN_FUNCTION_SIMPLIFYPT:
2045
  case MS_TOKEN_FUNCTION_GENERALIZE:
2046
  case MS_TOKEN_FUNCTION_SMOOTHSIA:
2047
  case MS_TOKEN_FUNCTION_JAVASCRIPT:
2048
  case MS_TOKEN_FUNCTION_UPPER:
2049
  case MS_TOKEN_FUNCTION_LOWER:
2050
  case MS_TOKEN_FUNCTION_INITCAP:
2051
  case MS_TOKEN_FUNCTION_FIRSTCAP:
2052
  case MS_TOKEN_BINDING_MAP_CELLSIZE:
2053
  case MS_TOKEN_BINDING_DATA_CELLSIZE:
2054
  case MS_PARSE_TYPE_BOOLEAN:
2055
  case MS_PARSE_TYPE_STRING:
2056
  case MS_PARSE_TYPE_SHAPE:
2057
    break;
2058

2059
  default:
76✔
2060
    if (n->token < 128) {
76✔
2061
      char c = n->token;
76✔
2062
      const size_t nSize = 2;
2063
      out = (char *)msSmallMalloc(nSize);
76✔
2064
      snprintf(out, nSize, "%c", c);
76✔
2065
    }
2066
    break;
2067
  }
2068

2069
  n = n->next;
257✔
2070
  *node = n;
257✔
2071
  return out;
257✔
2072
}
2073

2074
/*
2075
 * msOGRLayerBuildSQLOrderBy()
2076
 *
2077
 * Returns the content of a SQL ORDER BY clause from the sortBy member of
2078
 * the layer. The string does not contain the "ORDER BY" keywords itself.
2079
 */
2080
static char *msOGRLayerBuildSQLOrderBy(layerObj *layer, msOGRFileInfo *psInfo) {
7✔
2081
  char *strOrderBy = NULL;
2082
  if (layer->sortBy.nProperties > 0) {
7✔
2083
    int i;
2084
    for (i = 0; i < layer->sortBy.nProperties; i++) {
19✔
2085
      if (i > 0)
12✔
2086
        strOrderBy = msStringConcatenate(strOrderBy, ", ");
5✔
2087
      char *escapedItem =
2088
          msLayerEscapePropertyName(layer, layer->sortBy.properties[i].item);
12✔
2089
      if (psInfo->pszTablePrefix) {
12✔
2090
        char *escapedTable =
2091
            msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
1✔
2092
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
1✔
2093
        strOrderBy = msStringConcatenate(strOrderBy, escapedTable);
1✔
2094
        strOrderBy = msStringConcatenate(strOrderBy, "\".\"");
1✔
2095
        strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
1✔
2096
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
1✔
2097
        msFree(escapedTable);
1✔
2098
      } else {
2099
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
11✔
2100
        strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
11✔
2101
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
11✔
2102
      }
2103
      msFree(escapedItem);
12✔
2104
      if (layer->sortBy.properties[i].sortOrder == SORT_DESC)
12✔
2105
        strOrderBy = msStringConcatenate(strOrderBy, " DESC");
6✔
2106
    }
2107
  }
2108
  return strOrderBy;
7✔
2109
}
2110

2111
/**********************************************************************
2112
 *                     msOGRFileWhichShapes()
2113
 *
2114
 * Init OGR layer structs ready for calls to msOGRFileNextShape().
2115
 *
2116
 * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
2117
 * layer's FILTER overlaps the selected region.
2118
 **********************************************************************/
2119
static int msOGRFileWhichShapes(layerObj *layer, rectObj rect,
305✔
2120
                                msOGRFileInfo *psInfo) {
2121
  // rect is from BBOX parameter in query (In lieu of a FEATUREID or FILTER) or
2122
  // mapfile somehow
2123
  if (psInfo == NULL || psInfo->hLayer == NULL) {
305✔
2124
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2125
               "msOGRFileWhichShapes()");
2126
    return (MS_FAILURE);
×
2127
  }
2128

2129
  char *select = (psInfo->pszSelect) ? msStrdup(psInfo->pszSelect) : NULL;
305✔
2130
  const rectObj rectInvalid = MS_INIT_INVALID_RECT;
305✔
2131
  bool bIsValidRect = memcmp(&rect, &rectInvalid, sizeof(rect)) != 0;
305✔
2132

2133
  // we'll go strictly two possible ways:
2134
  // 1) GetLayer + SetFilter
2135
  // 2) ExecuteSQL (psInfo->hLayer is an SQL result OR sortBy was requested OR
2136
  // have native_string and start from the second
2137

2138
  if (psInfo->bIsOKForSQLCompose &&
305✔
2139
      (psInfo->nLayerIndex == -1 || layer->sortBy.nProperties > 0 ||
303✔
2140
       layer->filter.native_string ||
285✔
2141
       (psInfo->bPaging && layer->maxfeatures > 0))) {
246✔
2142

2143
    const bool bHasGeometry = OGR_L_GetGeomType(psInfo->hLayer) != wkbNone;
64✔
2144

2145
    if (psInfo->nLayerIndex == -1 && select == NULL) {
64✔
2146
      select = msStrdup(psInfo->pszLayerDef);
8✔
2147
      /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
2148
      OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
8✔
2149
      psInfo->hLayer = NULL;
8✔
2150
    } else if (select == NULL) {
53✔
2151
      const char *pszGeometryColumn;
2152
      int i;
2153
      select = msStringConcatenate(select, "SELECT ");
53✔
2154
      for (i = 0; i < layer->numitems; i++) {
365✔
2155
        if (i > 0)
312✔
2156
          select = msStringConcatenate(select, ", ");
259✔
2157
        char *escaped = msOGRGetQuotedItem(layer, layer->items[i]);
312✔
2158
        select = msStringConcatenate(select, escaped);
312✔
2159
        msFree(escaped);
312✔
2160
        if (psInfo->pszTablePrefix) {
312✔
2161
          select = msStringConcatenate(select, " AS \"");
210✔
2162
          escaped = msLayerEscapePropertyName(layer, layer->items[i]);
210✔
2163
          select = msStringConcatenate(select, escaped);
210✔
2164
          msFree(escaped);
210✔
2165
          select = msStringConcatenate(select, "\"");
210✔
2166
        }
2167
      }
2168
      if (layer->numitems > 0)
53✔
2169
        select = msStringConcatenate(select, ", ");
53✔
2170
      pszGeometryColumn = OGR_L_GetGeometryColumn(psInfo->hLayer);
53✔
2171
      if (pszGeometryColumn != NULL && pszGeometryColumn[0] != '\0') {
53✔
2172
        char *escaped = msOGRGetQuotedItem(layer, pszGeometryColumn);
47✔
2173
        select = msStringConcatenate(select, escaped);
47✔
2174
        msFree(escaped);
47✔
2175
        if (psInfo->pszTablePrefix) {
47✔
2176
          select = msStringConcatenate(select, " AS \"");
47✔
2177
          escaped = msLayerEscapePropertyName(layer, pszGeometryColumn);
47✔
2178
          select = msStringConcatenate(select, escaped);
47✔
2179
          msFree(escaped);
47✔
2180
          select = msStringConcatenate(select, "\"");
47✔
2181
        }
2182
      } else {
2183
        /* Add ", *" so that we still have an hope to get the geometry */
2184
        if (psInfo->pszTablePrefix) {
6✔
2185
          select = msStringConcatenate(select, "\"");
×
2186
          char *escaped =
2187
              msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
×
2188
          select = msStringConcatenate(select, escaped);
×
2189
          msFree(escaped);
×
2190
          select = msStringConcatenate(select, "\".");
×
2191
        }
2192
        select = msStringConcatenate(select, "*");
6✔
2193
      }
2194
      select = msStringConcatenate(select, " FROM ");
53✔
2195
      if (psInfo->nLayerIndex == -1) {
53✔
2196
        select = msStringConcatenate(select, "(");
×
2197
        select = msStringConcatenate(select, psInfo->pszLayerDef);
×
2198
        select = msStringConcatenate(select, ") MSSUBSELECT");
×
2199
      } else {
2200
        select = msStringConcatenate(select, "\"");
53✔
2201
        char *escaped = msLayerEscapePropertyName(
53✔
2202
            layer, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
2203
        select = msStringConcatenate(select, escaped);
53✔
2204
        msFree(escaped);
53✔
2205
        select = msStringConcatenate(select, "\"");
53✔
2206
      }
2207
    }
2208

2209
    char *filter = NULL;
2210
    if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
64✔
2211
      filter = msStringConcatenate(filter, "(");
×
2212
      filter = msStringConcatenate(
×
2213
          filter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2214
      filter = msStringConcatenate(filter, ")");
×
2215
    }
2216

2217
    /* ------------------------------------------------------------------
2218
     * Set Spatial filter... this may result in no features being returned
2219
     * if layer does not overlap current view.
2220
     *
2221
     * __TODO__ We should return MS_DONE if no shape overlaps the selected
2222
     * region and matches the layer's FILTER expression, but there is currently
2223
     * no _efficient_ way to do that with OGR.
2224
     * ------------------------------------------------------------------ */
2225
    if (psInfo->rect_is_defined) {
64✔
2226
      rect.minx = MAX(psInfo->rect.minx, rect.minx);
12✔
2227
      rect.miny = MAX(psInfo->rect.miny, rect.miny);
12✔
2228
      rect.maxx = MIN(psInfo->rect.maxx, rect.maxx);
12✔
2229
      rect.maxy = MIN(psInfo->rect.maxy, rect.maxy);
17✔
2230
      bIsValidRect = true;
2231
    }
2232
    psInfo->rect = rect;
64✔
2233

2234
    bool bSpatialiteOrGPKGAddOrderByFID = false;
2235

2236
    const char *sql = layer->filter.native_string;
64✔
2237
    if (psInfo->dialect && sql && *sql != '\0' &&
64✔
2238
        (EQUAL(psInfo->dialect, "Spatialite") ||
42✔
2239
         EQUAL(psInfo->dialect, "GPKG") ||
2✔
2240
         EQUAL(psInfo->dialect, "PostgreSQL"))) {
×
2241
      if (filter)
42✔
2242
        filter = msStringConcatenate(filter, " AND ");
×
2243
      filter = msStringConcatenate(filter, "(");
42✔
2244
      filter = msStringConcatenate(filter, sql);
42✔
2245
      filter = msStringConcatenate(filter, ")");
42✔
2246
    } else if (psInfo->pszWHERE) {
22✔
2247
      if (filter)
×
2248
        filter = msStringConcatenate(filter, " AND ");
×
2249
      filter = msStringConcatenate(filter, "(");
×
2250
      filter = msStringConcatenate(filter, psInfo->pszWHERE);
×
2251
      filter = msStringConcatenate(filter, ")");
×
2252
    }
2253

2254
    // use spatial index
2255
    if (psInfo->dialect && bIsValidRect) {
64✔
2256
      if (EQUAL(psInfo->dialect, "PostgreSQL")) {
46✔
2257
        if (filter)
×
2258
          filter = msStringConcatenate(filter, " AND");
×
2259
        const char *col =
2260
            OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
×
2261
        filter = msStringConcatenate(filter, " (\"");
×
2262
        char *escaped = msLayerEscapePropertyName(layer, col);
×
2263
        filter = msStringConcatenate(filter, escaped);
×
2264
        msFree(escaped);
×
2265
        filter = msStringConcatenate(filter, "\" && ST_MakeEnvelope(");
×
2266
        char *points = (char *)msSmallMalloc(30 * 2 * 5);
×
2267
        snprintf(points, 30 * 4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny,
×
2268
                 rect.maxx, rect.maxy);
2269
        filter = msStringConcatenate(filter, points);
×
2270
        msFree(points);
×
2271
        filter = msStringConcatenate(filter, "))");
×
2272
      } else if (psInfo->dialect &&
46✔
2273
                 (EQUAL(psInfo->dialect, "Spatialite") ||
46✔
2274
                  EQUAL(psInfo->dialect, "GPKG")) &&
5✔
2275
                 psInfo->pszMainTableName != NULL) {
46✔
2276
        if ((EQUAL(psInfo->dialect, "Spatialite") &&
46✔
2277
             psInfo->bHasSpatialIndex) ||
41✔
2278
            EQUAL(psInfo->dialect, "GPKG")) {
5✔
2279
          if (filter)
46✔
2280
            filter = msStringConcatenate(filter, " AND ");
40✔
2281
          char *pszEscapedMainTableName =
2282
              msLayerEscapePropertyName(layer, psInfo->pszMainTableName);
46✔
2283
          filter = msStringConcatenate(filter, "\"");
46✔
2284
          filter = msStringConcatenate(filter, pszEscapedMainTableName);
46✔
2285
          msFree(pszEscapedMainTableName);
46✔
2286
          filter = msStringConcatenate(filter, "\".");
46✔
2287
          if (psInfo->pszRowId) {
46✔
2288
            char *pszEscapedRowId =
2289
                msLayerEscapePropertyName(layer, psInfo->pszRowId);
2✔
2290
            filter = msStringConcatenate(filter, "\"");
2✔
2291
            filter = msStringConcatenate(filter, pszEscapedRowId);
2✔
2292
            filter = msStringConcatenate(filter, "\"");
2✔
2293
            msFree(pszEscapedRowId);
2✔
2294
          } else
2295
            filter = msStringConcatenate(filter, "ROWID");
44✔
2296

2297
          filter = msStringConcatenate(filter, " IN ");
46✔
2298
          filter = msStringConcatenate(filter, "(");
46✔
2299
          filter = msStringConcatenate(filter, "SELECT ");
46✔
2300

2301
          if (EQUAL(psInfo->dialect, "Spatialite"))
46✔
2302
            filter = msStringConcatenate(filter, "ms_spat_idx.pkid");
41✔
2303
          else
2304
            filter = msStringConcatenate(filter, "ms_spat_idx.id");
5✔
2305

2306
          filter = msStringConcatenate(filter, " FROM ");
46✔
2307

2308
          char szSpatialIndexName[256];
2309
          snprintf(szSpatialIndexName, sizeof(szSpatialIndexName), "%s_%s_%s",
46✔
2310
                   EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree",
46✔
2311
                   psInfo->pszSpatialFilterTableName,
2312
                   psInfo->pszSpatialFilterGeometryColumn);
2313
          char *pszEscapedSpatialIndexName =
2314
              msLayerEscapePropertyName(layer, szSpatialIndexName);
46✔
2315

2316
          filter = msStringConcatenate(filter, "\"");
46✔
2317
          filter = msStringConcatenate(filter, pszEscapedSpatialIndexName);
46✔
2318
          msFree(pszEscapedSpatialIndexName);
46✔
2319

2320
          filter = msStringConcatenate(filter, "\" ms_spat_idx WHERE ");
46✔
2321

2322
          char szCond[256];
2323
          if (EQUAL(psInfo->dialect, "Spatialite")) {
46✔
2324
            snprintf(
41✔
2325
                szCond, sizeof(szCond),
2326
                "ms_spat_idx.xmin <= %.15g AND ms_spat_idx.xmax >= %.15g AND "
2327
                "ms_spat_idx.ymin <= %.15g AND ms_spat_idx.ymax >= %.15g",
2328
                rect.maxx, rect.minx, rect.maxy, rect.miny);
2329
          } else {
2330
            snprintf(
5✔
2331
                szCond, sizeof(szCond),
2332
                "ms_spat_idx.minx <= %.15g AND ms_spat_idx.maxx >= %.15g AND "
2333
                "ms_spat_idx.miny <= %.15g AND ms_spat_idx.maxy >= %.15g",
2334
                rect.maxx, rect.minx, rect.maxy, rect.miny);
2335
          }
2336
          filter = msStringConcatenate(filter, szCond);
46✔
2337

2338
          filter = msStringConcatenate(filter, ")");
46✔
2339

2340
          bSpatialiteOrGPKGAddOrderByFID = true;
2341
        }
2342

2343
        const bool isGPKG = EQUAL(psInfo->dialect, "GPKG");
46✔
2344
        if (filter)
46✔
2345
          filter = msStringConcatenate(filter, " AND");
46✔
2346
        const char *col =
2347
            OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
46✔
2348
        filter = msStringConcatenate(filter, " Intersects(");
46✔
2349
        if (isGPKG) {
46✔
2350
          // Casting GeoPackage geometries to spatialie ones is done
2351
          // automatically normally, since GDAL enables the
2352
          // "amphibious" mode, but without it
2353
          // explicitly specified, spatialite 4.3.0a does an
2354
          // out-of-bounds access.
2355
          filter = msStringConcatenate(filter, "GeomFromGPB(");
5✔
2356
        }
2357
        filter = msStringConcatenate(filter, "\"");
46✔
2358
        char *escaped = msLayerEscapePropertyName(layer, col);
46✔
2359
        filter = msStringConcatenate(filter, escaped);
46✔
2360
        msFree(escaped);
46✔
2361
        filter = msStringConcatenate(filter, "\"");
46✔
2362
        if (isGPKG)
46✔
2363
          filter = msStringConcatenate(filter, ")");
5✔
2364
        char *points = (char *)msSmallMalloc(30 * 2 * 5);
46✔
2365
        if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
46✔
2366
          filter = msStringConcatenate(filter, ",  ST_GeomFromText(");
1✔
2367
          snprintf(points, 30 * 4, "'POINT(%lf %lf)'", rect.minx, rect.miny);
2368
        } else {
2369
          filter = msStringConcatenate(filter, ", BuildMbr(");
45✔
2370
          snprintf(points, 30 * 4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny,
45✔
2371
                   rect.maxx, rect.maxy);
2372
        }
2373
        filter = msStringConcatenate(filter, points);
46✔
2374
        msFree(points);
46✔
2375
        filter = msStringConcatenate(filter, "))");
46✔
2376
      }
2377
    }
2378

2379
    /* get sortBy */
2380
    char *sort = NULL;
2381
    if (layer->sortBy.nProperties > 0) {
64✔
2382

2383
      char *strOrderBy = msOGRLayerBuildSQLOrderBy(layer, psInfo);
7✔
2384
      if (strOrderBy) {
7✔
2385
        if (psInfo->nLayerIndex == -1) {
7✔
2386
          if (strcasestr(psInfo->pszLayerDef, " ORDER BY ") == NULL)
×
2387
            sort = msStringConcatenate(sort, " ORDER BY ");
×
2388
          else
2389
            sort = msStringConcatenate(sort, ", ");
×
2390
        } else {
2391
          sort = msStringConcatenate(sort, " ORDER BY ");
7✔
2392
        }
2393
        sort = msStringConcatenate(sort, strOrderBy);
7✔
2394
        msFree(strOrderBy);
7✔
2395
      }
2396
    }
2397

2398
    if (bSpatialiteOrGPKGAddOrderByFID) {
64✔
2399
      if (sort == NULL)
46✔
2400
        sort = msStringConcatenate(NULL, " ORDER BY ");
45✔
2401
      else
2402
        sort = msStringConcatenate(sort, ", ");
1✔
2403
      char *pszEscapedMainTableName =
2404
          msLayerEscapePropertyName(layer, psInfo->pszMainTableName);
46✔
2405
      sort = msStringConcatenate(sort, "\"");
46✔
2406
      sort = msStringConcatenate(sort, pszEscapedMainTableName);
46✔
2407
      sort = msStringConcatenate(sort, "\".");
46✔
2408
      msFree(pszEscapedMainTableName);
46✔
2409
      if (psInfo->pszRowId) {
46✔
2410
        char *pszEscapedRowId =
2411
            msLayerEscapePropertyName(layer, psInfo->pszRowId);
2✔
2412
        sort = msStringConcatenate(sort, "\"");
2✔
2413
        sort = msStringConcatenate(sort, pszEscapedRowId);
2✔
2414
        sort = msStringConcatenate(sort, "\"");
2✔
2415
        msFree(pszEscapedRowId);
2✔
2416
      } else
2417
        sort = msStringConcatenate(sort, "ROWID");
44✔
2418
    }
2419

2420
    // compose SQL
2421
    if (filter) {
64✔
2422
      select = msStringConcatenate(select, " WHERE ");
48✔
2423
      select = msStringConcatenate(select, filter);
48✔
2424
      msFree(filter);
48✔
2425
    }
2426
    if (sort) {
64✔
2427
      select = msStringConcatenate(select, " ");
52✔
2428
      select = msStringConcatenate(select, sort);
52✔
2429
      msFree(sort);
52✔
2430
    }
2431

2432
    if (psInfo->bPaging && layer->maxfeatures >= 0) {
64✔
2433
      char szLimit[50];
2434
      snprintf(szLimit, sizeof(szLimit), " LIMIT %d", layer->maxfeatures);
2435
      select = msStringConcatenate(select, szLimit);
8✔
2436
    }
2437

2438
    if (psInfo->bPaging && layer->startindex > 0) {
64✔
2439
      char szOffset[50];
2440
      snprintf(szOffset, sizeof(szOffset), " OFFSET %d", layer->startindex - 1);
3✔
2441
      select = msStringConcatenate(select, szOffset);
3✔
2442
    }
2443

2444
    if (layer->debug)
64✔
2445
      msDebug("msOGRFileWhichShapes: SQL = %s.\n", select);
×
2446

2447
    ACQUIRE_OGR_LOCK;
64✔
2448
    if (psInfo->nLayerIndex == -1 && psInfo->hLayer != NULL) {
64✔
2449
      OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
3✔
2450
    }
2451

2452
    OGRGeometryH hGeom = NULL;
2453
    if (psInfo->dialect == NULL && bHasGeometry && bIsValidRect) {
64✔
2454
      if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
14✔
2455
        hGeom = OGR_G_CreateGeometry(wkbPoint);
×
2456
        OGR_G_SetPoint_2D(hGeom, 0, rect.minx, rect.miny);
×
2457
      } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
14✔
2458
        hGeom = OGR_G_CreateGeometry(wkbLineString);
×
2459
        OGR_G_AddPoint_2D(hGeom, rect.minx, rect.miny);
×
2460
        OGR_G_AddPoint_2D(hGeom, rect.maxx, rect.maxy);
×
2461
      } else {
2462
        hGeom = OGR_G_CreateGeometry(wkbPolygon);
14✔
2463
        OGRGeometryH hRing = OGR_G_CreateGeometry(wkbLinearRing);
14✔
2464

2465
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
14✔
2466
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.miny);
14✔
2467
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.maxy);
14✔
2468
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.maxy);
14✔
2469
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
14✔
2470
        OGR_G_AddGeometryDirectly(hGeom, hRing);
14✔
2471
      }
2472

2473
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
14✔
2474
        msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g "
×
2475
                "%.15g %.15g\n",
2476
                rect.minx, rect.miny, rect.maxx, rect.maxy);
2477
      }
2478
    }
2479

2480
    psInfo->hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, select, hGeom, NULL);
64✔
2481
    psInfo->nLayerIndex = -1;
64✔
2482
    if (hGeom != NULL)
64✔
2483
      OGR_G_DestroyGeometry(hGeom);
14✔
2484

2485
    if (psInfo->hLayer == NULL) {
64✔
2486
      RELEASE_OGR_LOCK;
×
2487
      msSetError(MS_OGRERR, "ExecuteSQL() failed. Check logs.",
×
2488
                 "msOGRFileWhichShapes()");
2489
      msDebug("ExecuteSQL(%s) failed.\n%s\n", select, CPLGetLastErrorMsg());
×
2490
      msFree(select);
×
2491
      return MS_FAILURE;
×
2492
    }
2493
  } else {
2494

2495
    // case of 1) GetLayer + SetFilter
2496

2497
    char *pszOGRFilter = NULL;
2498
    if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
241✔
2499
      pszOGRFilter = msStringConcatenate(pszOGRFilter, "(");
3✔
2500
      pszOGRFilter = msStringConcatenate(
3✔
2501
          pszOGRFilter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2502
      pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
3✔
2503
    }
2504

2505
    if (psInfo->pszWHERE) {
241✔
2506
      if (pszOGRFilter) {
42✔
2507
        pszOGRFilter = msStringConcatenate(pszOGRFilter, " AND (");
×
2508
        pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
×
2509
        pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
×
2510
      } else {
2511
        pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
42✔
2512
      }
2513
    }
2514

2515
    ACQUIRE_OGR_LOCK;
241✔
2516

2517
    if (OGR_L_GetGeomType(psInfo->hLayer) != wkbNone && bIsValidRect) {
241✔
2518
      if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
240✔
2519
        OGRGeometryH hSpatialFilterPoint = OGR_G_CreateGeometry(wkbPoint);
7✔
2520

2521
        OGR_G_SetPoint_2D(hSpatialFilterPoint, 0, rect.minx, rect.miny);
7✔
2522
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterPoint);
7✔
2523
        OGR_G_DestroyGeometry(hSpatialFilterPoint);
7✔
2524
      } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
233✔
2525
        OGRGeometryH hSpatialFilterLine = OGR_G_CreateGeometry(wkbLineString);
×
2526

2527
        OGR_G_AddPoint_2D(hSpatialFilterLine, rect.minx, rect.miny);
×
2528
        OGR_G_AddPoint_2D(hSpatialFilterLine, rect.maxx, rect.maxy);
×
2529
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterLine);
×
2530
        OGR_G_DestroyGeometry(hSpatialFilterLine);
×
2531
      } else {
2532
        OGRGeometryH hSpatialFilterPolygon = OGR_G_CreateGeometry(wkbPolygon);
233✔
2533
        OGRGeometryH hRing = OGR_G_CreateGeometry(wkbLinearRing);
233✔
2534

2535
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
233✔
2536
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.miny);
233✔
2537
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.maxy);
233✔
2538
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.maxy);
233✔
2539
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
233✔
2540
        OGR_G_AddGeometryDirectly(hSpatialFilterPolygon, hRing);
233✔
2541
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterPolygon);
233✔
2542
        OGR_G_DestroyGeometry(hSpatialFilterPolygon);
233✔
2543
      }
2544

2545
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
240✔
2546
        msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g "
2✔
2547
                "%.15g %.15g\n",
2548
                rect.minx, rect.miny, rect.maxx, rect.maxy);
2549
      }
2550
    }
2551

2552
    psInfo->rect = rect;
241✔
2553

2554
    /* ------------------------------------------------------------------
2555
     * Apply an attribute filter if we have one prefixed with a WHERE
2556
     * keyword in the filter string.  Otherwise, ensure the attribute
2557
     * filter is clear.
2558
     * ------------------------------------------------------------------ */
2559
    if (pszOGRFilter != NULL) {
241✔
2560

2561
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
45✔
2562
        msDebug("msOGRFileWhichShapes: Setting attribute filter to %s\n",
×
2563
                pszOGRFilter);
2564

2565
      CPLErrorReset();
45✔
2566
      if (OGR_L_SetAttributeFilter(psInfo->hLayer, pszOGRFilter) !=
45✔
2567
          OGRERR_NONE) {
2568
        msSetError(
×
2569
            MS_OGRERR, "SetAttributeFilter() failed on layer %s. Check logs.",
2570
            "msOGRFileWhichShapes()", layer->name ? layer->name : "(null)");
×
2571
        msDebug("SetAttributeFilter(%s) failed on layer %s.\n%s\n",
×
2572
                pszOGRFilter, layer->name ? layer->name : "(null)",
×
2573
                CPLGetLastErrorMsg());
2574
        RELEASE_OGR_LOCK;
×
2575
        msFree(pszOGRFilter);
×
2576
        msFree(select);
×
2577
        return MS_FAILURE;
×
2578
      }
2579
      msFree(pszOGRFilter);
45✔
2580
    } else
2581
      OGR_L_SetAttributeFilter(psInfo->hLayer, NULL);
196✔
2582
  }
2583

2584
  msFree(select);
305✔
2585

2586
  /* ------------------------------------------------------------------
2587
   * Reset current feature pointer
2588
   * ------------------------------------------------------------------ */
2589
  OGR_L_ResetReading(psInfo->hLayer);
305✔
2590
  psInfo->last_record_index_read = -1;
305✔
2591

2592
  RELEASE_OGR_LOCK;
305✔
2593

2594
  return MS_SUCCESS;
2595
}
2596

2597
/**********************************************************************
2598
 *                     msOGRPassThroughFieldDefinitions()
2599
 *
2600
 * Pass the field definitions through to the layer metadata in the
2601
 * "gml_[item]_{type,width,precision}" set of metadata items for
2602
 * defining fields.
2603
 **********************************************************************/
2604

2605
static void msOGRPassThroughFieldDefinitions(layerObj *layer,
349✔
2606
                                             msOGRFileInfo *psInfo)
2607

2608
{
2609
  OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn(psInfo->hLayer);
349✔
2610
  int numitems, i;
2611

2612
  numitems = OGR_FD_GetFieldCount(hDefn);
349✔
2613

2614
  for (i = 0; i < numitems; i++) {
2,527✔
2615
    OGRFieldDefnH hField = OGR_FD_GetFieldDefn(hDefn, i);
2,178✔
2616
    OGRFieldSubType eFieldSubType = OGR_Fld_GetSubType(hField);
2,178✔
2617

2618
    char gml_width[32], gml_precision[32];
2619
    const char *gml_type = NULL;
2620
    const char *item = OGR_Fld_GetNameRef(hField);
2,178✔
2621

2622
    gml_width[0] = '\0';
2,178✔
2623
    gml_precision[0] = '\0';
2,178✔
2624

2625
    switch (OGR_Fld_GetType(hField)) {
2,178✔
2626
    case OFTInteger:
1,159✔
2627
      if (eFieldSubType == OFSTBoolean) {
1,159✔
2628
        gml_type = "Boolean";
2629
      } else {
2630
        gml_type = "Integer";
2631
        if (OGR_Fld_GetWidth(hField) > 0)
1,148✔
2632
          snprintf(gml_width, sizeof(gml_width), "%d",
160✔
2633
                   OGR_Fld_GetWidth(hField));
2634
      }
2635
      break;
2636

2637
    case OFTInteger64:
91✔
2638
      gml_type = "Long";
2639
      if (OGR_Fld_GetWidth(hField) > 0)
91✔
2640
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
80✔
2641
      break;
2642

2643
    case OFTReal:
119✔
2644
      gml_type = "Real";
2645
      if (OGR_Fld_GetWidth(hField) > 0)
119✔
2646
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
80✔
2647
      if (OGR_Fld_GetPrecision(hField) > 0)
119✔
2648
        snprintf(gml_precision, sizeof(gml_precision), "%d",
80✔
2649
                 OGR_Fld_GetPrecision(hField));
2650
      break;
2651

2652
    case OFTString:
758✔
2653
      gml_type = "Character";
2654
      if (OGR_Fld_GetWidth(hField) > 0)
758✔
2655
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
382✔
2656
      break;
2657

2658
    case OFTDate:
2659
      gml_type = "Date";
2660
      break;
2661
    case OFTTime:
6✔
2662
      gml_type = "Time";
2663
      break;
6✔
2664
    case OFTDateTime:
28✔
2665
      gml_type = "DateTime";
2666
      break;
28✔
2667

2668
    default:
2669
      gml_type = "Character";
2670
      break;
2671
    }
2672

2673
    msUpdateGMLFieldMetadata(layer, item, gml_type, gml_width, gml_precision,
2,178✔
2674
                             0);
2675
  }
2676

2677
  /* Should we try to address style items, or other special items? */
2678
}
349✔
2679

2680
/**********************************************************************
2681
 *                     msOGRFileGetItems()
2682
 *
2683
 * Returns a list of field names in a NULL terminated list of strings.
2684
 **********************************************************************/
2685
static char **msOGRFileGetItems(layerObj *layer, msOGRFileInfo *psInfo) {
663✔
2686
  OGRFeatureDefnH hDefn;
2687
  int i, numitems, totalnumitems;
2688
  int numStyleItems = MSOGR_LABELNUMITEMS;
2689
  char **items;
2690
  const char *getShapeStyleItems, *value;
2691

2692
  if ((hDefn = OGR_L_GetLayerDefn(psInfo->hLayer)) == NULL) {
663✔
2693
    msSetError(MS_OGRERR,
×
2694
               "OGR Connection for layer `%s' contains no field definition.",
2695
               "msOGRFileGetItems()", layer->name ? layer->name : "(null)");
×
2696
    return NULL;
×
2697
  }
2698

2699
  totalnumitems = numitems = OGR_FD_GetFieldCount(hDefn);
663✔
2700

2701
  getShapeStyleItems = msLayerGetProcessingKey(layer, "GETSHAPE_STYLE_ITEMS");
663✔
2702
  if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all"))
663✔
2703
    totalnumitems += numStyleItems;
×
2704

2705
  if ((items = (char **)malloc(sizeof(char *) * (totalnumitems + 1))) == NULL) {
663✔
2706
    msSetError(MS_MEMERR, NULL, "msOGRFileGetItems()");
×
2707
    return NULL;
×
2708
  }
2709

2710
  for (i = 0; i < numitems; i++) {
6,522✔
2711
    OGRFieldDefnH hField = OGR_FD_GetFieldDefn(hDefn, i);
5,859✔
2712
    items[i] = msStrdup(OGR_Fld_GetNameRef(hField));
5,859✔
2713
  }
2714

2715
  if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all")) {
663✔
2716
    assert(numStyleItems == 21);
2717
    items[i++] = msStrdup(MSOGR_LABELFONTNAMENAME);
×
2718
    items[i++] = msStrdup(MSOGR_LABELSIZENAME);
×
2719
    items[i++] = msStrdup(MSOGR_LABELTEXTNAME);
×
2720
    items[i++] = msStrdup(MSOGR_LABELANGLENAME);
×
2721
    items[i++] = msStrdup(MSOGR_LABELFCOLORNAME);
×
2722
    items[i++] = msStrdup(MSOGR_LABELBCOLORNAME);
×
2723
    items[i++] = msStrdup(MSOGR_LABELPLACEMENTNAME);
×
2724
    items[i++] = msStrdup(MSOGR_LABELANCHORNAME);
×
2725
    items[i++] = msStrdup(MSOGR_LABELDXNAME);
×
2726
    items[i++] = msStrdup(MSOGR_LABELDYNAME);
×
2727
    items[i++] = msStrdup(MSOGR_LABELPERPNAME);
×
2728
    items[i++] = msStrdup(MSOGR_LABELBOLDNAME);
×
2729
    items[i++] = msStrdup(MSOGR_LABELITALICNAME);
×
2730
    items[i++] = msStrdup(MSOGR_LABELUNDERLINENAME);
×
2731
    items[i++] = msStrdup(MSOGR_LABELPRIORITYNAME);
×
2732
    items[i++] = msStrdup(MSOGR_LABELSTRIKEOUTNAME);
×
2733
    items[i++] = msStrdup(MSOGR_LABELSTRETCHNAME);
×
2734
    items[i++] = msStrdup(MSOGR_LABELADJHORNAME);
×
2735
    items[i++] = msStrdup(MSOGR_LABELADJVERTNAME);
×
2736
    items[i++] = msStrdup(MSOGR_LABELHCOLORNAME);
×
2737
    items[i++] = msStrdup(MSOGR_LABELOCOLORNAME);
×
2738
  }
2739
  items[i++] = NULL;
663✔
2740

2741
  /* -------------------------------------------------------------------- */
2742
  /*      consider populating the field definitions in metadata.          */
2743
  /* -------------------------------------------------------------------- */
2744
  if ((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL &&
663✔
2745
      strcasecmp(value, "auto") == 0)
349✔
2746
    msOGRPassThroughFieldDefinitions(layer, psInfo);
349✔
2747

2748
  return items;
2749
}
2750

2751
/**********************************************************************
2752
 *                     msOGRFileNextShape()
2753
 *
2754
 * Returns shape sequentially from OGR data source.
2755
 * msOGRLayerWhichShape() must have been called first.
2756
 *
2757
 * Returns MS_SUCCESS/MS_FAILURE
2758
 **********************************************************************/
2759
static int msOGRFileNextShape(layerObj *layer, shapeObj *shape,
15,377✔
2760
                              msOGRFileInfo *psInfo) {
2761
  OGRFeatureH hFeature = NULL;
2762

2763
  if (psInfo == NULL || psInfo->hLayer == NULL) {
15,377✔
2764
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2765
               "msOGRFileNextShape()");
2766
    return (MS_FAILURE);
×
2767
  }
2768

2769
  /* ------------------------------------------------------------------
2770
   * Read until we find a feature that matches attribute filter and
2771
   * whose geometry is compatible with current layer type.
2772
   * ------------------------------------------------------------------ */
2773
  msFreeShape(shape);
15,377✔
2774
  shape->type = MS_SHAPE_NULL;
15,377✔
2775

2776
  ACQUIRE_OGR_LOCK;
15,377✔
2777
  while (shape->type == MS_SHAPE_NULL) {
15,383✔
2778
    if (hFeature)
15,383✔
2779
      OGR_F_Destroy(hFeature);
6✔
2780

2781
    if ((hFeature = OGR_L_GetNextFeature(psInfo->hLayer)) == NULL) {
15,383✔
2782
      psInfo->last_record_index_read = -1;
278✔
2783
      if (CPLGetLastErrorType() == CE_Failure) {
278✔
2784
        msSetError(MS_OGRERR, "OGR GetNextFeature() error'd. Check logs.",
×
2785
                   "msOGRFileNextShape()");
2786
        msDebug("msOGRFileNextShape(): %s\n", CPLGetLastErrorMsg());
×
2787
        RELEASE_OGR_LOCK;
×
2788
        return MS_FAILURE;
×
2789
      } else {
2790
        RELEASE_OGR_LOCK;
278✔
2791
        if (layer->debug >= MS_DEBUGLEVEL_VV)
278✔
2792
          msDebug("msOGRFileNextShape: Returning MS_DONE (no more shapes)\n");
2✔
2793
        return MS_DONE; // No more features to read
278✔
2794
      }
2795
    }
2796

2797
    psInfo->last_record_index_read++;
15,105✔
2798

2799
    if (layer->numitems > 0) {
15,105✔
2800
      if (shape->values)
7,278✔
2801
        msFreeCharArray(shape->values, shape->numvalues);
×
2802
      shape->values = msOGRGetValues(layer, hFeature);
7,278✔
2803
      shape->numvalues = layer->numitems;
7,278✔
2804
      if (!shape->values) {
7,278✔
2805
        OGR_F_Destroy(hFeature);
×
2806
        RELEASE_OGR_LOCK;
×
2807
        return (MS_FAILURE);
×
2808
      }
2809
    }
2810

2811
    // Feature matched filter expression... process geometry
2812
    // shape->type will be set if geom is compatible with layer type
2813
    if (ogrConvertGeometry(ogrGetLinearGeometry(hFeature), shape,
15,105✔
2814
                           layer->type) == MS_SUCCESS) {
2815
      if (shape->type != MS_SHAPE_NULL)
15,105✔
2816
        break; // Shape is ready to be returned!
2817

2818
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
6✔
2819
        msDebug("msOGRFileNextShape: Rejecting feature (shapeid = " CPL_FRMT_GIB
×
2820
                ", tileid=%d) of incompatible type for this layer (feature "
2821
                "wkbType %d, layer type %d)\n",
2822
                (GIntBig)OGR_F_GetFID(hFeature), psInfo->nTileId,
2823
                OGR_F_GetGeometryRef(hFeature) == NULL
×
2824
                    ? wkbFlatten(wkbUnknown)
×
2825
                    : wkbFlatten(OGR_G_GetGeometryType(
×
2826
                          OGR_F_GetGeometryRef(hFeature))),
2827
                layer->type);
×
2828

2829
    } else {
2830
      msFreeShape(shape);
×
2831
      OGR_F_Destroy(hFeature);
×
2832
      RELEASE_OGR_LOCK;
×
2833
      return MS_FAILURE; // Error message already produced.
×
2834
    }
2835

2836
    // Feature rejected... free shape to clear attributes values.
2837
    msFreeShape(shape);
6✔
2838
    shape->type = MS_SHAPE_NULL;
6✔
2839
  }
2840

2841
  shape->index = (int)OGR_F_GetFID(
15,099✔
2842
      hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
2843
  shape->resultindex = psInfo->last_record_index_read;
15,099✔
2844
  shape->tileindex = psInfo->nTileId;
15,099✔
2845

2846
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
15,099✔
2847
    msDebug("msOGRFileNextShape: Returning shape=%ld, tile=%d\n", shape->index,
18✔
2848
            shape->tileindex);
2849

2850
  // Keep ref. to last feature read in case we need style info.
2851
  if (psInfo->hLastFeature)
15,099✔
2852
    OGR_F_Destroy(psInfo->hLastFeature);
14,812✔
2853
  psInfo->hLastFeature = hFeature;
15,099✔
2854

2855
  RELEASE_OGR_LOCK;
15,099✔
2856

2857
  return MS_SUCCESS;
15,099✔
2858
}
2859

2860
/**********************************************************************
2861
 *                     msOGRFileGetShape()
2862
 *
2863
 * Returns shape from OGR data source by id.
2864
 *
2865
 * Returns MS_SUCCESS/MS_FAILURE
2866
 **********************************************************************/
2867
static int msOGRFileGetShape(layerObj *layer, shapeObj *shape, long record,
1,309✔
2868
                             msOGRFileInfo *psInfo, int record_is_fid) {
2869
  OGRFeatureH hFeature;
2870

2871
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,309✔
2872
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2873
               "msOGRFileNextShape()");
2874
    return (MS_FAILURE);
×
2875
  }
2876

2877
  /* -------------------------------------------------------------------- */
2878
  /*      Clear previously loaded shape.                                  */
2879
  /* -------------------------------------------------------------------- */
2880
  msFreeShape(shape);
1,309✔
2881
  shape->type = MS_SHAPE_NULL;
1,309✔
2882

2883
  /* -------------------------------------------------------------------- */
2884
  /*      Support reading feature by fid.                                 */
2885
  /* -------------------------------------------------------------------- */
2886
  if (record_is_fid) {
1,309✔
2887
    ACQUIRE_OGR_LOCK;
2✔
2888
    if ((hFeature = OGR_L_GetFeature(psInfo->hLayer, record)) == NULL) {
2✔
2889
      RELEASE_OGR_LOCK;
×
2890
      return MS_FAILURE;
×
2891
    }
2892
  }
2893

2894
  /* -------------------------------------------------------------------- */
2895
  /*      Support reading shape by offset within the current              */
2896
  /*      resultset.                                                      */
2897
  /* -------------------------------------------------------------------- */
2898
  else {
2899
    ACQUIRE_OGR_LOCK;
1,307✔
2900
    if (record <= psInfo->last_record_index_read ||
1,307✔
2901
        psInfo->last_record_index_read == -1) {
2902
      OGR_L_ResetReading(psInfo->hLayer);
201✔
2903
      psInfo->last_record_index_read = -1;
201✔
2904
    }
2905

2906
    hFeature = NULL;
2907
    while (psInfo->last_record_index_read < record) {
3,176✔
2908
      if (hFeature != NULL) {
1,869✔
2909
        OGR_F_Destroy(hFeature);
562✔
2910
        hFeature = NULL;
2911
      }
2912
      if ((hFeature = OGR_L_GetNextFeature(psInfo->hLayer)) == NULL) {
1,869✔
2913
        RELEASE_OGR_LOCK;
×
2914
        return MS_FAILURE;
×
2915
      }
2916
      psInfo->last_record_index_read++;
1,869✔
2917
    }
2918
  }
2919

2920
  /* ------------------------------------------------------------------
2921
   * Handle shape geometry...
2922
   * ------------------------------------------------------------------ */
2923
  // shape->type will be set if geom is compatible with layer type
2924
  if (ogrConvertGeometry(ogrGetLinearGeometry(hFeature), shape, layer->type) !=
1,309✔
2925
      MS_SUCCESS) {
2926
    RELEASE_OGR_LOCK;
×
2927
    return MS_FAILURE; // Error message already produced.
×
2928
  }
2929

2930
  if (shape->type == MS_SHAPE_NULL) {
1,309✔
2931
    msSetError(MS_OGRERR, "Requested feature is incompatible with layer type",
×
2932
               "msOGRLayerGetShape()");
2933
    RELEASE_OGR_LOCK;
×
2934
    return MS_FAILURE;
×
2935
  }
2936

2937
  /* ------------------------------------------------------------------
2938
   * Process shape attributes
2939
   * ------------------------------------------------------------------ */
2940
  if (layer->numitems > 0) {
1,309✔
2941
    shape->values = msOGRGetValues(layer, hFeature);
1,309✔
2942
    shape->numvalues = layer->numitems;
1,309✔
2943
    if (!shape->values) {
1,309✔
2944
      RELEASE_OGR_LOCK;
×
2945
      return (MS_FAILURE);
×
2946
    }
2947
  }
2948

2949
  if (record_is_fid) {
1,309✔
2950
    shape->index = record;
2✔
2951
    shape->resultindex = -1;
2✔
2952
  } else {
2953
    shape->index = (int)OGR_F_GetFID(
1,307✔
2954
        hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
2955
    shape->resultindex = record;
1,307✔
2956
  }
2957

2958
  shape->tileindex = psInfo->nTileId;
1,309✔
2959

2960
  // Keep ref. to last feature read in case we need style info.
2961
  if (psInfo->hLastFeature)
1,309✔
2962
    OGR_F_Destroy(psInfo->hLastFeature);
1,304✔
2963
  psInfo->hLastFeature = hFeature;
1,309✔
2964

2965
  RELEASE_OGR_LOCK;
1,309✔
2966

2967
  return MS_SUCCESS;
1,309✔
2968
}
2969

2970
/************************************************************************/
2971
/*                         msOGRFileReadTile()                          */
2972
/*                                                                      */
2973
/*      Advance to the next tile (or if targetTile is not -1 advance    */
2974
/*      to that tile), causing the tile to become the poCurTile in      */
2975
/*      the tileindexes psInfo structure.  Returns MS_DONE if there     */
2976
/*      are no more available tiles.                                    */
2977
/*                                                                      */
2978
/*      Newly loaded tiles are automatically "WhichShaped" based on     */
2979
/*      the current rectangle.                                          */
2980
/************************************************************************/
2981

2982
int msOGRFileReadTile(layerObj *layer, msOGRFileInfo *psInfo,
13✔
2983
                      int targetTile = -1)
2984

2985
{
2986
  int nFeatureId;
2987

2988
  /* -------------------------------------------------------------------- */
2989
  /*      Close old tile if one is open.                                  */
2990
  /* -------------------------------------------------------------------- */
2991
  if (psInfo->poCurTile != NULL) {
13✔
2992
    msOGRFileClose(layer, psInfo->poCurTile);
6✔
2993
    psInfo->poCurTile = NULL;
6✔
2994
  }
2995

2996
  /* -------------------------------------------------------------------- */
2997
  /*      If -2 is passed, then seek reset reading of the tileindex.      */
2998
  /*      We want to start from the beginning even if this file is        */
2999
  /*      shared between layers or renders.                               */
3000
  /* -------------------------------------------------------------------- */
3001
  ACQUIRE_OGR_LOCK;
13✔
3002
  if (targetTile == -2) {
13✔
3003
    OGR_L_ResetReading(psInfo->hLayer);
×
3004
  }
3005

3006
  /* -------------------------------------------------------------------- */
3007
  /*      Get the name (connection string really) of the next tile.       */
3008
  /* -------------------------------------------------------------------- */
3009
  OGRFeatureH hFeature;
3010
  char *connection = NULL;
3011
  msOGRFileInfo *psTileInfo = NULL;
3012
  int status;
3013

3014
#ifndef IGNORE_MISSING_DATA
3015
NextFile:
13✔
3016
#endif
3017

3018
  if (targetTile < 0)
13✔
3019
    hFeature = OGR_L_GetNextFeature(psInfo->hLayer);
10✔
3020

3021
  else
3022
    hFeature = OGR_L_GetFeature(psInfo->hLayer, targetTile);
3✔
3023

3024
  if (hFeature == NULL) {
13✔
3025
    RELEASE_OGR_LOCK;
2✔
3026
    if (targetTile == -1)
2✔
3027
      return MS_DONE;
3028
    else
3029
      return MS_FAILURE;
3030
  }
3031

3032
  connection = msStrdup(OGR_F_GetFieldAsString(hFeature, layer->tileitemindex));
11✔
3033

3034
  char *pszSRS = NULL;
3035
  if (layer->tilesrs != NULL) {
11✔
3036
    int idx = OGR_F_GetFieldIndex(hFeature, layer->tilesrs);
6✔
3037
    if (idx >= 0) {
6✔
3038
      pszSRS = msStrdup(OGR_F_GetFieldAsString(hFeature, idx));
6✔
3039
    }
3040
  }
3041

3042
  nFeatureId = (int)OGR_F_GetFID(
11✔
3043
      hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
3044

3045
  OGR_F_Destroy(hFeature);
11✔
3046

3047
  RELEASE_OGR_LOCK;
11✔
3048

3049
  /* -------------------------------------------------------------------- */
3050
  /*      Open the new tile file.                                         */
3051
  /* -------------------------------------------------------------------- */
3052
  psTileInfo = msOGRFileOpen(layer, connection);
11✔
3053

3054
  free(connection);
11✔
3055

3056
#ifndef IGNORE_MISSING_DATA
3057
  if (psTileInfo == NULL && targetTile == -1) {
11✔
3058
    msFree(pszSRS);
×
3059
    goto NextFile;
×
3060
  }
3061
#endif
3062

3063
  if (psTileInfo == NULL) {
11✔
3064
    msFree(pszSRS);
×
3065
    return MS_FAILURE;
×
3066
  }
3067

3068
  if (pszSRS != NULL) {
11✔
3069
    if (msOGCWKT2ProjectionObj(pszSRS, &(psInfo->sTileProj), layer->debug) !=
6✔
3070
        MS_SUCCESS) {
3071
      msFree(pszSRS);
×
3072
      return MS_FAILURE;
×
3073
    }
3074
    msFree(pszSRS);
6✔
3075
  }
3076

3077
  psTileInfo->nTileId = nFeatureId;
11✔
3078

3079
  /* -------------------------------------------------------------------- */
3080
  /*      Initialize the spatial query on this file.                      */
3081
  /* -------------------------------------------------------------------- */
3082
  if (psInfo->rect.minx != 0 || psInfo->rect.maxx != 0) {
11✔
3083
    rectObj rect = psInfo->rect;
6✔
3084

3085
    if (layer->tileindex != NULL && psInfo->sTileProj.numargs > 0) {
6✔
3086
      msProjectRect(&(layer->projection), &(psInfo->sTileProj), &rect);
4✔
3087
    }
3088

3089
    status = msOGRFileWhichShapes(layer, rect, psTileInfo);
6✔
3090
    if (status != MS_SUCCESS)
6✔
3091
      return status;
×
3092
  }
3093

3094
  psInfo->poCurTile = psTileInfo;
11✔
3095

3096
  /* -------------------------------------------------------------------- */
3097
  /*      Update the iteminfo in case this layer has a different field    */
3098
  /*      list.                                                           */
3099
  /* -------------------------------------------------------------------- */
3100
  msOGRLayerInitItemInfo(layer);
11✔
3101

3102
  return MS_SUCCESS;
11✔
3103
}
3104

3105
/************************************************************************/
3106
/*                               msExprNode                             */
3107
/************************************************************************/
3108

3109
class msExprNode {
1,442✔
3110
public:
3111
  std::vector<std::unique_ptr<msExprNode>> m_aoChildren{};
3112
  int m_nToken = 0;
3113
  std::string m_osVal{};
3114
  double m_dfVal = 0;
3115
  struct tm m_tmVal {};
3116
};
3117

3118
/************************************************************************/
3119
/*                        exprGetPriority()                             */
3120
/************************************************************************/
3121

3122
static int exprGetPriority(int token) {
58✔
3123
  if (token == MS_TOKEN_LOGICAL_NOT)
3124
    return 9;
3125
  else if (token == '*' || token == '/' || token == '%')
3126
    return 8;
×
3127
  else if (token == '+' || token == '-')
3128
    return 7;
×
3129
  else if (token == MS_TOKEN_COMPARISON_GE || token == MS_TOKEN_COMPARISON_GT ||
3130
           token == MS_TOKEN_COMPARISON_LE || token == MS_TOKEN_COMPARISON_LT ||
47✔
3131
           token == MS_TOKEN_COMPARISON_IN)
3132
    return 6;
22✔
3133
  else if (token == MS_TOKEN_COMPARISON_EQ ||
3134
           token == MS_TOKEN_COMPARISON_IEQ ||
3135
           token == MS_TOKEN_COMPARISON_RE ||
3136
           token == MS_TOKEN_COMPARISON_IRE || token == MS_TOKEN_COMPARISON_NE)
3137
    return 5;
3138
  else if (token == MS_TOKEN_LOGICAL_AND)
3139
    return 4;
28✔
3140
  else if (token == MS_TOKEN_LOGICAL_OR)
3141
    return 3;
1✔
3142
  else
3143
    return 0;
×
3144
}
3145

3146
/************************************************************************/
3147
/*                           BuildExprTree()                            */
3148
/************************************************************************/
3149

3150
static std::unique_ptr<msExprNode> BuildExprTree(tokenListNodeObjPtr node,
419✔
3151
                                                 tokenListNodeObjPtr *pNodeNext,
3152
                                                 int nParenthesisLevel) {
3153
  std::vector<std::unique_ptr<msExprNode>> aoStackOp, aoStackVal;
3154
  while (node != NULL) {
1,097✔
3155
    if (node->token == '(') {
836✔
3156
      auto subExpr = BuildExprTree(node->next, &node, nParenthesisLevel + 1);
126✔
3157
      if (subExpr == NULL) {
126✔
3158
        return nullptr;
3159
      }
3160
      aoStackVal.emplace_back(std::move(subExpr));
126✔
3161
      continue;
3162
    } else if (node->token == ')') {
3163
      if (nParenthesisLevel > 0) {
158✔
3164
        break;
3165
      }
3166
      return nullptr;
3167
    } else if (node->token == '+' || node->token == '-' || node->token == '*' ||
3168
               node->token == '/' || node->token == '%' ||
3169
               node->token == MS_TOKEN_LOGICAL_NOT ||
3170
               node->token == MS_TOKEN_LOGICAL_AND ||
3171
               node->token == MS_TOKEN_LOGICAL_OR ||
3172
               node->token == MS_TOKEN_COMPARISON_GE ||
3173
               node->token == MS_TOKEN_COMPARISON_GT ||
3174
               node->token == MS_TOKEN_COMPARISON_LE ||
3175
               node->token == MS_TOKEN_COMPARISON_LT ||
3176
               node->token == MS_TOKEN_COMPARISON_EQ ||
3177
               node->token == MS_TOKEN_COMPARISON_IEQ ||
3178
               node->token == MS_TOKEN_COMPARISON_NE ||
3179
               node->token == MS_TOKEN_COMPARISON_RE ||
3180
               node->token == MS_TOKEN_COMPARISON_IRE ||
3181
               node->token == MS_TOKEN_COMPARISON_IN) {
3182
      while (!aoStackOp.empty() &&
192✔
3183
             exprGetPriority(node->token) <=
29✔
3184
                 exprGetPriority(aoStackOp.back()->m_nToken)) {
29✔
3185
        std::unique_ptr<msExprNode> val1;
18✔
3186
        std::unique_ptr<msExprNode> val2;
18✔
3187
        if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT) {
18✔
3188
          if (aoStackVal.empty())
18✔
3189
            return nullptr;
3190
          val2.reset(aoStackVal.back().release());
3191
          aoStackVal.pop_back();
3192
        }
3193
        if (aoStackVal.empty())
18✔
3194
          return nullptr;
3195
        val1.reset(aoStackVal.back().release());
3196
        aoStackVal.pop_back();
3197

3198
        std::unique_ptr<msExprNode> newNode(new msExprNode);
18✔
3199
        newNode->m_nToken = aoStackOp.back()->m_nToken;
18✔
3200
        newNode->m_aoChildren.emplace_back(std::move(val1));
18✔
3201
        if (val2)
18✔
3202
          newNode->m_aoChildren.emplace_back(std::move(val2));
18✔
3203
        aoStackVal.emplace_back(std::move(newNode));
18✔
3204
        aoStackOp.pop_back();
3205
      }
3206

3207
      std::unique_ptr<msExprNode> newNode(new msExprNode);
174✔
3208
      newNode->m_nToken = node->token;
174✔
3209
      aoStackOp.emplace_back(std::move(newNode));
174✔
3210
    } else if (node->token == ',') {
174✔
3211
    } else if (node->token == MS_TOKEN_COMPARISON_INTERSECTS ||
3212
               node->token == MS_TOKEN_COMPARISON_DISJOINT ||
3213
               node->token == MS_TOKEN_COMPARISON_TOUCHES ||
3214
               node->token == MS_TOKEN_COMPARISON_OVERLAPS ||
341✔
3215
               node->token == MS_TOKEN_COMPARISON_CROSSES ||
3216
               node->token == MS_TOKEN_COMPARISON_DWITHIN ||
3217
               node->token == MS_TOKEN_COMPARISON_BEYOND ||
3218
               node->token == MS_TOKEN_COMPARISON_WITHIN ||
3219
               node->token == MS_TOKEN_COMPARISON_CONTAINS ||
3220
               node->token == MS_TOKEN_COMPARISON_EQUALS ||
3221
               node->token == MS_TOKEN_FUNCTION_LENGTH ||
3222
               node->token == MS_TOKEN_FUNCTION_TOSTRING ||
3223
               node->token == MS_TOKEN_FUNCTION_COMMIFY ||
3224
               node->token == MS_TOKEN_FUNCTION_AREA ||
3225
               node->token == MS_TOKEN_FUNCTION_ROUND ||
3226
               node->token == MS_TOKEN_FUNCTION_FROMTEXT ||
3227
               node->token == MS_TOKEN_FUNCTION_BUFFER ||
3228
               node->token == MS_TOKEN_FUNCTION_DIFFERENCE ||
3229
               node->token == MS_TOKEN_FUNCTION_SIMPLIFY ||
3230
               node->token == MS_TOKEN_FUNCTION_SIMPLIFYPT ||
3231
               node->token == MS_TOKEN_FUNCTION_GENERALIZE ||
3232
               node->token == MS_TOKEN_FUNCTION_SMOOTHSIA ||
3233
               node->token == MS_TOKEN_FUNCTION_JAVASCRIPT ||
3234
               node->token == MS_TOKEN_FUNCTION_UPPER ||
3235
               node->token == MS_TOKEN_FUNCTION_LOWER ||
3236
               node->token == MS_TOKEN_FUNCTION_INITCAP ||
3237
               node->token == MS_TOKEN_FUNCTION_FIRSTCAP) {
3238
      if (node->next && node->next->token == '(') {
32✔
3239
        int node_token = node->token;
3240
        auto subExpr =
3241
            BuildExprTree(node->next->next, &node, nParenthesisLevel + 1);
32✔
3242
        if (subExpr == NULL) {
32✔
3243
          return nullptr;
3244
        }
3245
        std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3246
        newNode->m_nToken = node_token;
32✔
3247
        if (subExpr->m_nToken == 0) {
32✔
3248
          newNode->m_aoChildren = std::move(subExpr->m_aoChildren);
32✔
3249
        } else {
3250
          newNode->m_aoChildren.emplace_back(std::move(subExpr));
×
3251
        }
3252
        aoStackVal.emplace_back(std::move(newNode));
32✔
3253
        continue;
3254
      } else
3255
        return nullptr;
3256
    } else if (node->token == MS_TOKEN_LITERAL_NUMBER ||
3257
               node->token == MS_TOKEN_LITERAL_BOOLEAN) {
3258
      std::unique_ptr<msExprNode> newNode(new msExprNode);
67✔
3259
      newNode->m_nToken = node->token;
67✔
3260
      newNode->m_dfVal = node->tokenval.dblval;
67✔
3261
      aoStackVal.emplace_back(std::move(newNode));
67✔
3262
    } else if (node->token == MS_TOKEN_LITERAL_STRING) {
67✔
3263
      std::unique_ptr<msExprNode> newNode(new msExprNode);
62✔
3264
      newNode->m_nToken = node->token;
62✔
3265
      newNode->m_osVal = node->tokenval.strval;
62✔
3266
      aoStackVal.emplace_back(std::move(newNode));
62✔
3267
    } else if (node->token == MS_TOKEN_LITERAL_TIME) {
3268
      std::unique_ptr<msExprNode> newNode(new msExprNode);
12✔
3269
      newNode->m_nToken = node->token;
12✔
3270
      newNode->m_tmVal = node->tokenval.tmval;
12✔
3271
      aoStackVal.emplace_back(std::move(newNode));
12✔
3272
    } else if (node->token == MS_TOKEN_LITERAL_SHAPE) {
3273
      std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3274
      newNode->m_nToken = node->token;
32✔
3275
      char *wkt = msShapeToWKT(node->tokenval.shpval);
32✔
3276
      newNode->m_osVal = wkt;
32✔
3277
      msFree(wkt);
32✔
3278
      aoStackVal.emplace_back(std::move(newNode));
32✔
3279
    } else if (node->token == MS_TOKEN_BINDING_DOUBLE ||
3280
               node->token == MS_TOKEN_BINDING_INTEGER ||
3281
               node->token == MS_TOKEN_BINDING_STRING ||
3282
               node->token == MS_TOKEN_BINDING_TIME) {
3283
      std::unique_ptr<msExprNode> newNode(new msExprNode);
104✔
3284
      newNode->m_nToken = node->token;
104✔
3285
      newNode->m_osVal = node->tokenval.bindval.item;
104✔
3286
      aoStackVal.emplace_back(std::move(newNode));
104✔
3287
    } else {
3288
      std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3289
      newNode->m_nToken = node->token;
32✔
3290
      aoStackVal.emplace_back(std::move(newNode));
32✔
3291
    }
3292

3293
    node = node->next;
520✔
3294
  }
3295

3296
  while (!aoStackOp.empty()) {
575✔
3297
    std::unique_ptr<msExprNode> val1 = NULL;
3298
    std::unique_ptr<msExprNode> val2 = NULL;
3299
    if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT) {
156✔
3300
      if (aoStackVal.empty())
151✔
3301
        return nullptr;
3302
      val2.reset(aoStackVal.back().release());
3303
      aoStackVal.pop_back();
3304
    }
3305
    if (aoStackVal.empty())
156✔
3306
      return nullptr;
3307
    val1.reset(aoStackVal.back().release());
3308
    aoStackVal.pop_back();
3309

3310
    std::unique_ptr<msExprNode> newNode(new msExprNode);
156✔
3311
    newNode->m_nToken = aoStackOp.back()->m_nToken;
156✔
3312
    newNode->m_aoChildren.emplace_back(std::move(val1));
156✔
3313
    if (val2)
156✔
3314
      newNode->m_aoChildren.emplace_back(std::move(val2));
151✔
3315
    aoStackVal.emplace_back(std::move(newNode));
156✔
3316
    aoStackOp.pop_back();
3317
  }
3318

3319
  std::unique_ptr<msExprNode> poRet;
419✔
3320
  if (aoStackVal.size() == 1)
419✔
3321
    poRet.reset(aoStackVal.back().release());
3322
  else if (aoStackVal.size() > 1) {
190✔
3323
    poRet.reset(new msExprNode);
32✔
3324
    poRet->m_aoChildren = std::move(aoStackVal);
32✔
3325
  }
3326

3327
  if (pNodeNext)
419✔
3328
    *pNodeNext = node ? node->next : NULL;
158✔
3329

3330
  return poRet;
3331
}
419✔
3332

3333
/**********************************************************************
3334
 *                 msOGRExtractTopSpatialFilter()
3335
 *
3336
 * Recognize expressions like "Intersects([shape], wkt) == TRUE [AND ....]"
3337
 **********************************************************************/
3338
static int msOGRExtractTopSpatialFilter(msOGRFileInfo *info,
143✔
3339
                                        const msExprNode *expr,
3340
                                        const msExprNode **pSpatialFilterNode) {
3341
  if (expr == NULL)
174✔
3342
    return MS_FALSE;
3343

3344
  if (expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
64✔
3345
      expr->m_aoChildren.size() == 2 &&
64✔
3346
      expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_BOOLEAN &&
238✔
3347
      expr->m_aoChildren[1]->m_dfVal == 1.0) {
31✔
3348
    return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0].get(),
3349
                                        pSpatialFilterNode);
31✔
3350
  }
3351

3352
  if ((((expr->m_nToken == MS_TOKEN_COMPARISON_INTERSECTS ||
132✔
3353
         expr->m_nToken == MS_TOKEN_COMPARISON_OVERLAPS ||
130✔
3354
         expr->m_nToken == MS_TOKEN_COMPARISON_CROSSES ||
128✔
3355
         expr->m_nToken == MS_TOKEN_COMPARISON_WITHIN ||
126✔
3356
         expr->m_nToken == MS_TOKEN_COMPARISON_CONTAINS) &&
19✔
3357
        expr->m_aoChildren.size() == 2) ||
124✔
3358
       (expr->m_nToken == MS_TOKEN_COMPARISON_DWITHIN &&
3✔
3359
        expr->m_aoChildren.size() == 3)) &&
143✔
3360
      expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_SHAPE) {
22✔
3361
    if (info->rect_is_defined) {
22✔
3362
      // Several intersects...
3363
      *pSpatialFilterNode = NULL;
×
3364
      info->rect_is_defined = MS_FALSE;
×
3365
      return MS_FALSE;
×
3366
    }
3367
    OGRGeometryH hSpatialFilter = NULL;
22✔
3368
    char *wkt = const_cast<char *>(expr->m_aoChildren[1]->m_osVal.c_str());
22✔
3369
    OGRErr e = OGR_G_CreateFromWkt(&wkt, NULL, &hSpatialFilter);
22✔
3370
    if (e == OGRERR_NONE) {
22✔
3371
      OGREnvelope env;
3372
      if (expr->m_nToken == MS_TOKEN_COMPARISON_DWITHIN) {
22✔
3373
        OGRGeometryH hBuffer =
3374
            OGR_G_Buffer(hSpatialFilter, expr->m_aoChildren[2]->m_dfVal, 30);
3✔
3375
        OGR_G_GetEnvelope(hBuffer ? hBuffer : hSpatialFilter, &env);
3✔
3376
        OGR_G_DestroyGeometry(hBuffer);
3✔
3377
      } else {
3378
        OGR_G_GetEnvelope(hSpatialFilter, &env);
19✔
3379
      }
3380
      info->rect.minx = env.MinX;
22✔
3381
      info->rect.miny = env.MinY;
22✔
3382
      info->rect.maxx = env.MaxX;
22✔
3383
      info->rect.maxy = env.MaxY;
22✔
3384
      info->rect_is_defined = true;
22✔
3385
      *pSpatialFilterNode = expr;
22✔
3386
      OGR_G_DestroyGeometry(hSpatialFilter);
22✔
3387
      return MS_TRUE;
3388
    }
3389
    return MS_FALSE;
3390
  }
3391

3392
  if (expr->m_nToken == MS_TOKEN_LOGICAL_AND &&
121✔
3393
      expr->m_aoChildren.size() == 2) {
3394
    return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0].get(),
20✔
3395
                                        pSpatialFilterNode) &&
40✔
3396
           msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[1].get(),
20✔
3397
                                        pSpatialFilterNode);
20✔
3398
  }
3399

3400
  return MS_TRUE;
3401
}
3402

3403
/**********************************************************************
3404
 *                 msOGRTranslatePartialMSExpressionToOGRSQL()
3405
 *
3406
 * Tries to partially translate a mapserver expression to SQL
3407
 **********************************************************************/
3408

3409
static std::string msOGRGetTokenText(int nToken) {
55✔
3410
  switch (nToken) {
55✔
3411
  case '*':
3412
  case '+':
3413
  case '-':
3414
  case '/':
3415
  case '%':
3416
    return std::string(1, static_cast<char>(nToken));
3417

3418
  case MS_TOKEN_COMPARISON_GE:
3419
    return ">=";
5✔
3420
  case MS_TOKEN_COMPARISON_GT:
3421
    return ">";
1✔
3422
  case MS_TOKEN_COMPARISON_LE:
3423
    return "<=";
5✔
3424
  case MS_TOKEN_COMPARISON_LT:
3425
    return "<";
1✔
3426
  case MS_TOKEN_COMPARISON_EQ:
3427
    return "=";
42✔
3428
  case MS_TOKEN_COMPARISON_NE:
3429
    return "!=";
1✔
3430

3431
  default:
3432
    return std::string();
3433
  }
3434
}
3435

3436
static std::string
3437
msOGRTranslatePartialInternal(layerObj *layer, const msExprNode *expr,
285✔
3438
                              const msExprNode *spatialFilterNode,
3439
                              bool &bPartialFilter) {
3440
  switch (expr->m_nToken) {
285✔
3441
  case MS_TOKEN_LOGICAL_NOT: {
4✔
3442
    std::string osTmp(msOGRTranslatePartialInternal(
3443
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
4✔
3444
    if (osTmp.empty())
4✔
3445
      return std::string();
3446
    return "(NOT " + osTmp + ")";
8✔
3447
  }
3448

3449
  case MS_TOKEN_LOGICAL_AND: {
17✔
3450
    // We can deal with partially translated children
3451
    std::string osTmp1(msOGRTranslatePartialInternal(
3452
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
17✔
3453
    std::string osTmp2(msOGRTranslatePartialInternal(
3454
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
17✔
3455
    if (!osTmp1.empty() && !osTmp2.empty()) {
17✔
3456
      return "(" + osTmp1 + " AND " + osTmp2 + ")";
18✔
3457
    } else if (!osTmp1.empty())
8✔
3458
      return osTmp1;
×
3459
    else
3460
      return osTmp2;
8✔
3461
  }
3462

3463
  case MS_TOKEN_LOGICAL_OR: {
9✔
3464
    // We can NOT deal with partially translated children
3465
    std::string osTmp1(msOGRTranslatePartialInternal(
3466
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
9✔
3467
    std::string osTmp2(msOGRTranslatePartialInternal(
3468
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
9✔
3469
    if (!osTmp1.empty() && !osTmp2.empty()) {
9✔
3470
      return "(" + osTmp1 + " OR " + osTmp2 + ")";
16✔
3471
    } else
3472
      return std::string();
3473
  }
3474

3475
  case '*':
82✔
3476
  case '+':
3477
  case '-':
3478
  case '/':
3479
  case '%':
3480
  case MS_TOKEN_COMPARISON_GE:
3481
  case MS_TOKEN_COMPARISON_GT:
3482
  case MS_TOKEN_COMPARISON_LE:
3483
  case MS_TOKEN_COMPARISON_LT:
3484
  case MS_TOKEN_COMPARISON_EQ:
3485
  case MS_TOKEN_COMPARISON_NE: {
3486
    std::string osTmp1(msOGRTranslatePartialInternal(
3487
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
82✔
3488
    std::string osTmp2(msOGRTranslatePartialInternal(
3489
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
82✔
3490
    if (!osTmp1.empty() && !osTmp2.empty()) {
82✔
3491
      if (expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
55✔
3492
          osTmp2 == "'_MAPSERVER_NULL_'") {
42✔
3493
        return "(" + osTmp1 + " IS NULL )";
×
3494
      }
3495
      if (expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_STRING) {
55✔
3496
        bool bIsCharacter = msLayerPropertyIsCharacter(
32✔
3497
            layer, expr->m_aoChildren[0]->m_osVal.c_str());
3498
        // Cast if needed (or unsure)
3499
        if (!bIsCharacter) {
32✔
3500
          osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
87✔
3501
        }
3502
      }
3503
      return "(" + osTmp1 + " " + msOGRGetTokenText(expr->m_nToken) + " " +
165✔
3504
             osTmp2 + ")";
3505
    } else
3506
      return std::string();
3507
  }
3508

3509
  case MS_TOKEN_COMPARISON_RE: {
4✔
3510
    std::string osTmp1(msOGRTranslatePartialInternal(
3511
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
4✔
3512
    if (expr->m_aoChildren[1]->m_nToken != MS_TOKEN_LITERAL_STRING) {
4✔
3513
      return std::string();
3514
    }
3515
    std::string osRE("'");
4✔
3516
    const size_t nSize = expr->m_aoChildren[1]->m_osVal.size();
3517
    bool bHasUsedEscape = false;
3518
    for (size_t i = 0; i < nSize; i++) {
18✔
3519
      if (i == 0 && expr->m_aoChildren[1]->m_osVal[i] == '^')
18✔
3520
        continue;
4✔
3521
      if (i == nSize - 1 && expr->m_aoChildren[1]->m_osVal[i] == '$')
14✔
3522
        break;
3523
      if (expr->m_aoChildren[1]->m_osVal[i] == '.') {
10✔
3524
        if (i + 1 < nSize && expr->m_aoChildren[1]->m_osVal[i + 1] == '*') {
3✔
3525
          osRE += "%";
3526
          i++;
3527
        } else {
3528
          osRE += "_";
3529
        }
3530
      } else if (expr->m_aoChildren[1]->m_osVal[i] == '\\' && i + 1 < nSize) {
7✔
3531
        bHasUsedEscape = true;
3532
        osRE += 'X';
3533
        osRE += expr->m_aoChildren[1]->m_osVal[i + 1];
×
3534
        i++;
3535
      } else if (expr->m_aoChildren[1]->m_osVal[i] == 'X' ||
3536
                 expr->m_aoChildren[1]->m_osVal[i] == '%' ||
3537
                 expr->m_aoChildren[1]->m_osVal[i] == '_') {
3538
        bHasUsedEscape = true;
3539
        osRE += 'X';
3540
        osRE += expr->m_aoChildren[1]->m_osVal[i];
×
3541
      } else {
3542
        osRE += expr->m_aoChildren[1]->m_osVal[i];
3543
      }
3544
    }
3545
    osRE += "'";
3546
    char md_item_name[256];
3547
    snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type",
3548
             expr->m_aoChildren[0]->m_osVal.c_str());
3549
    const char *type = msLookupHashTable(&(layer->metadata), md_item_name);
4✔
3550
    // Cast if needed (or unsure)
3551
    if (type == NULL || !EQUAL(type, "Character")) {
4✔
3552
      osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
12✔
3553
    }
3554
    std::string osRet("(" + osTmp1 + " LIKE " + osRE);
4✔
3555
    if (bHasUsedEscape)
4✔
3556
      osRet += " ESCAPE 'X'";
3557
    osRet += ")";
3558
    return osRet;
4✔
3559
  }
3560

3561
  case MS_TOKEN_COMPARISON_IN: {
×
3562
    std::string osTmp1(msOGRTranslatePartialInternal(
3563
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
×
3564
    std::string osRet = "(" + osTmp1 + " IN (";
×
3565
    for (size_t i = 0; i < expr->m_aoChildren[1]->m_aoChildren.size(); ++i) {
×
3566
      if (i > 0)
×
3567
        osRet += ", ";
3568
      osRet += msOGRTranslatePartialInternal(
×
3569
          layer, expr->m_aoChildren[1]->m_aoChildren[i].get(),
3570
          spatialFilterNode, bPartialFilter);
3571
    }
3572
    osRet += ")";
3573
    return osRet;
×
3574
  }
3575

3576
  case MS_TOKEN_LITERAL_NUMBER:
3577
  case MS_TOKEN_LITERAL_BOOLEAN: {
3578
    return std::string(CPLSPrintf("%.18g", expr->m_dfVal));
38✔
3579
  }
3580

3581
  case MS_TOKEN_LITERAL_STRING: {
32✔
3582
    char *stresc = msOGREscapeSQLParam(layer, expr->m_osVal.c_str());
32✔
3583
    std::string osRet("'" + std::string(stresc) + "'");
32✔
3584
    msFree(stresc);
32✔
3585
    return osRet;
32✔
3586
  }
3587

3588
  case MS_TOKEN_LITERAL_TIME: {
3589
#ifdef notdef
3590
    // Breaks tests in msautotest/wxs/wfs_time_ogr.map
3591
    return std::string(CPLSPrintf(
3592
        "'%04d/%02d/%02d %02d:%02d:%02d'", expr->m_tmVal.tm_year + 1900,
3593
        expr->m_tmVal.tm_mon + 1, expr->m_tmVal.tm_mday, expr->m_tmVal.tm_hour,
3594
        expr->m_tmVal.tm_min, expr->m_tmVal.tm_sec));
3595
#endif
3596
    return std::string();
3597
  }
3598

3599
  case MS_TOKEN_BINDING_DOUBLE:
71✔
3600
  case MS_TOKEN_BINDING_INTEGER:
3601
  case MS_TOKEN_BINDING_STRING:
3602
  case MS_TOKEN_BINDING_TIME: {
3603
    char *pszTmp = msLayerEscapePropertyName(layer, expr->m_osVal.c_str());
71✔
3604
    std::string osRet;
3605
    osRet += '"';
3606
    osRet += pszTmp;
3607
    osRet += '"';
3608
    msFree(pszTmp);
71✔
3609
    return osRet;
71✔
3610
  }
3611

3612
  case MS_TOKEN_COMPARISON_INTERSECTS: {
4✔
3613
    if (expr != spatialFilterNode)
4✔
3614
      bPartialFilter = true;
×
3615
    return std::string();
3616
  }
3617

3618
  default: {
12✔
3619
    bPartialFilter = true;
12✔
3620
    return std::string();
3621
  }
3622
  }
3623
}
3624

3625
/* ==================================================================
3626
 * Here comes the REAL stuff... the functions below are called by maplayer.c
3627
 * ================================================================== */
3628

3629
/**********************************************************************
3630
 *                     msOGRTranslateMsExpressionToOGRSQL()
3631
 *
3632
 * Tries to translate a mapserver expression to OGR or driver native SQL
3633
 **********************************************************************/
3634
static int msOGRTranslateMsExpressionToOGRSQL(layerObj *layer,
295✔
3635
                                              expressionObj *psFilter,
3636
                                              char *filteritem) {
3637
  msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
295✔
3638

3639
  msFree(layer->filter.native_string);
295✔
3640
  layer->filter.native_string = NULL;
295✔
3641

3642
  msFree(info->pszWHERE);
295✔
3643
  info->pszWHERE = NULL;
295✔
3644

3645
  // reasons to not produce native string: not simple layer, or an explicit deny
3646
  const char *do_this =
3647
      msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
295✔
3648
  if (do_this && strcmp(do_this, "NO") == 0) {
295✔
3649
    return MS_SUCCESS;
3650
  }
3651

3652
  tokenListNodeObjPtr node = psFilter->tokens;
261✔
3653
  auto expr = BuildExprTree(node, NULL, 0);
261✔
3654
  info->rect_is_defined = MS_FALSE;
261✔
3655
  const msExprNode *spatialFilterNode = NULL;
261✔
3656
  if (expr)
261✔
3657
    msOGRExtractTopSpatialFilter(info, expr.get(), &spatialFilterNode);
103✔
3658

3659
  // more reasons to not produce native string: not a recognized driver
3660
  if (!info->dialect) {
261✔
3661
    // in which case we might still want to try to get a partial WHERE clause
3662
    if (filteritem == NULL && expr) {
202✔
3663
      bool bPartialFilter = false;
61✔
3664
      std::string osSQL(msOGRTranslatePartialInternal(
3665
          layer, expr.get(), spatialFilterNode, bPartialFilter));
61✔
3666
      if (!osSQL.empty()) {
61✔
3667
        info->pszWHERE = msStrdup(osSQL.c_str());
42✔
3668
        if (bPartialFilter) {
42✔
3669
          msDebug("Full filter has only been partially "
×
3670
                  "translated to OGR filter %s\n",
3671
                  info->pszWHERE);
3672
        }
3673
      } else if (bPartialFilter) {
19✔
3674
        msDebug("Filter could not be translated to OGR filter\n");
12✔
3675
      }
3676
    }
3677
    return MS_SUCCESS;
202✔
3678
  }
3679

3680
  char *sql = NULL;
3681

3682
  // node may be NULL if layer->filter.string != NULL and filteritem != NULL
3683
  // this is simple filter but string is regex
3684
  if (node == NULL && filteritem != NULL && layer->filter.string != NULL) {
59✔
3685
    sql = msStringConcatenate(sql, "\"");
×
3686
    sql = msStringConcatenate(sql, filteritem);
×
3687
    sql = msStringConcatenate(sql, "\"");
×
3688
    if (EQUAL(info->dialect, "PostgreSQL")) {
×
3689
      sql = msStringConcatenate(sql, " ~ ");
×
3690
    } else {
3691
      sql = msStringConcatenate(sql, " LIKE ");
×
3692
    }
3693
    sql = msStringConcatenate(sql, "'");
×
3694
    sql = msStringConcatenate(sql, layer->filter.string);
×
3695
    sql = msStringConcatenate(sql, "'");
×
3696
  }
3697

3698
  while (node != NULL) {
244✔
3699

3700
    if (node->next && node->next->token == MS_TOKEN_COMPARISON_IEQ) {
185✔
3701
      char *left = msOGRGetToken(layer, &node);
2✔
3702
      node = node->next; // skip =
2✔
3703
      char *right = msOGRGetToken(layer, &node);
2✔
3704
      sql = msStringConcatenate(sql, "upper(");
2✔
3705
      sql = msStringConcatenate(sql, left);
2✔
3706
      sql = msStringConcatenate(sql, ")");
2✔
3707
      sql = msStringConcatenate(sql, "=");
2✔
3708
      sql = msStringConcatenate(sql, "upper(");
2✔
3709
      sql = msStringConcatenate(sql, right);
2✔
3710
      sql = msStringConcatenate(sql, ")");
2✔
3711
      int ok = left && right;
2✔
3712
      msFree(left);
2✔
3713
      msFree(right);
2✔
3714
      if (!ok) {
2✔
3715
        goto fail;
×
3716
      }
3717
      continue;
2✔
3718
    }
2✔
3719

3720
    switch (node->token) {
183✔
3721
    case MS_TOKEN_COMPARISON_INTERSECTS:
17✔
3722
    case MS_TOKEN_COMPARISON_DISJOINT:
3723
    case MS_TOKEN_COMPARISON_TOUCHES:
3724
    case MS_TOKEN_COMPARISON_OVERLAPS:
3725
    case MS_TOKEN_COMPARISON_CROSSES:
3726
    case MS_TOKEN_COMPARISON_DWITHIN:
3727
    case MS_TOKEN_COMPARISON_BEYOND:
3728
    case MS_TOKEN_COMPARISON_WITHIN:
3729
    case MS_TOKEN_COMPARISON_CONTAINS:
3730
    case MS_TOKEN_COMPARISON_EQUALS: {
3731
      int token = node->token;
3732
      char *fct = msOGRGetToken(layer, &node);
17✔
3733
      node = node->next; // skip (
17✔
3734
      char *a1 = msOGRGetToken(layer, &node);
17✔
3735
      node = node->next; // skip ,
17✔
3736
      char *a2 = msOGRGetToken(layer, &node);
17✔
3737
      char *a3 = NULL;
3738
      if (token == MS_TOKEN_COMPARISON_DWITHIN ||
17✔
3739
          token == MS_TOKEN_COMPARISON_BEYOND) {
3740
        node = node->next; // skip ,
2✔
3741
        a3 = msOGRGetToken(layer, &node);
2✔
3742
      }
3743
      node = node->next; // skip )
17✔
3744
      char *eq = msOGRGetToken(layer, &node);
17✔
3745
      char *rval = msOGRGetToken(layer, &node);
17✔
3746
      if ((eq && strcmp(eq, " != ") == 0) ||
17✔
3747
          (rval && strcmp(rval, "FALSE") == 0)) {
17✔
3748
        sql = msStringConcatenate(sql, "NOT ");
×
3749
      }
3750
      // FIXME: case rval is more complex
3751
      sql = msStringConcatenate(sql, fct);
17✔
3752
      sql = msStringConcatenate(sql, "(");
17✔
3753
      sql = msStringConcatenate(sql, a1);
17✔
3754
      sql = msStringConcatenate(sql, ",");
17✔
3755
      sql = msStringConcatenate(sql, a2);
17✔
3756
      if (token == MS_TOKEN_COMPARISON_DWITHIN ||
17✔
3757
          token == MS_TOKEN_COMPARISON_BEYOND) {
3758
        sql = msStringConcatenate(sql, ")");
2✔
3759
        if (token == MS_TOKEN_COMPARISON_DWITHIN)
2✔
3760
          sql = msStringConcatenate(sql, "<=");
1✔
3761
        else
3762
          sql = msStringConcatenate(sql, ">");
1✔
3763
        sql = msStringConcatenate(sql, a3);
2✔
3764
      } else {
3765
        sql = msStringConcatenate(sql, ")");
15✔
3766
      }
3767
      int ok = fct && a1 && a2 && eq && rval;
17✔
3768
      if (token == MS_TOKEN_COMPARISON_DWITHIN) {
17✔
3769
        ok = ok && a3;
1✔
3770
      }
3771
      msFree(fct);
17✔
3772
      msFree(a1);
17✔
3773
      msFree(a2);
17✔
3774
      msFree(a3);
17✔
3775
      msFree(eq);
17✔
3776
      msFree(rval);
17✔
3777
      if (!ok) {
17✔
3778
        goto fail;
×
3779
      }
3780
      break;
3781
    }
3782
    default: {
166✔
3783
      char *token = msOGRGetToken(layer, &node);
166✔
3784
      if (!token) {
166✔
3785
        goto fail;
×
3786
      }
3787
      sql = msStringConcatenate(sql, token);
166✔
3788
      msFree(token);
166✔
3789
    }
3790
    }
3791
  }
3792

3793
  layer->filter.native_string = sql;
59✔
3794
  return MS_SUCCESS;
59✔
3795
fail:
×
3796
  // error producing native string
3797
  msDebug("Note: Error parsing token list, could produce only: %s. Trying in "
×
3798
          "partial mode\n",
3799
          sql);
3800
  msFree(sql);
×
3801

3802
  // in which case we might still want to try to get a partial WHERE clause
3803
  if (expr) {
×
3804
    bool bPartialFilter = false;
×
3805
    std::string osSQL(msOGRTranslatePartialInternal(
3806
        layer, expr.get(), spatialFilterNode, bPartialFilter));
×
3807
    if (!osSQL.empty()) {
×
3808
      info->pszWHERE = msStrdup(osSQL.c_str());
×
3809
      if (bPartialFilter) {
×
3810
        msDebug("Full filter has only been partially "
×
3811
                "translated to OGR filter %s\n",
3812
                info->pszWHERE);
3813
      }
3814
    } else if (bPartialFilter) {
×
3815
      msDebug("Filter could not be translated to OGR filter\n");
×
3816
    }
3817
  }
3818

3819
  return MS_SUCCESS;
3820
}
3821

3822
/**********************************************************************
3823
 *                     msOGRLayerOpen()
3824
 *
3825
 * Open OGR data source for the specified map layer.
3826
 *
3827
 * If pszOverrideConnection != NULL then this value is used as the connection
3828
 * string instead of lp->connection.  This is used for instance to open
3829
 * a WFS layer, in this case lp->connection is the WFS url, but we want
3830
 * OGR to open the local file on disk that was previously downloaded.
3831
 *
3832
 * An OGR connection string is:   <dataset_filename>[,<layer_index>]
3833
 *  <dataset_filename>   is file format specific
3834
 *  <layer_index>        (optional) is the OGR layer index
3835
 *                       default is 0, the first layer.
3836
 *
3837
 * One can use the "ogrinfo" program to find out the layer indices in a dataset
3838
 *
3839
 * Returns MS_SUCCESS/MS_FAILURE
3840
 **********************************************************************/
3841
int msOGRLayerOpen(layerObj *layer, const char *pszOverrideConnection) {
1,016✔
3842
  msOGRFileInfo *psInfo;
3843

3844
  if (layer->layerinfo != NULL) {
1,016✔
3845
    return MS_SUCCESS; // Nothing to do... layer is already opened
3846
  }
3847

3848
  /* -------------------------------------------------------------------- */
3849
  /*      If this is not a tiled layer, just directly open the target.    */
3850
  /* -------------------------------------------------------------------- */
3851
  if (layer->tileindex == NULL) {
861✔
3852
    psInfo = msOGRFileOpen(layer, (pszOverrideConnection ? pszOverrideConnection
855✔
3853
                                                         : layer->connection));
3854
    layer->layerinfo = psInfo;
855✔
3855
    layer->tileitemindex = -1;
855✔
3856

3857
    if (layer->layerinfo == NULL)
855✔
3858
      return MS_FAILURE;
3859
  }
3860

3861
  /* -------------------------------------------------------------------- */
3862
  /*      Otherwise we open the tile index, identify the tile item        */
3863
  /*      index and try to select the first file matching our query       */
3864
  /*      region.                                                         */
3865
  /* -------------------------------------------------------------------- */
3866
  else {
3867
    // Open tile index
3868

3869
    psInfo = msOGRFileOpen(layer, layer->tileindex);
6✔
3870
    layer->layerinfo = psInfo;
6✔
3871

3872
    if (layer->layerinfo == NULL)
6✔
3873
      return MS_FAILURE;
3874

3875
    // Identify TILEITEM
3876
    OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn(psInfo->hLayer);
6✔
3877
    layer->tileitemindex = OGR_FD_GetFieldIndex(hDefn, layer->tileitem);
6✔
3878
    if (layer->tileitemindex < 0) {
6✔
3879
      msSetError(MS_OGRERR,
×
3880
                 "Can't identify TILEITEM %s field in TILEINDEX `%s'.",
3881
                 "msOGRLayerOpen()", layer->tileitem, layer->tileindex);
3882
      msOGRFileClose(layer, psInfo);
×
3883
      layer->layerinfo = NULL;
×
3884
      return MS_FAILURE;
×
3885
    }
3886

3887
    // Identify TILESRS
3888
    if (layer->tilesrs != NULL &&
9✔
3889
        OGR_FD_GetFieldIndex(hDefn, layer->tilesrs) < 0) {
3✔
3890
      msSetError(MS_OGRERR,
×
3891
                 "Can't identify TILESRS %s field in TILEINDEX `%s'.",
3892
                 "msOGRLayerOpen()", layer->tilesrs, layer->tileindex);
3893
      msOGRFileClose(layer, psInfo);
×
3894
      layer->layerinfo = NULL;
×
3895
      return MS_FAILURE;
×
3896
    }
3897
    if (layer->tilesrs != NULL && layer->projection.numargs == 0) {
6✔
3898
      msSetError(MS_OGRERR,
×
3899
                 "A layer with TILESRS set in TILEINDEX `%s' must have a "
3900
                 "projection set on itself.",
3901
                 "msOGRLayerOpen()", layer->tileindex);
3902
      msOGRFileClose(layer, psInfo);
×
3903
      layer->layerinfo = NULL;
×
3904
      return MS_FAILURE;
×
3905
    }
3906
  }
3907

3908
  /* ------------------------------------------------------------------
3909
   * If projection was "auto" then set proj to the dataset's projection.
3910
   * For a tile index, it is assume the tile index has the projection.
3911
   * ------------------------------------------------------------------ */
3912
  if (layer->projection.numargs > 0 &&
861✔
3913
      EQUAL(layer->projection.args[0], "auto")) {
765✔
3914
    ACQUIRE_OGR_LOCK;
1✔
3915
    OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef(psInfo->hLayer);
1✔
3916

3917
    if (msOGRSpatialRef2ProjectionObj(hSRS, &(layer->projection),
1✔
3918
                                      layer->debug) != MS_SUCCESS) {
3919
      errorObj *ms_error = msGetErrorObj();
×
3920

3921
      RELEASE_OGR_LOCK;
×
3922
      msSetError(MS_OGRERR,
×
3923
                 "%s  "
3924
                 "PROJECTION AUTO cannot be used for this "
3925
                 "OGR connection (in layer `%s').",
3926
                 "msOGRLayerOpen()", ms_error->message,
×
3927
                 layer->name ? layer->name : "(null)");
×
3928
      msOGRFileClose(layer, psInfo);
×
3929
      layer->layerinfo = NULL;
×
3930
      return (MS_FAILURE);
×
3931
    }
3932
    RELEASE_OGR_LOCK;
1✔
3933
  }
3934

3935
  return MS_SUCCESS;
3936
}
3937

3938
/**********************************************************************
3939
 *                     msOGRLayerOpenVT()
3940
 *
3941
 * Overloaded version of msOGRLayerOpen for virtual table architecture
3942
 **********************************************************************/
3943
static int msOGRLayerOpenVT(layerObj *layer) {
933✔
3944
  return msOGRLayerOpen(layer, NULL);
1,012✔
3945
}
3946

3947
/**********************************************************************
3948
 *                     msOGRLayerClose()
3949
 **********************************************************************/
3950
int msOGRLayerClose(layerObj *layer) {
871✔
3951
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
871✔
3952

3953
  if (psInfo) {
871✔
3954
    if (layer->debug)
861✔
3955
      msDebug("msOGRLayerClose(%s).\n", layer->connection);
5✔
3956

3957
    msOGRFileClose(layer, psInfo);
861✔
3958
    layer->layerinfo = NULL;
861✔
3959
  }
3960

3961
  return MS_SUCCESS;
871✔
3962
}
3963

3964
/**********************************************************************
3965
 *                     msOGRLayerIsOpen()
3966
 **********************************************************************/
3967
static int msOGRLayerIsOpen(layerObj *layer) {
1,521✔
3968
  if (layer->layerinfo)
2,009✔
3969
    return MS_TRUE;
400✔
3970

3971
  return MS_FALSE;
3972
}
3973

3974
int msOGRSupportsIsNull(layerObj *layer) {
4✔
3975
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
4✔
3976
  if (psInfo && psInfo->dialect &&
4✔
3977
      (EQUAL(psInfo->dialect, "Spatialite") ||
2✔
3978
       EQUAL(psInfo->dialect, "GPKG"))) {
1✔
3979
    // reasons to not produce native string: not simple layer, or an explicit
3980
    // deny
3981
    const char *do_this =
3982
        msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
2✔
3983
    if (do_this && strcmp(do_this, "NO") == 0) {
2✔
3984
      return MS_FALSE;
3985
    }
3986
    return MS_TRUE;
3987
  }
3988

3989
  return MS_FALSE;
3990
}
3991

3992
/**********************************************************************
3993
 *                     msOGRLayerWhichShapes()
3994
 *
3995
 * Init OGR layer structs ready for calls to msOGRLayerNextShape().
3996
 *
3997
 * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
3998
 * layer's FILTER overlaps the selected region.
3999
 **********************************************************************/
4000
int msOGRLayerWhichShapes(layerObj *layer, rectObj rect, int /*isQuery*/) {
299✔
4001
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
299✔
4002
  int status;
4003

4004
  if (psInfo == NULL || psInfo->hLayer == NULL) {
299✔
4005
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4006
               "msOGRLayerWhichShapes()");
4007
    return (MS_FAILURE);
×
4008
  }
4009

4010
  status = msOGRFileWhichShapes(layer, rect, psInfo);
299✔
4011

4012
  // Update itemindexes / layer->iteminfo
4013
  if (status == MS_SUCCESS)
299✔
4014
    msOGRLayerInitItemInfo(layer);
299✔
4015

4016
  if (status != MS_SUCCESS || layer->tileindex == NULL)
299✔
4017
    return status;
297✔
4018

4019
  // If we are using a tile index, we need to advance to the first
4020
  // tile matching the spatial query, and load it.
4021

4022
  return msOGRFileReadTile(layer, psInfo);
2✔
4023
}
4024

4025
/**********************************************************************
4026
 *                     msOGRLayerGetItems()
4027
 *
4028
 * Load item (i.e. field) names in a char array.  If we are working
4029
 * with a tiled layer, ensure a tile is loaded and use it for the items.
4030
 * It is implicitly assumed that the schemas will match on all tiles.
4031
 **********************************************************************/
4032
int msOGRLayerGetItems(layerObj *layer) {
663✔
4033
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
663✔
4034

4035
  if (psInfo == NULL || psInfo->hLayer == NULL) {
663✔
4036
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4037
               "msOGRLayerGetItems()");
4038
    return (MS_FAILURE);
×
4039
  }
4040

4041
  if (layer->tileindex != NULL) {
663✔
4042
    if (psInfo->poCurTile == NULL &&
10✔
4043
        msOGRFileReadTile(layer, psInfo) != MS_SUCCESS)
5✔
4044
      return MS_FAILURE;
4045

4046
    psInfo = psInfo->poCurTile;
5✔
4047
  }
4048

4049
  layer->numitems = 0;
663✔
4050
  layer->items = msOGRFileGetItems(layer, psInfo);
663✔
4051
  if (layer->items == NULL)
663✔
4052
    return MS_FAILURE;
4053

4054
  while (layer->items[layer->numitems] != NULL)
6,522✔
4055
    layer->numitems++;
5,859✔
4056

4057
  return msOGRLayerInitItemInfo(layer);
663✔
4058
}
4059

4060
/**********************************************************************
4061
 *                     msOGRLayerInitItemInfo()
4062
 *
4063
 * Init the itemindexes array after items[] has been reset in a layer.
4064
 **********************************************************************/
4065
static int msOGRLayerInitItemInfo(layerObj *layer) {
1,248✔
4066
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
1,248✔
4067
  int i;
4068
  OGRFeatureDefnH hDefn;
4069

4070
  if (layer->numitems == 0)
1,248✔
4071
    return MS_SUCCESS;
4072

4073
  if (layer->tileindex != NULL) {
1,216✔
4074
    if (psInfo->poCurTile == NULL &&
15✔
4075
        msOGRFileReadTile(layer, psInfo, -2) != MS_SUCCESS)
×
4076
      return MS_FAILURE;
4077

4078
    psInfo = psInfo->poCurTile;
15✔
4079
  }
4080

4081
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,216✔
4082
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4083
               "msOGRLayerInitItemInfo()");
4084
    return (MS_FAILURE);
×
4085
  }
4086

4087
  if ((hDefn = OGR_L_GetLayerDefn(psInfo->hLayer)) == NULL) {
1,216✔
4088
    msSetError(MS_OGRERR, "Layer contains no fields.",
×
4089
               "msOGRLayerInitItemInfo()");
4090
    return (MS_FAILURE);
×
4091
  }
4092

4093
  if (layer->iteminfo)
1,216✔
4094
    free(layer->iteminfo);
512✔
4095
  if ((layer->iteminfo = (int *)malloc(sizeof(int) * layer->numitems)) ==
1,216✔
4096
      NULL) {
4097
    msSetError(MS_MEMERR, NULL, "msOGRLayerInitItemInfo()");
×
4098
    return (MS_FAILURE);
×
4099
  }
4100

4101
  int *itemindexes = (int *)layer->iteminfo;
4102
  for (i = 0; i < layer->numitems; i++) {
11,202✔
4103
    // Special case for handling text string and angle coming from
4104
    // OGR style strings.  We use special attribute snames.
4105
    if (EQUAL(layer->items[i], MSOGR_LABELFONTNAMENAME))
9,986✔
4106
      itemindexes[i] = MSOGR_LABELFONTNAMEINDEX;
×
4107
    else if (EQUAL(layer->items[i], MSOGR_LABELSIZENAME))
9,986✔
4108
      itemindexes[i] = MSOGR_LABELSIZEINDEX;
×
4109
    else if (EQUAL(layer->items[i], MSOGR_LABELTEXTNAME))
9,986✔
4110
      itemindexes[i] = MSOGR_LABELTEXTINDEX;
×
4111
    else if (EQUAL(layer->items[i], MSOGR_LABELANGLENAME))
9,986✔
4112
      itemindexes[i] = MSOGR_LABELANGLEINDEX;
×
4113
    else if (EQUAL(layer->items[i], MSOGR_LABELFCOLORNAME))
9,986✔
4114
      itemindexes[i] = MSOGR_LABELFCOLORINDEX;
×
4115
    else if (EQUAL(layer->items[i], MSOGR_LABELBCOLORNAME))
9,986✔
4116
      itemindexes[i] = MSOGR_LABELBCOLORINDEX;
×
4117
    else if (EQUAL(layer->items[i], MSOGR_LABELPLACEMENTNAME))
9,986✔
4118
      itemindexes[i] = MSOGR_LABELPLACEMENTINDEX;
×
4119
    else if (EQUAL(layer->items[i], MSOGR_LABELANCHORNAME))
9,986✔
4120
      itemindexes[i] = MSOGR_LABELANCHORINDEX;
×
4121
    else if (EQUAL(layer->items[i], MSOGR_LABELDXNAME))
9,986✔
4122
      itemindexes[i] = MSOGR_LABELDXINDEX;
×
4123
    else if (EQUAL(layer->items[i], MSOGR_LABELDYNAME))
9,986✔
4124
      itemindexes[i] = MSOGR_LABELDYINDEX;
×
4125
    else if (EQUAL(layer->items[i], MSOGR_LABELPERPNAME))
9,986✔
4126
      itemindexes[i] = MSOGR_LABELPERPINDEX;
×
4127
    else if (EQUAL(layer->items[i], MSOGR_LABELBOLDNAME))
9,986✔
4128
      itemindexes[i] = MSOGR_LABELBOLDINDEX;
×
4129
    else if (EQUAL(layer->items[i], MSOGR_LABELITALICNAME))
9,986✔
4130
      itemindexes[i] = MSOGR_LABELITALICINDEX;
×
4131
    else if (EQUAL(layer->items[i], MSOGR_LABELUNDERLINENAME))
9,986✔
4132
      itemindexes[i] = MSOGR_LABELUNDERLINEINDEX;
×
4133
    else if (EQUAL(layer->items[i], MSOGR_LABELPRIORITYNAME))
9,986✔
4134
      itemindexes[i] = MSOGR_LABELPRIORITYINDEX;
×
4135
    else if (EQUAL(layer->items[i], MSOGR_LABELSTRIKEOUTNAME))
9,986✔
4136
      itemindexes[i] = MSOGR_LABELSTRIKEOUTINDEX;
×
4137
    else if (EQUAL(layer->items[i], MSOGR_LABELSTRETCHNAME))
9,986✔
4138
      itemindexes[i] = MSOGR_LABELSTRETCHINDEX;
×
4139
    else if (EQUAL(layer->items[i], MSOGR_LABELADJHORNAME))
9,986✔
4140
      itemindexes[i] = MSOGR_LABELADJHORINDEX;
×
4141
    else if (EQUAL(layer->items[i], MSOGR_LABELADJVERTNAME))
9,986✔
4142
      itemindexes[i] = MSOGR_LABELADJVERTINDEX;
×
4143
    else if (EQUAL(layer->items[i], MSOGR_LABELHCOLORNAME))
9,986✔
4144
      itemindexes[i] = MSOGR_LABELHCOLORINDEX;
×
4145
    else if (EQUAL(layer->items[i], MSOGR_LABELOCOLORNAME))
9,986✔
4146
      itemindexes[i] = MSOGR_LABELOCOLORINDEX;
×
4147
    else if (EQUALN(layer->items[i], MSOGR_LABELPARAMNAME,
9,986✔
4148
                    MSOGR_LABELPARAMNAMELEN))
4149
      itemindexes[i] = MSOGR_LABELPARAMINDEX +
×
4150
                       atoi(layer->items[i] + MSOGR_LABELPARAMNAMELEN);
×
4151
    else if (EQUALN(layer->items[i], MSOGR_BRUSHPARAMNAME,
9,986✔
4152
                    MSOGR_BRUSHPARAMNAMELEN))
4153
      itemindexes[i] = MSOGR_BRUSHPARAMINDEX +
×
4154
                       atoi(layer->items[i] + MSOGR_BRUSHPARAMNAMELEN);
×
4155
    else if (EQUALN(layer->items[i], MSOGR_PENPARAMNAME, MSOGR_PENPARAMNAMELEN))
9,986✔
4156
      itemindexes[i] =
×
4157
          MSOGR_PENPARAMINDEX + atoi(layer->items[i] + MSOGR_PENPARAMNAMELEN);
×
4158
    else if (EQUALN(layer->items[i], MSOGR_SYMBOLPARAMNAME,
9,986✔
4159
                    MSOGR_SYMBOLPARAMNAMELEN))
4160
      itemindexes[i] = MSOGR_SYMBOLPARAMINDEX +
×
4161
                       atoi(layer->items[i] + MSOGR_SYMBOLPARAMNAMELEN);
×
4162
    else {
4163
      itemindexes[i] = OGR_FD_GetFieldIndex(hDefn, layer->items[i]);
9,986✔
4164
      if (itemindexes[i] == -1) {
9,986✔
4165
        if (EQUAL(layer->items[i], OGR_L_GetFIDColumn(psInfo->hLayer))) {
×
4166
          itemindexes[i] = MSOGR_FID_INDEX;
×
4167
        }
4168
      }
4169
    }
4170
    if (itemindexes[i] == -1) {
9,986✔
4171
      msSetError(MS_OGRERR, "Invalid Field name: %s in layer `%s'",
×
4172
                 "msOGRLayerInitItemInfo()", layer->items[i],
×
4173
                 layer->name ? layer->name : "(null)");
×
4174
      return (MS_FAILURE);
×
4175
    }
4176
  }
4177

4178
  return (MS_SUCCESS);
4179
}
4180

4181
/**********************************************************************
4182
 *                     msOGRLayerFreeItemInfo()
4183
 *
4184
 * Free the itemindexes array in a layer.
4185
 **********************************************************************/
4186
void msOGRLayerFreeItemInfo(layerObj *layer) {
1,840✔
4187

4188
  if (layer->iteminfo)
1,840✔
4189
    free(layer->iteminfo);
704✔
4190
  layer->iteminfo = NULL;
1,840✔
4191
}
1,840✔
4192

4193
/**********************************************************************
4194
 *                     msOGRLayerNextShape()
4195
 *
4196
 * Returns shape sequentially from OGR data source.
4197
 * msOGRLayerWhichShape() must have been called first.
4198
 *
4199
 * Returns MS_SUCCESS/MS_FAILURE
4200
 **********************************************************************/
4201
int msOGRLayerNextShape(layerObj *layer, shapeObj *shape) {
15,376✔
4202
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
15,376✔
4203
  int status;
4204

4205
  if (psInfo == NULL || psInfo->hLayer == NULL) {
15,376✔
4206
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4207
               "msOGRLayerNextShape()");
4208
    return (MS_FAILURE);
×
4209
  }
4210

4211
  if (layer->tileindex == NULL)
15,376✔
4212
    return msOGRFileNextShape(layer, shape, psInfo);
15,371✔
4213

4214
  // Do we need to load the first tile?
4215
  if (psInfo->poCurTile == NULL) {
5✔
4216
    status = msOGRFileReadTile(layer, psInfo);
×
4217
    if (status != MS_SUCCESS)
×
4218
      return status;
4219
  }
4220

4221
  do {
4222
    // Try getting a shape from this tile.
4223
    status = msOGRFileNextShape(layer, shape, psInfo->poCurTile);
6✔
4224
    if (status != MS_DONE) {
6✔
4225
      if (psInfo->sTileProj.numargs > 0) {
3✔
4226
        msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
2✔
4227
      }
4228

4229
      return status;
3✔
4230
    }
4231

4232
    // try next tile.
4233
    status = msOGRFileReadTile(layer, psInfo);
3✔
4234
    if (status != MS_SUCCESS)
3✔
4235
      return status;
2✔
4236
  } while (status == MS_SUCCESS);
4237
  return status; // make compiler happy. this is never reached however
4238
}
4239

4240
/**********************************************************************
4241
 *                     msOGRLayerGetShape()
4242
 *
4243
 * Returns shape from OGR data source by fid.
4244
 *
4245
 * Returns MS_SUCCESS/MS_FAILURE
4246
 **********************************************************************/
4247
int msOGRLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) {
1,309✔
4248
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
1,309✔
4249

4250
  long shapeindex = record->shapeindex;
1,309✔
4251
  int tileindex = record->tileindex;
1,309✔
4252
  int resultindex = record->resultindex;
1,309✔
4253
  int record_is_fid = TRUE;
4254

4255
  /* set the resultindex as shapeindex if available */
4256
  if (resultindex >= 0) {
1,309✔
4257
    record_is_fid = FALSE;
4258
    shapeindex = resultindex;
1,307✔
4259
  }
4260

4261
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,309✔
4262
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4263
               "msOGRLayerGetShape()");
4264
    return (MS_FAILURE);
×
4265
  }
4266

4267
  if (layer->tileindex == NULL)
1,309✔
4268
    return msOGRFileGetShape(layer, shape, shapeindex, psInfo, record_is_fid);
1,306✔
4269
  else {
4270
    if (psInfo->poCurTile == NULL || psInfo->poCurTile->nTileId != tileindex) {
3✔
4271
      if (msOGRFileReadTile(layer, psInfo, tileindex) != MS_SUCCESS)
3✔
4272
        return MS_FAILURE;
4273
    }
4274

4275
    int status = msOGRFileGetShape(layer, shape, shapeindex, psInfo->poCurTile,
3✔
4276
                                   record_is_fid);
4277
    if (status == MS_SUCCESS && psInfo->sTileProj.numargs > 0) {
3✔
4278
      msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
2✔
4279
    }
4280
    return status;
3✔
4281
  }
4282
}
4283

4284
/**********************************************************************
4285
 *                     msOGRLayerGetExtent()
4286
 *
4287
 * Returns the layer extents.
4288
 *
4289
 * Returns MS_SUCCESS/MS_FAILURE
4290
 **********************************************************************/
4291
int msOGRLayerGetExtent(layerObj *layer, rectObj *extent) {
76✔
4292
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
76✔
4293
  OGREnvelope oExtent;
4294

4295
  if (psInfo == NULL || psInfo->hLayer == NULL) {
76✔
4296
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4297
               "msOGRLayerGetExtent()");
4298
    return (MS_FAILURE);
×
4299
  }
4300

4301
  /* ------------------------------------------------------------------
4302
   * Call OGR's GetExtent()... note that for some formats this will
4303
   * result in a scan of the whole layer and can be an expensive call.
4304
   *
4305
   * For tile indexes layers we assume it is sufficient to get the
4306
   * extents of the tile index.
4307
   * ------------------------------------------------------------------ */
4308
  ACQUIRE_OGR_LOCK;
76✔
4309
  if (OGR_L_GetExtent(psInfo->hLayer, &oExtent, TRUE) != OGRERR_NONE) {
76✔
4310
    RELEASE_OGR_LOCK;
×
4311
    msSetError(MS_MISCERR, "Unable to get extents for this layer.",
×
4312
               "msOGRLayerGetExtent()");
4313
    return (MS_FAILURE);
×
4314
  }
4315
  RELEASE_OGR_LOCK;
76✔
4316

4317
  extent->minx = oExtent.MinX;
76✔
4318
  extent->miny = oExtent.MinY;
76✔
4319
  extent->maxx = oExtent.MaxX;
76✔
4320
  extent->maxy = oExtent.MaxY;
76✔
4321

4322
  return MS_SUCCESS;
76✔
4323
}
4324

4325
/**********************************************************************
4326
 *                     msOGRLayerGetNumFeatures()
4327
 *
4328
 * Returns the layer feature count.
4329
 *
4330
 * Returns the number of features on success, -1 on error
4331
 **********************************************************************/
4332
int msOGRLayerGetNumFeatures(layerObj *layer) {
×
4333
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
×
4334
  int result;
4335

4336
  if (psInfo == NULL || psInfo->hLayer == NULL) {
×
4337
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4338
               "msOGRLayerGetNumFeatures()");
4339
    return -1;
×
4340
  }
4341

4342
  /* ------------------------------------------------------------------
4343
   * Call OGR's GetFeatureCount()... note that for some formats this will
4344
   * result in a scan of the whole layer and can be an expensive call.
4345
   * ------------------------------------------------------------------ */
4346
  ACQUIRE_OGR_LOCK;
×
4347
  result = (int)OGR_L_GetFeatureCount(psInfo->hLayer, TRUE);
×
4348
  RELEASE_OGR_LOCK;
×
4349

4350
  return result;
×
4351
}
4352

4353
/**********************************************************************
4354
 *                     msOGRGetSymbolId()
4355
 *
4356
 * Returns a MapServer symbol number matching one of the symbols from
4357
 * the OGR symbol id string.  If not found then try to locate the
4358
 * default symbol name, and if not found return 0.
4359
 **********************************************************************/
4360
static int msOGRGetSymbolId(symbolSetObj *symbolset, const char *pszSymbolId,
16✔
4361
                            const char *pszDefaultSymbol,
4362
                            int try_addimage_if_notfound) {
4363
  // Symbol name mapping:
4364
  // First look for the native symbol name, then the ogr-...
4365
  // generic name, and in last resort try pszDefaultSymbol if
4366
  // provided by user.
4367
  char **params;
4368
  int numparams;
4369
  int nSymbol = -1;
4370

4371
  if (pszSymbolId && pszSymbolId[0] != '\0') {
16✔
4372
    params = msStringSplit(pszSymbolId, ',', &numparams);
16✔
4373
    if (params != NULL) {
16✔
4374
      for (int j = 0; j < numparams && nSymbol == -1; j++) {
32✔
4375
        nSymbol =
4376
            msGetSymbolIndex(symbolset, params[j], try_addimage_if_notfound);
16✔
4377
      }
4378
      msFreeCharArray(params, numparams);
16✔
4379
    }
4380
  }
4381
  if (nSymbol == -1 && pszDefaultSymbol) {
16✔
4382
    nSymbol = msGetSymbolIndex(symbolset, (char *)pszDefaultSymbol,
×
4383
                               try_addimage_if_notfound);
4384
  }
4385
  if (nSymbol == -1)
16✔
4386
    nSymbol = 0;
4387

4388
  return nSymbol;
16✔
4389
}
4390

4391
static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4392
                                      OGRStyleToolH hLabelStyle);
4393
static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
4394
                                    OGRStyleToolH hPenStyle, int bIsBrush,
4395
                                    int *pbPriority);
4396
static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
4397
                                      OGRStyleToolH hBrushStyle, int *pbIsBrush,
4398
                                      int *pbPriority);
4399
static int msOGRUpdateStyleParseSymbol(mapObj *map, styleObj *s,
4400
                                       OGRStyleToolH hSymbolStyle,
4401
                                       int *pbPriority);
4402
static int msOGRAddBgColorStyleParseBrush(styleObj *s,
4403
                                          OGRStyleToolH hSymbolStyle);
4404

4405
static int msOGRUpdateStyleCheckPenBrushOnly(OGRStyleMgrH hStyleMgr) {
39✔
4406
  int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
39✔
4407
  int countPen = 0, countBrush = 0;
4408
  int bIsNull;
4409

4410
  for (int i = 0; i < numParts; i++) {
78✔
4411
    OGRSTClassId eStylePartType;
4412
    OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
42✔
4413
    if (!hStylePart)
42✔
4414
      continue;
×
4415

4416
    eStylePartType = OGR_ST_GetType(hStylePart);
42✔
4417
    if (eStylePartType == OGRSTCPen) {
42✔
4418
      countPen++;
34✔
4419
      OGR_ST_GetParamNum(hStylePart, OGRSTPenPriority, &bIsNull);
34✔
4420
      if (!bIsNull) {
34✔
4421
        OGR_ST_Destroy(hStylePart);
3✔
4422
        return MS_FALSE;
3✔
4423
      }
4424
    } else if (eStylePartType == OGRSTCBrush) {
8✔
4425
      countBrush++;
4✔
4426
      OGR_ST_GetParamNum(hStylePart, OGRSTBrushPriority, &bIsNull);
4✔
4427
      if (!bIsNull) {
4✔
4428
        OGR_ST_Destroy(hStylePart);
×
4429
        return MS_FALSE;
×
4430
      }
4431
    } else if (eStylePartType == OGRSTCSymbol) {
4✔
4432
      OGR_ST_Destroy(hStylePart);
×
4433
      return MS_FALSE;
×
4434
    }
4435
    OGR_ST_Destroy(hStylePart);
39✔
4436
  }
4437
  return (countPen == 1 && countBrush == 1);
36✔
4438
}
4439

4440
/**********************************************************************
4441
 *                     msOGRUpdateStyle()
4442
 *
4443
 * Update the mapserver style according to the ogr style.
4444
 * The function is called by msOGRGetAutoStyle and
4445
 * msOGRUpdateStyleFromString
4446
 **********************************************************************/
4447

4448
typedef struct {
4449
  int nPriority; /* the explicit priority as specified by the 'l' option of PEN,
4450
                    BRUSH and SYMBOL tools */
4451
  int nApparitionIndex; /* the index of the tool as parsed from the OGR feature
4452
                           style string */
4453
} StyleSortStruct;
4454

4455
static int msOGRUpdateStyleSortFct(const void *pA, const void *pB) {
4✔
4456
  StyleSortStruct *sssa = (StyleSortStruct *)pA;
4457
  StyleSortStruct *sssb = (StyleSortStruct *)pB;
4458
  if (sssa->nPriority < sssb->nPriority)
4✔
4459
    return -1;
4460
  else if (sssa->nPriority > sssb->nPriority)
3✔
4461
    return 1;
4462
  else if (sssa->nApparitionIndex < sssb->nApparitionIndex)
2✔
4463
    return -1;
4464
  else
4465
    return 1;
×
4466
}
4467

4468
static int msOGRUpdateStyle(OGRStyleMgrH hStyleMgr, mapObj *map,
39✔
4469
                            layerObj *layer, classObj *c) {
4470
  int bIsBrush = MS_FALSE;
39✔
4471
  int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
39✔
4472
  int nPriority;
4473
  int bIsPenBrushOnly = msOGRUpdateStyleCheckPenBrushOnly(hStyleMgr);
39✔
4474
  StyleSortStruct *pasSortStruct =
4475
      (StyleSortStruct *)msSmallMalloc(sizeof(StyleSortStruct) * numParts);
39✔
4476
  int iSortStruct = 0;
4477
  int iBaseStyleIndex = c->numstyles;
39✔
4478
  int i;
4479

4480
  /* ------------------------------------------------------------------
4481
   * Handle each part
4482
   * ------------------------------------------------------------------ */
4483

4484
  for (i = 0; i < numParts; i++) {
84✔
4485
    OGRSTClassId eStylePartType;
4486
    OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
45✔
4487
    if (!hStylePart)
45✔
4488
      continue;
×
4489
    eStylePartType = OGR_ST_GetType(hStylePart);
45✔
4490
    nPriority = INT_MIN;
45✔
4491

4492
    // We want all size values returned in pixels.
4493
    //
4494
    // The scale factor that OGR expect is the ground/paper scale
4495
    // e.g. if 1 ground unit = 0.01 paper unit then scale=1/0.01=100
4496
    // cellsize if number of ground units/pixel, and OGR assumes that
4497
    // there is 72*39.37 pixels/ground units (since meter is assumed
4498
    // for ground... but what ground units we have does not matter
4499
    // as long as use the same assumptions everywhere)
4500
    // That gives scale = cellsize*72*39.37
4501

4502
    OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
45✔
4503
                   map->cellsize * map->resolution / map->defresolution * 72.0 *
45✔
4504
                       39.37);
4505

4506
    if (eStylePartType == OGRSTCLabel) {
45✔
4507
      int ret = msOGRUpdateStyleParseLabel(map, layer, c, hStylePart);
4✔
4508
      if (ret != MS_SUCCESS) {
4✔
4509
        OGR_ST_Destroy(hStylePart);
×
4510
        msFree(pasSortStruct);
×
4511
        return ret;
×
4512
      }
4513
    } else if (eStylePartType == OGRSTCPen) {
41✔
4514
      styleObj *s;
4515
      int nIndex;
4516
      if (bIsPenBrushOnly) {
37✔
4517
        /* Historic behavior when there is a PEN and BRUSH only */
4518
        if (bIsBrush || layer->type == MS_LAYER_POLYGON)
2✔
4519
          // This is a multipart symbology, so pen defn goes in the
4520
          // overlaysymbol params
4521
          nIndex = c->numstyles + 1;
2✔
4522
        else
4523
          nIndex = c->numstyles;
×
4524
      } else
4525
        nIndex = c->numstyles;
35✔
4526

4527
      if (msMaybeAllocateClassStyle(c, nIndex)) {
37✔
4528
        OGR_ST_Destroy(hStylePart);
×
4529
        msFree(pasSortStruct);
×
4530
        return (MS_FAILURE);
×
4531
      }
4532
      s = c->styles[nIndex];
37✔
4533

4534
      msOGRUpdateStyleParsePen(map, layer, s, hStylePart, bIsBrush, &nPriority);
37✔
4535

4536
    } else if (eStylePartType == OGRSTCBrush) {
4✔
4537

4538
      styleObj *s;
4539
      int nIndex = 0;
4540

4541
      int bBgColorIsNull = MS_TRUE;
4✔
4542
      OGR_ST_GetParamStr(hStylePart, OGRSTBrushBColor, &bBgColorIsNull);
4✔
4543

4544
      if (!bBgColorIsNull) {
4✔
4545

4546
        if (msMaybeAllocateClassStyle(c, nIndex)) {
3✔
4547
          OGR_ST_Destroy(hStylePart);
×
4548
          msFree(pasSortStruct);
×
4549
          return (MS_FAILURE);
×
4550
        }
4551

4552
        // add a backgroundcolor as a separate style
4553
        s = c->styles[nIndex];
3✔
4554
        msOGRAddBgColorStyleParseBrush(s, hStylePart);
3✔
4555
      }
4556

4557
      nIndex = (bIsPenBrushOnly) ? nIndex : c->numstyles;
4✔
4558

4559
      if (!bBgColorIsNull) {
4✔
4560
        // if we have a bgcolor style we need to increase the index
4561
        nIndex += 1;
3✔
4562
      }
4563

4564
      /* We need 1 style */
4565
      if (msMaybeAllocateClassStyle(c, nIndex)) {
4✔
4566
        OGR_ST_Destroy(hStylePart);
×
4567
        msFree(pasSortStruct);
×
4568
        return (MS_FAILURE);
×
4569
      }
4570
      s = c->styles[nIndex];
4✔
4571

4572
      msOGRUpdateStyleParseBrush(map, layer, s, hStylePart, &bIsBrush,
4✔
4573
                                 &nPriority);
4574

4575
    } else if (eStylePartType == OGRSTCSymbol) {
×
4576
      styleObj *s;
4577
      /* We need 1 style */
4578
      int nIndex = c->numstyles;
×
4579
      if (msMaybeAllocateClassStyle(c, nIndex)) {
×
4580
        OGR_ST_Destroy(hStylePart);
×
4581
        msFree(pasSortStruct);
×
4582
        return (MS_FAILURE);
×
4583
      }
4584
      s = c->styles[nIndex];
×
4585

4586
      msOGRUpdateStyleParseSymbol(map, s, hStylePart, &nPriority);
×
4587
    }
4588

4589
    /* Memorize the explicit priority and apparition order of the parsed
4590
     * tool/style */
4591
    if (!bIsPenBrushOnly &&
45✔
4592
        (eStylePartType == OGRSTCPen || eStylePartType == OGRSTCBrush ||
41✔
4593
         eStylePartType == OGRSTCSymbol)) {
4594
      pasSortStruct[iSortStruct].nPriority = nPriority;
37✔
4595
      pasSortStruct[iSortStruct].nApparitionIndex = iSortStruct;
37✔
4596
      iSortStruct++;
37✔
4597
    }
4598

4599
    OGR_ST_Destroy(hStylePart);
45✔
4600
  }
4601

4602
  if (iSortStruct > 1 && !bIsPenBrushOnly) {
39✔
4603
    /* Compute style order based on their explicit priority and apparition order
4604
     */
4605
    qsort(pasSortStruct, iSortStruct, sizeof(StyleSortStruct),
4✔
4606
          msOGRUpdateStyleSortFct);
4607

4608
    /* Now reorder styles in c->styles */
4609
    styleObj **ppsStyleTmp =
4610
        (styleObj **)msSmallMalloc(iSortStruct * sizeof(styleObj *));
4✔
4611
    memcpy(ppsStyleTmp, c->styles + iBaseStyleIndex,
4✔
4612
           iSortStruct * sizeof(styleObj *));
4613
    for (i = 0; i < iSortStruct; i++) {
12✔
4614
      c->styles[iBaseStyleIndex + i] =
8✔
4615
          ppsStyleTmp[pasSortStruct[i].nApparitionIndex];
8✔
4616
    }
4617
    msFree(ppsStyleTmp);
4✔
4618
  }
4619

4620
  msFree(pasSortStruct);
39✔
4621

4622
  return MS_SUCCESS;
39✔
4623
}
4624

4625
static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4✔
4626
                                      OGRStyleToolH hLabelStyle) {
4627
  int bIsNull;
4628
  int r = 0, g = 0, b = 0, t = 0;
4✔
4629

4630
  // Enclose the text string inside quotes to make sure it is seen
4631
  // as a string by the parser inside loadExpression(). (bug185)
4632
  /* See bug 3481 about the isalnum hack */
4633
  const char *labelTextString =
4634
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelTextString, &bIsNull);
4✔
4635

4636
  if (c->numlabels == 0) {
4✔
4637
    /* allocate a new label object */
4638
    if (msGrowClassLabels(c) == NULL)
4✔
4639
      return MS_FAILURE;
4640
    c->numlabels++;
4✔
4641
    initLabel(c->labels[0]);
4✔
4642
  }
4643
  msFreeExpression(&c->labels[0]->text);
4✔
4644
  c->labels[0]->text.type = MS_STRING;
4✔
4645
  c->labels[0]->text.string = msStrdup(labelTextString);
4✔
4646

4647
  c->labels[0]->angle =
8✔
4648
      OGR_ST_GetParamDbl(hLabelStyle, OGRSTLabelAngle, &bIsNull);
4✔
4649

4650
  c->labels[0]->size =
4✔
4651
      OGR_ST_GetParamDbl(hLabelStyle, OGRSTLabelSize, &bIsNull);
4✔
4652
  if (c->labels[0]->size < 1) /* no point dropping to zero size */
4✔
4653
    c->labels[0]->size = 1;
×
4654

4655
  // OGR default is anchor point = LL, so label is at UR of anchor
4656
  c->labels[0]->position = MS_UR;
4✔
4657

4658
  int nPosition = OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelAnchor, &bIsNull);
4✔
4659
  if (!bIsNull) {
4✔
4660
    switch (nPosition) {
4✔
4661
    case 1:
4✔
4662
      c->labels[0]->position = MS_UR;
4✔
4663
      break;
4✔
4664
    case 2:
×
4665
      c->labels[0]->position = MS_UC;
×
4666
      break;
×
4667
    case 3:
×
4668
      c->labels[0]->position = MS_UL;
×
4669
      break;
×
4670
    case 4:
×
4671
      c->labels[0]->position = MS_CR;
×
4672
      break;
×
4673
    case 5:
×
4674
      c->labels[0]->position = MS_CC;
×
4675
      break;
×
4676
    case 6:
×
4677
      c->labels[0]->position = MS_CL;
×
4678
      break;
×
4679
    case 7:
×
4680
      c->labels[0]->position = MS_LR;
×
4681
      break;
×
4682
    case 8:
×
4683
      c->labels[0]->position = MS_LC;
×
4684
      break;
×
4685
    case 9:
×
4686
      c->labels[0]->position = MS_LL;
×
4687
      break;
×
4688
    case 10:
×
4689
      c->labels[0]->position = MS_UR;
×
4690
      break; /*approximate*/
×
4691
    case 11:
×
4692
      c->labels[0]->position = MS_UC;
×
4693
      break;
×
4694
    case 12:
×
4695
      c->labels[0]->position = MS_UL;
×
4696
      break;
×
4697
    default:
4698
      break;
4699
    }
4700
  }
4701

4702
  const char *pszColor =
4703
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFColor, &bIsNull);
4✔
4704
  if (!bIsNull &&
8✔
4705
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
4✔
4706
    MS_INIT_COLOR(c->labels[0]->color, r, g, b, t);
4✔
4707
  }
4708

4709
  pszColor = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelHColor, &bIsNull);
4✔
4710
  if (!bIsNull &&
4✔
4711
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
×
4712
    MS_INIT_COLOR(c->labels[0]->shadowcolor, r, g, b, t);
×
4713
  }
4714

4715
  pszColor = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelOColor, &bIsNull);
4✔
4716
  if (!bIsNull &&
4✔
4717
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
×
4718
    MS_INIT_COLOR(c->labels[0]->outlinecolor, r, g, b, t);
×
4719
  }
4720

4721
  const char *pszBold =
4722
      OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelBold, &bIsNull) ? "-bold" : "";
4✔
4723
  const char *pszItalic =
4724
      OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelItalic, &bIsNull) ? "-italic"
4✔
4725
                                                                  : "";
4726
  const char *pszFontName =
4727
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFontName, &bIsNull);
4✔
4728
  /* replace spaces with hyphens to allow mapping to a valid hashtable entry*/
4729
  char *pszFontNameEscaped = NULL;
4730
  if (pszFontName != NULL) {
4✔
4731
    pszFontNameEscaped = msStrdup(pszFontName);
4✔
4732
    msReplaceChar(pszFontNameEscaped, ' ', '-');
4✔
4733
  }
4734

4735
  bool bFont = true;
4736

4737
  if (pszFontNameEscaped != NULL && !bIsNull && pszFontNameEscaped[0] != '\0') {
4✔
4738
    const char *pszName =
4739
        CPLSPrintf("%s%s%s", pszFontNameEscaped, pszBold, pszItalic);
4✔
4740
    if (msLookupHashTable(&(map->fontset.fonts), (char *)pszName) != NULL) {
4✔
4741
      c->labels[0]->font = msStrdup(pszName);
×
4742
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
4743
        msDebug("** Using '%s' TTF font **\n", pszName);
×
4744
    } else if ((strcmp(pszFontNameEscaped, pszName) != 0) &&
4✔
4745
               msLookupHashTable(&(map->fontset.fonts),
×
4746
                                 (char *)pszFontNameEscaped) != NULL) {
4747
      c->labels[0]->font = msStrdup(pszFontNameEscaped);
×
4748
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
4749
        msDebug("** Using '%s' TTF font **\n", pszFontNameEscaped);
×
4750
    } else if (msLookupHashTable(&(map->fontset.fonts), "default") != NULL) {
4✔
4751
      c->labels[0]->font = msStrdup("default");
×
4752
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
4753
        msDebug("** Using 'default' TTF font **\n");
×
4754
    } else
4755
      bFont = false;
4756
  }
4757

4758
  msFree(pszFontNameEscaped);
4✔
4759

4760
  if (!bFont) {
4✔
4761
    c->labels[0]->size = MS_MEDIUM;
4✔
4762
  }
4763

4764
  return MS_SUCCESS;
4765
}
4766

4767
static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
37✔
4768
                                    OGRStyleToolH hPenStyle, int bIsBrush,
4769
                                    int *pbPriority) {
4770
  int bIsNull;
4771
  int r = 0, g = 0, b = 0, t = 0;
37✔
4772

4773
  const char *pszPenName, *pszPattern, *pszCap, *pszJoin;
4774
  colorObj oPenColor;
4775
  int nPenSymbol = 0;
4776
  int nPenSize = 1;
4777
  t = -1;
37✔
4778
  double pattern[MS_MAXPATTERNLENGTH];
4779
  int patternlength = 0;
4780
  int linecap = MS_CJC_DEFAULT_CAPS;
4781
  int linejoin = MS_CJC_DEFAULT_JOINS;
4782
  double offsetx = 0.0;
4783
  double offsety = 0.0;
4784

4785
  // Make sure pen is always initialized
4786
  MS_INIT_COLOR(oPenColor, -1, -1, -1, 255);
4787

4788
  pszPenName = OGR_ST_GetParamStr(hPenStyle, OGRSTPenId, &bIsNull);
37✔
4789
  if (bIsNull)
37✔
4790
    pszPenName = NULL;
4791
  // Check for Pen Pattern "ogr-pen-1": the invisible pen
4792
  // If that's what we have then set pen color to -1
4793
  if (pszPenName && strstr(pszPenName, "ogr-pen-1") != NULL) {
12✔
4794
    MS_INIT_COLOR(oPenColor, -1, -1, -1, 255);
4795
  } else {
4796
    const char *pszColor =
4797
        OGR_ST_GetParamStr(hPenStyle, OGRSTPenColor, &bIsNull);
37✔
4798
    if (!bIsNull &&
74✔
4799
        OGR_ST_GetRGBFromString(hPenStyle, pszColor, &r, &g, &b, &t)) {
37✔
4800
      MS_INIT_COLOR(oPenColor, r, g, b, t);
37✔
4801
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
37✔
4802
        msDebug("** PEN COLOR = %d %d %d **\n", r, g, b);
×
4803
    }
4804

4805
    nPenSize = OGR_ST_GetParamNum(hPenStyle, OGRSTPenWidth, &bIsNull);
37✔
4806
    if (bIsNull)
37✔
4807
      nPenSize = 1;
4808
    if (pszPenName != NULL) {
37✔
4809
      // Try to match pen name in symbol file
4810
      nPenSymbol =
4811
          msOGRGetSymbolId(&(map->symbolset), pszPenName, NULL, MS_FALSE);
12✔
4812
    }
4813
  }
4814

4815
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
37✔
4816
    msDebug("** PEN COLOR = %d %d %d **\n", oPenColor.red, oPenColor.green,
×
4817
            oPenColor.blue);
4818

4819
  pszPattern = OGR_ST_GetParamStr(hPenStyle, OGRSTPenPattern, &bIsNull);
37✔
4820
  if (bIsNull)
37✔
4821
    pszPattern = NULL;
4822
  if (pszPattern != NULL) {
3✔
4823
    char **papszTokens =
4824
        CSLTokenizeStringComplex(pszPattern, " ", FALSE, FALSE);
3✔
4825
    int nTokenCount = CSLCount(papszTokens);
3✔
4826
    int bValidFormat = TRUE;
4827
    if (nTokenCount >= 2 && nTokenCount <= MS_MAXPATTERNLENGTH) {
3✔
4828
      for (int i = 0; i < nTokenCount; i++) {
6✔
4829
        if (strlen(papszTokens[i]) > 2 &&
5✔
4830
            strcmp(papszTokens[i] + strlen(papszTokens[i]) - 2, "px") == 0) {
4✔
4831
          pattern[patternlength++] = CPLAtof(papszTokens[i]);
4✔
4832
        } else {
4833
          bValidFormat = FALSE;
4834
          patternlength = 0;
4835
          break;
4836
        }
4837
      }
4838
    } else
4839
      bValidFormat = FALSE;
4840
    if (!bValidFormat && layer->debug >= MS_DEBUGLEVEL_VVV)
3✔
4841
      msDebug("Invalid/unhandled pen pattern format = %s\n", pszPattern);
×
4842
    CSLDestroy(papszTokens);
3✔
4843
  }
4844

4845
  pszCap = OGR_ST_GetParamStr(hPenStyle, OGRSTPenCap, &bIsNull);
37✔
4846
  if (bIsNull)
37✔
4847
    pszCap = NULL;
4848
  if (pszCap != NULL) {
4✔
4849
    /* Note: the default in OGR Feature style is BUTT, but the MapServer */
4850
    /* default is ROUND. Currently use MapServer default. */
4851
    if (strcmp(pszCap, "b") == 0) /* BUTT */
4✔
4852
      linecap = MS_CJC_BUTT;
4853
    else if (strcmp(pszCap, "r") == 0) /* ROUND */
3✔
4854
      linecap = MS_CJC_ROUND;
4855
    else if (strcmp(pszCap, "p") == 0) /* PROJECTING */
2✔
4856
      linecap = MS_CJC_SQUARE;
4857
    else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
4858
      msDebug("Invalid/unhandled pen cap = %s\n", pszCap);
×
4859
  }
4860

4861
  pszJoin = OGR_ST_GetParamStr(hPenStyle, OGRSTPenJoin, &bIsNull);
37✔
4862
  if (bIsNull)
37✔
4863
    pszJoin = NULL;
4864
  if (pszJoin != NULL) {
4✔
4865
    /* Note: the default in OGR Feature style is MITER, but the MapServer */
4866
    /* default is NONE. Currently use MapServer default. */
4867
    if (strcmp(pszJoin, "m") == 0) /* MITTER */
4✔
4868
      linejoin = MS_CJC_MITER;
4869
    else if (strcmp(pszJoin, "r") == 0) /* ROUND */
3✔
4870
      linejoin = MS_CJC_ROUND;
4871
    else if (strcmp(pszJoin, "b") == 0) /* BEVEL */
2✔
4872
      linejoin = MS_CJC_BEVEL;
4873
    else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
4874
      msDebug("Invalid/unhandled pen join = %s\n", pszJoin);
×
4875
  }
4876

4877
  offsetx = OGR_ST_GetParamDbl(hPenStyle, OGRSTPenPerOffset, &bIsNull);
37✔
4878
  if (bIsNull)
37✔
4879
    offsetx = 0;
4880
  if (offsetx != 0.0) {
2✔
4881
    /* OGR feature style and MapServer conventions related to offset */
4882
    /* sign are the same : negative values for left of line, positive for */
4883
    /* right of line */
4884
    offsety = MS_STYLE_SINGLE_SIDED_OFFSET;
4885
  }
4886

4887
  if (bIsBrush || layer->type == MS_LAYER_POLYGON) {
37✔
4888
    // This is a multipart symbology, so pen defn goes in the
4889
    // overlaysymbol params
4890
    s->outlinecolor = oPenColor;
10✔
4891
  } else {
4892
    // Single part symbology
4893
    s->color = oPenColor;
27✔
4894
  }
4895

4896
  s->symbol = nPenSymbol;
37✔
4897
  s->size = nPenSize;
37✔
4898
  s->width = nPenSize;
37✔
4899
  s->linecap = linecap;
37✔
4900
  s->linejoin = linejoin;
37✔
4901
  s->offsetx = offsetx;
37✔
4902
  s->offsety = offsety;
37✔
4903
  s->patternlength = patternlength;
37✔
4904
  if (patternlength > 0)
37✔
4905
    memcpy(s->pattern, pattern, sizeof(double) * patternlength);
1✔
4906

4907
  int nPriority = OGR_ST_GetParamNum(hPenStyle, OGRSTPenPriority, &bIsNull);
37✔
4908
  if (!bIsNull)
37✔
4909
    *pbPriority = nPriority;
6✔
4910

4911
  return MS_SUCCESS;
37✔
4912
}
4913

4914
static int msOGRAddBgColorStyleParseBrush(styleObj *s,
3✔
4915
                                          OGRStyleToolH hBrushStyle) {
4916
  int bIsNull;
4917
  int r = 0, g = 0, b = 0, t = 0;
3✔
4918
  const char *pszColor =
4919
      OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushBColor, &bIsNull);
3✔
4920

4921
  if (!bIsNull &&
6✔
4922
      OGR_ST_GetRGBFromString(hBrushStyle, pszColor, &r, &g, &b, &t)) {
3✔
4923
    MS_INIT_COLOR(s->color, r, g, b, t);
3✔
4924
  }
4925
  return MS_SUCCESS;
3✔
4926
}
4927

4928
static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
4✔
4929
                                      OGRStyleToolH hBrushStyle, int *pbIsBrush,
4930
                                      int *pbPriority) {
4931
  int bIsNull;
4932
  int r = 0, g = 0, b = 0, t = 0;
4✔
4933

4934
  const char *pszBrushName =
4935
      OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushId, &bIsNull);
4✔
4936
  if (bIsNull)
4✔
4937
    pszBrushName = NULL;
4938

4939
  // Check for Brush Pattern "ogr-brush-1": the invisible fill
4940
  // If that's what we have then set fill color to -1
4941
  if (pszBrushName && strstr(pszBrushName, "ogr-brush-1") != NULL) {
4✔
4942
    MS_INIT_COLOR(s->color, -1, -1, -1, 255);
×
4943
  } else {
4944
    *pbIsBrush = TRUE;
4✔
4945
    const char *pszColor =
4946
        OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushFColor, &bIsNull);
4✔
4947
    if (!bIsNull &&
8✔
4948
        OGR_ST_GetRGBFromString(hBrushStyle, pszColor, &r, &g, &b, &t)) {
4✔
4949
      MS_INIT_COLOR(s->color, r, g, b, t);
4✔
4950

4951
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
4✔
4952
        msDebug("** BRUSH COLOR = %d %d %d **\n", r, g, b);
×
4953
    }
4954

4955
    // Symbol name mapping:
4956
    // First look for the native symbol name, then the ogr-...
4957
    // generic name.
4958
    // If none provided or found then use 0: solid fill
4959

4960
    const char *pszName =
4961
        OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushId, &bIsNull);
4✔
4962
    s->symbol = msOGRGetSymbolId(&(map->symbolset), pszName, NULL, MS_FALSE);
4✔
4963

4964
    double angle = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushAngle, &bIsNull);
4✔
4965
    if (!bIsNull)
4✔
4966
      s->angle = angle;
1✔
4967

4968
    double size = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushSize, &bIsNull);
4✔
4969
    if (!bIsNull)
4✔
4970
      s->size = size;
3✔
4971

4972
    double spacingx = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDx, &bIsNull);
4✔
4973
    if (!bIsNull) {
4✔
4974
      double spacingy = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDy, &bIsNull);
4✔
4975
      if (!bIsNull) {
4✔
4976
        if (spacingx == spacingy)
4✔
4977
          s->gap = spacingx;
3✔
4978
        else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
4979
          msDebug("Ignoring brush dx and dy since they don't have the same "
×
4980
                  "value\n");
4981
      }
4982
    }
4983
  }
4984

4985
  int nPriority = OGR_ST_GetParamNum(hBrushStyle, OGRSTBrushPriority, &bIsNull);
4✔
4986
  if (!bIsNull)
4✔
4987
    *pbPriority = nPriority;
×
4988

4989
  return MS_SUCCESS;
4✔
4990
}
4991

4992
static int msOGRUpdateStyleParseSymbol(mapObj *map, styleObj *s,
×
4993
                                       OGRStyleToolH hSymbolStyle,
4994
                                       int *pbPriority) {
4995
  int bIsNull;
4996
  int r = 0, g = 0, b = 0, t = 0;
×
4997

4998
  const char *pszColor =
4999
      OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolColor, &bIsNull);
×
5000
  if (!bIsNull &&
×
5001
      OGR_ST_GetRGBFromString(hSymbolStyle, pszColor, &r, &g, &b, &t)) {
×
5002
    MS_INIT_COLOR(s->color, r, g, b, t);
×
5003
  }
5004

5005
  pszColor = OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolOColor, &bIsNull);
×
5006
  if (!bIsNull &&
×
5007
      OGR_ST_GetRGBFromString(hSymbolStyle, pszColor, &r, &g, &b, &t)) {
×
5008
    MS_INIT_COLOR(s->outlinecolor, r, g, b, t);
×
5009
  }
5010

5011
  s->angle = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolAngle, &bIsNull);
×
5012
  double dfTmp = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolSize, &bIsNull);
×
5013
  if (!bIsNull)
×
5014
    s->size = dfTmp;
×
5015

5016
  // Symbol name mapping:
5017
  // First look for the native symbol name, then the ogr-...
5018
  // generic name, and in last resort try "default-marker" if
5019
  // provided by user.
5020
  const char *pszName =
5021
      OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolId, &bIsNull);
×
5022
  if (bIsNull)
×
5023
    pszName = NULL;
5024

5025
  int try_addimage_if_notfound = MS_FALSE;
5026
#ifdef USE_CURL
5027
  if (pszName && strncasecmp(pszName, "http", 4) == 0)
×
5028
    try_addimage_if_notfound = MS_TRUE;
5029
#endif
5030
  if (!s->symbolname)
×
5031
    s->symbol = msOGRGetSymbolId(&(map->symbolset), pszName, "default-marker",
×
5032
                                 try_addimage_if_notfound);
5033

5034
  int nPriority =
5035
      OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolPriority, &bIsNull);
×
5036
  if (!bIsNull)
×
5037
    *pbPriority = nPriority;
×
5038

5039
  return MS_SUCCESS;
×
5040
}
5041

5042
/**********************************************************************
5043
 *                     msOGRLayerGetAutoStyle()
5044
 *
5045
 * Fills a classObj with style info from the specified shape.
5046
 * For optimal results, this should be called immediately after
5047
 * GetNextShape() or GetShape() so that the shape doesn't have to be read
5048
 * twice.
5049
 *
5050
 * The returned classObj is a ref. to a static structure valid only until
5051
 * the next call and that shouldn't be freed by the caller.
5052
 **********************************************************************/
5053
static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
38✔
5054
                                  shapeObj *shape) {
5055
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
38✔
5056

5057
  if (psInfo == NULL || psInfo->hLayer == NULL) {
38✔
5058
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
5059
               "msOGRLayerGetAutoStyle()");
5060
    return (MS_FAILURE);
×
5061
  }
5062

5063
  if (layer->tileindex != NULL) {
38✔
5064
    if ((psInfo->poCurTile == NULL ||
×
5065
         shape->tileindex != psInfo->poCurTile->nTileId) &&
×
5066
        msOGRFileReadTile(layer, psInfo) != MS_SUCCESS)
×
5067
      return MS_FAILURE;
5068

5069
    psInfo = psInfo->poCurTile;
×
5070
  }
5071

5072
  /* ------------------------------------------------------------------
5073
   * Read shape or reuse ref. to last shape read.
5074
   * ------------------------------------------------------------------ */
5075
  ACQUIRE_OGR_LOCK;
38✔
5076
  if (psInfo->hLastFeature == NULL ||
38✔
5077
      psInfo->last_record_index_read != shape->resultindex) {
38✔
5078
    RELEASE_OGR_LOCK;
×
5079
    msSetError(MS_MISCERR,
×
5080
               "Assertion failed: AutoStyle not requested on loaded shape.",
5081
               "msOGRLayerGetAutoStyle()");
5082
    return (MS_FAILURE);
×
5083
  }
5084

5085
  /* ------------------------------------------------------------------
5086
   * Reset style info in the class to defaults
5087
   * the only members we don't touch are name, expression, and join/query stuff
5088
   * ------------------------------------------------------------------ */
5089
  resetClassStyle(c);
38✔
5090
  if (msMaybeAllocateClassStyle(c, 0)) {
38✔
5091
    RELEASE_OGR_LOCK;
×
5092
    return (MS_FAILURE);
×
5093
  }
5094

5095
  // __TODO__ label cache incompatible with styleitem feature.
5096
  layer->labelcache = MS_OFF;
38✔
5097

5098
  int nRetVal = MS_SUCCESS;
5099
  if (psInfo->hLastFeature) {
38✔
5100
    OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
38✔
5101
    OGR_SM_InitFromFeature(hStyleMgr, psInfo->hLastFeature);
38✔
5102
    nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
38✔
5103
    OGR_SM_Destroy(hStyleMgr);
38✔
5104
  }
5105

5106
  RELEASE_OGR_LOCK;
38✔
5107
  return nRetVal;
38✔
5108
}
5109

5110
/**********************************************************************
5111
 *                     msOGRUpdateStyleFromString()
5112
 *
5113
 * Fills a classObj with style info from the specified style string.
5114
 * For optimal results, this should be called immediately after
5115
 * GetNextShape() or GetShape() so that the shape doesn't have to be read
5116
 * twice.
5117
 *
5118
 * The returned classObj is a ref. to a static structure valid only until
5119
 * the next call and that shouldn't be freed by the caller.
5120
 **********************************************************************/
5121
int msOGRUpdateStyleFromString(mapObj *map, layerObj *layer, classObj *c,
1✔
5122
                               const char *stylestring) {
5123
  /* ------------------------------------------------------------------
5124
   * Reset style info in the class to defaults
5125
   * the only members we don't touch are name, expression, and join/query stuff
5126
   * ------------------------------------------------------------------ */
5127
  resetClassStyle(c);
1✔
5128
  if (msMaybeAllocateClassStyle(c, 0)) {
1✔
5129
    return (MS_FAILURE);
5130
  }
5131

5132
  // __TODO__ label cache incompatible with styleitem feature.
5133
  layer->labelcache = MS_OFF;
1✔
5134

5135
  int nRetVal = MS_SUCCESS;
5136

5137
  ACQUIRE_OGR_LOCK;
1✔
5138

5139
  OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
1✔
5140
  OGR_SM_InitStyleString(hStyleMgr, stylestring);
1✔
5141
  nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
1✔
5142
  OGR_SM_Destroy(hStyleMgr);
1✔
5143

5144
  RELEASE_OGR_LOCK;
1✔
5145
  return nRetVal;
1✔
5146
}
5147

5148
/************************************************************************/
5149
/*                           msOGRLCleanup()                            */
5150
/************************************************************************/
5151

5152
void msOGRCleanup(void)
2,607✔
5153

5154
{
5155
  ACQUIRE_OGR_LOCK;
2,607✔
5156
  if (bOGRDriversRegistered == MS_TRUE) {
2,607✔
5157
    CPLPopErrorHandler();
343✔
5158
    bOGRDriversRegistered = MS_FALSE;
343✔
5159
  }
5160
  RELEASE_OGR_LOCK;
2,607✔
5161
}
2,607✔
5162

5163
/************************************************************************/
5164
/*                           msOGREscapeSQLParam                        */
5165
/************************************************************************/
5166
char *msOGREscapePropertyName(layerObj *layer, const char *pszString) {
1,288✔
5167
  char *pszEscapedStr = NULL;
5168
  if (layer && pszString && strlen(pszString) > 0) {
1,288✔
5169
    pszEscapedStr = (char *)msSmallMalloc(strlen(pszString) * 2 + 1);
1,288✔
5170
    int j = 0;
5171
    for (int i = 0; pszString[i] != '\0'; ++i) {
10,363✔
5172
      if (pszString[i] == '"') {
9,075✔
5173
        pszEscapedStr[j++] = '"';
1✔
5174
        pszEscapedStr[j++] = '"';
1✔
5175
      } else
5176
        pszEscapedStr[j++] = pszString[i];
9,074✔
5177
    }
5178
    pszEscapedStr[j] = 0;
1,288✔
5179
  }
5180
  return pszEscapedStr;
1,288✔
5181
}
5182

5183
static int msOGRLayerSupportsCommonFilters(layerObj *) { return MS_FALSE; }
295✔
5184

5185
static void msOGREnablePaging(layerObj *layer, int value) {
230✔
5186
  msOGRFileInfo *layerinfo = NULL;
5187

5188
  if (layer->debug) {
230✔
5189
    msDebug("msOGREnablePaging(%d) called.\n", value);
2✔
5190
  }
5191

5192
  if (!msOGRLayerIsOpen(layer)) {
5193
    if (msOGRLayerOpenVT(layer) != MS_SUCCESS) {
×
5194
      return;
5195
    }
5196
  }
5197

5198
  assert(layer->layerinfo != NULL);
5199

5200
  layerinfo = (msOGRFileInfo *)layer->layerinfo;
230✔
5201
  layerinfo->bPaging = value;
230✔
5202
}
5203

5204
static int msOGRGetPaging(layerObj *layer) {
258✔
5205
  msOGRFileInfo *layerinfo = NULL;
5206

5207
  if (layer->debug) {
258✔
5208
    msDebug("msOGRGetPaging called.\n");
2✔
5209
  }
5210

5211
  if (!msOGRLayerIsOpen(layer)) {
5212
    if (msOGRLayerOpenVT(layer) != MS_SUCCESS) {
79✔
5213
      return FALSE;
5214
    }
5215
  }
5216

5217
  assert(layer->layerinfo != NULL);
5218

5219
  layerinfo = (msOGRFileInfo *)layer->layerinfo;
258✔
5220
  return layerinfo->bPaging;
258✔
5221
}
5222

5223
/************************************************************************/
5224
/*                  msOGRLayerInitializeVirtualTable()                  */
5225
/************************************************************************/
5226
int msOGRLayerInitializeVirtualTable(layerObj *layer) {
1,095✔
5227
  assert(layer != NULL);
5228
  assert(layer->vtable != NULL);
5229

5230
  layer->vtable->LayerTranslateFilter = msOGRTranslateMsExpressionToOGRSQL;
1,095✔
5231

5232
  layer->vtable->LayerSupportsCommonFilters = msOGRLayerSupportsCommonFilters;
1,095✔
5233
  layer->vtable->LayerInitItemInfo = msOGRLayerInitItemInfo;
1,095✔
5234
  layer->vtable->LayerFreeItemInfo = msOGRLayerFreeItemInfo;
1,095✔
5235
  layer->vtable->LayerOpen = msOGRLayerOpenVT;
1,095✔
5236
  layer->vtable->LayerIsOpen = msOGRLayerIsOpen;
1,095✔
5237
  layer->vtable->LayerWhichShapes = msOGRLayerWhichShapes;
1,095✔
5238
  layer->vtable->LayerNextShape = msOGRLayerNextShape;
1,095✔
5239
  layer->vtable->LayerGetShape = msOGRLayerGetShape;
1,095✔
5240
  /* layer->vtable->LayerGetShapeCount, use default */
5241
  layer->vtable->LayerClose = msOGRLayerClose;
1,095✔
5242
  layer->vtable->LayerGetItems = msOGRLayerGetItems;
1,095✔
5243
  layer->vtable->LayerGetExtent = msOGRLayerGetExtent;
1,095✔
5244
  layer->vtable->LayerGetAutoStyle = msOGRLayerGetAutoStyle;
1,095✔
5245
  /* layer->vtable->LayerCloseConnection, use default */
5246
  layer->vtable->LayerApplyFilterToLayer = msLayerApplyCondSQLFilterToLayer;
1,095✔
5247
  layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
1,095✔
5248
  /* layer->vtable->LayerCreateItems, use default */
5249
  layer->vtable->LayerGetNumFeatures = msOGRLayerGetNumFeatures;
1,095✔
5250
  /* layer->vtable->LayerGetAutoProjection, use default*/
5251

5252
  layer->vtable->LayerEscapeSQLParam = msOGREscapeSQLParam;
1,095✔
5253
  layer->vtable->LayerEscapePropertyName = msOGREscapePropertyName;
1,095✔
5254
  layer->vtable->LayerEnablePaging = msOGREnablePaging;
1,095✔
5255
  layer->vtable->LayerGetPaging = msOGRGetPaging;
1,095✔
5256
  return MS_SUCCESS;
1,095✔
5257
}
5258

5259
/************************************************************************/
5260
/*                         msOGRShapeFromWKT()                          */
5261
/************************************************************************/
5262
shapeObj *msOGRShapeFromWKT(const char *string) {
×
5263

5264
  OGRGeometryH hGeom = NULL;
×
5265
  shapeObj *shape = NULL;
5266

5267
  if (!string)
×
5268
    return NULL;
5269

5270
  if (OGR_G_CreateFromWkt((char **)&string, NULL, &hGeom) != OGRERR_NONE) {
×
5271
    msSetError(MS_OGRERR, "Failed to parse WKT string.", "msOGRShapeFromWKT()");
×
5272
    return NULL;
×
5273
  }
5274

5275
  /* Initialize a corresponding shapeObj */
5276

5277
  shape = (shapeObj *)malloc(sizeof(shapeObj));
×
5278
  msInitShape(shape);
×
5279

5280
  /* translate WKT into an OGRGeometry. */
5281

5282
  if (msOGRGeometryToShape(hGeom, shape,
×
5283
                           wkbFlatten(OGR_G_GetGeometryType(hGeom))) ==
5284
      MS_FAILURE) {
5285
    msFreeShape(shape);
×
5286
    free(shape);
×
5287
    shape = NULL;
5288
  }
5289

5290
  OGR_G_DestroyGeometry(hGeom);
×
5291

5292
  return shape;
5293
}
5294

5295
/************************************************************************/
5296
/*                          msOGRShapeToWKT()                           */
5297
/************************************************************************/
5298
char *msOGRShapeToWKT(shapeObj *shape) {
×
5299
  OGRGeometryH hGeom = NULL;
5300
  int i;
5301
  char *wkt = NULL;
5302

5303
  if (!shape)
×
5304
    return NULL;
5305

5306
  if (shape->type == MS_SHAPE_POINT && shape->numlines == 1 &&
×
5307
      shape->line[0].numpoints == 1) {
×
5308
    hGeom = OGR_G_CreateGeometry(wkbPoint);
×
5309
    OGR_G_SetPoint_2D(hGeom, 0, shape->line[0].point[0].x,
×
5310
                      shape->line[0].point[0].y);
×
5311
  } else if (shape->type == MS_SHAPE_POINT && shape->numlines == 1 &&
×
5312
             shape->line[0].numpoints > 1) {
×
5313
    hGeom = OGR_G_CreateGeometry(wkbMultiPoint);
×
5314
    for (i = 0; i < shape->line[0].numpoints; i++) {
×
5315
      OGRGeometryH hPoint;
5316

5317
      hPoint = OGR_G_CreateGeometry(wkbPoint);
×
5318
      OGR_G_SetPoint_2D(hPoint, 0, shape->line[0].point[i].x,
×
5319
                        shape->line[0].point[i].y);
×
5320
      OGR_G_AddGeometryDirectly(hGeom, hPoint);
×
5321
    }
5322
  } else if (shape->type == MS_SHAPE_LINE && shape->numlines == 1) {
×
5323
    hGeom = OGR_G_CreateGeometry(wkbLineString);
×
5324
    for (i = 0; i < shape->line[0].numpoints; i++) {
×
5325
      OGR_G_AddPoint_2D(hGeom, shape->line[0].point[i].x,
×
5326
                        shape->line[0].point[i].y);
×
5327
    }
5328
  } else if (shape->type == MS_SHAPE_LINE && shape->numlines > 1) {
×
5329
    OGRGeometryH hMultiLine = OGR_G_CreateGeometry(wkbMultiLineString);
×
5330
    int iLine;
5331

5332
    for (iLine = 0; iLine < shape->numlines; iLine++) {
×
5333
      hGeom = OGR_G_CreateGeometry(wkbLineString);
×
5334
      for (i = 0; i < shape->line[iLine].numpoints; i++) {
×
5335
        OGR_G_AddPoint_2D(hGeom, shape->line[iLine].point[i].x,
×
5336
                          shape->line[iLine].point[i].y);
×
5337
      }
5338

5339
      OGR_G_AddGeometryDirectly(hMultiLine, hGeom);
×
5340
    }
5341

5342
    hGeom = hMultiLine;
5343
  } else if (shape->type == MS_SHAPE_POLYGON) {
×
5344
    int iLine;
5345

5346
    /* actually, it is pretty hard to be sure rings 1+ are interior */
5347
    hGeom = OGR_G_CreateGeometry(wkbPolygon);
×
5348
    for (iLine = 0; iLine < shape->numlines; iLine++) {
×
5349
      OGRGeometryH hRing;
5350
      hRing = OGR_G_CreateGeometry(wkbLinearRing);
×
5351

5352
      for (i = 0; i < shape->line[iLine].numpoints; i++) {
×
5353
        OGR_G_AddPoint_2D(hRing, shape->line[iLine].point[i].x,
×
5354
                          shape->line[iLine].point[i].y);
×
5355
      }
5356
      OGR_G_AddGeometryDirectly(hGeom, hRing);
×
5357
    }
5358
  } else {
5359
    msSetError(MS_OGRERR, "OGR support is not available.", "msOGRShapeToWKT()");
×
5360
  }
5361

5362
  if (hGeom != NULL) {
×
5363
    char *pszOGRWkt;
5364

5365
    OGR_G_ExportToWkt(hGeom, &pszOGRWkt);
×
5366
    wkt = msStrdup(pszOGRWkt);
×
5367
    CPLFree(pszOGRWkt);
×
5368
  }
5369

5370
  return wkt;
5371
}
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