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 #define ACQUIRE_OGR_LOCK       msAcquireLock( TLOCK_OGR )
45 #define RELEASE_OGR_LOCK       msReleaseLock( TLOCK_OGR )
46 
47 // GDAL 1.x API
48 #include "ogr_api.h"
49 
50 typedef struct ms_ogr_file_info_t {
51   char        *pszFname;
52   char        *pszLayerDef;
53   int         nLayerIndex;
54   OGRDataSourceH hDS;
55   OGRLayerH   hLayer;
56   OGRFeatureH hLastFeature;
57 
58   int         nTileId;                  /* applies on the tiles themselves. */
59   projectionObj sTileProj;              /* applies on the tiles themselves. */
60 
61   struct ms_ogr_file_info_t *poCurTile; /* exists on tile index, -> tiles */
62   bool        rect_is_defined;
63   rectObj     rect;                     /* set by TranslateMsExpression (possibly) and WhichShapes */
64 
65   int         last_record_index_read;
66 
67   const char* dialect; /* NULL, Spatialite or PostgreSQL */
68   char *pszSelect;
69   char *pszSpatialFilterTableName;
70   char *pszSpatialFilterGeometryColumn;
71   char *pszMainTableName;
72   char *pszRowId;
73   int   bIsOKForSQLCompose;
74   bool  bHasSpatialIndex; // used only for spatialite for now
75   char* pszTablePrefix; // prefix to qualify field names. used only for spatialite & gpkg for now when a join is done for spatial filtering.
76 
77   int   bPaging;
78 
79   char* pszWHERE;
80 
81 } msOGRFileInfo;
82 
83 static int msOGRLayerIsOpen(layerObj *layer);
84 static int msOGRLayerInitItemInfo(layerObj *layer);
85 static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
86                                   shapeObj* shape);
87 static void msOGRCloseConnection( void *conn_handle );
88 
89 /* ==================================================================
90  * Geometry conversion functions
91  * ================================================================== */
92 
93 /**********************************************************************
94  *                     ogrPointsAddPoint()
95  *
96  * NOTE: This function assumes the line->point array already has been
97  * allocated large enough for the point to be added, but that numpoints
98  * does not include this new point.
99  **********************************************************************/
ogrPointsAddPoint(lineObj * line,double dX,double dY,double dZ,int lineindex,rectObj * bounds)100 static void ogrPointsAddPoint(lineObj *line, double dX, double dY,
101 #ifdef USE_POINT_Z_M
102                               double dZ,
103 #endif
104                               int lineindex, rectObj *bounds)
105 {
106   /* Keep track of shape bounds */
107   if (line->numpoints == 0 && lineindex == 0) {
108     bounds->minx = bounds->maxx = dX;
109     bounds->miny = bounds->maxy = dY;
110   } else {
111     if (dX < bounds->minx)  bounds->minx = dX;
112     if (dX > bounds->maxx)  bounds->maxx = dX;
113     if (dY < bounds->miny)  bounds->miny = dY;
114     if (dY > bounds->maxy)  bounds->maxy = dY;
115   }
116 
117   line->point[line->numpoints].x = dX;
118   line->point[line->numpoints].y = dY;
119 #ifdef USE_POINT_Z_M
120   line->point[line->numpoints].z = dZ;
121   line->point[line->numpoints].m = 0.0;
122 #endif
123   line->numpoints++;
124 }
125 
126 /**********************************************************************
127  *                     ogrGeomPoints()
128  **********************************************************************/
ogrGeomPoints(OGRGeometryH hGeom,shapeObj * outshp)129 static int ogrGeomPoints(OGRGeometryH hGeom, shapeObj *outshp)
130 {
131   int   i;
132   int   numpoints;
133 
134   if (hGeom == NULL)
135     return 0;
136 
137   OGRwkbGeometryType eGType =  wkbFlatten( OGR_G_GetGeometryType( hGeom ) );
138 
139   /* -------------------------------------------------------------------- */
140   /*      Container types result in recursive invocation on each          */
141   /*      subobject to add a set of points to the current list.           */
142   /* -------------------------------------------------------------------- */
143   switch( eGType ) {
144     case wkbGeometryCollection:
145     case wkbMultiLineString:
146     case wkbMultiPolygon:
147     case wkbPolygon: {
148       /* Treat it as GeometryCollection */
149       for (int iGeom=0; iGeom < OGR_G_GetGeometryCount( hGeom ); iGeom++ ) {
150         if( ogrGeomPoints( OGR_G_GetGeometryRef( hGeom, iGeom ),
151                            outshp ) == -1 )
152           return -1;
153       }
154 
155       return 0;
156     }
157     break;
158 
159     case wkbPoint:
160     case wkbMultiPoint:
161     case wkbLineString:
162     case wkbLinearRing:
163       /* We will handle these directly */
164       break;
165 
166     default:
167       /* There shouldn't be any more cases should there? */
168       msSetError(MS_OGRERR,
169                  "OGRGeometry type `%s' not supported yet.",
170                  "ogrGeomPoints()",
171                  OGR_G_GetGeometryName( hGeom ) );
172       return(-1);
173   }
174 
175 
176   /* ------------------------------------------------------------------
177    * Count total number of points
178    * ------------------------------------------------------------------ */
179   if ( eGType == wkbPoint ) {
180     numpoints = 1;
181   } else if ( eGType == wkbLineString
182               ||  eGType == wkbLinearRing ) {
183     numpoints = OGR_G_GetPointCount( hGeom );
184   } else if ( eGType == wkbMultiPoint ) {
185     numpoints = OGR_G_GetGeometryCount( hGeom );
186   } else {
187     msSetError(MS_OGRERR,
188                "OGRGeometry type `%s' not supported yet.",
189                "ogrGeomPoints()",
190                OGR_G_GetGeometryName( hGeom ) );
191     return(-1);
192   }
193 
194   /* ------------------------------------------------------------------
195    * Do we need to allocate a line object to contain all our points?
196    * ------------------------------------------------------------------ */
197   if( outshp->numlines == 0 ) {
198     lineObj newline;
199 
200     newline.numpoints = 0;
201     newline.point = NULL;
202     msAddLine(outshp, &newline);
203   }
204 
205   /* ------------------------------------------------------------------
206    * Extend the point array for the new of points to add from the
207    * current geometry.
208    * ------------------------------------------------------------------ */
209   lineObj *line = outshp->line + outshp->numlines-1;
210 
211   if( line->point == NULL )
212     line->point = (pointObj *) malloc(sizeof(pointObj) * numpoints);
213   else
214     line->point = (pointObj *)
215                   realloc(line->point,sizeof(pointObj) * (numpoints+line->numpoints));
216 
217   if(!line->point) {
218     msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
219                "ogrGeomPoints()");
220     return(-1);
221   }
222 
223   /* ------------------------------------------------------------------
224    * alloc buffer and filter/transform points
225    * ------------------------------------------------------------------ */
226   if( eGType == wkbPoint ) {
227     ogrPointsAddPoint(line, OGR_G_GetX(hGeom, 0), OGR_G_GetY(hGeom, 0),
228 #ifdef USE_POINT_Z_M
229                       OGR_G_GetZ(hGeom, 0),
230 #endif
231                       outshp->numlines-1, &(outshp->bounds));
232   } else if( eGType == wkbLineString
233              || eGType == wkbLinearRing ) {
234     for(i=0; i<numpoints; i++)
235       ogrPointsAddPoint(line, OGR_G_GetX(hGeom, i), OGR_G_GetY(hGeom, i),
236 #ifdef USE_POINT_Z_M
237                         OGR_G_GetZ(hGeom, i),
238 #endif
239                         outshp->numlines-1, &(outshp->bounds));
240   } else if( eGType == wkbMultiPoint ) {
241     for(i=0; i<numpoints; i++) {
242       OGRGeometryH hPoint = OGR_G_GetGeometryRef( hGeom, i );
243       ogrPointsAddPoint(line, OGR_G_GetX(hPoint, 0), OGR_G_GetY(hPoint, 0),
244 #ifdef USE_POINT_Z_M
245                         OGR_G_GetZ(hPoint, 0),
246 #endif
247                         outshp->numlines-1, &(outshp->bounds));
248     }
249   }
250 
251   outshp->type = MS_SHAPE_POINT;
252 
253   return(0);
254 }
255 
256 
257 /**********************************************************************
258  *                     ogrGeomLine()
259  *
260  * Recursively convert any OGRGeometry into a shapeObj.  Each part becomes
261  * a line in the overall shapeObj.
262  **********************************************************************/
ogrGeomLine(OGRGeometryH hGeom,shapeObj * outshp,int bCloseRings)263 static int ogrGeomLine(OGRGeometryH hGeom, shapeObj *outshp,
264                        int bCloseRings)
265 {
266   if (hGeom == NULL)
267     return 0;
268 
269   /* ------------------------------------------------------------------
270    * Use recursive calls for complex geometries
271    * ------------------------------------------------------------------ */
272   OGRwkbGeometryType eGType =  wkbFlatten( OGR_G_GetGeometryType( hGeom ) );
273 
274 
275   if ( eGType == wkbPolygon
276        || eGType == wkbGeometryCollection
277        || eGType == wkbMultiLineString
278        || eGType == wkbMultiPolygon ) {
279     if (eGType == wkbPolygon && outshp->type == MS_SHAPE_NULL)
280       outshp->type = MS_SHAPE_POLYGON;
281 
282     /* Treat it as GeometryCollection */
283     for (int iGeom=0; iGeom < OGR_G_GetGeometryCount( hGeom ); iGeom++ ) {
284       if( ogrGeomLine( OGR_G_GetGeometryRef( hGeom, iGeom ),
285                        outshp, bCloseRings ) == -1 )
286         return -1;
287     }
288   }
289   /* ------------------------------------------------------------------
290    * OGRPoint and OGRMultiPoint
291    * ------------------------------------------------------------------ */
292   else if ( eGType == wkbPoint || eGType == wkbMultiPoint ) {
293     /* Hummmm a point when we're drawing lines/polygons... just drop it! */
294   }
295   /* ------------------------------------------------------------------
296    * OGRLinearRing/OGRLineString ... both are of type wkbLineString
297    * ------------------------------------------------------------------ */
298   else if ( eGType == wkbLineString ) {
299     int       j, numpoints;
300     lineObj   line= {0,NULL};
301     double    dX, dY;
302 
303     if ((numpoints = OGR_G_GetPointCount( hGeom )) < 2)
304       return 0;
305 
306     if (outshp->type == MS_SHAPE_NULL)
307       outshp->type = MS_SHAPE_LINE;
308 
309     line.numpoints = 0;
310     line.point = (pointObj *)malloc(sizeof(pointObj)*(numpoints+1));
311     if(!line.point) {
312       msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
313                  "ogrGeomLine");
314       return(-1);
315     }
316 
317     OGR_G_GetPoints(hGeom,
318                     &(line.point[0].x), sizeof(pointObj),
319                     &(line.point[0].y), sizeof(pointObj),
320 #ifdef USE_POINT_Z_M
321                     &(line.point[0].z), sizeof(pointObj));
322 #else
323                     NULL, 0);
324 #endif
325 
326     for(j=0; j<numpoints; j++) {
327       dX = line.point[j].x = OGR_G_GetX( hGeom, j);
328       dY = line.point[j].y = OGR_G_GetY( hGeom, j);
329 
330       /* Keep track of shape bounds */
331       if (j == 0 && outshp->numlines == 0) {
332         outshp->bounds.minx = outshp->bounds.maxx = dX;
333         outshp->bounds.miny = outshp->bounds.maxy = dY;
334       } else {
335         if (dX < outshp->bounds.minx)  outshp->bounds.minx = dX;
336         if (dX > outshp->bounds.maxx)  outshp->bounds.maxx = dX;
337         if (dY < outshp->bounds.miny)  outshp->bounds.miny = dY;
338         if (dY > outshp->bounds.maxy)  outshp->bounds.maxy = dY;
339       }
340 
341     }
342     line.numpoints = numpoints;
343 
344     if (bCloseRings &&
345         ( line.point[line.numpoints-1].x != line.point[0].x ||
346           line.point[line.numpoints-1].y != line.point[0].y  ) ) {
347       line.point[line.numpoints].x = line.point[0].x;
348       line.point[line.numpoints].y = line.point[0].y;
349 #ifdef USE_POINT_Z_M
350       line.point[line.numpoints].z = line.point[0].z;
351 #endif
352       line.numpoints++;
353     }
354 
355     msAddLineDirectly(outshp, &line);
356   } else {
357     msSetError(MS_OGRERR,
358                "OGRGeometry type `%s' not supported.",
359                "ogrGeomLine()",
360                OGR_G_GetGeometryName( hGeom ) );
361     return(-1);
362   }
363 
364   return(0);
365 }
366 
367 /**********************************************************************
368  *                     ogrGetLinearGeometry()
369  *
370  * Fetch geometry from OGR feature. If using GDAL 2.0 or later, the geometry
371  * might be of curve type, so linearize it.
372  **********************************************************************/
ogrGetLinearGeometry(OGRFeatureH hFeature)373 static OGRGeometryH ogrGetLinearGeometry(OGRFeatureH hFeature)
374 {
375 #if GDAL_VERSION_MAJOR >= 2
376     /* Convert in place and reassign to the feature */
377     OGRGeometryH hGeom = OGR_F_StealGeometry(hFeature);
378     if( hGeom != NULL )
379     {
380         hGeom = OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)), NULL);
381         OGR_F_SetGeometryDirectly(hFeature, hGeom);
382     }
383     return hGeom;
384 #else
385     return OGR_F_GetGeometryRef( hFeature );
386 #endif
387 }
388 
389 /**********************************************************************
390  *                     ogrConvertGeometry()
391  *
392  * Convert OGR geometry into a shape object doing the best possible
393  * job to match OGR Geometry type and layer type.
394  *
395  * If layer type is incompatible with geometry, then shape is returned with
396  * shape->type = MS_SHAPE_NULL
397  **********************************************************************/
ogrConvertGeometry(OGRGeometryH hGeom,shapeObj * outshp,enum MS_LAYER_TYPE layertype)398 static int ogrConvertGeometry(OGRGeometryH hGeom, shapeObj *outshp,
399                               enum MS_LAYER_TYPE layertype)
400 {
401   /* ------------------------------------------------------------------
402    * Process geometry according to layer type
403    * ------------------------------------------------------------------ */
404   int nStatus = MS_SUCCESS;
405 
406   if (hGeom == NULL) {
407     // Empty geometry... this is not an error... we'll just skip it
408     return MS_SUCCESS;
409   }
410 
411   switch(layertype) {
412       /* ------------------------------------------------------------------
413        *      POINT layer - Any geometry can be converted to point/multipoint
414        * ------------------------------------------------------------------ */
415     case MS_LAYER_POINT:
416       if(ogrGeomPoints(hGeom, outshp) == -1) {
417         nStatus = MS_FAILURE; // Error message already produced.
418       }
419       break;
420       /* ------------------------------------------------------------------
421        *      LINE layer
422        * ------------------------------------------------------------------ */
423     case MS_LAYER_LINE:
424       if(ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
425         nStatus = MS_FAILURE; // Error message already produced.
426       }
427       if (outshp->type != MS_SHAPE_LINE && outshp->type != MS_SHAPE_POLYGON)
428         outshp->type = MS_SHAPE_NULL;  // Incompatible type for this layer
429       break;
430       /* ------------------------------------------------------------------
431        *      POLYGON layer
432        * ------------------------------------------------------------------ */
433     case MS_LAYER_POLYGON:
434       if(ogrGeomLine(hGeom, outshp, MS_TRUE) == -1) {
435         nStatus = MS_FAILURE; // Error message already produced.
436       }
437       if (outshp->type != MS_SHAPE_POLYGON)
438         outshp->type = MS_SHAPE_NULL;  // Incompatible type for this layer
439       break;
440       /* ------------------------------------------------------------------
441        *      Chart or Query layers - return real feature type
442        * ------------------------------------------------------------------ */
443     case MS_LAYER_CHART:
444     case MS_LAYER_QUERY:
445       switch( OGR_G_GetGeometryType( hGeom ) ) {
446         case wkbPoint:
447         case wkbPoint25D:
448         case wkbMultiPoint:
449         case wkbMultiPoint25D:
450           if(ogrGeomPoints(hGeom, outshp) == -1) {
451             nStatus = MS_FAILURE; // Error message already produced.
452           }
453           break;
454         default:
455           // Handle any non-point types as lines/polygons ... ogrGeomLine()
456           // will decide the shape type
457           if(ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
458             nStatus = MS_FAILURE; // Error message already produced.
459           }
460       }
461       break;
462 
463     default:
464       msSetError(MS_MISCERR, "Unknown or unsupported layer type.",
465                  "msOGRLayerNextShape()");
466       nStatus = MS_FAILURE;
467   } /* switch layertype */
468 
469   return nStatus;
470 }
471 
472 /**********************************************************************
473  *                     msOGRGeometryToShape()
474  *
475  * Utility function to convert from OGR geometry to a mapserver shape
476  * object.
477  **********************************************************************/
msOGRGeometryToShape(OGRGeometryH hGeometry,shapeObj * psShape,OGRwkbGeometryType nType)478 int msOGRGeometryToShape(OGRGeometryH hGeometry, shapeObj *psShape,
479                          OGRwkbGeometryType nType)
480 {
481   if (hGeometry && psShape && nType > 0) {
482     if (nType == wkbPoint || nType == wkbMultiPoint )
483       return ogrConvertGeometry(hGeometry, psShape,  MS_LAYER_POINT);
484     else if (nType == wkbLineString || nType == wkbMultiLineString)
485       return ogrConvertGeometry(hGeometry, psShape,  MS_LAYER_LINE);
486     else if (nType == wkbPolygon || nType == wkbMultiPolygon)
487       return ogrConvertGeometry(hGeometry, psShape,  MS_LAYER_POLYGON);
488     else
489       return MS_FAILURE;
490   } else
491     return MS_FAILURE;
492 }
493 
494 
495 /* ==================================================================
496  * Attributes handling functions
497  * ================================================================== */
498 
499 // Special field index codes for handling text string and angle coming from
500 // OGR style strings.
501 
502 #define MSOGR_FID_INDEX            -99
503 
504 #define MSOGR_LABELNUMITEMS        21
505 #define MSOGR_LABELFONTNAMENAME    "OGR:LabelFont"
506 #define MSOGR_LABELFONTNAMEINDEX   -100
507 #define MSOGR_LABELSIZENAME        "OGR:LabelSize"
508 #define MSOGR_LABELSIZEINDEX       -101
509 #define MSOGR_LABELTEXTNAME        "OGR:LabelText"
510 #define MSOGR_LABELTEXTINDEX       -102
511 #define MSOGR_LABELANGLENAME       "OGR:LabelAngle"
512 #define MSOGR_LABELANGLEINDEX      -103
513 #define MSOGR_LABELFCOLORNAME      "OGR:LabelFColor"
514 #define MSOGR_LABELFCOLORINDEX     -104
515 #define MSOGR_LABELBCOLORNAME      "OGR:LabelBColor"
516 #define MSOGR_LABELBCOLORINDEX     -105
517 #define MSOGR_LABELPLACEMENTNAME   "OGR:LabelPlacement"
518 #define MSOGR_LABELPLACEMENTINDEX  -106
519 #define MSOGR_LABELANCHORNAME      "OGR:LabelAnchor"
520 #define MSOGR_LABELANCHORINDEX     -107
521 #define MSOGR_LABELDXNAME          "OGR:LabelDx"
522 #define MSOGR_LABELDXINDEX         -108
523 #define MSOGR_LABELDYNAME          "OGR:LabelDy"
524 #define MSOGR_LABELDYINDEX         -109
525 #define MSOGR_LABELPERPNAME        "OGR:LabelPerp"
526 #define MSOGR_LABELPERPINDEX       -110
527 #define MSOGR_LABELBOLDNAME        "OGR:LabelBold"
528 #define MSOGR_LABELBOLDINDEX       -111
529 #define MSOGR_LABELITALICNAME      "OGR:LabelItalic"
530 #define MSOGR_LABELITALICINDEX     -112
531 #define MSOGR_LABELUNDERLINENAME   "OGR:LabelUnderline"
532 #define MSOGR_LABELUNDERLINEINDEX  -113
533 #define MSOGR_LABELPRIORITYNAME    "OGR:LabelPriority"
534 #define MSOGR_LABELPRIORITYINDEX   -114
535 #define MSOGR_LABELSTRIKEOUTNAME   "OGR:LabelStrikeout"
536 #define MSOGR_LABELSTRIKEOUTINDEX  -115
537 #define MSOGR_LABELSTRETCHNAME     "OGR:LabelStretch"
538 #define MSOGR_LABELSTRETCHINDEX    -116
539 #define MSOGR_LABELADJHORNAME      "OGR:LabelAdjHor"
540 #define MSOGR_LABELADJHORINDEX     -117
541 #define MSOGR_LABELADJVERTNAME     "OGR:LabelAdjVert"
542 #define MSOGR_LABELADJVERTINDEX    -118
543 #define MSOGR_LABELHCOLORNAME      "OGR:LabelHColor"
544 #define MSOGR_LABELHCOLORINDEX     -119
545 #define MSOGR_LABELOCOLORNAME      "OGR:LabelOColor"
546 #define MSOGR_LABELOCOLORINDEX     -120
547 // Special codes for the OGR style parameters
548 #define MSOGR_LABELPARAMNAME       "OGR:LabelParam"
549 #define MSOGR_LABELPARAMNAMELEN    14
550 #define MSOGR_LABELPARAMINDEX      -500
551 #define MSOGR_BRUSHPARAMNAME       "OGR:BrushParam"
552 #define MSOGR_BRUSHPARAMNAMELEN    14
553 #define MSOGR_BRUSHPARAMINDEX      -600
554 #define MSOGR_PENPARAMNAME         "OGR:PenParam"
555 #define MSOGR_PENPARAMNAMELEN      12
556 #define MSOGR_PENPARAMINDEX        -700
557 #define MSOGR_SYMBOLPARAMNAME      "OGR:SymbolParam"
558 #define MSOGR_SYMBOLPARAMNAMELEN   15
559 #define MSOGR_SYMBOLPARAMINDEX     -800
560 
561 /**********************************************************************
562  *                     msOGRGetValues()
563  *
564  * Load selected item (i.e. field) values into a char array
565  *
566  * Some special attribute names are used to return some OGRFeature params
567  * like for instance stuff encoded in the OGRStyleString.
568  * For now the following pseudo-attribute names are supported:
569  *  "OGR:TextString"  OGRFeatureStyle's text string if present
570  *  "OGR:TextAngle"   OGRFeatureStyle's text angle, or 0 if not set
571  **********************************************************************/
msOGRGetValues(layerObj * layer,OGRFeatureH hFeature)572 static char **msOGRGetValues(layerObj *layer, OGRFeatureH hFeature)
573 {
574   char **values;
575   const char *pszValue = NULL;
576   int i;
577 
578   if(layer->numitems == 0)
579     return(NULL);
580 
581   if(!layer->iteminfo)  // Should not happen... but just in case!
582     if (msOGRLayerInitItemInfo(layer) != MS_SUCCESS)
583       return NULL;
584 
585   if((values = (char **)malloc(sizeof(char *)*layer->numitems)) == NULL) {
586     msSetError(MS_MEMERR, NULL, "msOGRGetValues()");
587     return(NULL);
588   }
589 
590   OGRStyleMgrH  hStyleMgr = NULL;
591   OGRStyleToolH hLabelStyle = NULL;
592   OGRStyleToolH hPenStyle = NULL;
593   OGRStyleToolH hBrushStyle = NULL;
594   OGRStyleToolH hSymbolStyle = NULL;
595 
596   int *itemindexes = (int*)layer->iteminfo;
597 
598   for(i=0; i<layer->numitems; i++) {
599     if (itemindexes[i] >= 0) {
600       // Extract regular attributes
601       values[i] = msStrdup(OGR_F_GetFieldAsString( hFeature, itemindexes[i]));
602     } else if (itemindexes[i] == MSOGR_FID_INDEX ) {
603       values[i] = msStrdup(CPLSPrintf(CPL_FRMT_GIB,
604                                       (GIntBig) OGR_F_GetFID(hFeature)));
605     } else {
606       // Handle special OGR attributes coming from StyleString
607       if (!hStyleMgr) {
608         hStyleMgr = OGR_SM_Create(NULL);
609         OGR_SM_InitFromFeature(hStyleMgr, hFeature);
610         int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
611         for(int i=0; i<numParts; i++) {
612           OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
613           if (hStylePart) {
614             if (OGR_ST_GetType(hStylePart) == OGRSTCLabel && !hLabelStyle)
615               hLabelStyle = hStylePart;
616             else if (OGR_ST_GetType(hStylePart) == OGRSTCPen && !hPenStyle)
617               hPenStyle = hStylePart;
618             else if (OGR_ST_GetType(hStylePart) == OGRSTCBrush && !hBrushStyle)
619               hBrushStyle = hStylePart;
620             else if (OGR_ST_GetType(hStylePart) == OGRSTCSymbol && !hSymbolStyle)
621               hSymbolStyle = hStylePart;
622             else {
623               OGR_ST_Destroy(hStylePart);
624               hStylePart =  NULL;
625             }
626           }
627           /* Setting up the size units according to msOGRLayerGetAutoStyle*/
628           if (hStylePart && layer->map)
629             OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
630               layer->map->cellsize*layer->map->resolution/layer->map->defresolution*72.0*39.37);
631         }
632       }
633       int bDefault;
634       if (itemindexes[i] == MSOGR_LABELTEXTINDEX) {
635         if (hLabelStyle == NULL
636             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
637                                                OGRSTLabelTextString,
638                                                &bDefault)) == NULL))
639           values[i] = msStrdup("");
640         else
641           values[i] = msStrdup(pszValue);
642 
643         if (layer->debug >= MS_DEBUGLEVEL_VVV)
644           msDebug(MSOGR_LABELTEXTNAME " = \"%s\"\n", values[i]);
645       } else if (itemindexes[i] == MSOGR_LABELANGLEINDEX) {
646         if (hLabelStyle == NULL
647             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
648                                                OGRSTLabelAngle,
649                                                &bDefault)) == NULL))
650           values[i] = msStrdup("0");
651         else
652           values[i] = msStrdup(pszValue);
653 
654         if (layer->debug >= MS_DEBUGLEVEL_VVV)
655           msDebug(MSOGR_LABELANGLENAME " = \"%s\"\n", values[i]);
656       } else if (itemindexes[i] == MSOGR_LABELSIZEINDEX) {
657         if (hLabelStyle == NULL
658             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
659                                                OGRSTLabelSize,
660                                                &bDefault)) == NULL))
661           values[i] = msStrdup("0");
662         else
663           values[i] = msStrdup(pszValue);
664 
665         if (layer->debug >= MS_DEBUGLEVEL_VVV)
666           msDebug(MSOGR_LABELSIZENAME " = \"%s\"\n", values[i]);
667       } else if (itemindexes[i] == MSOGR_LABELFCOLORINDEX) {
668         if (hLabelStyle == NULL
669             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
670                                                OGRSTLabelFColor,
671                                                &bDefault)) == NULL))
672           values[i] = msStrdup("#000000");
673         else
674           values[i] = msStrdup(pszValue);
675 
676         if (layer->debug >= MS_DEBUGLEVEL_VVV)
677           msDebug(MSOGR_LABELFCOLORNAME " = \"%s\"\n", values[i]);
678       } else if (itemindexes[i] == MSOGR_LABELFONTNAMEINDEX ) {
679         if (hLabelStyle == NULL
680             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
681                                                OGRSTLabelFontName,
682                                                &bDefault)) == NULL))
683           values[i] = msStrdup("Arial");
684         else
685           values[i] = msStrdup(pszValue);
686 
687         if (layer->debug >= MS_DEBUGLEVEL_VVV)
688           msDebug(MSOGR_LABELFONTNAMENAME " =       \"%s\"\n", values[i]);
689       } else if (itemindexes[i] == MSOGR_LABELBCOLORINDEX) {
690         if (hLabelStyle == NULL
691             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
692                                                OGRSTLabelBColor,
693                                                &bDefault)) == NULL))
694           values[i] = msStrdup("#000000");
695         else
696           values[i] = msStrdup(pszValue);
697 
698         if (layer->debug >= MS_DEBUGLEVEL_VVV)
699           msDebug(MSOGR_LABELBCOLORNAME " = \"%s\"\n", values[i]);
700       } else if (itemindexes[i] == MSOGR_LABELPLACEMENTINDEX) {
701         if (hLabelStyle == NULL
702             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
703                                                OGRSTLabelPlacement,
704                                                &bDefault)) == NULL))
705           values[i] = msStrdup("");
706         else
707           values[i] = msStrdup(pszValue);
708 
709         if (layer->debug >= MS_DEBUGLEVEL_VVV)
710           msDebug(MSOGR_LABELPLACEMENTNAME " = \"%s\"\n", values[i]);
711       } else if (itemindexes[i] == MSOGR_LABELANCHORINDEX) {
712         if (hLabelStyle == NULL
713             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
714                                                OGRSTLabelAnchor,
715                                                &bDefault)) == NULL))
716           values[i] = msStrdup("0");
717         else
718           values[i] = msStrdup(pszValue);
719 
720         if (layer->debug >= MS_DEBUGLEVEL_VVV)
721           msDebug(MSOGR_LABELANCHORNAME " = \"%s\"\n", values[i]);
722       } else if (itemindexes[i] == MSOGR_LABELDXINDEX) {
723         if (hLabelStyle == NULL
724             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
725                                                OGRSTLabelDx,
726                                                &bDefault)) == NULL))
727           values[i] = msStrdup("0");
728         else
729           values[i] = msStrdup(pszValue);
730 
731         if (layer->debug >= MS_DEBUGLEVEL_VVV)
732           msDebug(MSOGR_LABELDXNAME " = \"%s\"\n", values[i]);
733       } else if (itemindexes[i] == MSOGR_LABELDYINDEX) {
734         if (hLabelStyle == NULL
735             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
736                                                OGRSTLabelDy,
737                                                &bDefault)) == NULL))
738           values[i] = msStrdup("0");
739         else
740           values[i] = msStrdup(pszValue);
741 
742         if (layer->debug >= MS_DEBUGLEVEL_VVV)
743           msDebug(MSOGR_LABELDYNAME " = \"%s\"\n", values[i]);
744       } else if (itemindexes[i] == MSOGR_LABELPERPINDEX) {
745         if (hLabelStyle == NULL
746             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
747                                                OGRSTLabelPerp,
748                                                &bDefault)) == NULL))
749           values[i] = msStrdup("0");
750         else
751           values[i] = msStrdup(pszValue);
752 
753         if (layer->debug >= MS_DEBUGLEVEL_VVV)
754           msDebug(MSOGR_LABELPERPNAME " = \"%s\"\n", values[i]);
755       } else if (itemindexes[i] == MSOGR_LABELBOLDINDEX) {
756         if (hLabelStyle == NULL
757             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
758                                                OGRSTLabelBold,
759                                                &bDefault)) == NULL))
760           values[i] = msStrdup("0");
761         else
762           values[i] = msStrdup(pszValue);
763 
764         if (layer->debug >= MS_DEBUGLEVEL_VVV)
765           msDebug(MSOGR_LABELBOLDNAME " = \"%s\"\n", values[i]);
766       } else if (itemindexes[i] == MSOGR_LABELITALICINDEX) {
767         if (hLabelStyle == NULL
768             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
769                                                OGRSTLabelItalic,
770                                                &bDefault)) == NULL))
771           values[i] = msStrdup("0");
772         else
773           values[i] = msStrdup(pszValue);
774 
775         if (layer->debug >= MS_DEBUGLEVEL_VVV)
776           msDebug(MSOGR_LABELITALICNAME " = \"%s\"\n", values[i]);
777       } else if (itemindexes[i] == MSOGR_LABELUNDERLINEINDEX) {
778         if (hLabelStyle == NULL
779             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
780                                                OGRSTLabelUnderline,
781                                                &bDefault)) == NULL))
782           values[i] = msStrdup("0");
783         else
784           values[i] = msStrdup(pszValue);
785 
786         if (layer->debug >= MS_DEBUGLEVEL_VVV)
787           msDebug(MSOGR_LABELUNDERLINENAME " = \"%s\"\n", values[i]);
788       } else if (itemindexes[i] == MSOGR_LABELPRIORITYINDEX) {
789         if (hLabelStyle == NULL
790             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
791                                                OGRSTLabelPriority,
792                                                &bDefault)) == NULL))
793           values[i] = msStrdup("0");
794         else
795           values[i] = msStrdup(pszValue);
796 
797         if (layer->debug >= MS_DEBUGLEVEL_VVV)
798           msDebug(MSOGR_LABELPRIORITYNAME " = \"%s\"\n", values[i]);
799       } else if (itemindexes[i] == MSOGR_LABELSTRIKEOUTINDEX) {
800         if (hLabelStyle == NULL
801             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
802                                                OGRSTLabelStrikeout,
803                                                &bDefault)) == NULL))
804           values[i] = msStrdup("0");
805         else
806           values[i] = msStrdup(pszValue);
807 
808         if (layer->debug >= MS_DEBUGLEVEL_VVV)
809           msDebug(MSOGR_LABELSTRIKEOUTNAME " = \"%s\"\n", values[i]);
810       } else if (itemindexes[i] == MSOGR_LABELSTRETCHINDEX) {
811         if (hLabelStyle == NULL
812             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
813                                                OGRSTLabelStretch,
814                                                &bDefault)) == NULL))
815           values[i] = msStrdup("0");
816         else
817           values[i] = msStrdup(pszValue);
818 
819         if (layer->debug >= MS_DEBUGLEVEL_VVV)
820           msDebug(MSOGR_LABELSTRETCHNAME " = \"%s\"\n", values[i]);
821       } else if (itemindexes[i] == MSOGR_LABELADJHORINDEX) {
822         if (hLabelStyle == NULL
823             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
824                                                OGRSTLabelAdjHor,
825                                                &bDefault)) == NULL))
826           values[i] = msStrdup("");
827         else
828           values[i] = msStrdup(pszValue);
829 
830         if (layer->debug >= MS_DEBUGLEVEL_VVV)
831           msDebug(MSOGR_LABELADJHORNAME " = \"%s\"\n", values[i]);
832       } else if (itemindexes[i] == MSOGR_LABELADJVERTINDEX) {
833         if (hLabelStyle == NULL
834             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
835                                                OGRSTLabelAdjVert,
836                                                &bDefault)) == NULL))
837           values[i] = msStrdup("");
838         else
839           values[i] = msStrdup(pszValue);
840 
841         if (layer->debug >= MS_DEBUGLEVEL_VVV)
842           msDebug(MSOGR_LABELADJVERTNAME " = \"%s\"\n", values[i]);
843       } else if (itemindexes[i] == MSOGR_LABELHCOLORINDEX) {
844         if (hLabelStyle == NULL
845             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
846                                                OGRSTLabelHColor,
847                                                &bDefault)) == NULL))
848           values[i] = msStrdup("");
849         else
850           values[i] = msStrdup(pszValue);
851 
852         if (layer->debug >= MS_DEBUGLEVEL_VVV)
853           msDebug(MSOGR_LABELHCOLORNAME " = \"%s\"\n", values[i]);
854       }
855       else if (itemindexes[i] == MSOGR_LABELOCOLORINDEX) {
856         if (hLabelStyle == NULL
857             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
858                                                OGRSTLabelOColor,
859                                                &bDefault)) == NULL))
860           values[i] = msStrdup("");
861         else
862           values[i] = msStrdup(pszValue);
863 
864         if (layer->debug >= MS_DEBUGLEVEL_VVV)
865           msDebug(MSOGR_LABELOCOLORNAME " = \"%s\"\n", values[i]);
866       }
867       else if (itemindexes[i] >= MSOGR_LABELPARAMINDEX) {
868         if (hLabelStyle == NULL
869             || ((pszValue = OGR_ST_GetParamStr(hLabelStyle,
870                                                itemindexes[i] - MSOGR_LABELPARAMINDEX,
871                                                &bDefault)) == NULL))
872           values[i] = msStrdup("");
873         else
874           values[i] = msStrdup(pszValue);
875 
876         if (layer->debug >= MS_DEBUGLEVEL_VVV)
877           msDebug(MSOGR_LABELPARAMNAME " = \"%s\"\n", values[i]);
878       }
879       else if (itemindexes[i] >= MSOGR_BRUSHPARAMINDEX) {
880         if (hBrushStyle == NULL
881             || ((pszValue = OGR_ST_GetParamStr(hBrushStyle,
882                                                itemindexes[i] - MSOGR_BRUSHPARAMINDEX,
883                                                &bDefault)) == NULL))
884           values[i] = msStrdup("");
885         else
886           values[i] = msStrdup(pszValue);
887 
888         if (layer->debug >= MS_DEBUGLEVEL_VVV)
889           msDebug(MSOGR_BRUSHPARAMNAME " = \"%s\"\n", values[i]);
890       }
891       else if (itemindexes[i] >= MSOGR_PENPARAMINDEX) {
892         if (hPenStyle == NULL
893             || ((pszValue = OGR_ST_GetParamStr(hPenStyle,
894                                                itemindexes[i] - MSOGR_PENPARAMINDEX,
895                                                &bDefault)) == NULL))
896           values[i] = msStrdup("");
897         else
898           values[i] = msStrdup(pszValue);
899 
900         if (layer->debug >= MS_DEBUGLEVEL_VVV)
901           msDebug(MSOGR_PENPARAMNAME " = \"%s\"\n", values[i]);
902       }
903       else if (itemindexes[i] >= MSOGR_SYMBOLPARAMINDEX) {
904         if (hSymbolStyle == NULL
905             || ((pszValue = OGR_ST_GetParamStr(hSymbolStyle,
906                                                itemindexes[i] - MSOGR_SYMBOLPARAMINDEX,
907                                                &bDefault)) == NULL))
908           values[i] = msStrdup("");
909         else
910           values[i] = msStrdup(pszValue);
911 
912         if (layer->debug >= MS_DEBUGLEVEL_VVV)
913           msDebug(MSOGR_SYMBOLPARAMNAME " = \"%s\"\n", values[i]);
914       }
915       else {
916         msFreeCharArray(values,i);
917 
918         OGR_SM_Destroy(hStyleMgr);
919         OGR_ST_Destroy(hLabelStyle);
920         OGR_ST_Destroy(hPenStyle);
921         OGR_ST_Destroy(hBrushStyle);
922         OGR_ST_Destroy(hSymbolStyle);
923 
924         msSetError(MS_OGRERR,"Invalid field index!?!","msOGRGetValues()");
925         return(NULL);
926       }
927     }
928   }
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   return(values);
937 }
938 
939 /**********************************************************************
940  *                     msOGRSpatialRef2ProjectionObj()
941  *
942  * Init a MapServer projectionObj using an OGRSpatialRef
943  * Works only with PROJECTION AUTO
944  *
945  * Returns MS_SUCCESS/MS_FAILURE
946  **********************************************************************/
msOGRSpatialRef2ProjectionObj(OGRSpatialReferenceH hSRS,projectionObj * proj,int debug_flag)947 static int msOGRSpatialRef2ProjectionObj(OGRSpatialReferenceH hSRS,
948     projectionObj *proj, int debug_flag )
949 {
950   // First flush the "auto" name from the projargs[]...
951   msFreeProjectionExceptContext( proj );
952 
953   if (hSRS == NULL || OSRIsLocal( hSRS ) ) {
954     // Dataset had no set projection or is NonEarth (LOCAL_CS)...
955     // Nothing else to do. Leave proj empty and no reprojection will happen!
956     return MS_SUCCESS;
957   }
958 
959 #if PROJ_VERSION_MAJOR >= 6
960   // This could be done also in the < 6 case, but would be useless.
961   // Here this helps avoiding going through potentially lossy PROJ4 strings
962   const char* pszAuthName = OSRGetAuthorityName(hSRS, NULL);
963   if( pszAuthName && EQUAL(pszAuthName, "EPSG") )
964   {
965     const char* pszAuthCode = OSRGetAuthorityCode(hSRS, NULL);
966     if( pszAuthCode )
967     {
968         char szInitStr[32];
969         sprintf(szInitStr, "init=epsg:%d", atoi(pszAuthCode));
970 
971         if( debug_flag )
972             msDebug( "AUTO = %s\n", szInitStr );
973 
974         return msLoadProjectionString(proj, szInitStr) == 0 ? MS_SUCCESS : MS_FAILURE;
975     }
976   }
977 #endif
978 
979   // Export OGR SRS to a PROJ4 string
980   char *pszProj = NULL;
981 
982   if (OSRExportToProj4( hSRS, &pszProj ) != OGRERR_NONE ||
983       pszProj == NULL || strlen(pszProj) == 0) {
984     msSetError(MS_OGRERR, "Conversion from OGR SRS to PROJ4 failed.",
985                "msOGRSpatialRef2ProjectionObj()");
986     CPLFree(pszProj);
987     return(MS_FAILURE);
988   }
989 
990   if( debug_flag )
991     msDebug( "AUTO = %s\n", pszProj );
992 
993   if( msLoadProjectionString( proj, pszProj ) != 0 )
994     return MS_FAILURE;
995 
996   CPLFree(pszProj);
997 
998   return MS_SUCCESS;
999 }
1000 
1001 /**********************************************************************
1002  *                     msOGCWKT2ProjectionObj()
1003  *
1004  * Init a MapServer projectionObj using an OGC WKT definition.
1005  * Works only with PROJECTION AUTO
1006  *
1007  * Returns MS_SUCCESS/MS_FAILURE
1008  **********************************************************************/
1009 
msOGCWKT2ProjectionObj(const char * pszWKT,projectionObj * proj,int debug_flag)1010 int msOGCWKT2ProjectionObj( const char *pszWKT,
1011                             projectionObj *proj,
1012                             int debug_flag )
1013 
1014 {
1015   OGRSpatialReferenceH        hSRS;
1016   char      *pszAltWKT = (char *) pszWKT;
1017   OGRErr  eErr;
1018   int     ms_result;
1019 
1020   hSRS = OSRNewSpatialReference( NULL );
1021 
1022   if( !EQUALN(pszWKT,"GEOGCS",6)
1023       && !EQUALN(pszWKT,"PROJCS",6)
1024       && !EQUALN(pszWKT,"LOCAL_CS",8) )
1025     eErr = OSRSetFromUserInput( hSRS, pszWKT );
1026   else
1027     eErr = OSRImportFromWkt( hSRS, &pszAltWKT );
1028 
1029   if( eErr != OGRERR_NONE ) {
1030     OSRDestroySpatialReference( hSRS );
1031     msSetError(MS_OGRERR,
1032                "Ingestion of WKT string '%s' failed.",
1033                "msOGCWKT2ProjectionObj()",
1034                pszWKT );
1035     return MS_FAILURE;
1036   }
1037 
1038   ms_result = msOGRSpatialRef2ProjectionObj( hSRS, proj, debug_flag );
1039 
1040   OSRDestroySpatialReference( hSRS );
1041   return ms_result;
1042 }
1043 
1044 /**********************************************************************
1045  *                     msOGRFileOpen()
1046  *
1047  * Open an OGR connection, and initialize a msOGRFileInfo.
1048  **********************************************************************/
1049 
1050 static int bOGRDriversRegistered = MS_FALSE;
1051 
msOGRInitialize(void)1052 void msOGRInitialize(void)
1053 
1054 {
1055   /* ------------------------------------------------------------------
1056    * Register OGR Drivers, only once per execution
1057    * ------------------------------------------------------------------ */
1058   if (!bOGRDriversRegistered) {
1059     ACQUIRE_OGR_LOCK;
1060 
1061     OGRRegisterAll();
1062     CPLPushErrorHandler( CPLQuietErrorHandler );
1063 
1064     /* ------------------------------------------------------------------
1065      * Pass config option GML_FIELDTYPES=ALWAYS_STRING to OGR so that all
1066      * GML attributes are returned as strings to MapServer. This is most efficient
1067      * and prevents problems with autodetection of some attribute types.
1068      * ------------------------------------------------------------------ */
1069     CPLSetConfigOption("GML_FIELDTYPES","ALWAYS_STRING");
1070 
1071     bOGRDriversRegistered = MS_TRUE;
1072 
1073     RELEASE_OGR_LOCK;
1074   }
1075 }
1076 
1077 /* ==================================================================
1078  * The following functions closely relate to the API called from
1079  * maplayer.c, but are intended to be used for the tileindex or direct
1080  * layer access.
1081  * ================================================================== */
1082 
1083 static void msOGRFileOpenSpatialite( layerObj *layer,
1084                                      const char *pszLayerDef,
1085                                      msOGRFileInfo *psInfo );
1086 
1087 /**********************************************************************
1088  *                     msOGRFileOpen()
1089  *
1090  * Open an OGR connection, and initialize a msOGRFileInfo.
1091  **********************************************************************/
1092 
1093 static msOGRFileInfo *
msOGRFileOpen(layerObj * layer,const char * connection)1094 msOGRFileOpen(layerObj *layer, const char *connection )
1095 
1096 {
1097   char *conn_decrypted = NULL;
1098 
1099   msOGRInitialize();
1100 
1101   /* ------------------------------------------------------------------
1102    * Make sure any encrypted token in the connection string are decrypted
1103    * ------------------------------------------------------------------ */
1104   if (connection) {
1105     conn_decrypted = msDecryptStringTokens(layer->map, connection);
1106     if (conn_decrypted == NULL)
1107       return NULL;  /* An error should already have been reported */
1108   }
1109 
1110   /* ------------------------------------------------------------------
1111    * Parse connection string into dataset name, and layer name.
1112    * ------------------------------------------------------------------ */
1113   char *pszDSName = NULL, *pszLayerDef = NULL;
1114 
1115   if( conn_decrypted == NULL ) {
1116     /* we don't have anything */
1117   } else if( layer->data != NULL ) {
1118     pszDSName = CPLStrdup(conn_decrypted);
1119     pszLayerDef = CPLStrdup(layer->data);
1120   } else {
1121     char **papszTokens = NULL;
1122 
1123     papszTokens = CSLTokenizeStringComplex( conn_decrypted, ",", TRUE, FALSE );
1124 
1125     if( CSLCount(papszTokens) > 0 )
1126       pszDSName = CPLStrdup( papszTokens[0] );
1127     if( CSLCount(papszTokens) > 1 )
1128       pszLayerDef = CPLStrdup( papszTokens[1] );
1129 
1130     CSLDestroy(papszTokens);
1131   }
1132 
1133   /* Get rid of decrypted connection string. We'll use the original (not
1134    * decrypted) string for debug and error messages in the rest of the code.
1135    */
1136   msFree(conn_decrypted);
1137   conn_decrypted = NULL;
1138 
1139   if( pszDSName == NULL ) {
1140     msSetError(MS_OGRERR,
1141                "Error parsing OGR connection information in layer `%s'",
1142                "msOGRFileOpen()",
1143                layer->name?layer->name:"(null)" );
1144     return NULL;
1145   }
1146 
1147   if( pszLayerDef == NULL )
1148     pszLayerDef = CPLStrdup("0");
1149 
1150   /* -------------------------------------------------------------------- */
1151   /*      Can we get an existing connection for this layer?               */
1152   /* -------------------------------------------------------------------- */
1153   OGRDataSourceH hDS;
1154 
1155   hDS = (OGRDataSourceH) msConnPoolRequest( layer );
1156 
1157   /* -------------------------------------------------------------------- */
1158   /*      If not, open now, and register this connection with the         */
1159   /*      pool.                                                           */
1160   /* -------------------------------------------------------------------- */
1161   if( hDS == NULL ) {
1162     char szPath[MS_MAXPATHLEN] = "";
1163     const char *pszDSSelectedName = pszDSName;
1164 
1165     if( layer->debug )
1166       msDebug("msOGRFileOpen(%s)...\n", connection);
1167 
1168     CPLErrorReset();
1169     if (msTryBuildPath3(szPath, layer->map->mappath,
1170                         layer->map->shapepath, pszDSName) != NULL ||
1171         msTryBuildPath(szPath, layer->map->mappath, pszDSName) != NULL) {
1172       /* Use relative path */
1173       pszDSSelectedName = szPath;
1174     }
1175 
1176     if( layer->debug )
1177       msDebug("OGROPen(%s)\n", pszDSSelectedName);
1178 
1179     ACQUIRE_OGR_LOCK;
1180     char** connectionoptions = msGetStringListFromHashTable(&(layer->connectionoptions));
1181     hDS = (OGRDataSourceH) GDALOpenEx(pszDSSelectedName,
1182                                 GDAL_OF_VECTOR,
1183                                 NULL,
1184                                 (const char* const*)connectionoptions,
1185                                 NULL);
1186     CSLDestroy(connectionoptions);
1187     RELEASE_OGR_LOCK;
1188 
1189     if( hDS == NULL ) {
1190       msSetError(MS_OGRERR,
1191                    "Open failed for OGR connection in layer `%s'.  "
1192                    "File not found or unsupported format. Check server logs.",
1193                    "msOGRFileOpen()",
1194                    layer->name?layer->name:"(null)" );
1195       if( strlen(CPLGetLastErrorMsg()) == 0 )
1196         msDebug("Open failed for OGR connection in layer `%s'.\n%s\n",
1197                    layer->name?layer->name:"(null)",
1198                    CPLGetLastErrorMsg() );
1199       CPLFree( pszDSName );
1200       CPLFree( pszLayerDef );
1201       return NULL;
1202     }
1203 
1204     msConnPoolRegister( layer, hDS, msOGRCloseConnection );
1205   }
1206 
1207   CPLFree( pszDSName );
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) ) {
1220     ACQUIRE_OGR_LOCK;
1221     hLayer = OGR_DS_ExecuteSQL( hDS, pszLayerDef, NULL, NULL );
1222     if( hLayer == NULL ) {
1223       msSetError(MS_OGRERR,
1224                  "ExecuteSQL() failed. Check server logs.",
1225                  "msOGRFileOpen()");
1226       if( strlen(CPLGetLastErrorMsg()) == 0 )
1227         msDebug("ExecuteSQL(%s) failed.\n%s\n",
1228                 pszLayerDef, CPLGetLastErrorMsg() );
1229       RELEASE_OGR_LOCK;
1230       msConnPoolRelease( layer, hDS );
1231       CPLFree( pszLayerDef );
1232       return NULL;
1233     }
1234     RELEASE_OGR_LOCK;
1235     nLayerIndex = -1;
1236   }
1237 
1238   for( iLayer = 0; hLayer == NULL && iLayer < OGR_DS_GetLayerCount(hDS); iLayer++ ) {
1239     hLayer = OGR_DS_GetLayer( hDS, iLayer );
1240     if( hLayer != NULL
1241         && EQUAL(OGR_L_GetName(hLayer),pszLayerDef) )
1242     {
1243       nLayerIndex = iLayer;
1244       break;
1245     } else
1246       hLayer = NULL;
1247   }
1248 
1249   if( hLayer == NULL && (atoi(pszLayerDef) > 0 || EQUAL(pszLayerDef,"0")) ) {
1250     nLayerIndex = atoi(pszLayerDef);
1251     if( nLayerIndex <  OGR_DS_GetLayerCount(hDS) )
1252       hLayer = OGR_DS_GetLayer( hDS, nLayerIndex );
1253   }
1254 
1255   if (hLayer == NULL) {
1256     msSetError(MS_OGRERR, "GetLayer(%s) failed for OGR connection. Check logs.",
1257                "msOGRFileOpen()",
1258                pszLayerDef);
1259     msDebug("GetLayer(%s) failed for OGR connection `%s'.\n",
1260             pszLayerDef, connection );
1261     CPLFree( pszLayerDef );
1262     msConnPoolRelease( layer, hDS );
1263     return NULL;
1264   }
1265 
1266   /* ------------------------------------------------------------------
1267    * OK... open succeded... alloc and fill msOGRFileInfo inside layer obj
1268    * ------------------------------------------------------------------ */
1269   msOGRFileInfo *psInfo =(msOGRFileInfo*)CPLCalloc(1,sizeof(msOGRFileInfo));
1270 
1271   psInfo->pszFname = CPLStrdup(OGR_DS_GetName( hDS ));
1272   psInfo->pszLayerDef = pszLayerDef;
1273   psInfo->nLayerIndex = nLayerIndex;
1274   psInfo->hDS = hDS;
1275   psInfo->hLayer = hLayer;
1276 
1277   psInfo->nTileId = 0;
1278   msInitProjection(&(psInfo->sTileProj));
1279   msProjectionInheritContextFrom(&(psInfo->sTileProj),&(layer->projection));
1280   psInfo->poCurTile = NULL;
1281   psInfo->rect_is_defined = false;
1282   psInfo->rect.minx = psInfo->rect.maxx = 0;
1283   psInfo->rect.miny = psInfo->rect.maxy = 0;
1284   psInfo->last_record_index_read = -1;
1285   psInfo->dialect = NULL;
1286 
1287   psInfo->pszSelect = NULL;
1288   psInfo->pszSpatialFilterTableName = NULL;
1289   psInfo->pszSpatialFilterGeometryColumn = NULL;
1290   psInfo->pszMainTableName = NULL;
1291   psInfo->pszRowId = NULL;
1292   psInfo->bIsOKForSQLCompose = true;
1293   psInfo->bPaging = false;
1294   psInfo->bHasSpatialIndex = false;
1295   psInfo->pszTablePrefix = NULL;
1296   psInfo->pszWHERE = NULL;
1297 
1298     // GDAL 1.x API
1299   OGRSFDriverH dr = OGR_DS_GetDriver(hDS);
1300   const char *name = OGR_Dr_GetName(dr);
1301   if (strcmp(name, "SQLite") == 0) {
1302     bool have_spatialite = false;
1303 
1304     CPLPushErrorHandler(CPLQuietErrorHandler);
1305 
1306     // test for Spatialite support in driver
1307     const char *test_spatialite = "SELECT spatialite_version()";
1308     OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
1309     if (l) {
1310         OGR_DS_ReleaseResultSet(hDS, l);
1311         have_spatialite = true;
1312     }
1313 
1314     // test for Spatialite enabled db
1315     if (have_spatialite) {
1316         have_spatialite = false;
1317         const char *test_sql = "select 1 from sqlite_master where name = 'geometry_columns' and sql LIKE '%spatial_index_enabled%'";
1318         OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_sql, NULL, NULL);
1319         if (l) {
1320             if (OGR_L_GetFeatureCount(l, TRUE) == 1)
1321                 have_spatialite = true;
1322             OGR_DS_ReleaseResultSet(hDS, l);
1323         }
1324     }
1325 
1326     CPLPopErrorHandler();
1327     CPLErrorReset();
1328 
1329     if (have_spatialite)
1330         psInfo->dialect = "Spatialite";
1331     else
1332         msDebug("msOGRFileOpen: Native SQL not available, no Spatialite support and/or not a Spatialite enabled db\n");
1333   } else if (strcmp(name, "PostgreSQL") == 0) {
1334     psInfo->dialect = "PostgreSQL";
1335     // todo: PostgreSQL not yet tested
1336 
1337   } else if (strcmp(name, "GPKG") == 0 && nLayerIndex >= 0 &&
1338              atoi(GDALVersionInfo("VERSION_NUM")) >= 2000000) {
1339 
1340     bool has_rtree = false;
1341     const char* test_rtree =
1342         CPLSPrintf("SELECT 1 FROM sqlite_master WHERE name = 'rtree_%s_%s'",
1343                    OGR_L_GetName(hLayer), OGR_L_GetGeometryColumn(hLayer));
1344     OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_rtree, NULL, NULL);
1345     if( l )
1346     {
1347         if( OGR_L_GetFeatureCount(l, TRUE) == 1 )
1348         {
1349             has_rtree = true;
1350         }
1351         OGR_DS_ReleaseResultSet(hDS, l);
1352     }
1353     if( has_rtree )
1354     {
1355         bool have_gpkg_spatialite = false;
1356 
1357         CPLPushErrorHandler(CPLQuietErrorHandler);
1358 
1359         // test for Spatialite >= 4.3 support in driver
1360         const char *test_spatialite = "SELECT spatialite_version()";
1361         l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
1362         if (l) {
1363             OGRFeatureH hFeat = OGR_L_GetNextFeature(l);
1364             if( hFeat )
1365             {
1366                 const char* pszVersion = OGR_F_GetFieldAsString(hFeat, 0);
1367                 have_gpkg_spatialite = atof(pszVersion) >= 4.3;
1368                 OGR_F_Destroy(hFeat);
1369             }
1370             OGR_DS_ReleaseResultSet(hDS, l);
1371         }
1372         CPLPopErrorHandler();
1373         CPLErrorReset();
1374 
1375         if( have_gpkg_spatialite )
1376         {
1377             psInfo->pszMainTableName = msStrdup( OGR_L_GetName(hLayer) );
1378             psInfo->pszTablePrefix = msStrdup( psInfo->pszMainTableName );
1379             psInfo->pszSpatialFilterTableName = msStrdup( OGR_L_GetName(hLayer) );
1380             psInfo->pszSpatialFilterGeometryColumn = msStrdup( OGR_L_GetGeometryColumn(hLayer) );
1381             psInfo->dialect = "GPKG";
1382             psInfo->bPaging = true;
1383             psInfo->bHasSpatialIndex = true;
1384         }
1385         else
1386             msDebug("msOGRFileOpen: Spatialite support in GPKG not enabled\n");
1387     }
1388     else
1389     {
1390         msDebug("msOGRFileOpen: RTree index not available\n");
1391     }
1392   }
1393 
1394   if( psInfo->dialect != NULL && EQUAL(psInfo->dialect, "Spatialite") )
1395     msOGRFileOpenSpatialite(layer, pszLayerDef, psInfo);
1396 
1397   return psInfo;
1398 }
1399 
1400 /************************************************************************/
1401 /*                      msOGRFileOpenSpatialite()                       */
1402 /************************************************************************/
1403 
msOGRFileOpenSpatialite(layerObj * layer,const char * pszLayerDef,msOGRFileInfo * psInfo)1404 static void msOGRFileOpenSpatialite( layerObj *layer,
1405                                      const char* pszLayerDef,
1406                                      msOGRFileInfo *psInfo )
1407 {
1408   // In the case of a SQLite DB, check that we can identify the
1409   // underlying table
1410   if( psInfo->nLayerIndex == -1 )
1411   {
1412       psInfo->bIsOKForSQLCompose = false;
1413 
1414       const char* from = strstr( psInfo->pszLayerDef, " from ");
1415       if( from == NULL )
1416         from = strstr( psInfo->pszLayerDef, " FROM ");
1417       if( from )
1418       {
1419         const char* pszBeginningOfTable = from + strlen(" FROM ");
1420         const char* pszIter = pszBeginningOfTable;
1421         while( *pszIter && *pszIter != ' ' )
1422           pszIter ++;
1423         if( strchr(pszIter, ',') == NULL &&
1424             strstr(pszIter, " where ") == NULL && strstr(pszIter, " WHERE ") == NULL &&
1425             strstr(pszIter, " join ") == NULL && strstr(pszIter, " JOIN ") == NULL &&
1426             strstr(pszIter, " order by ") == NULL && strstr(pszIter, " ORDER BY ") == NULL)
1427         {
1428           psInfo->bIsOKForSQLCompose = true;
1429           psInfo->pszMainTableName = msStrdup(pszBeginningOfTable);
1430           psInfo->pszMainTableName[pszIter - pszBeginningOfTable] = '\0';
1431           psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
1432           psInfo->pszSpatialFilterGeometryColumn = msStrdup( OGR_L_GetGeometryColumn(psInfo->hLayer) );
1433 
1434           char* pszRequest = NULL;
1435           pszRequest = msStringConcatenate(pszRequest,
1436               "SELECT name FROM sqlite_master WHERE type = 'table' AND name = lower('");
1437           pszRequest = msStringConcatenate(pszRequest, psInfo->pszMainTableName);
1438           pszRequest = msStringConcatenate(pszRequest, "')");
1439           OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1440           msFree(pszRequest);
1441 
1442           if( hLayer )
1443           {
1444               OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1445               psInfo->bIsOKForSQLCompose = (hFeature != NULL);
1446               if( hFeature )
1447               {
1448                 msFree(psInfo->pszMainTableName);
1449                 msFree(psInfo->pszSpatialFilterTableName);
1450                 psInfo->pszMainTableName = msStrdup( OGR_F_GetFieldAsString( hFeature, 0) );
1451                 psInfo->pszSpatialFilterTableName = msStrdup( psInfo->pszMainTableName );
1452                 OGR_F_Destroy(hFeature);
1453               }
1454               OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1455           }
1456           if( psInfo->bIsOKForSQLCompose )
1457           {
1458             psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
1459           }
1460           else
1461           {
1462             // Test if it is a spatial view
1463             pszRequest = msStringConcatenate(NULL,
1464               "SELECT f_table_name, f_geometry_column, view_rowid FROM views_geometry_columns WHERE view_name = lower('");
1465             pszRequest = msStringConcatenate(pszRequest, psInfo->pszMainTableName);
1466             pszRequest = msStringConcatenate(pszRequest, "')");
1467             CPLPushErrorHandler(CPLQuietErrorHandler);
1468             OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1469             CPLPopErrorHandler();
1470             msFree(pszRequest);
1471 
1472             if( hLayer )
1473             {
1474                 OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1475                 psInfo->bIsOKForSQLCompose = (hFeature != NULL);
1476                 if( hFeature )
1477                 {
1478                   psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
1479                   msFree(psInfo->pszSpatialFilterTableName);
1480                   psInfo->pszSpatialFilterTableName = msStrdup( OGR_F_GetFieldAsString( hFeature, 0 ) );
1481                   CPLFree( psInfo->pszSpatialFilterGeometryColumn );
1482                   psInfo->pszSpatialFilterGeometryColumn = msStrdup( OGR_F_GetFieldAsString( hFeature, 1 ) );
1483                   psInfo->pszRowId = msStrdup( OGR_F_GetFieldAsString( hFeature, 2 ) );
1484                   OGR_F_Destroy(hFeature);
1485                 }
1486                 OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1487             }
1488           }
1489         }
1490       }
1491   }
1492   else
1493   {
1494       psInfo->bIsOKForSQLCompose = false;
1495 
1496       char* pszRequest = NULL;
1497       pszRequest = msStringConcatenate(pszRequest,
1498           "SELECT * FROM sqlite_master WHERE type = 'table' AND name = lower('");
1499       pszRequest = msStringConcatenate(pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1500       pszRequest = msStringConcatenate(pszRequest, "')");
1501       OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1502       msFree(pszRequest);
1503 
1504       if( hLayer )
1505       {
1506           OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1507           psInfo->bIsOKForSQLCompose = (hFeature != NULL);
1508           if( hFeature )
1509             OGR_F_Destroy(hFeature);
1510           OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1511       }
1512       if( psInfo->bIsOKForSQLCompose )
1513       {
1514         psInfo->pszMainTableName = msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1515         psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
1516         psInfo->pszSpatialFilterGeometryColumn = msStrdup( OGR_L_GetGeometryColumn(psInfo->hLayer) );
1517       }
1518       else
1519       {
1520         // Test if it is a spatial view
1521         pszRequest = msStringConcatenate(NULL,
1522           "SELECT f_table_name, f_geometry_column, view_rowid FROM views_geometry_columns WHERE view_name = lower('");
1523         pszRequest = msStringConcatenate(pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1524         pszRequest = msStringConcatenate(pszRequest, "')");
1525         CPLPushErrorHandler(CPLQuietErrorHandler);
1526         OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1527         CPLPopErrorHandler();
1528         msFree(pszRequest);
1529 
1530         if( hLayer )
1531         {
1532             OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1533             psInfo->bIsOKForSQLCompose = (hFeature != NULL);
1534             if( hFeature )
1535             {
1536               psInfo->pszMainTableName = msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1537               psInfo->pszSpatialFilterTableName = msStrdup( OGR_F_GetFieldAsString( hFeature, 0 ) );
1538               psInfo->pszSpatialFilterGeometryColumn = msStrdup( OGR_F_GetFieldAsString( hFeature, 1 ) );
1539               psInfo->pszRowId = msStrdup( OGR_F_GetFieldAsString( hFeature, 2 ) );
1540               OGR_F_Destroy(hFeature);
1541             }
1542             OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1543         }
1544       }
1545   }
1546 
1547   // in the case we cannot handle the native string, go back to the client
1548   // side evaluation by unsetting it.
1549   if( !psInfo->bIsOKForSQLCompose && psInfo->dialect != NULL )
1550   {
1551       if (layer->debug >= MS_DEBUGLEVEL_VVV)
1552       {
1553         msDebug("msOGRFileOpen(): Falling back to MapServer only evaluation\n");
1554       }
1555       psInfo->dialect = NULL;
1556   }
1557 
1558   // Check if spatial index has been disabled (testing purposes)
1559   if (msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX") != NULL &&
1560       !CSLTestBoolean(msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX")) )
1561   {
1562       if (layer->debug >= MS_DEBUGLEVEL_VVV)
1563       {
1564         msDebug("msOGRFileOpen(): Layer %s has spatial index disabled by processing option\n",
1565                 pszLayerDef);
1566       }
1567   }
1568   // Test if spatial index is available
1569   else if( psInfo->dialect != NULL )
1570   {
1571       char* pszRequest = NULL;
1572       pszRequest = msStringConcatenate(pszRequest,
1573           "SELECT * FROM sqlite_master WHERE type = 'table' AND name = 'idx_");
1574       pszRequest = msStringConcatenate(pszRequest,
1575                                        psInfo->pszSpatialFilterTableName);
1576       pszRequest = msStringConcatenate(pszRequest, "_");
1577       pszRequest = msStringConcatenate(pszRequest,
1578                                     OGR_L_GetGeometryColumn(psInfo->hLayer));
1579       pszRequest = msStringConcatenate(pszRequest, "'");
1580 
1581       psInfo->bHasSpatialIndex = false;
1582       //msDebug("msOGRFileOpen(): %s", pszRequest);
1583 
1584       OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1585       if( hLayer )
1586       {
1587         OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1588         if( hFeature )
1589         {
1590             psInfo->bHasSpatialIndex = true;
1591             OGR_F_Destroy(hFeature);
1592         }
1593         OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1594       }
1595       msFree(pszRequest);
1596       pszRequest = NULL;
1597 
1598       if( !psInfo->bHasSpatialIndex )
1599       {
1600           if (layer->debug >= MS_DEBUGLEVEL_VVV)
1601           {
1602             msDebug("msOGRFileOpen(): Layer %s has no spatial index table\n",
1603                     pszLayerDef);
1604           }
1605       }
1606       else
1607       {
1608           pszRequest = msStringConcatenate(pszRequest,
1609             "SELECT * FROM geometry_columns WHERE f_table_name = lower('");
1610           pszRequest = msStringConcatenate(pszRequest,
1611                                        psInfo->pszSpatialFilterTableName);
1612           pszRequest = msStringConcatenate(pszRequest, "') AND f_geometry_column = lower('");
1613           pszRequest = msStringConcatenate(pszRequest,
1614                                        psInfo->pszSpatialFilterGeometryColumn);
1615           pszRequest = msStringConcatenate(pszRequest, "') AND spatial_index_enabled = 1");
1616 
1617           psInfo->bHasSpatialIndex = false;
1618 
1619           OGRLayerH hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, pszRequest, NULL, NULL );
1620           if( hLayer )
1621           {
1622             OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
1623             if( hFeature )
1624             {
1625                 psInfo->bHasSpatialIndex = true;
1626                 OGR_F_Destroy(hFeature);
1627             }
1628             OGR_DS_ReleaseResultSet( psInfo->hDS, hLayer );
1629           }
1630           msFree(pszRequest);
1631           pszRequest = NULL;
1632 
1633           if( !psInfo->bHasSpatialIndex )
1634           {
1635             if (layer->debug >= MS_DEBUGLEVEL_VVV)
1636             {
1637               msDebug("msOGRFileOpen(): Layer %s has spatial index disabled\n",
1638                       pszLayerDef);
1639             }
1640           }
1641           else
1642           {
1643             if (layer->debug >= MS_DEBUGLEVEL_VVV)
1644             {
1645               msDebug("msOGRFileOpen(): Layer %s has spatial index enabled\n",
1646                       pszLayerDef);
1647             }
1648 
1649             psInfo->pszTablePrefix = msStrdup( psInfo->pszMainTableName );
1650           }
1651       }
1652   }
1653 
1654   psInfo->bPaging = (psInfo->dialect != NULL);
1655 }
1656 
1657 /************************************************************************/
1658 /*                        msOGRCloseConnection()                        */
1659 /*                                                                      */
1660 /*      Callback for thread pool to actually release an OGR             */
1661 /*      connection.                                                     */
1662 /************************************************************************/
1663 
msOGRCloseConnection(void * conn_handle)1664 static void msOGRCloseConnection( void *conn_handle )
1665 
1666 {
1667   OGRDataSourceH hDS = (OGRDataSourceH) conn_handle;
1668 
1669   ACQUIRE_OGR_LOCK;
1670   OGR_DS_Destroy( hDS );
1671   RELEASE_OGR_LOCK;
1672 }
1673 
1674 /**********************************************************************
1675  *                     msOGRFileClose()
1676  **********************************************************************/
msOGRFileClose(layerObj * layer,msOGRFileInfo * psInfo)1677 static int msOGRFileClose(layerObj *layer, msOGRFileInfo *psInfo )
1678 {
1679   if (!psInfo)
1680     return MS_SUCCESS;
1681 
1682   if( layer->debug )
1683     msDebug("msOGRFileClose(%s,%d).\n",
1684             psInfo->pszFname, psInfo->nLayerIndex);
1685 
1686   CPLFree(psInfo->pszFname);
1687   CPLFree(psInfo->pszLayerDef);
1688 
1689   ACQUIRE_OGR_LOCK;
1690   if (psInfo->hLastFeature)
1691     OGR_F_Destroy( psInfo->hLastFeature );
1692 
1693   /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
1694   if( psInfo->nLayerIndex == -1 )
1695     OGR_DS_ReleaseResultSet( psInfo->hDS, psInfo->hLayer );
1696 
1697   // Release (potentially close) the datasource connection.
1698   // Make sure we aren't holding the lock when the callback may need it.
1699   RELEASE_OGR_LOCK;
1700   msConnPoolRelease( layer, psInfo->hDS );
1701 
1702   // Free current tile if there is one.
1703   if( psInfo->poCurTile != NULL )
1704     msOGRFileClose( layer, psInfo->poCurTile );
1705 
1706   msFreeProjection(&(psInfo->sTileProj));
1707   msFree(psInfo->pszSelect);
1708   msFree(psInfo->pszSpatialFilterTableName);
1709   msFree(psInfo->pszSpatialFilterGeometryColumn);
1710   msFree(psInfo->pszMainTableName);
1711   msFree(psInfo->pszRowId);
1712   msFree(psInfo->pszTablePrefix);
1713   msFree(psInfo->pszWHERE);
1714 
1715   CPLFree(psInfo);
1716 
1717   return MS_SUCCESS;
1718 }
1719 
1720 /************************************************************************/
1721 /*                           msOGREscapeSQLParam                        */
1722 /************************************************************************/
msOGREscapeSQLParam(layerObj * layer,const char * pszString)1723 static char *msOGREscapeSQLParam(layerObj *layer, const char *pszString)
1724 {
1725   char* pszEscapedStr =NULL;
1726   if(layer && pszString) {
1727     char* pszEscapedOGRStr =  CPLEscapeString(pszString, strlen(pszString),
1728                               CPLES_SQL );
1729     pszEscapedStr = msStrdup(pszEscapedOGRStr);
1730     CPLFree(pszEscapedOGRStr);
1731   }
1732   return pszEscapedStr;
1733 }
1734 
1735 // http://www.sqlite.org/lang_expr.html
1736 // http://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html
1737 
msOGRGetQuotedItem(layerObj * layer,const char * pszItem)1738 static char* msOGRGetQuotedItem(layerObj* layer, const char* pszItem )
1739 {
1740     msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
1741     char* ret = NULL;
1742     char* escapedItem = msLayerEscapePropertyName(layer, pszItem);
1743     if( psInfo->pszTablePrefix)
1744     {
1745         char* escapedTable = msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
1746         ret = msStringConcatenate(ret, "\"");
1747         ret = msStringConcatenate(ret, escapedTable);
1748         ret = msStringConcatenate(ret, "\".\"");
1749         ret = msStringConcatenate(ret, escapedItem);
1750         ret = msStringConcatenate(ret, "\"");
1751         msFree(escapedTable);
1752     }
1753     else
1754     {
1755         ret = msStringConcatenate(ret, "\"");
1756         ret = msStringConcatenate(ret, escapedItem);
1757         ret = msStringConcatenate(ret, "\"");
1758     }
1759     msFree(escapedItem);
1760     return ret;
1761 }
1762 
1763 static
msOGRGetToken(layerObj * layer,tokenListNodeObjPtr * node)1764 char *msOGRGetToken(layerObj* layer, tokenListNodeObjPtr *node) {
1765     msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
1766     tokenListNodeObjPtr n = *node;
1767     if (!n) return NULL;
1768     char *out = NULL;
1769     size_t nOutSize;
1770 
1771     switch(n->token) {
1772     case MS_TOKEN_LOGICAL_AND:
1773         out = msStrdup(" AND ");
1774         break;
1775     case MS_TOKEN_LOGICAL_OR:
1776         out = msStrdup(" OR ");
1777         break;
1778     case MS_TOKEN_LOGICAL_NOT:
1779         out = msStrdup(" NOT ");
1780         break;
1781     case MS_TOKEN_LITERAL_NUMBER:
1782         nOutSize = 80;
1783         out = (char *)msSmallMalloc(nOutSize);
1784         snprintf(out, nOutSize, "%lf",  n->tokenval.dblval);
1785         break;
1786     case MS_TOKEN_LITERAL_STRING: {
1787         char *stresc = msOGREscapeSQLParam(layer, n->tokenval.strval);
1788         nOutSize = strlen(stresc)+3;
1789         out = (char *)msSmallMalloc(nOutSize);
1790         snprintf(out, nOutSize, "'%s'", stresc);
1791         msFree(stresc);
1792         break;
1793     }
1794     case MS_TOKEN_LITERAL_TIME:
1795         // seems to require METADATA gml_types => auto
1796         nOutSize = 80;
1797         out = (char *)msSmallMalloc(nOutSize);
1798 #if 0
1799         // FIXME? or perhaps just remove me. tm_zone is not supported on Windows, and not used anywhere else in the code base
1800         if (n->tokenval.tmval.tm_zone)
1801             snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d%s'",
1802                      n->tokenval.tmval.tm_year+1900, n->tokenval.tmval.tm_mon+1, n->tokenval.tmval.tm_mday,
1803                      n->tokenval.tmval.tm_hour, n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec,
1804                      n->tokenval.tmval.tm_zone);
1805         else
1806 #endif
1807             snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d'",
1808                      n->tokenval.tmval.tm_year+1900, n->tokenval.tmval.tm_mon+1, n->tokenval.tmval.tm_mday,
1809                      n->tokenval.tmval.tm_hour, n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec);
1810         break;
1811     case MS_TOKEN_LITERAL_SHAPE: {
1812         // assumed to be in right srs after FLTGetSpatialComparisonCommonExpression
1813         char *wkt = msShapeToWKT(n->tokenval.shpval);
1814         char *stresc = msOGRGetQuotedItem(layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
1815         nOutSize = strlen(wkt)+strlen(stresc)+35;
1816         out = (char *)msSmallMalloc(nOutSize);
1817         snprintf(out, nOutSize, "ST_GeomFromText('%s',ST_SRID(%s))", wkt, stresc);
1818         msFree(wkt);
1819         msFree(stresc);
1820         break;
1821     }
1822     case MS_TOKEN_LITERAL_BOOLEAN:
1823         out = msStrdup(n->tokenval.dblval == 0 ? "FALSE" : "TRUE");
1824         break;
1825     case MS_TOKEN_COMPARISON_EQ:
1826         if(n->next != NULL && n->next->token == MS_TOKEN_LITERAL_STRING &&
1827            strcmp(n->next->tokenval.strval, "_MAPSERVER_NULL_") == 0 )
1828         {
1829             out = msStrdup(" IS NULL");
1830             n = n->next;
1831             break;
1832         }
1833 
1834         out = msStrdup(" = ");
1835         break;
1836     case MS_TOKEN_COMPARISON_NE:
1837         out = msStrdup(" != ");
1838         break;
1839     case MS_TOKEN_COMPARISON_GT:
1840         out = msStrdup(" > ");
1841         break;
1842     case MS_TOKEN_COMPARISON_LT:
1843         out = msStrdup(" < ");
1844         break;
1845     case MS_TOKEN_COMPARISON_LE:
1846         out = msStrdup(" <= ");
1847         break;
1848     case MS_TOKEN_COMPARISON_GE:
1849         out = msStrdup(" >= ");
1850         break;
1851     case MS_TOKEN_COMPARISON_IEQ:
1852         out = msStrdup(" = ");
1853         break;
1854     case MS_TOKEN_COMPARISON_IN:
1855         out = msStrdup(" IN ");
1856         break;
1857     // the origin may be mapfile (complex regexes, layer->map.query.filter.string == NULL, regex may have //) or
1858     // OGC Filter (simple patterns only, layer->map.query.filter.string != NULL)
1859     case MS_TOKEN_COMPARISON_RE:
1860     case MS_TOKEN_COMPARISON_IRE:
1861     case MS_TOKEN_COMPARISON_LIKE: {
1862         int case_sensitive = n->token == MS_TOKEN_COMPARISON_RE || n->token == MS_TOKEN_COMPARISON_LIKE;
1863         // in PostgreSQL and OGR: LIKE (case sensitive) and ILIKE (case insensitive)
1864         // in SQLite: LIKE (case insensitive) and GLOB (case sensitive)
1865         const char *op = case_sensitive ? "LIKE" : "ILIKE";
1866         char wild_any =  '%';
1867         char wild_one = '_';
1868 
1869         if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG")) {
1870             if (case_sensitive) {
1871                 op = "GLOB";
1872                 wild_any = '*';
1873                 wild_one = '?';
1874             } else {
1875                 op = "LIKE";
1876             }
1877         }
1878 
1879         n = n->next;
1880         if (n->token != MS_TOKEN_LITERAL_STRING) return NULL;
1881 
1882         char *regex = msStrdup(n->tokenval.strval);
1883         int complex_regex = *n->tokenval.strval == '/'; // could be non-complex but that is soo corner case
1884 
1885         // PostgreSQL has POSIX regexes, SQLite does not by default, OGR does not
1886         if (complex_regex) {
1887             if (!EQUAL(info->dialect, "PostgreSQL")) {
1888                 msFree(regex);
1889                 return NULL;
1890             }
1891             // remove //
1892             regex++;
1893             regex[strlen(regex) - 1] = '\0';
1894             if (case_sensitive)
1895                 op = "~";
1896             else
1897                 op = "~*";
1898         }
1899 
1900         char *re = (char *) msSmallMalloc(strlen(regex)+3);
1901         size_t i = 0, j = 0;
1902         re[j++] = '\'';
1903         while (i < strlen(regex)) {
1904             char c = regex[i];
1905             char c_next = regex[i+1];
1906 
1907             if (c == '.' && c_next == '*') {
1908                 i++;
1909                 c = wild_any;
1910             }
1911             else if (c == '.')
1912                 c = wild_one;
1913             else if (i == 0 && c == '^') {
1914                 i++;
1915                 continue;
1916             }
1917             else if( c == '$' && c_next == 0 ) {
1918                 break;
1919             }
1920 
1921             re[j++] = c;
1922             i++;
1923 
1924         }
1925         re[j++] = '\'';
1926         re[j] = '\0';
1927 
1928         nOutSize = 1 + strlen(op)+ 1 + strlen(re) + 1;
1929         out = (char *)msSmallMalloc(nOutSize);
1930         snprintf(out, nOutSize, " %s %s", op, re);
1931         msFree(re);
1932         msFree(regex);
1933         break;
1934     }
1935     case MS_TOKEN_COMPARISON_INTERSECTS:
1936         out = msStrdup("ST_Intersects");
1937         break;
1938     case MS_TOKEN_COMPARISON_DISJOINT:
1939         out = msStrdup("ST_Disjoint");
1940         break;
1941     case MS_TOKEN_COMPARISON_TOUCHES:
1942         out = msStrdup("ST_Touches");
1943         break;
1944     case MS_TOKEN_COMPARISON_OVERLAPS:
1945         out = msStrdup("ST_Overlaps");
1946         break;
1947     case MS_TOKEN_COMPARISON_CROSSES:
1948         out = msStrdup("ST_Crosses");
1949         break;
1950     case MS_TOKEN_COMPARISON_WITHIN:
1951         out = msStrdup("ST_Within");
1952         break;
1953     case MS_TOKEN_COMPARISON_DWITHIN:
1954         out = msStrdup("ST_Distance");
1955         break;
1956     case MS_TOKEN_COMPARISON_BEYOND:
1957         out = msStrdup("ST_Distance");
1958         break;
1959     case MS_TOKEN_COMPARISON_CONTAINS:
1960         out = msStrdup("ST_Contains");
1961         break;
1962     case MS_TOKEN_COMPARISON_EQUALS:
1963         out = msStrdup("ST_Equals");
1964         break;
1965     case MS_TOKEN_FUNCTION_LENGTH:
1966         out = msStrdup("ST_Length");
1967         break;
1968     case MS_TOKEN_FUNCTION_AREA:
1969         out = msStrdup("ST_Area");
1970         break;
1971     case MS_TOKEN_BINDING_DOUBLE: {
1972         char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
1973         nOutSize = strlen(stresc)+ + 30;
1974         out = (char *)msSmallMalloc(nOutSize);
1975 
1976         char md_item_name[256];
1977         snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type",
1978                   n->tokenval.bindval.item );
1979         const char* type = msLookupHashTable(&(layer->metadata), md_item_name);
1980         // Do not cast if the variable is of the appropriate type as it can
1981         // prevent using database indexes, such as for SQlite
1982         if( type != NULL && (EQUAL(type, "Integer") ||
1983                              EQUAL(type, "Long") ||
1984                              EQUAL(type, "Real")) )
1985         {
1986              snprintf(out, nOutSize, "%s", stresc);
1987         }
1988         else
1989         {
1990             const char *SQLtype = "float(16)";
1991             if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG"))
1992                 SQLtype = "REAL";
1993             else if (EQUAL(info->dialect, "PostgreSQL"))
1994                 SQLtype = "double precision";
1995             snprintf(out, nOutSize, "CAST(%s AS %s)", stresc, SQLtype);
1996         }
1997         msFree(stresc);
1998         break;
1999     }
2000     case MS_TOKEN_BINDING_INTEGER: {
2001         char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
2002         nOutSize = strlen(stresc)+ 20;
2003         out = (char *)msSmallMalloc(nOutSize);
2004 
2005         char md_item_name[256];
2006         snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type",
2007                   n->tokenval.bindval.item );
2008         const char* type = msLookupHashTable(&(layer->metadata), md_item_name);
2009         // Do not cast if the variable is of the appropriate type as it can
2010         // prevent using database indexes, such as for SQlite
2011         if( type != NULL && (EQUAL(type, "Integer") ||
2012                              EQUAL(type, "Long") ||
2013                              EQUAL(type, "Real")) )
2014         {
2015             snprintf(out, nOutSize, "%s", stresc);
2016         }
2017         else
2018         {
2019             snprintf(out, nOutSize, "CAST(%s AS integer)", stresc);
2020         }
2021         msFree(stresc);
2022         break;
2023     }
2024     case MS_TOKEN_BINDING_STRING: {
2025         char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
2026         nOutSize = strlen(stresc) + 30;
2027         out = (char *)msSmallMalloc(nOutSize);
2028 
2029         char md_item_name[256];
2030         snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type",
2031                   n->tokenval.bindval.item );
2032         const char* type = msLookupHashTable(&(layer->metadata), md_item_name);
2033         // Do not cast if the variable is of the appropriate type as it can
2034         // prevent using database indexes, such as for SQlite
2035         if( type != NULL && EQUAL(type, "Character") )
2036         {
2037             snprintf(out, nOutSize, "%s", stresc);
2038         }
2039         else
2040         {
2041             snprintf(out, nOutSize, "CAST(%s AS text)", stresc);
2042         }
2043         msFree(stresc);
2044         break;
2045     }
2046     case MS_TOKEN_BINDING_TIME: {
2047         // won't get here unless col is parsed as time and they are not
2048         char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
2049         nOutSize = strlen(stresc)+ 10;
2050         out = (char *)msSmallMalloc(nOutSize);
2051         snprintf(out, nOutSize, "%s", stresc);
2052         msFree(stresc);
2053         break;
2054     }
2055     case MS_TOKEN_BINDING_SHAPE: {
2056         char *stresc = msOGRGetQuotedItem(layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
2057         nOutSize = strlen(stresc)+ 10;
2058         out = (char *)msSmallMalloc(nOutSize);
2059         snprintf(out, nOutSize, "%s", stresc);
2060         msFree(stresc);
2061         break;
2062     }
2063 
2064     // unhandled below until default
2065 
2066     case MS_TOKEN_FUNCTION_TOSTRING:
2067     case MS_TOKEN_FUNCTION_COMMIFY:
2068     case MS_TOKEN_FUNCTION_ROUND:
2069     case MS_TOKEN_FUNCTION_FROMTEXT:
2070     case MS_TOKEN_FUNCTION_BUFFER:
2071     case MS_TOKEN_FUNCTION_DIFFERENCE:
2072     case MS_TOKEN_FUNCTION_SIMPLIFY:
2073     case MS_TOKEN_FUNCTION_SIMPLIFYPT:
2074     case MS_TOKEN_FUNCTION_GENERALIZE:
2075     case MS_TOKEN_FUNCTION_SMOOTHSIA:
2076     case MS_TOKEN_FUNCTION_JAVASCRIPT:
2077     case MS_TOKEN_FUNCTION_UPPER:
2078     case MS_TOKEN_FUNCTION_LOWER:
2079     case MS_TOKEN_FUNCTION_INITCAP:
2080     case MS_TOKEN_FUNCTION_FIRSTCAP:
2081     case MS_TOKEN_BINDING_MAP_CELLSIZE:
2082     case MS_TOKEN_BINDING_DATA_CELLSIZE:
2083     case MS_PARSE_TYPE_BOOLEAN:
2084     case MS_PARSE_TYPE_STRING:
2085     case MS_PARSE_TYPE_SHAPE:
2086         break;
2087 
2088     default:
2089         if (n->token < 128) {
2090             char c = n->token;
2091             out = (char *)msSmallMalloc(2);
2092             sprintf(out, "%c", c);
2093         }
2094         break;
2095     }
2096 
2097     n = n->next;
2098     *node = n;
2099     return out;
2100 }
2101 
2102 /*
2103  * msOGRLayerBuildSQLOrderBy()
2104  *
2105  * Returns the content of a SQL ORDER BY clause from the sortBy member of
2106  * the layer. The string does not contain the "ORDER BY" keywords itself.
2107  */
msOGRLayerBuildSQLOrderBy(layerObj * layer,msOGRFileInfo * psInfo)2108 static char* msOGRLayerBuildSQLOrderBy(layerObj *layer, msOGRFileInfo *psInfo)
2109 {
2110   char* strOrderBy = NULL;
2111   if( layer->sortBy.nProperties > 0 ) {
2112     int i;
2113     for(i=0;i<layer->sortBy.nProperties;i++) {
2114       if( i > 0 )
2115         strOrderBy = msStringConcatenate(strOrderBy, ", ");
2116       char* escapedItem = msLayerEscapePropertyName(layer, layer->sortBy.properties[i].item);
2117       if( psInfo->pszTablePrefix)
2118       {
2119           char* escapedTable = msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
2120           strOrderBy = msStringConcatenate(strOrderBy, "\"");
2121           strOrderBy = msStringConcatenate(strOrderBy, escapedTable);
2122           strOrderBy = msStringConcatenate(strOrderBy, "\".\"");
2123           strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
2124           strOrderBy = msStringConcatenate(strOrderBy, "\"");
2125           msFree(escapedTable);
2126       }
2127       else
2128       {
2129 #if GDAL_VERSION_MAJOR < 2
2130           // Old GDAL don't like quoted identifiers in ORDER BY
2131           strOrderBy = msStringConcatenate(strOrderBy,
2132                                            layer->sortBy.properties[i].item);
2133 #else
2134           strOrderBy = msStringConcatenate(strOrderBy, "\"");
2135           strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
2136           strOrderBy = msStringConcatenate(strOrderBy, "\"");
2137 #endif
2138       }
2139       msFree(escapedItem);
2140       if( layer->sortBy.properties[i].sortOrder == SORT_DESC )
2141         strOrderBy = msStringConcatenate(strOrderBy, " DESC");
2142     }
2143   }
2144   return strOrderBy;
2145 }
2146 
2147 /**********************************************************************
2148  *                     msOGRFileWhichShapes()
2149  *
2150  * Init OGR layer structs ready for calls to msOGRFileNextShape().
2151  *
2152  * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
2153  * layer's FILTER overlaps the selected region.
2154  **********************************************************************/
msOGRFileWhichShapes(layerObj * layer,rectObj rect,msOGRFileInfo * psInfo)2155 static int msOGRFileWhichShapes(layerObj *layer, rectObj rect, msOGRFileInfo *psInfo)
2156 {
2157     // rect is from BBOX parameter in query (In lieu of a FEATUREID or FILTER) or mapfile somehow
2158     if (psInfo == NULL || psInfo->hLayer == NULL) {
2159         msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!", "msOGRFileWhichShapes()");
2160         return(MS_FAILURE);
2161     }
2162 
2163     char *select = (psInfo->pszSelect) ? msStrdup(psInfo->pszSelect) : NULL;
2164     const rectObj rectInvalid = MS_INIT_INVALID_RECT;
2165     bool bIsValidRect = memcmp(&rect, &rectInvalid, sizeof(rect)) != 0;
2166 
2167     // we'll go strictly two possible ways:
2168     // 1) GetLayer + SetFilter
2169     // 2) ExecuteSQL (psInfo->hLayer is an SQL result OR sortBy was requested OR have native_string
2170     // and start from the second
2171 
2172     if ( psInfo->bIsOKForSQLCompose && (psInfo->nLayerIndex == -1 ||
2173                                         layer->sortBy.nProperties > 0 ||
2174                                         layer->filter.native_string ||
2175                                         (psInfo->bPaging && layer->maxfeatures > 0)) ) {
2176 
2177         const bool bHasGeometry =
2178                                 OGR_L_GetGeomType( psInfo->hLayer ) != wkbNone;
2179 
2180         if( psInfo->nLayerIndex == -1 && select == NULL ) {
2181             select = msStrdup(psInfo->pszLayerDef);
2182             /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
2183             OGR_DS_ReleaseResultSet( psInfo->hDS, psInfo->hLayer );
2184             psInfo->hLayer = NULL;
2185         }
2186         else if( select == NULL ) {
2187             const char* pszGeometryColumn;
2188             int i;
2189             select = msStringConcatenate(select, "SELECT ");
2190             for(i = 0; i < layer->numitems; i++) {
2191                 if( i > 0 )
2192                     select = msStringConcatenate(select, ", ");
2193                 char* escaped = msOGRGetQuotedItem(layer, layer->items[i]);
2194                 select = msStringConcatenate(select, escaped);
2195                 msFree(escaped);
2196                 if( psInfo->pszTablePrefix )
2197                 {
2198                     select = msStringConcatenate(select, " AS \"");
2199                     escaped = msLayerEscapePropertyName(layer, layer->items[i]);
2200                     select = msStringConcatenate(select, escaped);
2201                     msFree(escaped);
2202                     select = msStringConcatenate(select, "\"");
2203                 }
2204             }
2205             if( layer->numitems > 0 )
2206                 select = msStringConcatenate(select, ", ");
2207             pszGeometryColumn = OGR_L_GetGeometryColumn(psInfo->hLayer);
2208             if( pszGeometryColumn != NULL && pszGeometryColumn[0] != '\0' ) {
2209                 char* escaped = msOGRGetQuotedItem(layer, pszGeometryColumn);
2210                 select = msStringConcatenate(select, escaped);
2211                 msFree(escaped);
2212                 if( psInfo->pszTablePrefix )
2213                 {
2214                     select = msStringConcatenate(select, " AS \"");
2215                     escaped = msLayerEscapePropertyName(layer, pszGeometryColumn);
2216                     select = msStringConcatenate(select, escaped);
2217                     msFree(escaped);
2218                     select = msStringConcatenate(select, "\"");
2219                 }
2220             } else {
2221                 /* Add ", *" so that we still have an hope to get the geometry */
2222                 if( psInfo->pszTablePrefix )
2223                 {
2224                     select = msStringConcatenate(select, "\"");
2225                     char* escaped = msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
2226                     select = msStringConcatenate(select, escaped);
2227                     msFree(escaped);
2228                     select = msStringConcatenate(select, "\".");
2229                 }
2230                 select = msStringConcatenate(select, "*");
2231             }
2232             select = msStringConcatenate(select, " FROM ");
2233             if( psInfo->nLayerIndex == -1 )
2234             {
2235               select = msStringConcatenate(select, "(");
2236               select = msStringConcatenate(select, psInfo->pszLayerDef);
2237               select = msStringConcatenate(select, ") MSSUBSELECT");
2238             }
2239             else
2240             {
2241               select = msStringConcatenate(select, "\"");
2242               char* escaped = msLayerEscapePropertyName(layer, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
2243               select = msStringConcatenate(select, escaped);
2244               msFree(escaped);
2245               select = msStringConcatenate(select, "\"");
2246             }
2247         }
2248 
2249         char *filter = NULL;
2250         if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
2251             filter = msStringConcatenate(filter, "(");
2252             filter = msStringConcatenate(filter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2253             filter = msStringConcatenate(filter, ")");
2254         }
2255 
2256         /* ------------------------------------------------------------------
2257          * Set Spatial filter... this may result in no features being returned
2258          * if layer does not overlap current view.
2259          *
2260          * __TODO__ We should return MS_DONE if no shape overlaps the selected
2261          * region and matches the layer's FILTER expression, but there is currently
2262          * no _efficient_ way to do that with OGR.
2263          * ------------------------------------------------------------------ */
2264         if (psInfo->rect_is_defined) {
2265             rect.minx = MAX(psInfo->rect.minx, rect.minx);
2266             rect.miny = MAX(psInfo->rect.miny, rect.miny);
2267             rect.maxx = MIN(psInfo->rect.maxx, rect.maxx);
2268             rect.maxy = MIN(psInfo->rect.maxy, rect.maxy);
2269             bIsValidRect = true;
2270         }
2271         psInfo->rect = rect;
2272 
2273         bool bSpatialiteOrGPKGAddOrderByFID = false;
2274 
2275         const char *sql = layer->filter.native_string;
2276         if (psInfo->dialect && sql && *sql != '\0' &&
2277             (EQUAL(psInfo->dialect, "Spatialite") ||
2278              EQUAL(psInfo->dialect, "GPKG") ||
2279              EQUAL(psInfo->dialect, "PostgreSQL")) )
2280         {
2281             if (filter) filter = msStringConcatenate(filter, " AND ");
2282             filter = msStringConcatenate(filter, "(");
2283             filter = msStringConcatenate(filter, sql);
2284             filter = msStringConcatenate(filter, ")");
2285         }
2286         else if( psInfo->pszWHERE )
2287         {
2288             if (filter) filter = msStringConcatenate(filter, " AND ");
2289             filter = msStringConcatenate(filter, "(");
2290             filter = msStringConcatenate(filter, psInfo->pszWHERE);
2291             filter = msStringConcatenate(filter, ")");
2292         }
2293 
2294         bool bOffsetAlreadyAdded = false;
2295         // use spatial index
2296         if (psInfo->dialect && bIsValidRect ) {
2297             if (EQUAL(psInfo->dialect, "PostgreSQL")) {
2298                 if (filter) filter = msStringConcatenate(filter, " AND");
2299                 const char *col = OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
2300                 filter = msStringConcatenate(filter, " (\"");
2301                 char* escaped = msLayerEscapePropertyName(layer, col);
2302                 filter = msStringConcatenate(filter, escaped);
2303                 msFree(escaped);
2304                 filter = msStringConcatenate(filter, "\" && ST_MakeEnvelope(");
2305                 char *points = (char *)msSmallMalloc(30*2*5);
2306                 snprintf(points, 30*4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny, rect.maxx, rect.maxy);
2307                 filter = msStringConcatenate(filter, points);
2308                 msFree(points);
2309                 filter = msStringConcatenate(filter, "))");
2310             }
2311             else if( psInfo->dialect &&
2312                      (EQUAL(psInfo->dialect, "Spatialite") ||
2313                       EQUAL(psInfo->dialect, "GPKG")) &&
2314                      psInfo->pszMainTableName != NULL )
2315             {
2316                 if( (EQUAL(psInfo->dialect, "Spatialite") && psInfo->bHasSpatialIndex)
2317                       || EQUAL(psInfo->dialect, "GPKG") )
2318                 {
2319                     if (filter) filter = msStringConcatenate(filter, " AND ");
2320                     char* pszEscapedMainTableName = msLayerEscapePropertyName(
2321                                                     layer, psInfo->pszMainTableName);
2322                     filter = msStringConcatenate(filter, "\"");
2323                     filter = msStringConcatenate(filter, pszEscapedMainTableName);
2324                     msFree(pszEscapedMainTableName);
2325                     filter = msStringConcatenate(filter, "\".");
2326                     if( psInfo->pszRowId )
2327                     {
2328                         char* pszEscapedRowId = msLayerEscapePropertyName(
2329                                                             layer, psInfo->pszRowId);
2330                         filter = msStringConcatenate(filter, "\"");
2331                         filter = msStringConcatenate(filter, pszEscapedRowId);
2332                         filter = msStringConcatenate(filter, "\"");
2333                         msFree(pszEscapedRowId);
2334                     }
2335                     else
2336                         filter = msStringConcatenate(filter, "ROWID");
2337 
2338                     filter = msStringConcatenate(filter, " IN ");
2339                     filter = msStringConcatenate(filter, "(");
2340                     filter = msStringConcatenate(filter, "SELECT ");
2341 
2342                     if( EQUAL(psInfo->dialect, "Spatialite") )
2343                         filter = msStringConcatenate(filter, "ms_spat_idx.pkid");
2344                     else
2345                         filter = msStringConcatenate(filter, "ms_spat_idx.id");
2346 
2347                     filter = msStringConcatenate(filter, " FROM ");
2348 
2349                     char szSpatialIndexName[256];
2350                     snprintf( szSpatialIndexName, sizeof(szSpatialIndexName),
2351                                 "%s_%s_%s",
2352                                 EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree",
2353                                 psInfo->pszSpatialFilterTableName,
2354                                 psInfo->pszSpatialFilterGeometryColumn );
2355                     char* pszEscapedSpatialIndexName = msLayerEscapePropertyName(
2356                                                     layer, szSpatialIndexName);
2357 
2358                     filter = msStringConcatenate(filter, "\"");
2359                     filter = msStringConcatenate(filter, pszEscapedSpatialIndexName);
2360                     msFree(pszEscapedSpatialIndexName);
2361 
2362                     filter = msStringConcatenate(filter, "\" ms_spat_idx WHERE ");
2363 
2364                     char szCond[256];
2365                     if( EQUAL(psInfo->dialect, "Spatialite") )
2366                     {
2367                         snprintf(szCond, sizeof(szCond),
2368                                 "ms_spat_idx.xmin <= %.15g AND ms_spat_idx.xmax >= %.15g AND "
2369                                 "ms_spat_idx.ymin <= %.15g AND ms_spat_idx.ymax >= %.15g",
2370                                 rect.maxx, rect.minx, rect.maxy, rect.miny);
2371                     }
2372                     else
2373                     {
2374                         snprintf(szCond, sizeof(szCond),
2375                                 "ms_spat_idx.minx <= %.15g AND ms_spat_idx.maxx >= %.15g AND "
2376                                 "ms_spat_idx.miny <= %.15g AND ms_spat_idx.maxy >= %.15g",
2377                                 rect.maxx, rect.minx, rect.maxy, rect.miny);
2378                     }
2379                     filter = msStringConcatenate(filter, szCond);
2380 
2381                     filter = msStringConcatenate(filter, ")");
2382 
2383                     bSpatialiteOrGPKGAddOrderByFID = true;
2384                 }
2385 
2386                 const bool isGPKG = EQUAL(psInfo->dialect, "GPKG");
2387                 if (filter) filter = msStringConcatenate(filter, " AND");
2388                 const char *col = OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
2389                 filter = msStringConcatenate(filter, " Intersects(");
2390                 if( isGPKG )
2391                 {
2392                     // Casting GeoPackage geometries to spatialie ones is done
2393                     // automatically normally, since GDAL enables the
2394                     // "amphibious" mode, but without it
2395                     // explicilty specified, spatialite 4.3.0a does an
2396                     // out-of-bounds access.
2397                     filter = msStringConcatenate(filter, "GeomFromGPB(");
2398                 }
2399                 filter = msStringConcatenate(filter, "\"");
2400                 char* escaped = msLayerEscapePropertyName(layer, col);
2401                 filter = msStringConcatenate(filter, escaped);
2402                 msFree(escaped);
2403                 filter = msStringConcatenate(filter, "\"");
2404                 if( isGPKG )
2405                     filter = msStringConcatenate(filter, ")");
2406                 filter = msStringConcatenate(filter, ", BuildMbr(");
2407                 char *points = (char *)msSmallMalloc(30*2*5);
2408                 snprintf(points, 30*4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny, rect.maxx, rect.maxy);
2409                 filter = msStringConcatenate(filter, points);
2410                 msFree(points);
2411                 filter = msStringConcatenate(filter, "))");
2412             }
2413         }
2414 
2415         /* get sortBy */
2416         char *sort = NULL;
2417         if( layer->sortBy.nProperties > 0) {
2418 
2419             char *strOrderBy = msOGRLayerBuildSQLOrderBy(layer, psInfo);
2420             if (strOrderBy) {
2421                 if( psInfo->nLayerIndex == -1 ) {
2422                     if( strcasestr(psInfo->pszLayerDef, " ORDER BY ") == NULL )
2423                         sort = msStringConcatenate(sort, " ORDER BY ");
2424                     else
2425                         sort = msStringConcatenate(sort, ", ");
2426                 } else {
2427                     sort = msStringConcatenate(sort, " ORDER BY ");
2428                 }
2429                 sort = msStringConcatenate(sort, strOrderBy);
2430                 msFree(strOrderBy);
2431             }
2432         }
2433 
2434         if( bSpatialiteOrGPKGAddOrderByFID )
2435         {
2436             if( sort == NULL )
2437                 sort = msStringConcatenate(NULL, " ORDER BY ");
2438             else
2439                 sort = msStringConcatenate(sort, ", ");
2440             char* pszEscapedMainTableName = msLayerEscapePropertyName(
2441                                             layer, psInfo->pszMainTableName);
2442             sort = msStringConcatenate(sort, "\"");
2443             sort = msStringConcatenate(sort, pszEscapedMainTableName);
2444             sort = msStringConcatenate(sort, "\".");
2445             msFree(pszEscapedMainTableName);
2446             if( psInfo->pszRowId )
2447             {
2448                 char* pszEscapedRowId = msLayerEscapePropertyName(
2449                                                     layer, psInfo->pszRowId);
2450                 sort = msStringConcatenate(sort, "\"");
2451                 sort = msStringConcatenate(sort, pszEscapedRowId);
2452                 sort = msStringConcatenate(sort, "\"");
2453                 msFree(pszEscapedRowId);
2454             }
2455             else
2456                 sort = msStringConcatenate(sort, "ROWID");
2457         }
2458 
2459         // compose SQL
2460         if (filter) {
2461             select = msStringConcatenate(select, " WHERE ");
2462             select = msStringConcatenate(select, filter);
2463             msFree(filter);
2464         }
2465         if (sort) {
2466             select = msStringConcatenate(select, " ");
2467             select = msStringConcatenate(select, sort);
2468             msFree(sort);
2469         }
2470 
2471         if ( psInfo->bPaging && layer->maxfeatures >= 0 ) {
2472             char szLimit[50];
2473             snprintf(szLimit, sizeof(szLimit), " LIMIT %d", layer->maxfeatures);
2474             select = msStringConcatenate(select, szLimit);
2475         }
2476 
2477         if ( !bOffsetAlreadyAdded && psInfo->bPaging && layer->startindex > 0 ) {
2478             char szOffset[50];
2479             snprintf(szOffset, sizeof(szOffset), " OFFSET %d", layer->startindex-1);
2480             select = msStringConcatenate(select, szOffset);
2481         }
2482 
2483         if( layer->debug )
2484             msDebug("msOGRFileWhichShapes: SQL = %s.\n", select);
2485 
2486         ACQUIRE_OGR_LOCK;
2487         if( psInfo->nLayerIndex == -1 && psInfo->hLayer != NULL )
2488         {
2489           OGR_DS_ReleaseResultSet( psInfo->hDS, psInfo->hLayer );
2490         }
2491 
2492         OGRGeometryH hGeom = NULL;
2493         if( psInfo->dialect == NULL &&
2494             bHasGeometry && bIsValidRect ) {
2495             if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
2496                 hGeom = OGR_G_CreateGeometry( wkbPoint );
2497                 OGR_G_SetPoint_2D( hGeom, 0, rect.minx, rect.miny );
2498             } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
2499                 hGeom = OGR_G_CreateGeometry( wkbLineString );
2500                 OGR_G_AddPoint_2D( hGeom, rect.minx, rect.miny );
2501                 OGR_G_AddPoint_2D( hGeom, rect.maxx, rect.maxy );
2502             } else {
2503                 hGeom = OGR_G_CreateGeometry( wkbPolygon );
2504                 OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing );
2505 
2506                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.miny);
2507                 OGR_G_AddPoint_2D( hRing, rect.maxx, rect.miny);
2508                 OGR_G_AddPoint_2D( hRing, rect.maxx, rect.maxy);
2509                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.maxy);
2510                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.miny);
2511                 OGR_G_AddGeometryDirectly( hGeom, hRing );
2512             }
2513 
2514             if (layer->debug >= MS_DEBUGLEVEL_VVV)
2515             {
2516                 msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g %.15g %.15g\n",
2517                         rect.minx, rect.miny, rect.maxx, rect.maxy );
2518             }
2519         }
2520 
2521         psInfo->hLayer = OGR_DS_ExecuteSQL( psInfo->hDS, select, hGeom, NULL );
2522         psInfo->nLayerIndex = -1;
2523         if( hGeom != NULL )
2524             OGR_G_DestroyGeometry(hGeom);
2525 
2526         if( psInfo->hLayer == NULL ) {
2527             RELEASE_OGR_LOCK;
2528             msSetError(MS_OGRERR, "ExecuteSQL() failed. Check logs.", "msOGRFileWhichShapes()");
2529             msDebug("ExecuteSQL(%s) failed.\n%s\n", select, CPLGetLastErrorMsg());
2530             msFree(select);
2531             return MS_FAILURE;
2532         }
2533 
2534         // Update itemindexes / layer->iteminfo
2535         msOGRLayerInitItemInfo(layer);
2536     }
2537     else {
2538 
2539         // case of 1) GetLayer + SetFilter
2540 
2541         char *pszOGRFilter = NULL;
2542         if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
2543             pszOGRFilter = msStringConcatenate(pszOGRFilter, "(");
2544             pszOGRFilter = msStringConcatenate(pszOGRFilter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2545             pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
2546         }
2547 
2548         if( psInfo->pszWHERE )
2549         {
2550             if( pszOGRFilter )
2551             {
2552                 pszOGRFilter = msStringConcatenate(pszOGRFilter, " AND (");
2553                 pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
2554                 pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
2555             }
2556             else
2557             {
2558                 pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
2559             }
2560         }
2561 
2562         ACQUIRE_OGR_LOCK;
2563 
2564         if( OGR_L_GetGeomType( psInfo->hLayer ) != wkbNone && bIsValidRect ) {
2565             if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
2566                 OGRGeometryH hSpatialFilterPoint = OGR_G_CreateGeometry( wkbPoint );
2567 
2568                 OGR_G_SetPoint_2D( hSpatialFilterPoint, 0, rect.minx, rect.miny );
2569                 OGR_L_SetSpatialFilter( psInfo->hLayer, hSpatialFilterPoint );
2570                 OGR_G_DestroyGeometry( hSpatialFilterPoint );
2571             } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
2572                 OGRGeometryH hSpatialFilterLine = OGR_G_CreateGeometry( wkbLineString );
2573 
2574                 OGR_G_AddPoint_2D( hSpatialFilterLine, rect.minx, rect.miny );
2575                 OGR_G_AddPoint_2D( hSpatialFilterLine, rect.maxx, rect.maxy );
2576                 OGR_L_SetSpatialFilter( psInfo->hLayer, hSpatialFilterLine );
2577                 OGR_G_DestroyGeometry( hSpatialFilterLine );
2578             } else {
2579                 OGRGeometryH hSpatialFilterPolygon = OGR_G_CreateGeometry( wkbPolygon );
2580                 OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing );
2581 
2582                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.miny);
2583                 OGR_G_AddPoint_2D( hRing, rect.maxx, rect.miny);
2584                 OGR_G_AddPoint_2D( hRing, rect.maxx, rect.maxy);
2585                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.maxy);
2586                 OGR_G_AddPoint_2D( hRing, rect.minx, rect.miny);
2587                 OGR_G_AddGeometryDirectly( hSpatialFilterPolygon, hRing );
2588                 OGR_L_SetSpatialFilter( psInfo->hLayer, hSpatialFilterPolygon );
2589                 OGR_G_DestroyGeometry( hSpatialFilterPolygon );
2590             }
2591 
2592             if (layer->debug >= MS_DEBUGLEVEL_VVV)
2593             {
2594                 msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g %.15g %.15g\n", rect.minx, rect.miny, rect.maxx, rect.maxy );
2595             }
2596         }
2597 
2598         psInfo->rect = rect;
2599 
2600         /* ------------------------------------------------------------------
2601          * Apply an attribute filter if we have one prefixed with a WHERE
2602          * keyword in the filter string.  Otherwise, ensure the attribute
2603          * filter is clear.
2604          * ------------------------------------------------------------------ */
2605         if( pszOGRFilter != NULL ) {
2606 
2607             if (layer->debug >= MS_DEBUGLEVEL_VVV)
2608                 msDebug("msOGRFileWhichShapes: Setting attribute filter to %s\n", pszOGRFilter );
2609 
2610             CPLErrorReset();
2611             if( OGR_L_SetAttributeFilter( psInfo->hLayer, pszOGRFilter ) != OGRERR_NONE ) {
2612                 msSetError(MS_OGRERR, "SetAttributeFilter() failed on layer %s. Check logs.", "msOGRFileWhichShapes()", layer->name?layer->name:"(null)");
2613                 msDebug("SetAttributeFilter(%s) failed on layer %s.\n%s\n", pszOGRFilter, layer->name?layer->name:"(null)", CPLGetLastErrorMsg() );
2614                 RELEASE_OGR_LOCK;
2615                 msFree(pszOGRFilter);
2616                 msFree(select);
2617                 return MS_FAILURE;
2618             }
2619             msFree(pszOGRFilter);
2620         } else
2621             OGR_L_SetAttributeFilter( psInfo->hLayer, NULL );
2622 
2623     }
2624 
2625     msFree(select);
2626 
2627     /* ------------------------------------------------------------------
2628      * Reset current feature pointer
2629      * ------------------------------------------------------------------ */
2630     OGR_L_ResetReading( psInfo->hLayer );
2631     psInfo->last_record_index_read = -1;
2632 
2633     RELEASE_OGR_LOCK;
2634 
2635     return MS_SUCCESS;
2636 }
2637 
2638 /**********************************************************************
2639  *                     msOGRPassThroughFieldDefinitions()
2640  *
2641  * Pass the field definitions through to the layer metadata in the
2642  * "gml_[item]_{type,width,precision}" set of metadata items for
2643  * defining fields.
2644  **********************************************************************/
2645 
2646 static void
msOGRPassThroughFieldDefinitions(layerObj * layer,msOGRFileInfo * psInfo)2647 msOGRPassThroughFieldDefinitions( layerObj *layer, msOGRFileInfo *psInfo )
2648 
2649 {
2650   OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn( psInfo->hLayer );
2651   int numitems, i;
2652 
2653   numitems = OGR_FD_GetFieldCount( hDefn );
2654 
2655   for(i=0; i<numitems; i++) {
2656     OGRFieldDefnH hField = OGR_FD_GetFieldDefn( hDefn, i );
2657     char md_item_name[256];
2658     char gml_width[32], gml_precision[32];
2659     const char *gml_type = NULL;
2660     const char *item = OGR_Fld_GetNameRef( hField );
2661 
2662     gml_width[0] = '\0';
2663     gml_precision[0] = '\0';
2664 
2665     switch( OGR_Fld_GetType( hField ) ) {
2666       case OFTInteger:
2667         gml_type = "Integer";
2668         if( OGR_Fld_GetWidth( hField) > 0 )
2669           sprintf( gml_width, "%d", OGR_Fld_GetWidth( hField) );
2670         break;
2671 
2672 #if GDAL_VERSION_MAJOR >= 2
2673       case OFTInteger64:
2674         gml_type = "Long";
2675         if( OGR_Fld_GetWidth( hField) > 0 )
2676           sprintf( gml_width, "%d", OGR_Fld_GetWidth( hField) );
2677         break;
2678 #endif
2679 
2680       case OFTReal:
2681         gml_type = "Real";
2682         if( OGR_Fld_GetWidth( hField) > 0 )
2683           sprintf( gml_width, "%d", OGR_Fld_GetWidth( hField) );
2684         if( OGR_Fld_GetPrecision( hField ) > 0 )
2685           sprintf( gml_precision, "%d", OGR_Fld_GetPrecision( hField) );
2686         break;
2687 
2688       case OFTString:
2689         gml_type = "Character";
2690         if( OGR_Fld_GetWidth( hField) > 0 )
2691           sprintf( gml_width, "%d", OGR_Fld_GetWidth( hField) );
2692         break;
2693 
2694       case OFTDate:
2695         gml_type = "Date";
2696         break;
2697       case OFTTime:
2698         gml_type = "Time";
2699         break;
2700       case OFTDateTime:
2701         gml_type = "DateTime";
2702         break;
2703 
2704       default:
2705         gml_type = "Character";
2706         break;
2707     }
2708 
2709     snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type", item );
2710     if( msOWSLookupMetadata(&(layer->metadata), "G", "type") == NULL )
2711       msInsertHashTable(&(layer->metadata), md_item_name, gml_type );
2712 
2713     snprintf( md_item_name, sizeof(md_item_name), "gml_%s_width", item );
2714     if( strlen(gml_width) > 0
2715         && msOWSLookupMetadata(&(layer->metadata), "G", "width") == NULL )
2716       msInsertHashTable(&(layer->metadata), md_item_name, gml_width );
2717 
2718     snprintf( md_item_name, sizeof(md_item_name), "gml_%s_precision",item );
2719     if( strlen(gml_precision) > 0
2720         && msOWSLookupMetadata(&(layer->metadata), "G", "precision")==NULL )
2721       msInsertHashTable(&(layer->metadata), md_item_name, gml_precision );
2722   }
2723 
2724   /* Should we try to address style items, or other special items? */
2725 }
2726 
2727 /**********************************************************************
2728  *                     msOGRFileGetItems()
2729  *
2730  * Returns a list of field names in a NULL terminated list of strings.
2731  **********************************************************************/
msOGRFileGetItems(layerObj * layer,msOGRFileInfo * psInfo)2732 static char **msOGRFileGetItems(layerObj *layer, msOGRFileInfo *psInfo )
2733 {
2734   OGRFeatureDefnH hDefn;
2735   int i, numitems,totalnumitems;
2736   int numStyleItems = MSOGR_LABELNUMITEMS;
2737   char **items;
2738   const char *getShapeStyleItems, *value;
2739 
2740   if((hDefn = OGR_L_GetLayerDefn( psInfo->hLayer )) == NULL) {
2741     msSetError(MS_OGRERR,
2742                "OGR Connection for layer `%s' contains no field definition.",
2743                "msOGRFileGetItems()",
2744                layer->name?layer->name:"(null)" );
2745     return NULL;
2746   }
2747 
2748   totalnumitems = numitems = OGR_FD_GetFieldCount( hDefn );
2749 
2750   getShapeStyleItems = msLayerGetProcessingKey( layer, "GETSHAPE_STYLE_ITEMS" );
2751   if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all"))
2752     totalnumitems += numStyleItems;
2753 
2754   if((items = (char**)malloc(sizeof(char *)*(totalnumitems+1))) == NULL) {
2755     msSetError(MS_MEMERR, NULL, "msOGRFileGetItems()");
2756     return NULL;
2757   }
2758 
2759   for(i=0; i<numitems; i++) {
2760     OGRFieldDefnH hField = OGR_FD_GetFieldDefn( hDefn, i );
2761     items[i] = msStrdup( OGR_Fld_GetNameRef( hField ));
2762   }
2763 
2764   if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all")) {
2765     assert(numStyleItems == 21);
2766     items[i++] = msStrdup( MSOGR_LABELFONTNAMENAME );
2767     items[i++] = msStrdup( MSOGR_LABELSIZENAME );
2768     items[i++] = msStrdup( MSOGR_LABELTEXTNAME );
2769     items[i++] = msStrdup( MSOGR_LABELANGLENAME );
2770     items[i++] = msStrdup( MSOGR_LABELFCOLORNAME );
2771     items[i++] = msStrdup( MSOGR_LABELBCOLORNAME );
2772     items[i++] = msStrdup( MSOGR_LABELPLACEMENTNAME );
2773     items[i++] = msStrdup( MSOGR_LABELANCHORNAME );
2774     items[i++] = msStrdup( MSOGR_LABELDXNAME );
2775     items[i++] = msStrdup( MSOGR_LABELDYNAME );
2776     items[i++] = msStrdup( MSOGR_LABELPERPNAME );
2777     items[i++] = msStrdup( MSOGR_LABELBOLDNAME );
2778     items[i++] = msStrdup( MSOGR_LABELITALICNAME );
2779     items[i++] = msStrdup( MSOGR_LABELUNDERLINENAME );
2780     items[i++] = msStrdup( MSOGR_LABELPRIORITYNAME );
2781     items[i++] = msStrdup( MSOGR_LABELSTRIKEOUTNAME );
2782     items[i++] = msStrdup( MSOGR_LABELSTRETCHNAME );
2783     items[i++] = msStrdup( MSOGR_LABELADJHORNAME );
2784     items[i++] = msStrdup( MSOGR_LABELADJVERTNAME );
2785     items[i++] = msStrdup( MSOGR_LABELHCOLORNAME );
2786     items[i++] = msStrdup( MSOGR_LABELOCOLORNAME );
2787   }
2788   items[i++] = NULL;
2789 
2790   /* -------------------------------------------------------------------- */
2791   /*      consider populating the field definitions in metadata.          */
2792   /* -------------------------------------------------------------------- */
2793   if((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL
2794       && strcasecmp(value,"auto") == 0 )
2795     msOGRPassThroughFieldDefinitions( layer, psInfo );
2796 
2797   return items;
2798 }
2799 
2800 /**********************************************************************
2801  *                     msOGRFileNextShape()
2802  *
2803  * Returns shape sequentially from OGR data source.
2804  * msOGRLayerWhichShape() must have been called first.
2805  *
2806  * Returns MS_SUCCESS/MS_FAILURE
2807  **********************************************************************/
2808 static int
msOGRFileNextShape(layerObj * layer,shapeObj * shape,msOGRFileInfo * psInfo)2809 msOGRFileNextShape(layerObj *layer, shapeObj *shape,
2810                    msOGRFileInfo *psInfo )
2811 {
2812   OGRFeatureH hFeature = NULL;
2813 
2814   if (psInfo == NULL || psInfo->hLayer == NULL) {
2815     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
2816                "msOGRFileNextShape()");
2817     return(MS_FAILURE);
2818   }
2819 
2820   /* ------------------------------------------------------------------
2821    * Read until we find a feature that matches attribute filter and
2822    * whose geometry is compatible with current layer type.
2823    * ------------------------------------------------------------------ */
2824   msFreeShape(shape);
2825   shape->type = MS_SHAPE_NULL;
2826 
2827   ACQUIRE_OGR_LOCK;
2828   while (shape->type == MS_SHAPE_NULL) {
2829     if( hFeature )
2830       OGR_F_Destroy( hFeature );
2831 
2832     if( (hFeature = OGR_L_GetNextFeature( psInfo->hLayer )) == NULL ) {
2833       psInfo->last_record_index_read = -1;
2834       if( CPLGetLastErrorType() == CE_Failure ) {
2835         msSetError(MS_OGRERR, "OGR GetNextFeature() error'd. Check logs.",
2836                    "msOGRFileNextShape()");
2837         msDebug("msOGRFileNextShape(): %s\n",
2838                 CPLGetLastErrorMsg() );
2839         RELEASE_OGR_LOCK;
2840         return MS_FAILURE;
2841       } else {
2842         RELEASE_OGR_LOCK;
2843         if (layer->debug >= MS_DEBUGLEVEL_VV)
2844           msDebug("msOGRFileNextShape: Returning MS_DONE (no more shapes)\n" );
2845         return MS_DONE;  // No more features to read
2846       }
2847     }
2848 
2849     psInfo->last_record_index_read++;
2850 
2851     if(layer->numitems > 0) {
2852       if (shape->values) msFreeCharArray(shape->values, shape->numvalues);
2853       shape->values = msOGRGetValues(layer, hFeature);
2854       shape->numvalues = layer->numitems;
2855       if(!shape->values) {
2856         OGR_F_Destroy( hFeature );
2857         RELEASE_OGR_LOCK;
2858         return(MS_FAILURE);
2859       }
2860     }
2861 
2862     // Feature matched filter expression... process geometry
2863     // shape->type will be set if geom is compatible with layer type
2864     if (ogrConvertGeometry(ogrGetLinearGeometry( hFeature ), shape,
2865                            layer->type) == MS_SUCCESS) {
2866       if (shape->type != MS_SHAPE_NULL)
2867         break; // Shape is ready to be returned!
2868 
2869       if (layer->debug >= MS_DEBUGLEVEL_VVV)
2870         msDebug("msOGRFileNextShape: Rejecting feature (shapeid = " CPL_FRMT_GIB ", tileid=%d) of incompatible type for this layer (feature wkbType %d, layer type %d)\n",
2871                 (GIntBig)OGR_F_GetFID( hFeature ), psInfo->nTileId,
2872                 OGR_F_GetGeometryRef( hFeature )==NULL ? wkbFlatten(wkbUnknown):wkbFlatten( OGR_G_GetGeometryType( OGR_F_GetGeometryRef( hFeature ) ) ),
2873                 layer->type);
2874 
2875     } else {
2876       msFreeShape(shape);
2877       OGR_F_Destroy( hFeature );
2878       RELEASE_OGR_LOCK;
2879       return MS_FAILURE; // Error message already produced.
2880     }
2881 
2882     // Feature rejected... free shape to clear attributes values.
2883     msFreeShape(shape);
2884     shape->type = MS_SHAPE_NULL;
2885   }
2886 
2887   shape->index =  (int)OGR_F_GetFID( hFeature ); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
2888   shape->resultindex = psInfo->last_record_index_read;
2889   shape->tileindex = psInfo->nTileId;
2890 
2891   if (layer->debug >= MS_DEBUGLEVEL_VVV)
2892     msDebug("msOGRFileNextShape: Returning shape=%ld, tile=%d\n",
2893             shape->index, shape->tileindex );
2894 
2895   // Keep ref. to last feature read in case we need style info.
2896   if (psInfo->hLastFeature)
2897     OGR_F_Destroy( psInfo->hLastFeature );
2898   psInfo->hLastFeature = hFeature;
2899 
2900   RELEASE_OGR_LOCK;
2901 
2902   return MS_SUCCESS;
2903 }
2904 
2905 /**********************************************************************
2906  *                     msOGRFileGetShape()
2907  *
2908  * Returns shape from OGR data source by id.
2909  *
2910  * Returns MS_SUCCESS/MS_FAILURE
2911  **********************************************************************/
2912 static int
msOGRFileGetShape(layerObj * layer,shapeObj * shape,long record,msOGRFileInfo * psInfo,int record_is_fid)2913 msOGRFileGetShape(layerObj *layer, shapeObj *shape, long record,
2914                   msOGRFileInfo *psInfo, int record_is_fid )
2915 {
2916   OGRFeatureH hFeature;
2917 
2918   if (psInfo == NULL || psInfo->hLayer == NULL) {
2919     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
2920                "msOGRFileNextShape()");
2921     return(MS_FAILURE);
2922   }
2923 
2924   /* -------------------------------------------------------------------- */
2925   /*      Clear previously loaded shape.                                  */
2926   /* -------------------------------------------------------------------- */
2927   msFreeShape(shape);
2928   shape->type = MS_SHAPE_NULL;
2929 
2930   /* -------------------------------------------------------------------- */
2931   /*      Support reading feature by fid.                                 */
2932   /* -------------------------------------------------------------------- */
2933   if( record_is_fid ) {
2934     ACQUIRE_OGR_LOCK;
2935     if( (hFeature = OGR_L_GetFeature( psInfo->hLayer, record )) == NULL ) {
2936       RELEASE_OGR_LOCK;
2937       return MS_FAILURE;
2938     }
2939   }
2940 
2941   /* -------------------------------------------------------------------- */
2942   /*      Support reading shape by offset within the current              */
2943   /*      resultset.                                                      */
2944   /* -------------------------------------------------------------------- */
2945   else {
2946     ACQUIRE_OGR_LOCK;
2947     if( record <= psInfo->last_record_index_read
2948         || psInfo->last_record_index_read == -1 ) {
2949       OGR_L_ResetReading( psInfo->hLayer );
2950       psInfo->last_record_index_read = -1;
2951     }
2952 
2953     hFeature = NULL;
2954     while( psInfo->last_record_index_read < record ) {
2955       if( hFeature != NULL ) {
2956         OGR_F_Destroy( hFeature );
2957         hFeature = NULL;
2958       }
2959       if( (hFeature = OGR_L_GetNextFeature( psInfo->hLayer )) == NULL ) {
2960         RELEASE_OGR_LOCK;
2961         return MS_FAILURE;
2962       }
2963       psInfo->last_record_index_read++;
2964     }
2965   }
2966 
2967   /* ------------------------------------------------------------------
2968    * Handle shape geometry...
2969    * ------------------------------------------------------------------ */
2970   // shape->type will be set if geom is compatible with layer type
2971   if (ogrConvertGeometry(ogrGetLinearGeometry( hFeature ), shape,
2972                          layer->type) != MS_SUCCESS) {
2973     RELEASE_OGR_LOCK;
2974     return MS_FAILURE; // Error message already produced.
2975   }
2976 
2977   if (shape->type == MS_SHAPE_NULL) {
2978     msSetError(MS_OGRERR,
2979                "Requested feature is incompatible with layer type",
2980                "msOGRLayerGetShape()");
2981     RELEASE_OGR_LOCK;
2982     return MS_FAILURE;
2983   }
2984 
2985   /* ------------------------------------------------------------------
2986    * Process shape attributes
2987    * ------------------------------------------------------------------ */
2988   if(layer->numitems > 0) {
2989     shape->values = msOGRGetValues(layer, hFeature);
2990     shape->numvalues = layer->numitems;
2991     if(!shape->values) {
2992       RELEASE_OGR_LOCK;
2993       return(MS_FAILURE);
2994     }
2995 
2996   }
2997 
2998   if (record_is_fid) {
2999     shape->index = record;
3000     shape->resultindex = -1;
3001   } else {
3002     shape->index = (int)OGR_F_GetFID( hFeature ); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
3003     shape->resultindex = record;
3004   }
3005 
3006   shape->tileindex = psInfo->nTileId;
3007 
3008   // Keep ref. to last feature read in case we need style info.
3009   if (psInfo->hLastFeature)
3010     OGR_F_Destroy( psInfo->hLastFeature );
3011   psInfo->hLastFeature = hFeature;
3012 
3013   RELEASE_OGR_LOCK;
3014 
3015   return MS_SUCCESS;
3016 }
3017 
3018 /************************************************************************/
3019 /*                         msOGRFileReadTile()                          */
3020 /*                                                                      */
3021 /*      Advance to the next tile (or if targetTile is not -1 advance    */
3022 /*      to that tile), causing the tile to become the poCurTile in      */
3023 /*      the tileindexes psInfo structure.  Returns MS_DONE if there     */
3024 /*      are no more available tiles.                                    */
3025 /*                                                                      */
3026 /*      Newly loaded tiles are automatically "WhichShaped" based on     */
3027 /*      the current rectangle.                                          */
3028 /************************************************************************/
3029 
msOGRFileReadTile(layerObj * layer,msOGRFileInfo * psInfo,int targetTile=-1)3030 int msOGRFileReadTile( layerObj *layer, msOGRFileInfo *psInfo,
3031                        int targetTile = -1 )
3032 
3033 {
3034   int nFeatureId;
3035 
3036   /* -------------------------------------------------------------------- */
3037   /*      Close old tile if one is open.                                  */
3038   /* -------------------------------------------------------------------- */
3039   if( psInfo->poCurTile != NULL ) {
3040     msOGRFileClose( layer, psInfo->poCurTile );
3041     psInfo->poCurTile = NULL;
3042   }
3043 
3044   /* -------------------------------------------------------------------- */
3045   /*      If -2 is passed, then seek reset reading of the tileindex.      */
3046   /*      We want to start from the beginning even if this file is        */
3047   /*      shared between layers or renders.                               */
3048   /* -------------------------------------------------------------------- */
3049   ACQUIRE_OGR_LOCK;
3050   if( targetTile == -2 ) {
3051     OGR_L_ResetReading( psInfo->hLayer );
3052   }
3053 
3054   /* -------------------------------------------------------------------- */
3055   /*      Get the name (connection string really) of the next tile.       */
3056   /* -------------------------------------------------------------------- */
3057   OGRFeatureH hFeature;
3058   char       *connection = NULL;
3059   msOGRFileInfo *psTileInfo = NULL;
3060   int status;
3061 
3062 #ifndef IGNORE_MISSING_DATA
3063 NextFile:
3064 #endif
3065 
3066   if( targetTile < 0 )
3067     hFeature = OGR_L_GetNextFeature( psInfo->hLayer );
3068 
3069   else
3070     hFeature = OGR_L_GetFeature( psInfo->hLayer, targetTile );
3071 
3072   if( hFeature == NULL ) {
3073     RELEASE_OGR_LOCK;
3074     if( targetTile == -1 )
3075       return MS_DONE;
3076     else
3077       return MS_FAILURE;
3078 
3079   }
3080 
3081   connection = msStrdup( OGR_F_GetFieldAsString( hFeature,
3082                          layer->tileitemindex ));
3083 
3084   char* pszSRS = NULL;
3085   if( layer->tilesrs != NULL )
3086   {
3087       int idx = OGR_F_GetFieldIndex( hFeature, layer->tilesrs);
3088       if( idx >= 0 )
3089       {
3090           pszSRS = msStrdup( OGR_F_GetFieldAsString( hFeature, idx ));
3091       }
3092   }
3093 
3094   nFeatureId = (int)OGR_F_GetFID( hFeature ); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
3095 
3096   OGR_F_Destroy( hFeature );
3097 
3098   RELEASE_OGR_LOCK;
3099 
3100   /* -------------------------------------------------------------------- */
3101   /*      Open the new tile file.                                         */
3102   /* -------------------------------------------------------------------- */
3103   psTileInfo = msOGRFileOpen( layer, connection );
3104 
3105   free( connection );
3106 
3107 #ifndef IGNORE_MISSING_DATA
3108   if( psTileInfo == NULL && targetTile == -1 )
3109   {
3110     msFree(pszSRS);
3111     goto NextFile;
3112   }
3113 #endif
3114 
3115   if( psTileInfo == NULL )
3116   {
3117     msFree(pszSRS);
3118     return MS_FAILURE;
3119   }
3120 
3121   if( pszSRS != NULL )
3122   {
3123     if( msOGCWKT2ProjectionObj( pszSRS, &(psInfo->sTileProj),
3124                                 layer->debug ) != MS_SUCCESS )
3125     {
3126         msFree(pszSRS);
3127         return MS_FAILURE;
3128     }
3129     msFree(pszSRS);
3130   }
3131 
3132   psTileInfo->nTileId = nFeatureId;
3133 
3134   /* -------------------------------------------------------------------- */
3135   /*      Initialize the spatial query on this file.                      */
3136   /* -------------------------------------------------------------------- */
3137   if( psInfo->rect.minx != 0 || psInfo->rect.maxx != 0 ) {
3138     rectObj rect = psInfo->rect;
3139 
3140     if( layer->tileindex != NULL && psInfo->sTileProj.numargs > 0 )
3141     {
3142       msProjectRect(&(layer->projection), &(psInfo->sTileProj), &rect);
3143     }
3144 
3145     status = msOGRFileWhichShapes( layer, rect, psTileInfo );
3146     if( status != MS_SUCCESS )
3147       return status;
3148   }
3149 
3150   psInfo->poCurTile = psTileInfo;
3151 
3152   /* -------------------------------------------------------------------- */
3153   /*      Update the iteminfo in case this layer has a different field    */
3154   /*      list.                                                           */
3155   /* -------------------------------------------------------------------- */
3156   msOGRLayerInitItemInfo( layer );
3157 
3158   return MS_SUCCESS;
3159 }
3160 
3161 /************************************************************************/
3162 /*                               msExprNode                             */
3163 /************************************************************************/
3164 
3165 class msExprNode
3166 {
3167     public:
3168         std::vector<msExprNode*> m_aoChildren;
3169         int         m_nToken;
3170         std::string m_osVal;
3171         double      m_dfVal;
3172         struct tm   m_tmVal;
3173 
msExprNode()3174         msExprNode() : m_nToken(0), m_dfVal(0.0) {}
3175        ~msExprNode();
3176 };
3177 
~msExprNode()3178 msExprNode::~msExprNode()
3179 {
3180     for(size_t i=0;i<m_aoChildren.size();++i)
3181         delete m_aoChildren[i];
3182 }
3183 
3184 /************************************************************************/
3185 /*                        exprGetPriority()                             */
3186 /************************************************************************/
3187 
exprGetPriority(int token)3188 static int exprGetPriority(int token)
3189 {
3190     if (token == MS_TOKEN_LOGICAL_NOT)
3191         return 9;
3192     else if (token == '*' || token == '/' || token == '%' )
3193         return 8;
3194     else if (token == '+' || token == '-' )
3195         return 7;
3196     else if (token == MS_TOKEN_COMPARISON_GE ||
3197              token == MS_TOKEN_COMPARISON_GT ||
3198              token == MS_TOKEN_COMPARISON_LE ||
3199              token == MS_TOKEN_COMPARISON_LT ||
3200              token == MS_TOKEN_COMPARISON_IN)
3201         return 6;
3202     else if (token == MS_TOKEN_COMPARISON_EQ ||
3203              token == MS_TOKEN_COMPARISON_IEQ ||
3204              token == MS_TOKEN_COMPARISON_LIKE ||
3205              token == MS_TOKEN_COMPARISON_RE ||
3206              token == MS_TOKEN_COMPARISON_IRE ||
3207              token == MS_TOKEN_COMPARISON_NE)
3208         return 5;
3209     else if (token == MS_TOKEN_LOGICAL_AND)
3210         return 4;
3211     else if (token == MS_TOKEN_LOGICAL_OR)
3212         return 3;
3213     else
3214         return 0;
3215 }
3216 
3217 /************************************************************************/
3218 /*                           BuildExprTree()                            */
3219 /************************************************************************/
3220 
BuildExprTree(tokenListNodeObjPtr node,tokenListNodeObjPtr * pNodeNext,int nParenthesisLevel)3221 static msExprNode* BuildExprTree(tokenListNodeObjPtr node,
3222                                  tokenListNodeObjPtr* pNodeNext,
3223                                  int nParenthesisLevel)
3224 {
3225     msExprNode* poRet = NULL;
3226     std::vector<msExprNode*> aoStackOp, aoStackVal;
3227     while( node != NULL )
3228     {
3229         if( node->token == '(' )
3230         {
3231             msExprNode* subExpr = BuildExprTree(node->next, &node,
3232                                                 nParenthesisLevel + 1);
3233             if( subExpr == NULL )
3234             {
3235                 goto fail;
3236             }
3237             aoStackVal.push_back(subExpr);
3238             continue;
3239         }
3240         else if( node->token == ')' )
3241         {
3242             if( nParenthesisLevel > 0 )
3243             {
3244                 break;
3245             }
3246             goto fail;
3247         }
3248         else if( node->token == '+' ||
3249                  node->token == '-' ||
3250                  node->token == '*' ||
3251                  node->token == '/' ||
3252                  node->token == '%' ||
3253                  node->token == MS_TOKEN_LOGICAL_NOT ||
3254                  node->token == MS_TOKEN_LOGICAL_AND ||
3255                  node->token == MS_TOKEN_LOGICAL_OR  ||
3256                  node->token == MS_TOKEN_COMPARISON_GE ||
3257                  node->token == MS_TOKEN_COMPARISON_GT ||
3258                  node->token == MS_TOKEN_COMPARISON_LE ||
3259                  node->token == MS_TOKEN_COMPARISON_LT ||
3260                  node->token == MS_TOKEN_COMPARISON_EQ ||
3261                  node->token == MS_TOKEN_COMPARISON_IEQ ||
3262                  node->token == MS_TOKEN_COMPARISON_LIKE ||
3263                  node->token == MS_TOKEN_COMPARISON_NE ||
3264                  node->token == MS_TOKEN_COMPARISON_RE ||
3265                  node->token == MS_TOKEN_COMPARISON_IRE ||
3266                  node->token == MS_TOKEN_COMPARISON_IN )
3267         {
3268             while( !aoStackOp.empty() &&
3269                    exprGetPriority(node->token) <=
3270                         exprGetPriority(aoStackOp.back()->m_nToken))
3271             {
3272                 msExprNode* val1 = NULL;
3273                 msExprNode* val2 = NULL;
3274                 msExprNode* newNode = NULL;
3275                 if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT)
3276                 {
3277                     if( aoStackVal.empty() )
3278                         goto fail;
3279                     val2 = aoStackVal.back();
3280                     aoStackVal.pop_back();
3281                 }
3282                 if( aoStackVal.empty() )
3283                     goto fail;
3284                 val1 = aoStackVal.back();
3285                 aoStackVal.pop_back();
3286 
3287                 newNode = new msExprNode;
3288                 newNode->m_nToken = aoStackOp.back()->m_nToken;
3289                 newNode->m_aoChildren.push_back(val1);
3290                 if( val2 )
3291                     newNode->m_aoChildren.push_back(val2);
3292                 aoStackVal.push_back(newNode);
3293                 delete aoStackOp.back();
3294                 aoStackOp.pop_back();
3295             }
3296 
3297             msExprNode* newNode = new msExprNode;
3298             newNode->m_nToken = node->token;
3299             aoStackOp.push_back(newNode);
3300         }
3301         else if( node->token == ',' )
3302         {
3303         }
3304         else if( node->token == MS_TOKEN_COMPARISON_INTERSECTS ||
3305                  node->token == MS_TOKEN_COMPARISON_DISJOINT ||
3306                  node->token == MS_TOKEN_COMPARISON_TOUCHES ||
3307                  node->token == MS_TOKEN_COMPARISON_OVERLAPS ||
3308                  node->token == MS_TOKEN_COMPARISON_CROSSES ||
3309                  node->token == MS_TOKEN_COMPARISON_DWITHIN ||
3310                  node->token == MS_TOKEN_COMPARISON_BEYOND ||
3311                  node->token == MS_TOKEN_COMPARISON_WITHIN ||
3312                  node->token == MS_TOKEN_COMPARISON_CONTAINS ||
3313                  node->token == MS_TOKEN_COMPARISON_EQUALS ||
3314                  node->token == MS_TOKEN_FUNCTION_LENGTH ||
3315                  node->token == MS_TOKEN_FUNCTION_TOSTRING ||
3316                  node->token == MS_TOKEN_FUNCTION_COMMIFY ||
3317                  node->token == MS_TOKEN_FUNCTION_AREA ||
3318                  node->token == MS_TOKEN_FUNCTION_ROUND ||
3319                  node->token == MS_TOKEN_FUNCTION_FROMTEXT ||
3320                  node->token == MS_TOKEN_FUNCTION_BUFFER ||
3321                  node->token == MS_TOKEN_FUNCTION_DIFFERENCE ||
3322                  node->token == MS_TOKEN_FUNCTION_SIMPLIFY ||
3323                  node->token == MS_TOKEN_FUNCTION_SIMPLIFYPT ||
3324                  node->token == MS_TOKEN_FUNCTION_GENERALIZE ||
3325                  node->token == MS_TOKEN_FUNCTION_SMOOTHSIA ||
3326                  node->token == MS_TOKEN_FUNCTION_JAVASCRIPT ||
3327                  node->token == MS_TOKEN_FUNCTION_UPPER ||
3328                  node->token == MS_TOKEN_FUNCTION_LOWER ||
3329                  node->token == MS_TOKEN_FUNCTION_INITCAP ||
3330                  node->token == MS_TOKEN_FUNCTION_FIRSTCAP )
3331         {
3332             if( node->next && node->next->token == '(' )
3333             {
3334                 int node_token = node->token;
3335                 msExprNode* subExpr = BuildExprTree(node->next->next, &node,
3336                                                     nParenthesisLevel + 1);
3337                 if( subExpr == NULL )
3338                 {
3339                     goto fail;
3340                 }
3341                 msExprNode* newNode = new msExprNode;
3342                 newNode->m_nToken = node_token;
3343                 if( subExpr->m_nToken == 0 )
3344                 {
3345                     newNode->m_aoChildren = subExpr->m_aoChildren;
3346                     subExpr->m_aoChildren.clear();
3347                     delete subExpr;
3348                 }
3349                 else
3350                 {
3351                     newNode->m_aoChildren.push_back(subExpr);
3352                 }
3353                 aoStackVal.push_back(newNode);
3354                 continue;
3355             }
3356             else
3357                 goto fail;
3358         }
3359         else if( node->token == MS_TOKEN_LITERAL_NUMBER ||
3360                  node->token == MS_TOKEN_LITERAL_BOOLEAN )
3361         {
3362             msExprNode* newNode = new msExprNode;
3363             newNode->m_nToken = node->token;
3364             newNode->m_dfVal = node->tokenval.dblval;
3365             aoStackVal.push_back(newNode);
3366         }
3367         else if( node->token == MS_TOKEN_LITERAL_STRING )
3368         {
3369             msExprNode* newNode = new msExprNode;
3370             newNode->m_nToken = node->token;
3371             newNode->m_osVal = node->tokenval.strval;
3372             aoStackVal.push_back(newNode);
3373         }
3374         else if( node->token == MS_TOKEN_LITERAL_TIME )
3375         {
3376             msExprNode* newNode = new msExprNode;
3377             newNode->m_nToken = node->token;
3378             newNode->m_tmVal = node->tokenval.tmval;
3379             aoStackVal.push_back(newNode);
3380         }
3381         else if( node->token == MS_TOKEN_LITERAL_SHAPE )
3382         {
3383             msExprNode* newNode = new msExprNode;
3384             newNode->m_nToken = node->token;
3385             char *wkt = msShapeToWKT(node->tokenval.shpval);
3386             newNode->m_osVal = wkt;
3387             msFree(wkt);
3388             aoStackVal.push_back(newNode);
3389         }
3390         else if( node->token == MS_TOKEN_BINDING_DOUBLE ||
3391                  node->token == MS_TOKEN_BINDING_INTEGER ||
3392                  node->token == MS_TOKEN_BINDING_STRING ||
3393                  node->token == MS_TOKEN_BINDING_TIME )
3394         {
3395             msExprNode* newNode = new msExprNode;
3396             newNode->m_nToken = node->token;
3397             newNode->m_osVal = node->tokenval.bindval.item;
3398             aoStackVal.push_back(newNode);
3399         }
3400         else
3401         {
3402             msExprNode* newNode = new msExprNode;
3403             newNode->m_nToken = node->token;
3404             aoStackVal.push_back(newNode);
3405         }
3406 
3407         node = node->next;
3408     }
3409 
3410     while( !aoStackOp.empty() )
3411     {
3412         msExprNode* val1 = NULL;
3413         msExprNode* val2 = NULL;
3414         msExprNode* newNode = NULL;
3415         if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT)
3416         {
3417             if( aoStackVal.empty() )
3418                 goto fail;
3419             val2 = aoStackVal.back();
3420             aoStackVal.pop_back();
3421         }
3422         if( aoStackVal.empty() )
3423             goto fail;
3424         val1 = aoStackVal.back();
3425         aoStackVal.pop_back();
3426 
3427         newNode = new msExprNode;
3428         newNode->m_nToken = aoStackOp.back()->m_nToken;
3429         newNode->m_aoChildren.push_back(val1);
3430         if( val2 )
3431             newNode->m_aoChildren.push_back(val2);
3432         aoStackVal.push_back(newNode);
3433         delete aoStackOp.back();
3434         aoStackOp.pop_back();
3435     }
3436 
3437     if( aoStackVal.size() == 1 )
3438         poRet = aoStackVal.back();
3439     else if( aoStackVal.size() > 1 )
3440     {
3441         poRet = new msExprNode;
3442         poRet->m_aoChildren = aoStackVal;
3443     }
3444 
3445     if( pNodeNext )
3446         *pNodeNext = node ? node->next : NULL;
3447 
3448     return poRet;
3449 
3450 fail:
3451     for( size_t i=0; i<aoStackOp.size(); ++i )
3452         delete aoStackOp[i];
3453     for( size_t i=0; i<aoStackVal.size(); ++i )
3454         delete aoStackVal[i];
3455     return NULL;
3456 }
3457 
3458 /**********************************************************************
3459  *                 msOGRExtractTopSpatialFilter()
3460  *
3461  * Recognize expressions like "Intersects([shape], wkt) == TRUE [AND ....]"
3462  **********************************************************************/
msOGRExtractTopSpatialFilter(msOGRFileInfo * info,const msExprNode * expr,const msExprNode ** pSpatialFilterNode)3463 static int  msOGRExtractTopSpatialFilter( msOGRFileInfo *info,
3464                                           const msExprNode* expr,
3465                                           const msExprNode** pSpatialFilterNode )
3466 {
3467   if( expr == NULL )
3468       return MS_FALSE;
3469 
3470   if( expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
3471       expr->m_aoChildren.size() == 2 &&
3472       expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_BOOLEAN &&
3473       expr->m_aoChildren[1]->m_dfVal == 1.0 )
3474   {
3475       return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0],
3476                                           pSpatialFilterNode);
3477   }
3478 
3479   if( (expr->m_nToken == MS_TOKEN_COMPARISON_INTERSECTS ||
3480       expr->m_nToken == MS_TOKEN_COMPARISON_OVERLAPS ||
3481       expr->m_nToken == MS_TOKEN_COMPARISON_CROSSES ||
3482       expr->m_nToken == MS_TOKEN_COMPARISON_WITHIN ||
3483       expr->m_nToken == MS_TOKEN_COMPARISON_CONTAINS) &&
3484       expr->m_aoChildren.size() == 2 &&
3485       expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_SHAPE )
3486   {
3487         if( info->rect_is_defined )
3488         {
3489             // Several intersects...
3490             *pSpatialFilterNode = NULL;
3491             info->rect_is_defined = MS_FALSE;
3492             return MS_FALSE;
3493         }
3494         OGRGeometryH hSpatialFilter = NULL;
3495         char* wkt = const_cast<char*>(expr->m_aoChildren[1]->m_osVal.c_str());
3496         OGRErr e = OGR_G_CreateFromWkt(&wkt, NULL, &hSpatialFilter);
3497         if (e == OGRERR_NONE) {
3498             OGREnvelope env;
3499             OGR_G_GetEnvelope(hSpatialFilter, &env);
3500             info->rect.minx = env.MinX;
3501             info->rect.miny = env.MinY;
3502             info->rect.maxx = env.MaxX;
3503             info->rect.maxy = env.MaxY;
3504             info->rect_is_defined = true;
3505             *pSpatialFilterNode = expr;
3506             OGR_G_DestroyGeometry(hSpatialFilter);
3507             return MS_TRUE;
3508         }
3509         return MS_FALSE;
3510   }
3511 
3512   if( expr->m_nToken == MS_TOKEN_LOGICAL_AND &&
3513       expr->m_aoChildren.size() == 2 )
3514   {
3515       return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0],
3516                                           pSpatialFilterNode) &&
3517              msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[1],
3518                                           pSpatialFilterNode);
3519   }
3520 
3521   return MS_TRUE;
3522 }
3523 
3524 /**********************************************************************
3525  *                 msOGRTranslatePartialMSExpressionToOGRSQL()
3526  *
3527  * Tries to partially translate a mapserver expression to SQL
3528  **********************************************************************/
3529 
msOGRGetTokenText(int nToken)3530 static std::string msOGRGetTokenText(int nToken)
3531 {
3532     switch( nToken )
3533     {
3534         case '*':
3535         case '+':
3536         case '-':
3537         case '/':
3538         case '%':
3539             return std::string(1, static_cast<char>(nToken));
3540 
3541         case MS_TOKEN_COMPARISON_GE: return ">=";
3542         case MS_TOKEN_COMPARISON_GT: return ">";
3543         case MS_TOKEN_COMPARISON_LE: return "<=";
3544         case MS_TOKEN_COMPARISON_LT: return "<";
3545         case MS_TOKEN_COMPARISON_EQ: return "=";
3546         case MS_TOKEN_COMPARISON_NE: return "!=";
3547         case MS_TOKEN_COMPARISON_LIKE: return "LIKE";
3548 
3549         default:
3550             return std::string();
3551     }
3552 }
3553 
msOGRTranslatePartialInternal(layerObj * layer,const msExprNode * expr,const msExprNode * spatialFilterNode,bool & bPartialFilter)3554 static std::string msOGRTranslatePartialInternal(layerObj* layer,
3555                                                  const msExprNode* expr,
3556                                                  const msExprNode* spatialFilterNode,
3557                                                  bool& bPartialFilter)
3558 {
3559     switch( expr->m_nToken )
3560     {
3561         case MS_TOKEN_LOGICAL_NOT:
3562         {
3563             std::string osTmp(msOGRTranslatePartialInternal(
3564                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3565             if( osTmp.empty() )
3566                 return std::string();
3567             return "(NOT " + osTmp + ")";
3568         }
3569 
3570         case MS_TOKEN_LOGICAL_AND:
3571         {
3572             // We can deal with partially translated children
3573             std::string osTmp1(msOGRTranslatePartialInternal(
3574                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3575             std::string osTmp2(msOGRTranslatePartialInternal(
3576                 layer, expr->m_aoChildren[1], spatialFilterNode, bPartialFilter ));
3577             if( !osTmp1.empty() && !osTmp2.empty() )
3578             {
3579                 return "(" + osTmp1 + " AND " + osTmp2 + ")";
3580             }
3581             else if( !osTmp1.empty() )
3582                 return osTmp1;
3583             else
3584                 return osTmp2;
3585         }
3586 
3587         case MS_TOKEN_LOGICAL_OR:
3588         {
3589             // We can NOT deal with partially translated children
3590             std::string osTmp1(msOGRTranslatePartialInternal(
3591                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3592             std::string osTmp2(msOGRTranslatePartialInternal(
3593                 layer, expr->m_aoChildren[1], spatialFilterNode, bPartialFilter ));
3594             if( !osTmp1.empty() && !osTmp2.empty() )
3595             {
3596                 return "(" + osTmp1 + " OR " + osTmp2 + ")";
3597             }
3598             else
3599                 return std::string();
3600         }
3601 
3602         case '*':
3603         case '+':
3604         case '-':
3605         case '/':
3606         case '%':
3607         case MS_TOKEN_COMPARISON_GE:
3608         case MS_TOKEN_COMPARISON_GT:
3609         case MS_TOKEN_COMPARISON_LE:
3610         case MS_TOKEN_COMPARISON_LT:
3611         case MS_TOKEN_COMPARISON_EQ:
3612         case MS_TOKEN_COMPARISON_NE:
3613         {
3614             std::string osTmp1(msOGRTranslatePartialInternal(
3615                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3616             std::string osTmp2(msOGRTranslatePartialInternal(
3617                 layer, expr->m_aoChildren[1], spatialFilterNode, bPartialFilter ));
3618             if( !osTmp1.empty() && !osTmp2.empty() )
3619             {
3620                 if( expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
3621                     osTmp2 == "'_MAPSERVER_NULL_'" )
3622                 {
3623                     return "(" + osTmp1 + " IS NULL )";
3624                 }
3625                 if( expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_STRING )
3626                 {
3627                     char md_item_name[256];
3628                     snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type",
3629                               expr->m_aoChildren[0]->m_osVal.c_str() );
3630                     const char* type =
3631                         msLookupHashTable(&(layer->metadata), md_item_name);
3632                     // Cast if needed (or unsure)
3633                     if( type == NULL || !EQUAL(type, "Character") )
3634                     {
3635                         osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
3636                     }
3637                 }
3638                 return "(" + osTmp1 + " " + msOGRGetTokenText(expr->m_nToken) +
3639                        " " + osTmp2 + ")";
3640             }
3641             else
3642                 return std::string();
3643         }
3644 
3645         case MS_TOKEN_COMPARISON_RE:
3646         {
3647             std::string osTmp1(msOGRTranslatePartialInternal(
3648                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3649             if( expr->m_aoChildren[1]->m_nToken != MS_TOKEN_LITERAL_STRING )
3650             {
3651                 return std::string();
3652             }
3653             std::string osRE("'");
3654             const size_t nSize = expr->m_aoChildren[1]->m_osVal.size();
3655             bool bHasUsedEscape = false;
3656             for( size_t i=0; i<nSize;i++ )
3657             {
3658                 if( i == 0 && expr->m_aoChildren[1]->m_osVal[i] == '^' )
3659                     continue;
3660                 if( i == nSize-1 && expr->m_aoChildren[1]->m_osVal[i] == '$' )
3661                     break;
3662                 if( expr->m_aoChildren[1]->m_osVal[i] == '.' )
3663                 {
3664                     if( i+1<nSize &&
3665                         expr->m_aoChildren[1]->m_osVal[i+1] == '*' )
3666                     {
3667                         osRE += "%";
3668                         i++;
3669                     }
3670                     else
3671                     {
3672                         osRE += "_";
3673                     }
3674                 }
3675                 else if( expr->m_aoChildren[1]->m_osVal[i] == '\\' &&
3676                          i+1<nSize )
3677                 {
3678                     bHasUsedEscape = true;
3679                     osRE += 'X';
3680                     osRE += expr->m_aoChildren[1]->m_osVal[i+1];
3681                     i++;
3682                 }
3683                 else if( expr->m_aoChildren[1]->m_osVal[i] == 'X' ||
3684                          expr->m_aoChildren[1]->m_osVal[i] == '%' ||
3685                          expr->m_aoChildren[1]->m_osVal[i] == '_' )
3686                 {
3687                     bHasUsedEscape = true;
3688                     osRE += 'X';
3689                     osRE += expr->m_aoChildren[1]->m_osVal[i];
3690                 }
3691                 else
3692                 {
3693                     osRE += expr->m_aoChildren[1]->m_osVal[i];
3694                 }
3695             }
3696             osRE += "'";
3697             char md_item_name[256];
3698             snprintf( md_item_name, sizeof(md_item_name), "gml_%s_type",
3699                         expr->m_aoChildren[0]->m_osVal.c_str() );
3700             const char* type =
3701                         msLookupHashTable(&(layer->metadata), md_item_name);
3702             // Cast if needed (or unsure)
3703             if( type == NULL || !EQUAL(type, "Character") )
3704             {
3705                 osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
3706             }
3707             std::string osRet( "(" + osTmp1 + " LIKE " + osRE );
3708             if( bHasUsedEscape )
3709                 osRet += " ESCAPE 'X'";
3710             osRet += ")";
3711             return osRet;
3712         }
3713 
3714         case MS_TOKEN_COMPARISON_IN:
3715         {
3716             std::string osTmp1(msOGRTranslatePartialInternal(
3717                 layer, expr->m_aoChildren[0], spatialFilterNode, bPartialFilter ));
3718             std::string osRet = "(" + osTmp1 + " IN (";
3719             for( size_t i=0; i< expr->m_aoChildren[1]->m_aoChildren.size(); ++i )
3720             {
3721                 if( i > 0 )
3722                     osRet += ", ";
3723                 osRet += msOGRTranslatePartialInternal(
3724                             layer, expr->m_aoChildren[1]->m_aoChildren[i],
3725                             spatialFilterNode, bPartialFilter );
3726             }
3727             osRet += ")";
3728             return osRet;
3729         }
3730 
3731         case MS_TOKEN_LITERAL_NUMBER:
3732         case MS_TOKEN_LITERAL_BOOLEAN:
3733         {
3734             return std::string(CPLSPrintf("%.18g", expr->m_dfVal));
3735         }
3736 
3737         case MS_TOKEN_LITERAL_STRING:
3738         {
3739             char *stresc = msOGREscapeSQLParam(layer, expr->m_osVal.c_str());
3740             std::string osRet("'" + std::string(stresc) + "'");
3741             msFree(stresc);
3742             return osRet;
3743         }
3744 
3745         case MS_TOKEN_LITERAL_TIME:
3746         {
3747 #ifdef notdef
3748             // Breaks tests in msautotest/wxs/wfs_time_ogr.map
3749             return std::string(CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
3750                      expr->m_tmVal.tm_year+1900,
3751                      expr->m_tmVal.tm_mon+1,
3752                      expr->m_tmVal.tm_mday,
3753                      expr->m_tmVal.tm_hour,
3754                      expr->m_tmVal.tm_min,
3755                      expr->m_tmVal.tm_sec));
3756 #endif
3757             return std::string();
3758         }
3759 
3760         case MS_TOKEN_BINDING_DOUBLE:
3761         case MS_TOKEN_BINDING_INTEGER:
3762         case MS_TOKEN_BINDING_STRING:
3763         case MS_TOKEN_BINDING_TIME:
3764         {
3765             char* pszTmp = msOGRGetQuotedItem(layer, expr->m_osVal.c_str());
3766             std::string osRet(pszTmp);
3767             msFree(pszTmp);
3768             return osRet;
3769         }
3770 
3771         case MS_TOKEN_COMPARISON_INTERSECTS:
3772         {
3773             if( expr != spatialFilterNode )
3774                 bPartialFilter = true;
3775             return std::string();
3776         }
3777 
3778         default:
3779         {
3780             bPartialFilter = true;
3781             return std::string();
3782         }
3783     }
3784 }
3785 
3786 /* ==================================================================
3787  * Here comes the REAL stuff... the functions below are called by maplayer.c
3788  * ================================================================== */
3789 
3790 /**********************************************************************
3791  *                     msOGRTranslateMsExpressionToOGRSQL()
3792  *
3793  * Tries to translate a mapserver expression to OGR or driver native SQL
3794  **********************************************************************/
msOGRTranslateMsExpressionToOGRSQL(layerObj * layer,expressionObj * psFilter,char * filteritem)3795 static int msOGRTranslateMsExpressionToOGRSQL(layerObj* layer,
3796                                               expressionObj* psFilter,
3797                                               char *filteritem)
3798 {
3799     msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
3800 
3801     msFree(layer->filter.native_string);
3802     layer->filter.native_string = NULL;
3803 
3804     msFree(info->pszWHERE);
3805     info->pszWHERE = NULL;
3806 
3807     // reasons to not produce native string: not simple layer, or an explicit deny
3808     char *do_this = msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
3809     if (do_this && strcmp(do_this, "NO") == 0) {
3810         return MS_SUCCESS;
3811     }
3812 
3813     tokenListNodeObjPtr node = psFilter->tokens;
3814     msExprNode* expr = BuildExprTree(node, NULL, 0);
3815     info->rect_is_defined = MS_FALSE;
3816     const msExprNode* spatialFilterNode = NULL;
3817     if( expr )
3818         msOGRExtractTopSpatialFilter( info, expr, &spatialFilterNode );
3819 
3820     // more reasons to not produce native string: not a recognized driver
3821     if (!info->dialect)
3822     {
3823         // in which case we might still want to try to get a partial WHERE clause
3824         if( filteritem == NULL && expr )
3825         {
3826             bool bPartialFilter = false;
3827             std::string osSQL( msOGRTranslatePartialInternal(layer, expr,
3828                                                              spatialFilterNode,
3829                                                              bPartialFilter) );
3830             if( !osSQL.empty() )
3831             {
3832                 info->pszWHERE = msStrdup(osSQL.c_str());
3833                 if( bPartialFilter )
3834                 {
3835                     msDebug("Full filter has only been partially "
3836                             "translated to OGR filter %s\n",
3837                             info->pszWHERE);
3838                 }
3839             }
3840             else if( bPartialFilter )
3841             {
3842                 msDebug("Filter could not be translated to OGR filter\n");
3843             }
3844         }
3845         delete expr;
3846         return MS_SUCCESS;
3847     }
3848 
3849     char *sql = NULL;
3850 
3851     // node may be NULL if layer->filter.string != NULL and filteritem != NULL
3852     // this is simple filter but string is regex
3853     if (node == NULL && filteritem != NULL && layer->filter.string != NULL) {
3854         sql = msStringConcatenate(sql, "\"");
3855         sql = msStringConcatenate(sql, filteritem);
3856         sql = msStringConcatenate(sql, "\"");
3857         if (EQUAL(info->dialect, "PostgreSQL") ) {
3858             sql = msStringConcatenate(sql, " ~ ");
3859         } else {
3860             sql = msStringConcatenate(sql, " LIKE ");
3861         }
3862         sql = msStringConcatenate(sql, "'");
3863         sql = msStringConcatenate(sql, layer->filter.string);
3864         sql = msStringConcatenate(sql, "'");
3865     }
3866 
3867     while (node != NULL) {
3868 
3869         if (node->next && node->next->token == MS_TOKEN_COMPARISON_IEQ) {
3870             char *left = msOGRGetToken(layer, &node);
3871             node = node->next; // skip =
3872             char *right = msOGRGetToken(layer, &node);
3873             sql = msStringConcatenate(sql, "upper(");
3874             sql = msStringConcatenate(sql, left);
3875             sql = msStringConcatenate(sql, ")");
3876             sql = msStringConcatenate(sql, "=");
3877             sql = msStringConcatenate(sql, "upper(");
3878             sql = msStringConcatenate(sql, right);
3879             sql = msStringConcatenate(sql, ")");
3880             int ok = left && right;
3881             msFree(left);
3882             msFree(right);
3883             if (!ok) {
3884                 goto fail;
3885             }
3886             continue;
3887         }
3888 
3889         switch(node->token) {
3890         case MS_TOKEN_COMPARISON_INTERSECTS:
3891         case MS_TOKEN_COMPARISON_DISJOINT:
3892         case MS_TOKEN_COMPARISON_TOUCHES:
3893         case MS_TOKEN_COMPARISON_OVERLAPS:
3894         case MS_TOKEN_COMPARISON_CROSSES:
3895         case MS_TOKEN_COMPARISON_DWITHIN:
3896         case MS_TOKEN_COMPARISON_BEYOND:
3897         case MS_TOKEN_COMPARISON_WITHIN:
3898         case MS_TOKEN_COMPARISON_CONTAINS:
3899         case MS_TOKEN_COMPARISON_EQUALS:{
3900             int token = node->token;
3901             char *fct = msOGRGetToken(layer, &node);
3902             node = node->next; // skip (
3903             char *a1 = msOGRGetToken(layer, &node);
3904             node = node->next; // skip ,
3905             char *a2 = msOGRGetToken(layer, &node);
3906             char *a3 = NULL;
3907             if (token == MS_TOKEN_COMPARISON_DWITHIN || token == MS_TOKEN_COMPARISON_BEYOND) {
3908                 node = node->next; // skip ,
3909                 a3 = msOGRGetToken(layer, &node);
3910             }
3911             node = node->next; // skip )
3912             char *eq = msOGRGetToken(layer, &node);
3913             char *rval = msOGRGetToken(layer, &node);
3914             if (strcmp(eq, " != ") == 0 || strcmp(rval, "FALSE") == 0) {
3915                 sql = msStringConcatenate(sql, "NOT ");
3916             }
3917             // FIXME: case rval is more complex
3918             sql = msStringConcatenate(sql, fct);
3919             sql = msStringConcatenate(sql, "(");
3920             sql = msStringConcatenate(sql, a1);
3921             sql = msStringConcatenate(sql, ",");
3922             sql = msStringConcatenate(sql, a2);
3923             if (token == MS_TOKEN_COMPARISON_DWITHIN || token == MS_TOKEN_COMPARISON_BEYOND) {
3924                 sql = msStringConcatenate(sql, ")");
3925                 if (token == MS_TOKEN_COMPARISON_DWITHIN)
3926                     sql = msStringConcatenate(sql, "<=");
3927                 else
3928                     sql = msStringConcatenate(sql, ">");
3929                 sql = msStringConcatenate(sql, a3);
3930             } else {
3931                 sql = msStringConcatenate(sql, ")");
3932             }
3933             int ok = fct && a1 && a2 && eq && rval;
3934             if (token == MS_TOKEN_COMPARISON_DWITHIN) {
3935                 ok = ok && a3;
3936             }
3937             msFree(fct);
3938             msFree(a1);
3939             msFree(a2);
3940             msFree(a3);
3941             msFree(eq);
3942             msFree(rval);
3943             if (!ok) {
3944                 goto fail;
3945             }
3946             break;
3947         }
3948         default: {
3949             char *token = msOGRGetToken(layer, &node);
3950             if (!token) {
3951                 goto fail;
3952             }
3953             sql = msStringConcatenate(sql, token);
3954             msFree(token);
3955         }
3956         }
3957     }
3958 
3959     layer->filter.native_string = sql;
3960     delete expr;
3961     return MS_SUCCESS;
3962 fail:
3963     // error producing native string
3964     msDebug("Note: Error parsing token list, could produce only: %s. Trying in partial mode\n", sql);
3965     msFree(sql);
3966 
3967     // in which case we might still want to try to get a partial WHERE clause
3968     if( expr )
3969     {
3970         bool bPartialFilter = false;
3971         std::string osSQL( msOGRTranslatePartialInternal(layer, expr,
3972                                                             spatialFilterNode,
3973                                                             bPartialFilter) );
3974         if( !osSQL.empty() )
3975         {
3976             info->pszWHERE = msStrdup(osSQL.c_str());
3977             if( bPartialFilter )
3978             {
3979                 msDebug("Full filter has only been partially "
3980                         "translated to OGR filter %s\n",
3981                         info->pszWHERE);
3982             }
3983         }
3984         else if( bPartialFilter )
3985         {
3986             msDebug("Filter could not be translated to OGR filter\n");
3987         }
3988     }
3989     delete expr;
3990 
3991     return MS_SUCCESS;
3992 }
3993 
3994 /**********************************************************************
3995  *                     msOGRLayerOpen()
3996  *
3997  * Open OGR data source for the specified map layer.
3998  *
3999  * If pszOverrideConnection != NULL then this value is used as the connection
4000  * string instead of lp->connection.  This is used for instance to open
4001  * a WFS layer, in this case lp->connection is the WFS url, but we want
4002  * OGR to open the local file on disk that was previously downloaded.
4003  *
4004  * An OGR connection string is:   <dataset_filename>[,<layer_index>]
4005  *  <dataset_filename>   is file format specific
4006  *  <layer_index>        (optional) is the OGR layer index
4007  *                       default is 0, the first layer.
4008  *
4009  * One can use the "ogrinfo" program to find out the layer indices in a dataset
4010  *
4011  * Returns MS_SUCCESS/MS_FAILURE
4012  **********************************************************************/
msOGRLayerOpen(layerObj * layer,const char * pszOverrideConnection)4013 int msOGRLayerOpen(layerObj *layer, const char *pszOverrideConnection)
4014 {
4015   msOGRFileInfo *psInfo;
4016 
4017   if (layer->layerinfo != NULL) {
4018     return MS_SUCCESS;  // Nothing to do... layer is already opened
4019   }
4020 
4021   /* -------------------------------------------------------------------- */
4022   /*      If this is not a tiled layer, just directly open the target.    */
4023   /* -------------------------------------------------------------------- */
4024   if( layer->tileindex == NULL ) {
4025     psInfo = msOGRFileOpen( layer,
4026                             (pszOverrideConnection ? pszOverrideConnection:
4027                              layer->connection) );
4028     layer->layerinfo = psInfo;
4029     layer->tileitemindex = -1;
4030 
4031     if( layer->layerinfo == NULL )
4032       return MS_FAILURE;
4033   }
4034 
4035   /* -------------------------------------------------------------------- */
4036   /*      Otherwise we open the tile index, identify the tile item        */
4037   /*      index and try to select the first file matching our query       */
4038   /*      region.                                                         */
4039   /* -------------------------------------------------------------------- */
4040   else {
4041     // Open tile index
4042 
4043     psInfo = msOGRFileOpen( layer, layer->tileindex );
4044     layer->layerinfo = psInfo;
4045 
4046     if( layer->layerinfo == NULL )
4047       return MS_FAILURE;
4048 
4049     // Identify TILEITEM
4050     OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn( psInfo->hLayer );
4051     layer->tileitemindex = OGR_FD_GetFieldIndex(hDefn, layer->tileitem);
4052     if( layer->tileitemindex < 0 ) {
4053       msSetError(MS_OGRERR,
4054                  "Can't identify TILEITEM %s field in TILEINDEX `%s'.",
4055                  "msOGRLayerOpen()",
4056                  layer->tileitem, layer->tileindex );
4057       msOGRFileClose( layer, psInfo );
4058       layer->layerinfo = NULL;
4059       return MS_FAILURE;
4060     }
4061 
4062     // Identify TILESRS
4063     if( layer->tilesrs != NULL &&
4064         OGR_FD_GetFieldIndex(hDefn, layer->tilesrs) < 0 ) {
4065       msSetError(MS_OGRERR,
4066                  "Can't identify TILESRS %s field in TILEINDEX `%s'.",
4067                  "msOGRLayerOpen()",
4068                  layer->tilesrs, layer->tileindex );
4069       msOGRFileClose( layer, psInfo );
4070       layer->layerinfo = NULL;
4071       return MS_FAILURE;
4072     }
4073     if( layer->tilesrs != NULL && layer->projection.numargs == 0 )
4074     {
4075         msSetError(MS_OGRERR,
4076                  "A layer with TILESRS set in TILEINDEX `%s' must have a "
4077                  "projection set on itself.",
4078                  "msOGRLayerOpen()",
4079                  layer->tileindex );
4080         msOGRFileClose( layer, psInfo );
4081         layer->layerinfo = NULL;
4082         return MS_FAILURE;
4083     }
4084   }
4085 
4086   /* ------------------------------------------------------------------
4087    * If projection was "auto" then set proj to the dataset's projection.
4088    * For a tile index, it is assume the tile index has the projection.
4089    * ------------------------------------------------------------------ */
4090   if (layer->projection.numargs > 0 &&
4091       EQUAL(layer->projection.args[0], "auto")) {
4092     ACQUIRE_OGR_LOCK;
4093     OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef( psInfo->hLayer );
4094 
4095     if (msOGRSpatialRef2ProjectionObj(hSRS,
4096                                       &(layer->projection),
4097                                       layer->debug ) != MS_SUCCESS) {
4098       errorObj *ms_error = msGetErrorObj();
4099 
4100       RELEASE_OGR_LOCK;
4101       msSetError(MS_OGRERR,
4102                  "%s  "
4103                  "PROJECTION AUTO cannot be used for this "
4104                  "OGR connection (in layer `%s').",
4105                  "msOGRLayerOpen()",
4106                  ms_error->message,
4107                  layer->name?layer->name:"(null)" );
4108       msOGRFileClose( layer, psInfo );
4109       layer->layerinfo = NULL;
4110       return(MS_FAILURE);
4111     }
4112     RELEASE_OGR_LOCK;
4113   }
4114 
4115   return MS_SUCCESS;
4116 }
4117 
4118 /**********************************************************************
4119  *                     msOGRLayerOpenVT()
4120  *
4121  * Overloaded version of msOGRLayerOpen for virtual table architecture
4122  **********************************************************************/
msOGRLayerOpenVT(layerObj * layer)4123 static int msOGRLayerOpenVT(layerObj *layer)
4124 {
4125   return msOGRLayerOpen(layer, NULL);
4126 }
4127 
4128 /**********************************************************************
4129  *                     msOGRLayerClose()
4130  **********************************************************************/
msOGRLayerClose(layerObj * layer)4131 int msOGRLayerClose(layerObj *layer)
4132 {
4133   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4134 
4135   if (psInfo) {
4136     if( layer->debug )
4137       msDebug("msOGRLayerClose(%s).\n", layer->connection);
4138 
4139     msOGRFileClose( layer, psInfo );
4140     layer->layerinfo = NULL;
4141   }
4142 
4143   return MS_SUCCESS;
4144 }
4145 
4146 /**********************************************************************
4147  *                     msOGRLayerIsOpen()
4148  **********************************************************************/
msOGRLayerIsOpen(layerObj * layer)4149 static int msOGRLayerIsOpen(layerObj *layer)
4150 {
4151   if (layer->layerinfo)
4152     return MS_TRUE;
4153 
4154   return MS_FALSE;
4155 }
4156 
msOGRIsSpatialite(layerObj * layer)4157 int msOGRIsSpatialite(layerObj* layer)
4158 {
4159   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4160   if (psInfo && psInfo->dialect &&
4161       EQUAL(psInfo->dialect, "Spatialite") )
4162   {
4163     // reasons to not produce native string: not simple layer, or an explicit deny
4164     char *do_this = msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
4165     if (do_this && strcmp(do_this, "NO") == 0) {
4166         return MS_FALSE;
4167     }
4168     return MS_TRUE;
4169   }
4170 
4171   return MS_FALSE;
4172 }
4173 
4174 
4175 /**********************************************************************
4176  *                     msOGRLayerWhichShapes()
4177  *
4178  * Init OGR layer structs ready for calls to msOGRLayerNextShape().
4179  *
4180  * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
4181  * layer's FILTER overlaps the selected region.
4182  **********************************************************************/
msOGRLayerWhichShapes(layerObj * layer,rectObj rect,int isQuery)4183 int msOGRLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
4184 {
4185   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4186   int   status;
4187 
4188   if (psInfo == NULL || psInfo->hLayer == NULL) {
4189     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4190                "msOGRLayerWhichShapes()");
4191     return(MS_FAILURE);
4192   }
4193 
4194   status = msOGRFileWhichShapes( layer, rect, psInfo );
4195 
4196   if( status != MS_SUCCESS || layer->tileindex == NULL )
4197     return status;
4198 
4199   // If we are using a tile index, we need to advance to the first
4200   // tile matching the spatial query, and load it.
4201 
4202   return msOGRFileReadTile( layer, psInfo );
4203 }
4204 
4205 /**********************************************************************
4206  *                     msOGRLayerGetItems()
4207  *
4208  * Load item (i.e. field) names in a char array.  If we are working
4209  * with a tiled layer, ensure a tile is loaded and use it for the items.
4210  * It is implicitly assumed that the schemas will match on all tiles.
4211  **********************************************************************/
msOGRLayerGetItems(layerObj * layer)4212 int msOGRLayerGetItems(layerObj *layer)
4213 {
4214   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4215 
4216   if (psInfo == NULL || psInfo->hLayer == NULL) {
4217     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4218                "msOGRLayerGetItems()");
4219     return(MS_FAILURE);
4220   }
4221 
4222   if( layer->tileindex != NULL ) {
4223     if( psInfo->poCurTile == NULL
4224         && msOGRFileReadTile( layer, psInfo ) != MS_SUCCESS )
4225       return MS_FAILURE;
4226 
4227     psInfo = psInfo->poCurTile;
4228   }
4229 
4230   layer->numitems = 0;
4231   layer->items = msOGRFileGetItems(layer, psInfo);
4232   if( layer->items == NULL )
4233     return MS_FAILURE;
4234 
4235   while( layer->items[layer->numitems] != NULL )
4236     layer->numitems++;
4237 
4238   return msOGRLayerInitItemInfo(layer);
4239 }
4240 
4241 /**********************************************************************
4242  *                     msOGRLayerInitItemInfo()
4243  *
4244  * Init the itemindexes array after items[] has been reset in a layer.
4245  **********************************************************************/
msOGRLayerInitItemInfo(layerObj * layer)4246 static int msOGRLayerInitItemInfo(layerObj *layer)
4247 {
4248   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4249   int   i;
4250   OGRFeatureDefnH hDefn;
4251 
4252   if (layer->numitems == 0)
4253     return MS_SUCCESS;
4254 
4255   if( layer->tileindex != NULL ) {
4256     if( psInfo->poCurTile == NULL
4257         && msOGRFileReadTile( layer, psInfo, -2 ) != MS_SUCCESS )
4258       return MS_FAILURE;
4259 
4260     psInfo = psInfo->poCurTile;
4261   }
4262 
4263   if (psInfo == NULL || psInfo->hLayer == NULL) {
4264     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4265                "msOGRLayerInitItemInfo()");
4266     return(MS_FAILURE);
4267   }
4268 
4269   if((hDefn = OGR_L_GetLayerDefn( psInfo->hLayer )) == NULL) {
4270     msSetError(MS_OGRERR, "Layer contains no fields.",
4271                "msOGRLayerInitItemInfo()");
4272     return(MS_FAILURE);
4273   }
4274 
4275   if (layer->iteminfo)
4276     free(layer->iteminfo);
4277   if((layer->iteminfo = (int *)malloc(sizeof(int)*layer->numitems))== NULL) {
4278     msSetError(MS_MEMERR, NULL, "msOGRLayerInitItemInfo()");
4279     return(MS_FAILURE);
4280   }
4281 
4282   int *itemindexes = (int*)layer->iteminfo;
4283   for(i=0; i<layer->numitems; i++) {
4284     // Special case for handling text string and angle coming from
4285     // OGR style strings.  We use special attribute snames.
4286     if (EQUAL(layer->items[i], MSOGR_LABELFONTNAMENAME))
4287       itemindexes[i] = MSOGR_LABELFONTNAMEINDEX;
4288     else if (EQUAL(layer->items[i], MSOGR_LABELSIZENAME))
4289       itemindexes[i] = MSOGR_LABELSIZEINDEX;
4290     else if (EQUAL(layer->items[i], MSOGR_LABELTEXTNAME))
4291       itemindexes[i] = MSOGR_LABELTEXTINDEX;
4292     else if (EQUAL(layer->items[i], MSOGR_LABELANGLENAME))
4293       itemindexes[i] = MSOGR_LABELANGLEINDEX;
4294     else if (EQUAL(layer->items[i], MSOGR_LABELFCOLORNAME))
4295       itemindexes[i] = MSOGR_LABELFCOLORINDEX;
4296     else if (EQUAL(layer->items[i], MSOGR_LABELBCOLORNAME))
4297       itemindexes[i] = MSOGR_LABELBCOLORINDEX;
4298     else if (EQUAL(layer->items[i], MSOGR_LABELPLACEMENTNAME))
4299       itemindexes[i] = MSOGR_LABELPLACEMENTINDEX;
4300     else if (EQUAL(layer->items[i], MSOGR_LABELANCHORNAME))
4301       itemindexes[i] = MSOGR_LABELANCHORINDEX;
4302     else if (EQUAL(layer->items[i], MSOGR_LABELDXNAME))
4303       itemindexes[i] = MSOGR_LABELDXINDEX;
4304     else if (EQUAL(layer->items[i], MSOGR_LABELDYNAME))
4305       itemindexes[i] = MSOGR_LABELDYINDEX;
4306     else if (EQUAL(layer->items[i], MSOGR_LABELPERPNAME))
4307       itemindexes[i] = MSOGR_LABELPERPINDEX;
4308     else if (EQUAL(layer->items[i], MSOGR_LABELBOLDNAME))
4309       itemindexes[i] = MSOGR_LABELBOLDINDEX;
4310     else if (EQUAL(layer->items[i], MSOGR_LABELITALICNAME))
4311       itemindexes[i] = MSOGR_LABELITALICINDEX;
4312     else if (EQUAL(layer->items[i], MSOGR_LABELUNDERLINENAME))
4313       itemindexes[i] = MSOGR_LABELUNDERLINEINDEX;
4314     else if (EQUAL(layer->items[i], MSOGR_LABELPRIORITYNAME))
4315       itemindexes[i] = MSOGR_LABELPRIORITYINDEX;
4316     else if (EQUAL(layer->items[i], MSOGR_LABELSTRIKEOUTNAME))
4317       itemindexes[i] = MSOGR_LABELSTRIKEOUTINDEX;
4318     else if (EQUAL(layer->items[i], MSOGR_LABELSTRETCHNAME))
4319       itemindexes[i] = MSOGR_LABELSTRETCHINDEX;
4320     else if (EQUAL(layer->items[i], MSOGR_LABELADJHORNAME))
4321       itemindexes[i] = MSOGR_LABELADJHORINDEX;
4322     else if (EQUAL(layer->items[i], MSOGR_LABELADJVERTNAME))
4323       itemindexes[i] = MSOGR_LABELADJVERTINDEX;
4324     else if (EQUAL(layer->items[i], MSOGR_LABELHCOLORNAME))
4325       itemindexes[i] = MSOGR_LABELHCOLORINDEX;
4326     else if (EQUAL(layer->items[i], MSOGR_LABELOCOLORNAME))
4327       itemindexes[i] = MSOGR_LABELOCOLORINDEX;
4328     else if (EQUALN(layer->items[i], MSOGR_LABELPARAMNAME, MSOGR_LABELPARAMNAMELEN))
4329         itemindexes[i] = MSOGR_LABELPARAMINDEX
4330                           + atoi(layer->items[i] + MSOGR_LABELPARAMNAMELEN);
4331     else if (EQUALN(layer->items[i], MSOGR_BRUSHPARAMNAME, MSOGR_BRUSHPARAMNAMELEN))
4332         itemindexes[i] = MSOGR_BRUSHPARAMINDEX
4333                           + atoi(layer->items[i] + MSOGR_BRUSHPARAMNAMELEN);
4334     else if (EQUALN(layer->items[i], MSOGR_PENPARAMNAME, MSOGR_PENPARAMNAMELEN))
4335         itemindexes[i] = MSOGR_PENPARAMINDEX
4336                           + atoi(layer->items[i] + MSOGR_PENPARAMNAMELEN);
4337     else if (EQUALN(layer->items[i], MSOGR_SYMBOLPARAMNAME, MSOGR_SYMBOLPARAMNAMELEN))
4338         itemindexes[i] = MSOGR_SYMBOLPARAMINDEX
4339                           + atoi(layer->items[i] + MSOGR_SYMBOLPARAMNAMELEN);
4340     else
4341     {
4342       itemindexes[i] = OGR_FD_GetFieldIndex( hDefn, layer->items[i] );
4343       if( itemindexes[i] == -1 )
4344       {
4345           if( EQUAL( layer->items[i], OGR_L_GetFIDColumn( psInfo->hLayer ) ) )
4346           {
4347               itemindexes[i] = MSOGR_FID_INDEX;
4348           }
4349       }
4350     }
4351     if(itemindexes[i] == -1) {
4352       msSetError(MS_OGRERR,
4353                  "Invalid Field name: %s in layer `%s'",
4354                  "msOGRLayerInitItemInfo()",
4355                  layer->items[i], layer->name ? layer->name : "(null)");
4356       return(MS_FAILURE);
4357     }
4358   }
4359 
4360   return(MS_SUCCESS);
4361 }
4362 
4363 /**********************************************************************
4364  *                     msOGRLayerFreeItemInfo()
4365  *
4366  * Free the itemindexes array in a layer.
4367  **********************************************************************/
msOGRLayerFreeItemInfo(layerObj * layer)4368 void msOGRLayerFreeItemInfo(layerObj *layer)
4369 {
4370 
4371   if (layer->iteminfo)
4372     free(layer->iteminfo);
4373   layer->iteminfo = NULL;
4374 }
4375 
4376 
4377 /**********************************************************************
4378  *                     msOGRLayerNextShape()
4379  *
4380  * Returns shape sequentially from OGR data source.
4381  * msOGRLayerWhichShape() must have been called first.
4382  *
4383  * Returns MS_SUCCESS/MS_FAILURE
4384  **********************************************************************/
msOGRLayerNextShape(layerObj * layer,shapeObj * shape)4385 int msOGRLayerNextShape(layerObj *layer, shapeObj *shape)
4386 {
4387   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4388   int  status;
4389 
4390   if (psInfo == NULL || psInfo->hLayer == NULL) {
4391     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4392                "msOGRLayerNextShape()");
4393     return(MS_FAILURE);
4394   }
4395 
4396   if( layer->tileindex == NULL )
4397     return msOGRFileNextShape( layer, shape, psInfo );
4398 
4399   // Do we need to load the first tile?
4400   if( psInfo->poCurTile == NULL ) {
4401     status = msOGRFileReadTile( layer, psInfo );
4402     if( status != MS_SUCCESS )
4403       return status;
4404   }
4405 
4406   do {
4407     // Try getting a shape from this tile.
4408     status = msOGRFileNextShape( layer, shape, psInfo->poCurTile );
4409     if( status != MS_DONE )
4410     {
4411       if( psInfo->sTileProj.numargs > 0 )
4412       {
4413         msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
4414       }
4415 
4416       return status;
4417     }
4418 
4419     // try next tile.
4420     status = msOGRFileReadTile( layer, psInfo );
4421     if( status != MS_SUCCESS )
4422       return status;
4423   } while( status == MS_SUCCESS );
4424   return status; //make compiler happy. this is never reached however
4425 }
4426 
4427 /**********************************************************************
4428  *                     msOGRLayerGetShape()
4429  *
4430  * Returns shape from OGR data source by fid.
4431  *
4432  * Returns MS_SUCCESS/MS_FAILURE
4433  **********************************************************************/
msOGRLayerGetShape(layerObj * layer,shapeObj * shape,resultObj * record)4434 int msOGRLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record)
4435 {
4436   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4437 
4438   long shapeindex = record->shapeindex;
4439   int tileindex = record->tileindex;
4440   int resultindex = record->resultindex;
4441   int record_is_fid = TRUE;
4442 
4443   /* set the resultindex as shapeindex if available */
4444   if (resultindex >= 0) {
4445     record_is_fid = FALSE;
4446     shapeindex = resultindex;
4447   }
4448 
4449   if (psInfo == NULL || psInfo->hLayer == NULL) {
4450     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!", "msOGRLayerGetShape()");
4451     return(MS_FAILURE);
4452   }
4453 
4454   if( layer->tileindex == NULL )
4455     return msOGRFileGetShape(layer, shape, shapeindex, psInfo, record_is_fid );
4456   else {
4457     if( psInfo->poCurTile == NULL
4458         || psInfo->poCurTile->nTileId != tileindex ) {
4459       if( msOGRFileReadTile( layer, psInfo, tileindex ) != MS_SUCCESS )
4460         return MS_FAILURE;
4461     }
4462 
4463     int status = msOGRFileGetShape(layer, shape, shapeindex, psInfo->poCurTile, record_is_fid );
4464     if( status == MS_SUCCESS && psInfo->sTileProj.numargs > 0 )
4465     {
4466       msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
4467     }
4468     return status;
4469   }
4470 }
4471 
4472 /**********************************************************************
4473  *                     msOGRLayerGetExtent()
4474  *
4475  * Returns the layer extents.
4476  *
4477  * Returns MS_SUCCESS/MS_FAILURE
4478  **********************************************************************/
msOGRLayerGetExtent(layerObj * layer,rectObj * extent)4479 int msOGRLayerGetExtent(layerObj *layer, rectObj *extent)
4480 {
4481   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
4482   OGREnvelope oExtent;
4483 
4484   if (psInfo == NULL || psInfo->hLayer == NULL) {
4485     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4486                "msOGRLayerGetExtent()");
4487     return(MS_FAILURE);
4488   }
4489 
4490   /* ------------------------------------------------------------------
4491    * Call OGR's GetExtent()... note that for some formats this will
4492    * result in a scan of the whole layer and can be an expensive call.
4493    *
4494    * For tile indexes layers we assume it is sufficient to get the
4495    * extents of the tile index.
4496    * ------------------------------------------------------------------ */
4497   ACQUIRE_OGR_LOCK;
4498   if (OGR_L_GetExtent( psInfo->hLayer, &oExtent, TRUE) != OGRERR_NONE) {
4499     RELEASE_OGR_LOCK;
4500     msSetError(MS_MISCERR, "Unable to get extents for this layer.",
4501                "msOGRLayerGetExtent()");
4502     return(MS_FAILURE);
4503   }
4504   RELEASE_OGR_LOCK;
4505 
4506   extent->minx = oExtent.MinX;
4507   extent->miny = oExtent.MinY;
4508   extent->maxx = oExtent.MaxX;
4509   extent->maxy = oExtent.MaxY;
4510 
4511   return MS_SUCCESS;
4512 }
4513 
4514 /**********************************************************************
4515 *                     msOGRLayerGetNumFeatures()
4516 *
4517 * Returns the layer feature count.
4518 *
4519 * Returns the number of features on success, -1 on error
4520 **********************************************************************/
msOGRLayerGetNumFeatures(layerObj * layer)4521 int msOGRLayerGetNumFeatures(layerObj *layer)
4522 {
4523     msOGRFileInfo *psInfo = (msOGRFileInfo*)layer->layerinfo;
4524     int result;
4525 
4526     if (psInfo == NULL || psInfo->hLayer == NULL) {
4527         msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
4528             "msOGRLayerGetNumFeatures()");
4529         return -1;
4530     }
4531 
4532     /* ------------------------------------------------------------------
4533     * Call OGR's GetFeatureCount()... note that for some formats this will
4534     * result in a scan of the whole layer and can be an expensive call.
4535     * ------------------------------------------------------------------ */
4536     ACQUIRE_OGR_LOCK;
4537     result = (int)OGR_L_GetFeatureCount(psInfo->hLayer, TRUE);
4538     RELEASE_OGR_LOCK;
4539 
4540     return result;
4541 }
4542 
4543 /**********************************************************************
4544  *                     msOGRGetSymbolId()
4545  *
4546  * Returns a MapServer symbol number matching one of the symbols from
4547  * the OGR symbol id string.  If not found then try to locate the
4548  * default symbol name, and if not found return 0.
4549  **********************************************************************/
msOGRGetSymbolId(symbolSetObj * symbolset,const char * pszSymbolId,const char * pszDefaultSymbol,int try_addimage_if_notfound)4550 static int msOGRGetSymbolId(symbolSetObj *symbolset, const char *pszSymbolId,
4551                             const char *pszDefaultSymbol, int try_addimage_if_notfound)
4552 {
4553   // Symbol name mapping:
4554   // First look for the native symbol name, then the ogr-...
4555   // generic name, and in last resort try pszDefaultSymbol if
4556   // provided by user.
4557   char  **params;
4558   int   numparams;
4559   int   nSymbol = -1;
4560 
4561   if (pszSymbolId && pszSymbolId[0] != '\0') {
4562     params = msStringSplit(pszSymbolId, ',', &numparams);
4563     if (params != NULL) {
4564       for(int j=0; j<numparams && nSymbol == -1; j++) {
4565         nSymbol = msGetSymbolIndex(symbolset, params[j],
4566                                    try_addimage_if_notfound);
4567       }
4568       msFreeCharArray(params, numparams);
4569     }
4570   }
4571   if (nSymbol == -1 && pszDefaultSymbol) {
4572     nSymbol = msGetSymbolIndex(symbolset,(char*)pszDefaultSymbol,
4573                                try_addimage_if_notfound);
4574   }
4575   if (nSymbol == -1)
4576     nSymbol = 0;
4577 
4578   return nSymbol;
4579 }
4580 
4581 static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4582                                       OGRStyleToolH hLabelStyle);
4583 static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
4584                                     OGRStyleToolH hPenStyle, int bIsBrush, int* pbPriority);
4585 static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
4586                                       OGRStyleToolH hBrushStyle, int* pbIsBrush, int* pbPriority);
4587 static int msOGRUpdateStyleParseSymbol(mapObj *map, layerObj *layer, styleObj *s,
4588                                        OGRStyleToolH hSymbolStyle, int* pbPriority);
4589 
msOGRUpdateStyleCheckPenBrushOnly(OGRStyleMgrH hStyleMgr)4590 static int msOGRUpdateStyleCheckPenBrushOnly(OGRStyleMgrH hStyleMgr)
4591 {
4592   int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
4593   int countPen = 0, countBrush = 0;
4594   int bIsNull;
4595 
4596   for(int i=0; i<numParts; i++) {
4597     OGRSTClassId eStylePartType;
4598     OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
4599     if (!hStylePart)
4600       continue;
4601 
4602     eStylePartType = OGR_ST_GetType(hStylePart);
4603     if (eStylePartType == OGRSTCPen) {
4604       countPen ++;
4605       OGR_ST_GetParamNum(hStylePart, OGRSTPenPriority, &bIsNull);
4606       if( !bIsNull ) {
4607         OGR_ST_Destroy(hStylePart);
4608         return MS_FALSE;
4609       }
4610     }
4611     else if (eStylePartType == OGRSTCBrush) {
4612       countBrush ++;
4613       OGR_ST_GetParamNum(hStylePart, OGRSTBrushPriority, &bIsNull);
4614       if( !bIsNull ) {
4615         OGR_ST_Destroy(hStylePart);
4616         return MS_FALSE;
4617       }
4618     }
4619     else if (eStylePartType == OGRSTCSymbol) {
4620       OGR_ST_Destroy(hStylePart);
4621       return MS_FALSE;
4622     }
4623     OGR_ST_Destroy(hStylePart);
4624   }
4625   return (countPen == 1 && countBrush == 1);
4626 }
4627 
4628 /**********************************************************************
4629  *                     msOGRUpdateStyle()
4630  *
4631  * Update the mapserver style according to the ogr style.
4632  * The function is called by msOGRGetAutoStyle and
4633  * msOGRUpdateStyleFromString
4634  **********************************************************************/
4635 
4636 typedef struct
4637 {
4638     int nPriority; /* the explicit priority as specified by the 'l' option of PEN, BRUSH and SYMBOL tools */
4639     int nApparitionIndex; /* the index of the tool as parsed from the OGR feature style string */
4640 } StyleSortStruct;
4641 
msOGRUpdateStyleSortFct(const void * pA,const void * pB)4642 static int msOGRUpdateStyleSortFct(const void* pA, const void* pB)
4643 {
4644     StyleSortStruct* sssa = (StyleSortStruct*)pA;
4645     StyleSortStruct* sssb = (StyleSortStruct*)pB;
4646     if( sssa->nPriority < sssb->nPriority )
4647         return -1;
4648     else if( sssa->nPriority > sssb->nPriority )
4649         return 1;
4650     else if( sssa->nApparitionIndex < sssb->nApparitionIndex )
4651         return -1;
4652     else
4653         return 1;
4654 }
4655 
msOGRUpdateStyle(OGRStyleMgrH hStyleMgr,mapObj * map,layerObj * layer,classObj * c)4656 static int msOGRUpdateStyle(OGRStyleMgrH hStyleMgr, mapObj *map, layerObj *layer, classObj *c)
4657 {
4658   GBool bIsBrush=MS_FALSE;
4659   int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
4660   int nPriority;
4661   int bIsPenBrushOnly = msOGRUpdateStyleCheckPenBrushOnly(hStyleMgr);
4662   StyleSortStruct* pasSortStruct = (StyleSortStruct*) msSmallMalloc(sizeof(StyleSortStruct) * numParts);
4663   int iSortStruct = 0;
4664   int iBaseStyleIndex = c->numstyles;
4665   int i;
4666 
4667   /* ------------------------------------------------------------------
4668    * Handle each part
4669    * ------------------------------------------------------------------ */
4670 
4671   for(i=0; i<numParts; i++) {
4672     OGRSTClassId eStylePartType;
4673     OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
4674     if (!hStylePart)
4675       continue;
4676     eStylePartType = OGR_ST_GetType(hStylePart);
4677     nPriority = INT_MIN;
4678 
4679     // We want all size values returned in pixels.
4680     //
4681     // The scale factor that OGR expect is the ground/paper scale
4682     // e.g. if 1 ground unit = 0.01 paper unit then scale=1/0.01=100
4683     // cellsize if number of ground units/pixel, and OGR assumes that
4684     // there is 72*39.37 pixels/ground units (since meter is assumed
4685     // for ground... but what ground units we have does not matter
4686     // as long as use the same assumptions everywhere)
4687     // That gives scale = cellsize*72*39.37
4688 
4689     OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
4690       map->cellsize*map->resolution/map->defresolution*72.0*39.37);
4691 
4692     if (eStylePartType == OGRSTCLabel) {
4693       int ret = msOGRUpdateStyleParseLabel(map, layer, c, hStylePart);
4694       if( ret != MS_SUCCESS ) {
4695         OGR_ST_Destroy(hStylePart);
4696         msFree(pasSortStruct);
4697         return ret;
4698       }
4699     } else if (eStylePartType == OGRSTCPen) {
4700       styleObj* s;
4701       int nIndex;
4702       if( bIsPenBrushOnly ) {
4703         /* Historic behaviour when there is a PEN and BRUSH only */
4704         if (bIsBrush || layer->type == MS_LAYER_POLYGON)
4705             // This is a multipart symbology, so pen defn goes in the
4706             // overlaysymbol params
4707           nIndex = 1;
4708         else
4709           nIndex = 0;
4710       }
4711       else
4712         nIndex = c->numstyles;
4713 
4714       if (msMaybeAllocateClassStyle(c, nIndex)) {
4715         OGR_ST_Destroy(hStylePart);
4716         msFree(pasSortStruct);
4717         return(MS_FAILURE);
4718       }
4719       s = c->styles[nIndex];
4720 
4721       msOGRUpdateStyleParsePen(map, layer, s, hStylePart, bIsBrush, &nPriority);
4722 
4723     } else if (eStylePartType == OGRSTCBrush) {
4724       styleObj* s;
4725       int nIndex = ( bIsPenBrushOnly ) ? 0 : c->numstyles;
4726       /* We need 1 style */
4727       if (msMaybeAllocateClassStyle(c, nIndex)) {
4728         OGR_ST_Destroy(hStylePart);
4729         msFree(pasSortStruct);
4730         return(MS_FAILURE);
4731       }
4732       s = c->styles[nIndex];
4733 
4734       msOGRUpdateStyleParseBrush(map, layer, s, hStylePart, &bIsBrush, &nPriority);
4735 
4736     } else if (eStylePartType == OGRSTCSymbol) {
4737       styleObj* s;
4738       /* We need 1 style */
4739       int nIndex = c->numstyles;
4740       if (msMaybeAllocateClassStyle(c, nIndex)) {
4741         OGR_ST_Destroy(hStylePart);
4742         msFree(pasSortStruct);
4743         return(MS_FAILURE);
4744       }
4745       s = c->styles[nIndex];
4746 
4747       msOGRUpdateStyleParseSymbol(map, layer, s, hStylePart, &nPriority);
4748     }
4749 
4750     /* Memorize the explicit priority and apparition order of the parsed tool/style */
4751     if( !bIsPenBrushOnly &&
4752         (eStylePartType == OGRSTCPen || eStylePartType == OGRSTCBrush ||
4753          eStylePartType == OGRSTCSymbol) ) {
4754         pasSortStruct[iSortStruct].nPriority = nPriority;
4755         pasSortStruct[iSortStruct].nApparitionIndex = iSortStruct;
4756         iSortStruct++;
4757     }
4758 
4759     OGR_ST_Destroy(hStylePart);
4760 
4761   }
4762 
4763   if( iSortStruct > 1 && !bIsPenBrushOnly ) {
4764       /* Compute style order based on their explicit priority and apparition order */
4765       qsort(pasSortStruct, iSortStruct, sizeof(StyleSortStruct), msOGRUpdateStyleSortFct);
4766 
4767       /* Now reorder styles in c->styles */
4768       styleObj** ppsStyleTmp = (styleObj**)msSmallMalloc( iSortStruct * sizeof(styleObj*) );
4769       memcpy( ppsStyleTmp, c->styles + iBaseStyleIndex, iSortStruct * sizeof(styleObj*) );
4770       for( i = 0; i < iSortStruct; i++)
4771       {
4772           c->styles[iBaseStyleIndex + i] = ppsStyleTmp[pasSortStruct[i].nApparitionIndex];
4773       }
4774       msFree(ppsStyleTmp);
4775   }
4776 
4777   msFree(pasSortStruct);
4778 
4779   return MS_SUCCESS;
4780 }
4781 
msOGRUpdateStyleParseLabel(mapObj * map,layerObj * layer,classObj * c,OGRStyleToolH hLabelStyle)4782 static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4783                                       OGRStyleToolH hLabelStyle)
4784 {
4785   GBool bIsNull;
4786   int r=0,g=0,b=0,t=0;
4787 
4788       // Enclose the text string inside quotes to make sure it is seen
4789       // as a string by the parser inside loadExpression(). (bug185)
4790       /* See bug 3481 about the isalnum hack */
4791       const char *labelTextString = OGR_ST_GetParamStr(hLabelStyle,
4792                                     OGRSTLabelTextString,
4793                                     &bIsNull);
4794 
4795       if (c->numlabels == 0) {
4796         /* allocate a new label object */
4797         if(msGrowClassLabels(c) == NULL)
4798           return MS_FAILURE;
4799         c->numlabels++;
4800         initLabel(c->labels[0]);
4801       }
4802       msFreeExpression(&c->labels[0]->text);
4803       c->labels[0]->text.type = MS_STRING;
4804       c->labels[0]->text.string = msStrdup(labelTextString);
4805 
4806       c->labels[0]->angle = OGR_ST_GetParamDbl(hLabelStyle,
4807                             OGRSTLabelAngle, &bIsNull);
4808 
4809       c->labels[0]->size = OGR_ST_GetParamDbl(hLabelStyle,
4810                                               OGRSTLabelSize, &bIsNull);
4811       if( c->labels[0]->size < 1 ) /* no point dropping to zero size */
4812         c->labels[0]->size = 1;
4813 
4814       // OGR default is anchor point = LL, so label is at UR of anchor
4815       c->labels[0]->position = MS_UR;
4816 
4817       int nPosition = OGR_ST_GetParamNum(hLabelStyle,
4818                                          OGRSTLabelAnchor,
4819                                          &bIsNull);
4820       if( !bIsNull ) {
4821         switch( nPosition ) {
4822           case 1:
4823             c->labels[0]->position = MS_UR;
4824             break;
4825           case 2:
4826             c->labels[0]->position = MS_UC;
4827             break;
4828           case 3:
4829             c->labels[0]->position = MS_UL;
4830             break;
4831           case 4:
4832             c->labels[0]->position = MS_CR;
4833             break;
4834           case 5:
4835             c->labels[0]->position = MS_CC;
4836             break;
4837           case 6:
4838             c->labels[0]->position = MS_CL;
4839             break;
4840           case 7:
4841             c->labels[0]->position = MS_LR;
4842             break;
4843           case 8:
4844             c->labels[0]->position = MS_LC;
4845             break;
4846           case 9:
4847             c->labels[0]->position = MS_LL;
4848             break;
4849           case 10:
4850             c->labels[0]->position = MS_UR;
4851             break; /*approximate*/
4852           case 11:
4853             c->labels[0]->position = MS_UC;
4854             break;
4855           case 12:
4856             c->labels[0]->position = MS_UL;
4857             break;
4858           default:
4859             break;
4860         }
4861       }
4862 
4863       const char *pszColor = OGR_ST_GetParamStr(hLabelStyle,
4864                              OGRSTLabelFColor,
4865                              &bIsNull);
4866       if (!bIsNull && OGR_ST_GetRGBFromString(hLabelStyle, pszColor,
4867                                               &r, &g, &b, &t)) {
4868         MS_INIT_COLOR(c->labels[0]->color, r, g, b, t);
4869       }
4870 
4871       pszColor = OGR_ST_GetParamStr(hLabelStyle,
4872                                     OGRSTLabelHColor,
4873                                     &bIsNull);
4874       if (!bIsNull && OGR_ST_GetRGBFromString(hLabelStyle, pszColor,
4875                                               &r, &g, &b, &t)) {
4876         MS_INIT_COLOR(c->labels[0]->shadowcolor, r, g, b, t);
4877       }
4878 
4879       pszColor = OGR_ST_GetParamStr(hLabelStyle,
4880                                     OGRSTLabelOColor,
4881                                     &bIsNull);
4882       if (!bIsNull && OGR_ST_GetRGBFromString(hLabelStyle, pszColor,
4883                                               &r, &g, &b, &t)) {
4884         MS_INIT_COLOR(c->labels[0]->outlinecolor, r, g, b, t);
4885       }
4886 
4887       const char *pszBold = OGR_ST_GetParamNum(hLabelStyle,
4888                             OGRSTLabelBold,
4889                             &bIsNull) ? "-bold" : "";
4890       const char *pszItalic = OGR_ST_GetParamNum(hLabelStyle,
4891                               OGRSTLabelItalic,
4892                               &bIsNull) ? "-italic" : "";
4893       const char *pszFontName = OGR_ST_GetParamStr(hLabelStyle,
4894                                 OGRSTLabelFontName,
4895                                 &bIsNull);
4896       /* replace spaces with hyphens to allow mapping to a valid hashtable entry*/
4897       char* pszFontNameEscaped = NULL;
4898       if (pszFontName != NULL) {
4899           pszFontNameEscaped = msStrdup(pszFontName);
4900           msReplaceChar(pszFontNameEscaped, ' ', '-');
4901       }
4902 
4903       const char *pszName = CPLSPrintf("%s%s%s", pszFontNameEscaped, pszBold, pszItalic);
4904       bool bFont = true;
4905 
4906       if (pszFontNameEscaped != NULL && !bIsNull && pszFontNameEscaped[0] != '\0') {
4907         if (msLookupHashTable(&(map->fontset.fonts), (char*)pszName) != NULL) {
4908           c->labels[0]->font = msStrdup(pszName);
4909           if (layer->debug >= MS_DEBUGLEVEL_VVV)
4910             msDebug("** Using '%s' TTF font **\n", pszName);
4911         } else if ( (strcmp(pszFontNameEscaped,pszName) != 0) &&
4912                     msLookupHashTable(&(map->fontset.fonts), (char*)pszFontNameEscaped) != NULL) {
4913           c->labels[0]->font = msStrdup(pszFontNameEscaped);
4914           if (layer->debug >= MS_DEBUGLEVEL_VVV)
4915             msDebug("** Using '%s' TTF font **\n", pszFontNameEscaped);
4916         } else if (msLookupHashTable(&(map->fontset.fonts),"default") != NULL) {
4917           c->labels[0]->font = msStrdup("default");
4918           if (layer->debug >= MS_DEBUGLEVEL_VVV)
4919             msDebug("** Using 'default' TTF font **\n");
4920         } else
4921           bFont = false;
4922       }
4923 
4924       msFree(pszFontNameEscaped);
4925 
4926       if (!bFont) {
4927         c->labels[0]->size = MS_MEDIUM;
4928       }
4929 
4930       return MS_SUCCESS;
4931 }
4932 
msOGRUpdateStyleParsePen(mapObj * map,layerObj * layer,styleObj * s,OGRStyleToolH hPenStyle,int bIsBrush,int * pbPriority)4933 static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
4934                                     OGRStyleToolH hPenStyle, int bIsBrush,
4935                                     int* pbPriority)
4936 {
4937   GBool bIsNull;
4938   int r=0,g=0,b=0,t=0;
4939 
4940       const char *pszPenName, *pszPattern, *pszCap, *pszJoin;
4941       colorObj oPenColor;
4942       int nPenSymbol = 0;
4943       int nPenSize = 1;
4944       t =-1;
4945       double pattern[MS_MAXPATTERNLENGTH];
4946       int patternlength = 0;
4947       int linecap = MS_CJC_DEFAULT_CAPS;
4948       int linejoin = MS_CJC_DEFAULT_JOINS;
4949       double offsetx = 0.0;
4950       double offsety = 0.0;
4951 
4952       // Make sure pen is always initialized
4953       MS_INIT_COLOR(oPenColor, -1, -1, -1,255);
4954 
4955       pszPenName = OGR_ST_GetParamStr(hPenStyle,
4956                                OGRSTPenId,
4957                                &bIsNull);
4958       if (bIsNull) pszPenName = NULL;
4959       // Check for Pen Pattern "ogr-pen-1": the invisible pen
4960       // If that's what we have then set pen color to -1
4961       if (pszPenName && strstr(pszPenName, "ogr-pen-1") != NULL) {
4962         MS_INIT_COLOR(oPenColor, -1, -1, -1,255);
4963       } else {
4964         const char *pszColor = OGR_ST_GetParamStr(hPenStyle,
4965                                OGRSTPenColor,
4966                                &bIsNull);
4967         if (!bIsNull && OGR_ST_GetRGBFromString(hPenStyle, pszColor,
4968                                                 &r, &g, &b, &t)) {
4969           MS_INIT_COLOR(oPenColor, r, g, b, t);
4970           if (layer->debug >= MS_DEBUGLEVEL_VVV)
4971             msDebug("** PEN COLOR = %d %d %d **\n", r,g,b);
4972         }
4973 
4974         nPenSize = OGR_ST_GetParamNum(hPenStyle,
4975                                       OGRSTPenWidth, &bIsNull);
4976         if (bIsNull)
4977           nPenSize = 1;
4978         if (pszPenName!=NULL) {
4979           // Try to match pen name in symbol file
4980           nPenSymbol = msOGRGetSymbolId(&(map->symbolset),
4981                                         pszPenName, NULL, MS_FALSE);
4982         }
4983       }
4984 
4985       if (layer->debug >= MS_DEBUGLEVEL_VVV)
4986         msDebug("** PEN COLOR = %d %d %d **\n", oPenColor.red,oPenColor.green,oPenColor.blue);
4987 
4988       pszPattern = OGR_ST_GetParamStr(hPenStyle, OGRSTPenPattern, &bIsNull);
4989       if (bIsNull) pszPattern = NULL;
4990       if( pszPattern != NULL )
4991       {
4992           char** papszTokens = CSLTokenizeStringComplex(pszPattern, " ", FALSE, FALSE);
4993           int nTokenCount = CSLCount(papszTokens);
4994           int bValidFormat = TRUE;
4995           if( nTokenCount >= 2 && nTokenCount <= MS_MAXPATTERNLENGTH)
4996           {
4997               for(int i=0;i<nTokenCount;i++)
4998               {
4999                   if( strlen(papszTokens[i]) > 2 &&
5000                       strcmp(papszTokens[i] + strlen(papszTokens[i]) - 2, "px") == 0 )
5001                   {
5002                       pattern[patternlength++] = CPLAtof(papszTokens[i]);
5003                   }
5004                   else
5005                   {
5006                       bValidFormat = FALSE;
5007                       patternlength = 0;
5008                       break;
5009                   }
5010               }
5011           }
5012           else
5013               bValidFormat = FALSE;
5014           if( !bValidFormat && layer->debug >= MS_DEBUGLEVEL_VVV)
5015             msDebug("Invalid/unhandled pen pattern format = %s\n", pszPattern);
5016           CSLDestroy(papszTokens);
5017       }
5018 
5019       pszCap = OGR_ST_GetParamStr(hPenStyle, OGRSTPenCap, &bIsNull);
5020       if (bIsNull) pszCap = NULL;
5021       if( pszCap != NULL )
5022       {
5023           /* Note: the default in OGR Feature style is BUTT, but the MapServer */
5024           /* default is ROUND. Currently use MapServer default. */
5025           if( strcmp(pszCap, "b") == 0 ) /* BUTT */
5026               linecap = MS_CJC_BUTT;
5027           else if( strcmp(pszCap, "r") == 0 ) /* ROUND */
5028               linecap = MS_CJC_ROUND;
5029           else if( strcmp(pszCap, "p") == 0 ) /* PROJECTING */
5030               linecap = MS_CJC_SQUARE;
5031           else if( layer->debug >= MS_DEBUGLEVEL_VVV)
5032             msDebug("Invalid/unhandled pen cap = %s\n", pszCap);
5033       }
5034 
5035       pszJoin = OGR_ST_GetParamStr(hPenStyle, OGRSTPenJoin, &bIsNull);
5036       if (bIsNull) pszJoin = NULL;
5037       if( pszJoin != NULL )
5038       {
5039           /* Note: the default in OGR Feature style is MITER, but the MapServer */
5040           /* default is NONE. Currently use MapServer default. */
5041           if( strcmp(pszJoin, "m") == 0 ) /* MITTER */
5042               linejoin = MS_CJC_MITER;
5043           else if( strcmp(pszJoin, "r") == 0 ) /* ROUND */
5044               linejoin = MS_CJC_ROUND;
5045           else if( strcmp(pszJoin, "b") == 0 ) /* BEVEL */
5046               linejoin = MS_CJC_BEVEL;
5047           else if( layer->debug >= MS_DEBUGLEVEL_VVV)
5048             msDebug("Invalid/unhandled pen join = %s\n", pszJoin);
5049       }
5050 
5051       offsetx = OGR_ST_GetParamDbl(hPenStyle, OGRSTPenPerOffset, &bIsNull);
5052       if( bIsNull ) offsetx = 0;
5053       if( offsetx != 0.0 )
5054       {
5055           /* OGR feature style and MapServer conventions related to offset */
5056           /* sign are the same : negative values for left of line, positive for */
5057           /* right of line */
5058           offsety = MS_STYLE_SINGLE_SIDED_OFFSET;
5059       }
5060 
5061       if (bIsBrush || layer->type == MS_LAYER_POLYGON) {
5062         // This is a multipart symbology, so pen defn goes in the
5063         // overlaysymbol params
5064         s->outlinecolor = oPenColor;
5065       } else {
5066         // Single part symbology
5067         s->color = oPenColor;
5068       }
5069 
5070       s->symbol = nPenSymbol;
5071       s->size = nPenSize;
5072       s->width = nPenSize;
5073       s->linecap = linecap;
5074       s->linejoin = linejoin;
5075       s->offsetx = offsetx;
5076       s->offsety = offsety;
5077       s->patternlength = patternlength;
5078       if( patternlength > 0 )
5079           memcpy(s->pattern, pattern, sizeof(double) * patternlength);
5080 
5081       int nPriority = OGR_ST_GetParamNum(hPenStyle, OGRSTPenPriority, &bIsNull);
5082       if( !bIsNull )
5083           *pbPriority = nPriority;
5084 
5085       return MS_SUCCESS;
5086 }
5087 
msOGRUpdateStyleParseBrush(mapObj * map,layerObj * layer,styleObj * s,OGRStyleToolH hBrushStyle,int * pbIsBrush,int * pbPriority)5088 static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
5089                                       OGRStyleToolH hBrushStyle, int* pbIsBrush,
5090                                       int* pbPriority)
5091 {
5092   GBool bIsNull;
5093   int r=0,g=0,b=0,t=0;
5094 
5095       const char *pszBrushName = OGR_ST_GetParamStr(hBrushStyle,
5096                                  OGRSTBrushId,
5097                                  &bIsNull);
5098       if (bIsNull) pszBrushName = NULL;
5099 
5100       // Check for Brush Pattern "ogr-brush-1": the invisible fill
5101       // If that's what we have then set fill color to -1
5102       if (pszBrushName && strstr(pszBrushName, "ogr-brush-1") != NULL) {
5103         MS_INIT_COLOR(s->color, -1, -1, -1, 255);
5104       } else {
5105         *pbIsBrush = TRUE;
5106         const char *pszColor = OGR_ST_GetParamStr(hBrushStyle,
5107                                OGRSTBrushFColor,
5108                                &bIsNull);
5109         if (!bIsNull && OGR_ST_GetRGBFromString(hBrushStyle,
5110                                                 pszColor,
5111                                                 &r, &g, &b, &t)) {
5112           MS_INIT_COLOR(s->color, r, g, b, t);
5113 
5114           if (layer->debug >= MS_DEBUGLEVEL_VVV)
5115             msDebug("** BRUSH COLOR = %d %d %d **\n", r,g,b);
5116         }
5117 
5118         pszColor = OGR_ST_GetParamStr(hBrushStyle,
5119                                       OGRSTBrushBColor, &bIsNull);
5120         if (!bIsNull && OGR_ST_GetRGBFromString(hBrushStyle,
5121                                                 pszColor,
5122                                                 &r, &g, &b, &t)) {
5123           MS_INIT_COLOR(s->backgroundcolor, r, g, b, t);
5124         }
5125 
5126         // Symbol name mapping:
5127         // First look for the native symbol name, then the ogr-...
5128         // generic name.
5129         // If none provided or found then use 0: solid fill
5130 
5131         const char *pszName = OGR_ST_GetParamStr(hBrushStyle,
5132                               OGRSTBrushId,
5133                               &bIsNull);
5134         s->symbol = msOGRGetSymbolId(&(map->symbolset),
5135                                                 pszName, NULL, MS_FALSE);
5136 
5137         double angle = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushAngle, &bIsNull);
5138         if( !bIsNull )
5139             s->angle = angle;
5140 
5141         double size = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushSize, &bIsNull);
5142         if( !bIsNull )
5143             s->size = size;
5144 
5145         double spacingx = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDx, &bIsNull);
5146         if( !bIsNull )
5147         {
5148             double spacingy = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDy, &bIsNull);
5149             if( !bIsNull )
5150             {
5151                 if( spacingx == spacingy )
5152                     s->gap = spacingx;
5153                 else if( layer->debug >= MS_DEBUGLEVEL_VVV )
5154                     msDebug("Ignoring brush dx and dy since they don't have the same value\n");
5155             }
5156         }
5157       }
5158 
5159       int nPriority = OGR_ST_GetParamNum(hBrushStyle, OGRSTBrushPriority, &bIsNull);
5160       if( !bIsNull )
5161           *pbPriority = nPriority;
5162 
5163       return MS_SUCCESS;
5164 }
5165 
msOGRUpdateStyleParseSymbol(mapObj * map,layerObj * layer,styleObj * s,OGRStyleToolH hSymbolStyle,int * pbPriority)5166 static int msOGRUpdateStyleParseSymbol(mapObj *map, layerObj *layer, styleObj *s,
5167                                        OGRStyleToolH hSymbolStyle,
5168                                        int* pbPriority)
5169 {
5170   GBool bIsNull;
5171   int r=0,g=0,b=0,t=0;
5172 
5173       const char *pszColor = OGR_ST_GetParamStr(hSymbolStyle,
5174                              OGRSTSymbolColor,
5175                              &bIsNull);
5176       if (!bIsNull && OGR_ST_GetRGBFromString(hSymbolStyle,
5177                                               pszColor,
5178                                               &r, &g, &b, &t)) {
5179         MS_INIT_COLOR(s->color, r, g, b, t);
5180       }
5181 
5182       pszColor = OGR_ST_GetParamStr(hSymbolStyle,
5183                                     OGRSTSymbolOColor,
5184                                     &bIsNull);
5185       if (!bIsNull && OGR_ST_GetRGBFromString(hSymbolStyle,
5186                                               pszColor,
5187                                               &r, &g, &b, &t)) {
5188         MS_INIT_COLOR(s->outlinecolor, r, g, b, t);
5189       }
5190 
5191       s->angle = OGR_ST_GetParamNum(hSymbolStyle,
5192                             OGRSTSymbolAngle,
5193                             &bIsNull);
5194       double dfTmp = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolSize, &bIsNull);
5195       if (!bIsNull)
5196         s->size = dfTmp;
5197 
5198       // Symbol name mapping:
5199       // First look for the native symbol name, then the ogr-...
5200       // generic name, and in last resort try "default-marker" if
5201       // provided by user.
5202       const char *pszName = OGR_ST_GetParamStr(hSymbolStyle,
5203                             OGRSTSymbolId,
5204                             &bIsNull);
5205       if (bIsNull)
5206         pszName = NULL;
5207 
5208       int try_addimage_if_notfound = MS_FALSE;
5209 #ifdef USE_CURL
5210       if (pszName && strncasecmp(pszName, "http", 4) == 0)
5211         try_addimage_if_notfound =MS_TRUE;
5212 #endif
5213       if (!s->symbolname)
5214         s->symbol = msOGRGetSymbolId(&(map->symbolset),
5215                                                 pszName,
5216                                                 "default-marker",  try_addimage_if_notfound);
5217 
5218       int nPriority = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolPriority, &bIsNull);
5219       if( !bIsNull )
5220           *pbPriority = nPriority;
5221 
5222       return MS_SUCCESS;
5223 }
5224 
5225 
5226 /**********************************************************************
5227  *                     msOGRLayerGetAutoStyle()
5228  *
5229  * Fills a classObj with style info from the specified shape.
5230  * For optimal results, this should be called immediately after
5231  * GetNextShape() or GetShape() so that the shape doesn't have to be read
5232  * twice.
5233  *
5234  * The returned classObj is a ref. to a static structure valid only until
5235  * the next call and that shouldn't be freed by the caller.
5236  **********************************************************************/
msOGRLayerGetAutoStyle(mapObj * map,layerObj * layer,classObj * c,shapeObj * shape)5237 static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
5238                                   shapeObj* shape)
5239 {
5240   msOGRFileInfo *psInfo =(msOGRFileInfo*)layer->layerinfo;
5241 
5242   if (psInfo == NULL || psInfo->hLayer == NULL) {
5243     msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
5244                "msOGRLayerGetAutoStyle()");
5245     return(MS_FAILURE);
5246   }
5247 
5248   if( layer->tileindex != NULL ) {
5249     if( (psInfo->poCurTile == NULL || shape->tileindex != psInfo->poCurTile->nTileId)
5250         && msOGRFileReadTile( layer, psInfo ) != MS_SUCCESS )
5251       return MS_FAILURE;
5252 
5253     psInfo = psInfo->poCurTile;
5254   }
5255 
5256   /* ------------------------------------------------------------------
5257    * Read shape or reuse ref. to last shape read.
5258    * ------------------------------------------------------------------ */
5259   ACQUIRE_OGR_LOCK;
5260   if (psInfo->hLastFeature == NULL ||
5261       psInfo->last_record_index_read != shape->resultindex) {
5262     RELEASE_OGR_LOCK;
5263     msSetError(MS_MISCERR,
5264                "Assertion failed: AutoStyle not requested on loaded shape.",
5265                "msOGRLayerGetAutoStyle()");
5266     return(MS_FAILURE);
5267   }
5268 
5269   /* ------------------------------------------------------------------
5270    * Reset style info in the class to defaults
5271    * the only members we don't touch are name, expression, and join/query stuff
5272    * ------------------------------------------------------------------ */
5273   resetClassStyle(c);
5274   if (msMaybeAllocateClassStyle(c, 0)) {
5275     RELEASE_OGR_LOCK;
5276     return(MS_FAILURE);
5277   }
5278 
5279   // __TODO__ label cache incompatible with styleitem feature.
5280   layer->labelcache = MS_OFF;
5281 
5282   int nRetVal = MS_SUCCESS;
5283   if (psInfo->hLastFeature) {
5284     OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
5285     OGR_SM_InitFromFeature(hStyleMgr, psInfo->hLastFeature);
5286     nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
5287     OGR_SM_Destroy(hStyleMgr);
5288   }
5289 
5290   RELEASE_OGR_LOCK;
5291   return nRetVal;
5292 }
5293 
5294 
5295 /**********************************************************************
5296  *                     msOGRUpdateStyleFromString()
5297  *
5298  * Fills a classObj with style info from the specified style string.
5299  * For optimal results, this should be called immediately after
5300  * GetNextShape() or GetShape() so that the shape doesn't have to be read
5301  * twice.
5302  *
5303  * The returned classObj is a ref. to a static structure valid only until
5304  * the next call and that shouldn't be freed by the caller.
5305  **********************************************************************/
msOGRUpdateStyleFromString(mapObj * map,layerObj * layer,classObj * c,const char * stylestring)5306 int msOGRUpdateStyleFromString(mapObj *map, layerObj *layer, classObj *c,
5307                                const char *stylestring)
5308 {
5309   /* ------------------------------------------------------------------
5310    * Reset style info in the class to defaults
5311    * the only members we don't touch are name, expression, and join/query stuff
5312    * ------------------------------------------------------------------ */
5313   resetClassStyle(c);
5314   if (msMaybeAllocateClassStyle(c, 0)) {
5315     return(MS_FAILURE);
5316   }
5317 
5318   // __TODO__ label cache incompatible with styleitem feature.
5319   layer->labelcache = MS_OFF;
5320 
5321   int nRetVal = MS_SUCCESS;
5322 
5323   ACQUIRE_OGR_LOCK;
5324 
5325   OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
5326   OGR_SM_InitStyleString(hStyleMgr, stylestring);
5327   nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
5328   OGR_SM_Destroy(hStyleMgr);
5329 
5330   RELEASE_OGR_LOCK;
5331   return nRetVal;
5332 }
5333 
5334 /************************************************************************/
5335 /*                           msOGRLCleanup()                            */
5336 /************************************************************************/
5337 
msOGRCleanup(void)5338 void msOGRCleanup( void )
5339 
5340 {
5341   ACQUIRE_OGR_LOCK;
5342   if( bOGRDriversRegistered == MS_TRUE ) {
5343     CPLPopErrorHandler();
5344     bOGRDriversRegistered = MS_FALSE;
5345   }
5346   RELEASE_OGR_LOCK;
5347 }
5348 
5349 /************************************************************************/
5350 /*                           msOGREscapeSQLParam                        */
5351 /************************************************************************/
msOGREscapePropertyName(layerObj * layer,const char * pszString)5352 char *msOGREscapePropertyName(layerObj *layer, const char *pszString)
5353 {
5354   char* pszEscapedStr =NULL;
5355   if(layer && pszString && strlen(pszString) > 0) {
5356     pszEscapedStr = (char*) msSmallMalloc( strlen(pszString) * 2 + 1 );
5357     int j = 0;
5358     for( int i = 0; pszString[i] != '\0'; ++i )
5359     {
5360         if( pszString[i] == '"' )
5361         {
5362             pszEscapedStr[j++] = '"';
5363             pszEscapedStr[j++] = '"';
5364         }
5365         else
5366             pszEscapedStr[j++] = pszString[i];
5367     }
5368     pszEscapedStr[j] = 0;
5369   }
5370   return pszEscapedStr;
5371 }
5372 
msOGRLayerSupportsCommonFilters(layerObj * layer)5373 static int msOGRLayerSupportsCommonFilters(layerObj *layer)
5374 {
5375   return MS_FALSE;
5376 }
5377 
msOGREnablePaging(layerObj * layer,int value)5378 static void msOGREnablePaging(layerObj *layer, int value)
5379 {
5380   msOGRFileInfo *layerinfo = NULL;
5381 
5382   if (layer->debug) {
5383     msDebug("msOGREnablePaging(%d) called.\n", value);
5384   }
5385 
5386   if(!msOGRLayerIsOpen(layer))
5387     msOGRLayerOpenVT(layer);
5388 
5389   assert( layer->layerinfo != NULL);
5390 
5391   layerinfo = (msOGRFileInfo *)layer->layerinfo;
5392   layerinfo->bPaging = value;
5393 }
5394 
msOGRGetPaging(layerObj * layer)5395 static int msOGRGetPaging(layerObj *layer)
5396 {
5397   msOGRFileInfo *layerinfo = NULL;
5398 
5399   if (layer->debug) {
5400     msDebug("msOGRGetPaging called.\n");
5401   }
5402 
5403   if(!msOGRLayerIsOpen(layer))
5404     msOGRLayerOpenVT(layer);
5405 
5406   assert( layer->layerinfo != NULL);
5407 
5408   layerinfo = (msOGRFileInfo *)layer->layerinfo;
5409   return layerinfo->bPaging;
5410 }
5411 
5412 /************************************************************************/
5413 /*                  msOGRLayerInitializeVirtualTable()                  */
5414 /************************************************************************/
msOGRLayerInitializeVirtualTable(layerObj * layer)5415 int msOGRLayerInitializeVirtualTable(layerObj *layer)
5416 {
5417   assert(layer != NULL);
5418   assert(layer->vtable != NULL);
5419 
5420   layer->vtable->LayerTranslateFilter = msOGRTranslateMsExpressionToOGRSQL;
5421 
5422   layer->vtable->LayerSupportsCommonFilters = msOGRLayerSupportsCommonFilters;
5423   layer->vtable->LayerInitItemInfo = msOGRLayerInitItemInfo;
5424   layer->vtable->LayerFreeItemInfo = msOGRLayerFreeItemInfo;
5425   layer->vtable->LayerOpen = msOGRLayerOpenVT;
5426   layer->vtable->LayerIsOpen = msOGRLayerIsOpen;
5427   layer->vtable->LayerWhichShapes = msOGRLayerWhichShapes;
5428   layer->vtable->LayerNextShape = msOGRLayerNextShape;
5429   layer->vtable->LayerGetShape = msOGRLayerGetShape;
5430   /* layer->vtable->LayerGetShapeCount, use default */
5431   layer->vtable->LayerClose = msOGRLayerClose;
5432   layer->vtable->LayerGetItems = msOGRLayerGetItems;
5433   layer->vtable->LayerGetExtent = msOGRLayerGetExtent;
5434   layer->vtable->LayerGetAutoStyle = msOGRLayerGetAutoStyle;
5435   /* layer->vtable->LayerCloseConnection, use default */
5436   layer->vtable->LayerApplyFilterToLayer = msLayerApplyCondSQLFilterToLayer;
5437   layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
5438   /* layer->vtable->LayerCreateItems, use default */
5439   layer->vtable->LayerGetNumFeatures = msOGRLayerGetNumFeatures;
5440   /* layer->vtable->LayerGetAutoProjection, use defaut*/
5441 
5442   layer->vtable->LayerEscapeSQLParam = msOGREscapeSQLParam;
5443   layer->vtable->LayerEscapePropertyName = msOGREscapePropertyName;
5444   layer->vtable->LayerEnablePaging = msOGREnablePaging;
5445   layer->vtable->LayerGetPaging = msOGRGetPaging;
5446   return MS_SUCCESS;
5447 }
5448 
5449 /************************************************************************/
5450 /*                         msOGRShapeFromWKT()                          */
5451 /************************************************************************/
msOGRShapeFromWKT(const char * string)5452 shapeObj *msOGRShapeFromWKT(const char *string)
5453 {
5454 
5455   OGRGeometryH hGeom = NULL;
5456   shapeObj *shape=NULL;
5457 
5458   if(!string)
5459     return NULL;
5460 
5461   if( OGR_G_CreateFromWkt( (char **)&string, NULL, &hGeom ) != OGRERR_NONE ) {
5462     msSetError(MS_OGRERR, "Failed to parse WKT string.",
5463                "msOGRShapeFromWKT()" );
5464     return NULL;
5465   }
5466 
5467   /* Initialize a corresponding shapeObj */
5468 
5469   shape = (shapeObj *) malloc(sizeof(shapeObj));
5470   msInitShape(shape);
5471 
5472   /* translate WKT into an OGRGeometry. */
5473 
5474   if( msOGRGeometryToShape( hGeom, shape,
5475                             wkbFlatten(OGR_G_GetGeometryType(hGeom)) )
5476       == MS_FAILURE ) {
5477     free( shape );
5478     return NULL;
5479   }
5480 
5481   OGR_G_DestroyGeometry( hGeom );
5482 
5483   return shape;
5484 }
5485 
5486 /************************************************************************/
5487 /*                          msOGRShapeToWKT()                           */
5488 /************************************************************************/
msOGRShapeToWKT(shapeObj * shape)5489 char *msOGRShapeToWKT(shapeObj *shape)
5490 {
5491   OGRGeometryH hGeom = NULL;
5492   int          i;
5493   char        *wkt = NULL;
5494 
5495   if(!shape)
5496     return NULL;
5497 
5498   if( shape->type == MS_SHAPE_POINT && shape->numlines == 1
5499       && shape->line[0].numpoints == 1 ) {
5500     hGeom = OGR_G_CreateGeometry( wkbPoint );
5501     OGR_G_SetPoint_2D( hGeom, 0,
5502                        shape->line[0].point[0].x,
5503                        shape->line[0].point[0].y );
5504   } else if( shape->type == MS_SHAPE_POINT && shape->numlines == 1
5505              && shape->line[0].numpoints > 1 ) {
5506     hGeom = OGR_G_CreateGeometry( wkbMultiPoint );
5507     for( i = 0; i < shape->line[0].numpoints; i++ ) {
5508       OGRGeometryH hPoint;
5509 
5510       hPoint = OGR_G_CreateGeometry( wkbPoint );
5511       OGR_G_SetPoint_2D( hPoint, 0,
5512                          shape->line[0].point[i].x,
5513                          shape->line[0].point[i].y );
5514       OGR_G_AddGeometryDirectly( hGeom, hPoint );
5515     }
5516   } else if( shape->type == MS_SHAPE_LINE && shape->numlines == 1 ) {
5517     hGeom = OGR_G_CreateGeometry( wkbLineString );
5518     for( i = 0; i < shape->line[0].numpoints; i++ ) {
5519       OGR_G_AddPoint_2D( hGeom,
5520                          shape->line[0].point[i].x,
5521                          shape->line[0].point[i].y );
5522     }
5523   } else if( shape->type == MS_SHAPE_LINE && shape->numlines > 1 ) {
5524     OGRGeometryH hMultiLine = OGR_G_CreateGeometry( wkbMultiLineString );
5525     int iLine;
5526 
5527     for( iLine = 0; iLine < shape->numlines; iLine++ ) {
5528       hGeom = OGR_G_CreateGeometry( wkbLineString );
5529       for( i = 0; i < shape->line[iLine].numpoints; i++ ) {
5530         OGR_G_AddPoint_2D( hGeom,
5531                            shape->line[iLine].point[i].x,
5532                            shape->line[iLine].point[i].y );
5533       }
5534 
5535       OGR_G_AddGeometryDirectly( hMultiLine, hGeom );
5536     }
5537 
5538     hGeom = hMultiLine;
5539   } else if( shape->type == MS_SHAPE_POLYGON ) {
5540     int iLine;
5541 
5542     /* actually, it is pretty hard to be sure rings 1+ are interior */
5543     hGeom = OGR_G_CreateGeometry( wkbPolygon );
5544     for( iLine = 0; iLine < shape->numlines; iLine++ ) {
5545       OGRGeometryH hRing;
5546       hRing = OGR_G_CreateGeometry( wkbLinearRing );
5547 
5548       for( i = 0; i < shape->line[iLine].numpoints; i++ ) {
5549         OGR_G_AddPoint_2D( hRing,
5550                            shape->line[iLine].point[i].x,
5551                            shape->line[iLine].point[i].y );
5552       }
5553       OGR_G_AddGeometryDirectly( hGeom, hRing );
5554     }
5555   } else {
5556     msSetError(MS_OGRERR, "OGR support is not available.", "msOGRShapeToWKT()");
5557   }
5558 
5559   if( hGeom != NULL ) {
5560     char *pszOGRWkt;
5561 
5562     OGR_G_ExportToWkt( hGeom, &pszOGRWkt );
5563     wkt = msStrdup( pszOGRWkt );
5564     CPLFree( pszOGRWkt );
5565   }
5566 
5567   return wkt;
5568 }
5569