1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: Implementation of most layerObj functions.
6 * Author: Steve Lime and the MapServer team.
7 *
8 ******************************************************************************
9 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "mapserver.h"
31 #include "maptime.h"
32 #include "mapogcfilter.h"
33 #include "mapthread.h"
34 #include "mapfile.h"
35
36 #include "mapparser.h"
37
38 #include <assert.h>
39
40 static int populateVirtualTable(layerVTableObj *vtable);
41
42 /*
43 ** Iteminfo is a layer parameter that holds information necessary to retrieve an individual item for
44 ** a particular source. It is an array built from a list of items. The type of iteminfo will vary by
45 ** source. For shapefiles and OGR it is simply an array of integers where each value is an index for
46 ** the item. For SDE it's a ESRI specific type that contains index and column type information. Two
47 ** helper functions below initialize and free that structure member which is used locally by layer
48 ** specific functions.
49 */
msLayerInitItemInfo(layerObj * layer)50 int msLayerInitItemInfo(layerObj *layer)
51 {
52 if ( ! layer->vtable) {
53 int rv = msInitializeVirtualTable(layer);
54 if (rv != MS_SUCCESS)
55 return rv;
56 }
57 return layer->vtable->LayerInitItemInfo(layer);
58 }
59
msLayerFreeItemInfo(layerObj * layer)60 void msLayerFreeItemInfo(layerObj *layer)
61 {
62 if ( ! layer->vtable) {
63 int rv = msInitializeVirtualTable(layer);
64 if (rv != MS_SUCCESS)
65 return;
66 }
67 layer->vtable->LayerFreeItemInfo(layer);
68
69 /*
70 * Layer expressions with attribute binding hold a numeric index pointing
71 * to an iteminfo (node->tokenval.bindval.index). If iteminfo changes,
72 * an expression may be no longer valid. (#5161)
73 */
74 msLayerFreeExpressions(layer);
75 }
76
msLayerRestoreFromScaletokens(layerObj * layer)77 int msLayerRestoreFromScaletokens(layerObj *layer)
78 {
79 if(!layer->scaletokens || !layer->orig_st) {
80 return MS_SUCCESS;
81 }
82 if(layer->orig_st->data) {
83 msFree(layer->data);
84 layer->data = layer->orig_st->data;
85 }
86 if(layer->orig_st->tileindex) {
87 msFree(layer->tileindex);
88 layer->tileindex = layer->orig_st->tileindex;
89 }
90 if(layer->orig_st->tileitem) {
91 msFree(layer->tileitem);
92 layer->tileitem = layer->orig_st->tileitem;
93 }
94 if(layer->orig_st->filter) {
95 msLoadExpressionString(&(layer->filter),layer->orig_st->filter);
96 msFree(layer->orig_st->filter);
97 }
98 if(layer->orig_st->filteritem) {
99 msFree(layer->filteritem);
100 layer->filteritem = layer->orig_st->filteritem;
101 }
102 if(layer->orig_st->n_processing) {
103 int i;
104 for(i=0;i<layer->orig_st->n_processing;i++) {
105 msFree(layer->processing[layer->orig_st->processing_idx[i]]);
106 layer->processing[layer->orig_st->processing_idx[i]] = layer->orig_st->processing[i];
107 }
108 msFree(layer->orig_st->processing);
109 msFree(layer->orig_st->processing_idx);
110 }
111 msFree(layer->orig_st);
112 layer->orig_st = NULL;
113 return MS_SUCCESS;
114 }
115
116 #define check_st_alloc(l) if(!l->orig_st) l->orig_st=msSmallCalloc(1,sizeof(originalScaleTokenStrings));
msLayerApplyScaletokens(layerObj * layer,double scale)117 int msLayerApplyScaletokens(layerObj *layer, double scale)
118 {
119 int i,p;
120 if(!layer->scaletokens) {
121 return MS_SUCCESS;
122 }
123 msLayerRestoreFromScaletokens(layer);
124 for(i=0;i<layer->numscaletokens;i++) {
125 scaleTokenObj *st = &layer->scaletokens[i];
126 scaleTokenEntryObj *ste = NULL;
127 if(scale<=0) {
128 ste = &(st->tokens[0]);
129 /* no scale defined, use first entry */
130 } else {
131 int tokenindex=0;
132 while(tokenindex<st->n_entries) {
133 ste = &(st->tokens[tokenindex]);
134 if(scale < ste->maxscale && scale >= ste->minscale) break; /* current token is the correct one */
135 tokenindex++;
136 ste = NULL;
137 }
138 }
139 assert(ste);
140 if(layer->data && strstr(layer->data,st->name)) {
141 if(layer->debug >= MS_DEBUGLEVEL_DEBUG) {
142 msDebug("replacing scaletoken (%s) with (%s) in layer->data (%s) for scale=%f\n",
143 st->name,ste->value,layer->name,scale);
144 }
145 check_st_alloc(layer);
146 layer->orig_st->data = layer->data;
147 layer->data = msStrdup(layer->data);
148 layer->data = msReplaceSubstring(layer->data,st->name,ste->value);
149 }
150 if(layer->tileindex && strstr(layer->tileindex,st->name)) {
151 if(layer->debug >= MS_DEBUGLEVEL_DEBUG) {
152 msDebug("replacing scaletoken (%s) with (%s) in layer->tileindex (%s) for scale=%f\n",
153 st->name,ste->value,layer->name,scale);
154 }
155 check_st_alloc(layer);
156 layer->orig_st->tileindex = layer->tileindex;
157 layer->tileindex = msStrdup(layer->tileindex);
158 layer->tileindex = msReplaceSubstring(layer->tileindex,st->name,ste->value);
159 }
160 if(layer->tileitem && strstr(layer->tileitem,st->name)) {
161 if(layer->debug >= MS_DEBUGLEVEL_DEBUG) {
162 msDebug("replacing scaletoken (%s) with (%s) in layer->tileitem (%s) for scale=%f\n",
163 st->name,ste->value,layer->name,scale);
164 }
165 check_st_alloc(layer);
166 layer->orig_st->tileitem = layer->tileitem;
167 layer->tileitem = msStrdup(layer->tileitem);
168 layer->tileitem = msReplaceSubstring(layer->tileitem,st->name,ste->value);
169 }
170 if(layer->filteritem && strstr(layer->filteritem,st->name)) {
171 if(layer->debug >= MS_DEBUGLEVEL_DEBUG) {
172 msDebug("replacing scaletoken (%s) with (%s) in layer->filteritem (%s) for scale=%f\n",
173 st->name,ste->value,layer->name,scale);
174 }
175 check_st_alloc(layer);
176 layer->orig_st->filteritem = layer->filteritem;
177 layer->filteritem = msStrdup(layer->filteritem);
178 layer->filteritem = msReplaceSubstring(layer->filteritem,st->name,ste->value);
179 }
180 if(layer->filter.string && strstr(layer->filter.string,st->name)) {
181 char *tmpval;
182 if(layer->debug >= MS_DEBUGLEVEL_DEBUG) {
183 msDebug("replacing scaletoken (%s) with (%s) in layer->filter (%s) for scale=%f\n",
184 st->name,ste->value,layer->name,scale);
185 }
186 check_st_alloc(layer);
187 layer->orig_st->filter = msStrdup(layer->filter.string);
188 tmpval = msStrdup(layer->filter.string);
189 tmpval = msReplaceSubstring(tmpval,st->name,ste->value);
190 if(msLoadExpressionString(&(layer->filter),tmpval) == -1) return(MS_FAILURE); /* msLoadExpressionString() cleans up previously allocated expression */
191 msFree(tmpval);
192 }
193 for(p=0;p<layer->numprocessing;p++) {
194 if(strstr(layer->processing[p],st->name)) {
195 check_st_alloc(layer);
196 layer->orig_st->n_processing++;
197 layer->orig_st->processing = msSmallRealloc(layer->orig_st->processing, layer->orig_st->n_processing * sizeof(char*));
198 layer->orig_st->processing_idx = msSmallRealloc(layer->orig_st->processing_idx, layer->orig_st->n_processing * sizeof(int));
199 layer->orig_st->processing[layer->orig_st->n_processing-1] = layer->processing[p];
200 layer->orig_st->processing_idx[layer->orig_st->n_processing-1] = p;
201 layer->processing[p] = msStrdup(layer->processing[p]);
202 layer->processing[p] = msReplaceSubstring(layer->processing[p],st->name,ste->value);
203 }
204 }
205
206 }
207 return MS_SUCCESS;
208 }
209
210
211 /*
212 ** Does exactly what it implies, readies a layer for processing.
213 */
msLayerOpen(layerObj * layer)214 int msLayerOpen(layerObj *layer)
215 {
216 int rv;
217
218 /* RFC-86 Scale dependant token replacements*/
219 rv = msLayerApplyScaletokens(layer,(layer->map)?layer->map->scaledenom:-1);
220 if (rv != MS_SUCCESS) return rv;
221
222 /* RFC-69 clustering support */
223 if (layer->cluster.region)
224 return msClusterLayerOpen(layer);
225
226 if(layer->features && layer->connectiontype != MS_GRATICULE )
227 layer->connectiontype = MS_INLINE;
228
229 if(layer->tileindex && layer->connectiontype == MS_SHAPEFILE)
230 layer->connectiontype = MS_TILED_SHAPEFILE;
231
232 if(layer->type == MS_LAYER_RASTER && layer->connectiontype != MS_WMS
233 && layer->connectiontype != MS_KERNELDENSITY)
234 layer->connectiontype = MS_RASTER;
235
236 if ( ! layer->vtable) {
237 rv = msInitializeVirtualTable(layer);
238 if (rv != MS_SUCCESS)
239 return rv;
240 }
241 return layer->vtable->LayerOpen(layer);
242 }
243
244 /*
245 ** Returns MS_TRUE if layer has been opened using msLayerOpen(), MS_FALSE otherwise
246 */
msLayerIsOpen(layerObj * layer)247 int msLayerIsOpen(layerObj *layer)
248 {
249 if ( ! layer->vtable) {
250 int rv = msInitializeVirtualTable(layer);
251 if (rv != MS_SUCCESS)
252 return rv;
253 }
254 return layer->vtable->LayerIsOpen(layer);
255 }
256
257 /*
258 ** Returns MS_TRUE is a layer supports the common expression/filter syntax (RFC 64) and MS_FALSE otherwise.
259 */
msLayerSupportsCommonFilters(layerObj * layer)260 int msLayerSupportsCommonFilters(layerObj *layer)
261 {
262 if ( ! layer->vtable) {
263 int rv = msInitializeVirtualTable(layer);
264 if (rv != MS_SUCCESS)
265 return rv;
266 }
267 return layer->vtable->LayerSupportsCommonFilters(layer);
268 }
269
msLayerTranslateFilter(layerObj * layer,expressionObj * filter,char * filteritem)270 int msLayerTranslateFilter(layerObj *layer, expressionObj *filter, char *filteritem)
271 {
272 if (!layer->vtable) {
273 int rv = msInitializeVirtualTable(layer);
274 if (rv != MS_SUCCESS)
275 return rv;
276 }
277 return layer->vtable->LayerTranslateFilter(layer, filter, filteritem);
278 }
279
280 /*
281 ** Performs a spatial, and optionally an attribute based feature search. The function basically
282 ** prepares things so that candidate features can be accessed by query or drawing functions. For
283 ** OGR and shapefiles this sets an internal bit vector that indicates whether a particular feature
284 ** is to processed. For SDE it executes an SQL statement on the SDE server. Once run the msLayerNextShape
285 ** function should be called to actually access the shapes.
286 **
287 ** Note that for shapefiles we apply any maxfeatures constraint at this point. That may be the only
288 ** connection type where this is feasible.
289 */
msLayerWhichShapes(layerObj * layer,rectObj rect,int isQuery)290 int msLayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
291 {
292 if(!msLayerSupportsCommonFilters(layer))
293 msLayerTranslateFilter(layer, &layer->filter, layer->filteritem);
294
295 if ( ! layer->vtable) {
296 int rv = msInitializeVirtualTable(layer);
297 if (rv != MS_SUCCESS)
298 return rv;
299 }
300 return layer->vtable->LayerWhichShapes(layer, rect, isQuery);
301 }
302
303 /*
304 ** Called after msWhichShapes has been called to actually retrieve shapes within a given area
305 ** and matching a vendor specific filter (i.e. layer FILTER attribute).
306 **
307 ** Shapefiles: NULL shapes (shapes with attributes but NO vertices are skipped)
308 */
msLayerNextShape(layerObj * layer,shapeObj * shape)309 int msLayerNextShape(layerObj *layer, shapeObj *shape)
310 {
311 int rv, filter_passed;
312
313 if ( ! layer->vtable) {
314 rv = msInitializeVirtualTable(layer);
315 if (rv != MS_SUCCESS)
316 return rv;
317 }
318
319 #ifdef USE_V8_MAPSCRIPT
320 /* we need to force the GetItems for the geomtransform attributes */
321 if(!layer->items &&
322 layer->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION &&
323 strstr(layer->_geomtransform.string, "javascript"))
324 msLayerGetItems(layer);
325 #endif
326
327 /* At the end of switch case (default -> break; -> return MS_FAILURE),
328 * was following TODO ITEM:
329 *
330 * TO DO! This is where dynamic joins will happen. Joined attributes will be
331 * tagged on to the main attributes with the naming scheme [join name].[item name].
332 * We need to leverage the iteminfo (I think) at this point
333 */
334
335 /* RFC 91: MapServer-based filtering is done at a more general level. */
336 do {
337 rv = layer->vtable->LayerNextShape(layer, shape);
338 if(rv != MS_SUCCESS) return rv;
339
340 filter_passed = MS_TRUE; /* By default accept ANY shape */
341
342 /* attributes need to be iconv'd to UTF-8 before any filter logic is applied */
343 if(layer->encoding) {
344 rv = msLayerEncodeShapeAttributes(layer,shape);
345 if(rv != MS_SUCCESS)
346 return rv;
347 }
348
349 // if(layer->numitems > 0 && layer->iteminfo) {
350 filter_passed = msEvalExpression(layer, shape, &(layer->filter), layer->filteritemindex);
351 // }
352
353 if(!filter_passed) msFreeShape(shape);
354 } while(!filter_passed);
355
356 /* RFC89 Apply Layer GeomTransform */
357 if(layer->_geomtransform.type != MS_GEOMTRANSFORM_NONE && rv == MS_SUCCESS) {
358 rv = msGeomTransformShape(layer->map, layer, shape);
359 if(rv != MS_SUCCESS)
360 return rv;
361 }
362
363
364 return rv;
365 }
366
367 /*
368 ** Used to retrieve a shape from a result set by index. Result sets are created by the various
369 ** msQueryBy...() functions. The index is assigned by the data source.
370 */
371 /* int msLayerResultsGetShape(layerObj *layer, shapeObj *shape, int tile, long record)
372 {
373 if ( ! layer->vtable) {
374 int rv = msInitializeVirtualTable(layer);
375 if (rv != MS_SUCCESS)
376 return rv;
377 }
378
379 return layer->vtable->LayerResultsGetShape(layer, shape, tile, record);
380 } */
381
382 /*
383 ** Used to retrieve a shape by index. All data sources must be capable of random access using
384 ** a record number(s) of some sort.
385 */
msLayerGetShape(layerObj * layer,shapeObj * shape,resultObj * record)386 int msLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record)
387 {
388 int rv;
389
390 if( ! layer->vtable) {
391 rv = msInitializeVirtualTable(layer);
392 if(rv != MS_SUCCESS)
393 return rv;
394 }
395
396 /*
397 ** TODO: This is where dynamic joins could happen. Joined attributes would be
398 ** tagged on to the main attributes with the naming scheme [join name].[item name].
399 */
400
401 rv = layer->vtable->LayerGetShape(layer, shape, record);
402 if(rv != MS_SUCCESS)
403 return rv;
404
405 /* RFC89 Apply Layer GeomTransform */
406 if(layer->_geomtransform.type != MS_GEOMTRANSFORM_NONE && rv == MS_SUCCESS) {
407 rv = msGeomTransformShape(layer->map, layer, shape);
408 if(rv != MS_SUCCESS)
409 return rv;
410 }
411
412 if(layer->encoding) {
413 rv = msLayerEncodeShapeAttributes(layer,shape);
414 if(rv != MS_SUCCESS)
415 return rv;
416 }
417
418 return rv;
419 }
420
421 /*
422 ** Returns the number of shapes that match the potential filter and extent.
423 * rectProjection is the projection in which rect is expressed, or can be NULL if
424 * rect should be considered in the layer projection.
425 * This should be equivalent to calling msLayerWhichShapes() and counting the
426 * number of shapes returned by msLayerNextShape(), honouring layer->maxfeatures
427 * limitation if layer->maxfeatures>=0, and honouring layer->startindex if
428 * layer->startindex >= 1 and paging is enabled.
429 * Returns -1 in case of failure.
430 */
msLayerGetShapeCount(layerObj * layer,rectObj rect,projectionObj * rectProjection)431 int msLayerGetShapeCount(layerObj *layer, rectObj rect, projectionObj *rectProjection)
432 {
433 int rv;
434
435 if( ! layer->vtable) {
436 rv = msInitializeVirtualTable(layer);
437 if(rv != MS_SUCCESS)
438 return -1;
439 }
440
441 return layer->vtable->LayerGetShapeCount(layer, rect, rectProjection);
442 }
443
444
445 /*
446 ** Closes resources used by a particular layer.
447 */
msLayerClose(layerObj * layer)448 void msLayerClose(layerObj *layer)
449 {
450 /* no need for items once the layer is closed */
451 msLayerFreeItemInfo(layer);
452 if(layer->items) {
453 msFreeCharArray(layer->items, layer->numitems);
454 layer->items = NULL;
455 layer->numitems = 0;
456 }
457
458 /* clear out items used as part of expressions (bug #2702) -- what about the layer filter? */
459 msLayerFreeExpressions(layer);
460
461 if (layer->vtable) {
462 layer->vtable->LayerClose(layer);
463 }
464 msLayerRestoreFromScaletokens(layer);
465 }
466
467 /*
468 ** Clear out items used as part of expressions.
469 */
msLayerFreeExpressions(layerObj * layer)470 void msLayerFreeExpressions(layerObj *layer)
471 {
472 int i,j,k;
473
474 msFreeExpressionTokens(&(layer->filter));
475 msFreeExpressionTokens(&(layer->cluster.group));
476 msFreeExpressionTokens(&(layer->cluster.filter));
477 for(i=0; i<layer->numclasses; i++) {
478 msFreeExpressionTokens(&(layer->class[i]->expression));
479 msFreeExpressionTokens(&(layer->class[i]->text));
480 for(j=0; j<layer->class[i]->numstyles; j++)
481 msFreeExpressionTokens(&(layer->class[i]->styles[j]->_geomtransform));
482 for(k=0; k<layer->class[i]->numlabels; k++) {
483 msFreeExpressionTokens(&(layer->class[i]->labels[k]->expression));
484 msFreeExpressionTokens(&(layer->class[i]->labels[k]->text));
485 }
486 }
487 }
488
489 /*
490 ** Retrieves a list of attributes available for this layer. Most sources also set the iteminfo array
491 ** at this point. This function is used when processing query results to expose attributes to query
492 ** templates. At that point all attributes are fair game.
493 */
msLayerGetItems(layerObj * layer)494 int msLayerGetItems(layerObj *layer)
495 {
496 const char *itemNames;
497 /* clean up any previously allocated instances */
498 msLayerFreeItemInfo(layer);
499 if(layer->items) {
500 msFreeCharArray(layer->items, layer->numitems);
501 layer->items = NULL;
502 layer->numitems = 0;
503 }
504
505 if ( ! layer->vtable) {
506 int rv = msInitializeVirtualTable(layer);
507 if (rv != MS_SUCCESS)
508 return rv;
509 }
510
511 /* At the end of switch case (default -> break; -> return MS_FAILURE),
512 * was following TODO ITEM:
513 */
514 /* TO DO! Need to add any joined itemd on to the core layer items, one long list! */
515 itemNames = msLayerGetProcessingKey( layer, "ITEMS" );
516 if (itemNames) {
517 layer->items = msStringSplit(itemNames, ',', &layer->numitems);
518 /* populate the iteminfo array */
519 return (msLayerInitItemInfo(layer));
520 } else
521 return layer->vtable->LayerGetItems(layer);
522 }
523
524 /*
525 ** Returns extent of spatial coverage for a layer.
526 **
527 ** If layer->extent is set then this value is used, otherwise the
528 ** driver-specific implementation is called (this can be expensive).
529 **
530 ** If layer is not already opened then it is opened and closed (so this
531 ** function can be called on both opened or closed layers).
532 **
533 ** Returns MS_SUCCESS/MS_FAILURE.
534 */
msLayerGetExtent(layerObj * layer,rectObj * extent)535 int msLayerGetExtent(layerObj *layer, rectObj *extent)
536 {
537 int need_to_close = MS_FALSE, status = MS_SUCCESS;
538
539 if (MS_VALID_EXTENT(layer->extent)) {
540 *extent = layer->extent;
541 return MS_SUCCESS;
542 }
543
544 if (!msLayerIsOpen(layer)) {
545 if (msLayerOpen(layer) != MS_SUCCESS)
546 return MS_FAILURE;
547 need_to_close = MS_TRUE;
548 }
549
550 if ( ! layer->vtable) {
551 int rv = msInitializeVirtualTable(layer);
552 if (rv != MS_SUCCESS) {
553 if (need_to_close)
554 msLayerClose(layer);
555 return rv;
556 }
557 }
558 status = layer->vtable->LayerGetExtent(layer, extent);
559
560 if (need_to_close)
561 msLayerClose(layer);
562
563 return(status);
564 }
565
msLayerGetItemIndex(layerObj * layer,char * item)566 int msLayerGetItemIndex(layerObj *layer, char *item)
567 {
568 int i;
569
570 for(i=0; i<layer->numitems; i++) {
571 if(strcasecmp(layer->items[i], item) == 0) return(i);
572 }
573
574 return -1; /* item not found */
575 }
576
string2list(char ** list,int * listsize,char * string)577 static int string2list(char **list, int *listsize, char *string)
578 {
579 int i;
580
581 for(i=0; i<(*listsize); i++) {
582 if(strcasecmp(list[i], string) == 0) {
583 /* printf("string2list (duplicate): %s %d\n", string, i); */
584 return(i);
585 }
586 }
587
588 list[i] = msStrdup(string);
589 (*listsize)++;
590
591 /* printf("string2list: %s %d\n", string, i); */
592
593 return(i);
594 }
595
596 extern int msyylex(void);
597 extern int msyylex_destroy(void);
598
599 extern int msyystate;
600 extern char *msyystring; /* string to tokenize */
601
602 extern double msyynumber; /* token containers */
603 extern char *msyystring_buffer;
604
msExpressionTokenToString(int token)605 const char *msExpressionTokenToString(int token) {
606 switch(token) {
607 case '(': return "(";
608 case ')': return ")";
609 case ',': return ",";
610 case '+': return "+";
611 case '-': return "-";
612 case '/': return "/";
613 case '*': return "*";
614 case '%': return "%";
615
616 case MS_TOKEN_LOGICAL_AND: return " and ";
617 case MS_TOKEN_LOGICAL_OR: return " or ";
618 case MS_TOKEN_LOGICAL_NOT: return " not ";
619
620 case MS_TOKEN_COMPARISON_EQ: return " = ";
621 case MS_TOKEN_COMPARISON_NE: return " != ";
622 case MS_TOKEN_COMPARISON_GT: return " > ";
623 case MS_TOKEN_COMPARISON_GE: return " >= ";
624 case MS_TOKEN_COMPARISON_LT: return " < ";
625 case MS_TOKEN_COMPARISON_LE: return " <= ";
626 case MS_TOKEN_COMPARISON_IEQ: return "";
627 case MS_TOKEN_COMPARISON_RE: return " ~ ";
628 case MS_TOKEN_COMPARISON_IRE: return " ~* ";
629 case MS_TOKEN_COMPARISON_IN: return " in ";
630 case MS_TOKEN_COMPARISON_LIKE: return " like ";
631
632 case MS_TOKEN_COMPARISON_INTERSECTS: return "intersects";
633 case MS_TOKEN_COMPARISON_DISJOINT: return "disjoint";
634 case MS_TOKEN_COMPARISON_TOUCHES: return "touches";
635 case MS_TOKEN_COMPARISON_OVERLAPS: return "overlaps";
636 case MS_TOKEN_COMPARISON_CROSSES: return "crosses";
637 case MS_TOKEN_COMPARISON_WITHIN: return "within";
638 case MS_TOKEN_COMPARISON_CONTAINS: return "contains";
639 case MS_TOKEN_COMPARISON_EQUALS: return "equals";
640 case MS_TOKEN_COMPARISON_BEYOND: return "beyond";
641 case MS_TOKEN_COMPARISON_DWITHIN: return "dwithin";
642
643 case MS_TOKEN_FUNCTION_LENGTH: return "length";
644 case MS_TOKEN_FUNCTION_TOSTRING: return "tostring";
645 case MS_TOKEN_FUNCTION_COMMIFY: return "commify";
646 case MS_TOKEN_FUNCTION_AREA: return "area";
647 case MS_TOKEN_FUNCTION_ROUND: return "round";
648 case MS_TOKEN_FUNCTION_BUFFER: return "buffer";
649 case MS_TOKEN_FUNCTION_DIFFERENCE: return "difference";
650 case MS_TOKEN_FUNCTION_SIMPLIFY: return "simplify";
651 // case MS_TOKEN_FUNCTION_SIMPLIFYPT:
652 case MS_TOKEN_FUNCTION_GENERALIZE: return "generalize";
653 default: return NULL;
654 }
655 }
656
msTokenizeExpression(expressionObj * expression,char ** list,int * listsize)657 int msTokenizeExpression(expressionObj *expression, char **list, int *listsize)
658 {
659 tokenListNodeObjPtr node;
660 int token;
661
662 /* TODO: make sure the constants can't somehow reference invalid expression types */
663 /* if(expression->type != MS_EXPRESSION && expression->type != MS_GEOMTRANSFORM_EXPRESSION) return MS_SUCCESS; */
664
665 msAcquireLock(TLOCK_PARSER);
666 msyystate = MS_TOKENIZE_EXPRESSION;
667 msyystring = expression->string; /* the thing we're tokenizing */
668
669 while((token = msyylex()) != 0) { /* keep processing tokens until the end of the string (\0) */
670
671 if((node = (tokenListNodeObjPtr) malloc(sizeof(tokenListNodeObj))) == NULL) {
672 msSetError(MS_MEMERR, NULL, "msTokenizeExpression()");
673 goto parse_error;
674 }
675
676 node->tokensrc = NULL;
677
678 node->tailifhead = NULL;
679 node->next = NULL;
680
681 switch(token) {
682 case MS_TOKEN_LITERAL_BOOLEAN:
683 case MS_TOKEN_LITERAL_NUMBER:
684 node->token = token;
685 node->tokenval.dblval = msyynumber;
686 break;
687 case MS_TOKEN_LITERAL_STRING:
688 node->token = token;
689 node->tokenval.strval = msStrdup(msyystring_buffer);
690 break;
691 case MS_TOKEN_LITERAL_TIME:
692 node->tokensrc = msStrdup(msyystring_buffer);
693 node->token = token;
694 msTimeInit(&(node->tokenval.tmval));
695 if(msParseTime(msyystring_buffer, &(node->tokenval.tmval)) != MS_TRUE) {
696 msSetError(MS_PARSEERR, "Parsing time value failed.", "msTokenizeExpression()");
697 free(node);
698 goto parse_error;
699 }
700 break;
701 case MS_TOKEN_BINDING_DOUBLE: /* we've encountered an attribute (binding) reference */
702 case MS_TOKEN_BINDING_INTEGER:
703 case MS_TOKEN_BINDING_STRING:
704 case MS_TOKEN_BINDING_TIME:
705 node->token = token; /* binding type */
706 node->tokenval.bindval.item = msStrdup(msyystring_buffer);
707 if(list) node->tokenval.bindval.index = string2list(list, listsize, msyystring_buffer);
708 break;
709 case MS_TOKEN_BINDING_SHAPE:
710 node->token = token;
711 break;
712 case MS_TOKEN_BINDING_MAP_CELLSIZE:
713 node->token = token;
714 break;
715 case MS_TOKEN_BINDING_DATA_CELLSIZE:
716 node->token = token;
717 break;
718 case MS_TOKEN_FUNCTION_FROMTEXT: /* we want to process a shape from WKT once and not for every feature being evaluated */
719 if((token = msyylex()) != 40) { /* ( */
720 msSetError(MS_PARSEERR, "Parsing fromText function failed.", "msTokenizeExpression()");
721 free(node);
722 goto parse_error;
723 }
724
725 if((token = msyylex()) != MS_TOKEN_LITERAL_STRING) {
726 msSetError(MS_PARSEERR, "Parsing fromText function failed.", "msTokenizeExpression()");
727 free(node);
728 goto parse_error;
729 }
730
731 node->token = MS_TOKEN_LITERAL_SHAPE;
732 node->tokenval.shpval = msShapeFromWKT(msyystring_buffer);
733
734 if(!node->tokenval.shpval) {
735 msSetError(MS_PARSEERR, "Parsing fromText function failed, WKT processing failed.", "msTokenizeExpression()");
736 free(node);
737 goto parse_error;
738 }
739
740 /* todo: perhaps process optional args (e.g. projection) */
741
742 if((token = msyylex()) != 41) { /* ) */
743 msSetError(MS_PARSEERR, "Parsing fromText function failed.", "msTokenizeExpression()");
744 msFreeShape(node->tokenval.shpval);
745 free(node->tokenval.shpval);
746 free(node);
747 goto parse_error;
748 }
749 break;
750 default:
751 node->token = token; /* for everything else */
752 break;
753 }
754
755 /* add node to token list */
756 if(expression->tokens == NULL) {
757 expression->tokens = node;
758 } else {
759 if(expression->tokens->tailifhead != NULL) /* this should never be NULL, but just in case */
760 expression->tokens->tailifhead->next = node; /* put the node at the end of the list */
761 }
762
763 /* repoint the head of the list to the end - our new element
764 this causes a loop if we are at the head, be careful not to
765 walk in a loop */
766 expression->tokens->tailifhead = node;
767 }
768
769 expression->curtoken = expression->tokens; /* point at the first token */
770
771 msReleaseLock(TLOCK_PARSER);
772 return MS_SUCCESS;
773
774 parse_error:
775 msReleaseLock(TLOCK_PARSER);
776 return MS_FAILURE;
777 }
778
779 /*
780 ** This function builds a list of items necessary to draw or query a particular layer by
781 ** examining the contents of the various xxxxitem parameters and expressions. That list is
782 ** then used to set the iteminfo variable.
783 */
msLayerWhichItems(layerObj * layer,int get_all,const char * metadata)784 int msLayerWhichItems(layerObj *layer, int get_all, const char *metadata)
785 {
786 int i, j, k, l, rv;
787 int nt=0;
788
789 if (!layer->vtable) {
790 rv = msInitializeVirtualTable(layer);
791 if (rv != MS_SUCCESS) return rv;
792 }
793
794 /* Cleanup any previous item selection */
795 msLayerFreeItemInfo(layer);
796 if(layer->items) {
797 msFreeCharArray(layer->items, layer->numitems);
798 layer->items = NULL;
799 layer->numitems = 0;
800 }
801
802 /*
803 ** need a count of potential items/attributes needed
804 */
805
806 /* layer level counts */
807 layer->classitemindex = -1;
808 layer->filteritemindex = -1;
809 layer->styleitemindex = -1;
810 layer->labelitemindex = -1;
811 layer->utfitemindex = -1;
812
813 if(layer->classitem) nt++;
814 if(layer->filteritem) nt++;
815 if(layer->styleitem &&
816 (strcasecmp(layer->styleitem, "AUTO") != 0) &&
817 (strncasecmp(layer->styleitem, "javascript://", 13) != 0)) nt++;
818
819 if(layer->filter.type == MS_EXPRESSION)
820 nt += msCountChars(layer->filter.string, '[');
821
822 if(layer->cluster.group.type == MS_EXPRESSION)
823 nt += msCountChars(layer->cluster.group.string, '[');
824
825 if(layer->cluster.filter.type == MS_EXPRESSION)
826 nt += msCountChars(layer->cluster.filter.string, '[');
827
828 if(layer->labelitem) nt++;
829 if(layer->utfitem) nt++;
830
831 if(layer->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION)
832 msTokenizeExpression(&layer->_geomtransform, layer->items, &(layer->numitems));
833
834 /* class level counts */
835 for(i=0; i<layer->numclasses; i++) {
836
837 for(j=0; j<layer->class[i]->numstyles; j++) {
838 if(layer->class[i]->styles[j]->rangeitem) nt++;
839 nt += layer->class[i]->styles[j]->numbindings;
840 if(layer->class[i]->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION)
841 nt += msCountChars(layer->class[i]->styles[j]->_geomtransform.string, '[');
842 for(k=0; k<MS_STYLE_BINDING_LENGTH; k++) {
843 if (layer->class[i]->styles[j]->exprBindings[k].type == MS_EXPRESSION)
844 {
845 nt += msCountChars(layer->class[i]->styles[j]->exprBindings[k].string, '[');
846 }
847 }
848 }
849
850 if(layer->class[i]->expression.type == MS_EXPRESSION)
851 nt += msCountChars(layer->class[i]->expression.string, '[');
852
853 for(l=0; l<layer->class[i]->numlabels; l++) {
854 nt += layer->class[i]->labels[l]->numbindings;
855 for(j=0; j<layer->class[i]->labels[l]->numstyles; j++) {
856 if(layer->class[i]->labels[l]->styles[j]->rangeitem) nt++;
857 nt += layer->class[i]->labels[l]->styles[j]->numbindings;
858 if(layer->class[i]->labels[l]->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION)
859 nt += msCountChars(layer->class[i]->labels[l]->styles[j]->_geomtransform.string, '[');
860 }
861 for(k=0; k<MS_LABEL_BINDING_LENGTH; k++) {
862 if (layer->class[i]->labels[l]->exprBindings[k].type == MS_EXPRESSION)
863 {
864 nt += msCountChars(layer->class[i]->labels[l]->exprBindings[k].string, '[');
865 }
866 }
867
868 if(layer->class[i]->labels[l]->expression.type == MS_EXPRESSION)
869 nt += msCountChars(layer->class[i]->labels[l]->expression.string, '[');
870 if(layer->class[i]->labels[l]->text.type == MS_EXPRESSION || (layer->class[i]->labels[l]->text.string && strchr(layer->class[i]->labels[l]->text.string,'[') != NULL && strchr(layer->class[i]->labels[l]->text.string,']') != NULL))
871 nt += msCountChars(layer->class[i]->labels[l]->text.string, '[');
872 }
873
874 if(layer->class[i]->text.type == MS_EXPRESSION || (layer->class[i]->text.string && strchr(layer->class[i]->text.string,'[') != NULL && strchr(layer->class[i]->text.string,']') != NULL))
875 nt += msCountChars(layer->class[i]->text.string, '[');
876 }
877
878 /* utfgrid count */
879 if(layer->utfdata.type == MS_EXPRESSION || (layer->utfdata.string && strchr(layer->utfdata.string,'[') != NULL && strchr(layer->utfdata.string,']') != NULL))
880 nt += msCountChars(layer->utfdata.string, '[');
881
882 /*
883 ** allocate space for the item list (worse case size)
884 */
885
886 /* always retrieve all items in some cases */
887 if(layer->connectiontype == MS_INLINE || get_all == MS_TRUE ||
888 (layer->map->outputformat && layer->map->outputformat->renderer == MS_RENDER_WITH_KML)) {
889 rv = msLayerGetItems(layer);
890 if(nt > 0) /* need to realloc the array to accept the possible new items*/
891 layer->items = (char **)msSmallRealloc(layer->items, sizeof(char *)*(layer->numitems + nt));
892 } else {
893 rv = layer->vtable->LayerCreateItems(layer, nt);
894 }
895 if(rv != MS_SUCCESS)
896 return rv;
897
898 /*
899 ** build layer item list, compute item indexes for explicity item references (e.g. classitem) or item bindings
900 */
901
902 /* layer items */
903 if(layer->classitem) layer->classitemindex = string2list(layer->items, &(layer->numitems), layer->classitem);
904 if(layer->filteritem) layer->filteritemindex = string2list(layer->items, &(layer->numitems), layer->filteritem);
905 if(layer->styleitem && (strcasecmp(layer->styleitem, "AUTO") != 0) && (strncasecmp(layer->styleitem, "javascript://",13) != 0))
906 layer->styleitemindex = string2list(layer->items, &(layer->numitems), layer->styleitem);
907 if(layer->labelitem) layer->labelitemindex = string2list(layer->items, &(layer->numitems), layer->labelitem);
908 if(layer->utfitem) layer->utfitemindex = string2list(layer->items, &(layer->numitems), layer->utfitem);
909
910 /* layer classes */
911 for(i=0; i<layer->numclasses; i++) {
912
913 if(layer->class[i]->expression.type == MS_EXPRESSION) /* class expression */
914 msTokenizeExpression(&(layer->class[i]->expression), layer->items, &(layer->numitems));
915
916 /* class styles (items, bindings, geomtransform) */
917 for(j=0; j<layer->class[i]->numstyles; j++) {
918 if(layer->class[i]->styles[j]->rangeitem)
919 layer->class[i]->styles[j]->rangeitemindex = string2list(layer->items, &(layer->numitems), layer->class[i]->styles[j]->rangeitem);
920 for(k=0; k<MS_STYLE_BINDING_LENGTH; k++) {
921 if(layer->class[i]->styles[j]->bindings[k].item)
922 layer->class[i]->styles[j]->bindings[k].index = string2list(layer->items, &(layer->numitems), layer->class[i]->styles[j]->bindings[k].item);
923 if (layer->class[i]->styles[j]->exprBindings[k].type == MS_EXPRESSION)
924 {
925 msTokenizeExpression(
926 &(layer->class[i]->styles[j]->exprBindings[k]),
927 layer->items, &(layer->numitems));
928 }
929 }
930 if(layer->class[i]->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION)
931 msTokenizeExpression(&(layer->class[i]->styles[j]->_geomtransform), layer->items, &(layer->numitems));
932 }
933
934 /* class labels and label styles (items, bindings, geomtransform) */
935 for(l=0; l<layer->class[i]->numlabels; l++) {
936 for(j=0; j<layer->class[i]->labels[l]->numstyles; j++) {
937 if(layer->class[i]->labels[l]->styles[j]->rangeitem)
938 layer->class[i]->labels[l]->styles[j]->rangeitemindex = string2list(layer->items, &(layer->numitems), layer->class[i]->labels[l]->styles[j]->rangeitem);
939 for(k=0; k<MS_STYLE_BINDING_LENGTH; k++) {
940 if(layer->class[i]->labels[l]->styles[j]->bindings[k].item)
941 layer->class[i]->labels[l]->styles[j]->bindings[k].index = string2list(layer->items, &(layer->numitems), layer->class[i]->labels[l]->styles[j]->bindings[k].item);
942 if(layer->class[i]->labels[l]->styles[j]->_geomtransform.type == MS_GEOMTRANSFORM_EXPRESSION)
943 msTokenizeExpression(&(layer->class[i]->labels[l]->styles[j]->_geomtransform), layer->items, &(layer->numitems));
944 }
945 }
946 for(k=0; k<MS_LABEL_BINDING_LENGTH; k++) {
947 if(layer->class[i]->labels[l]->bindings[k].item)
948 layer->class[i]->labels[l]->bindings[k].index = string2list(layer->items, &(layer->numitems), layer->class[i]->labels[l]->bindings[k].item);
949 if (layer->class[i]->labels[l]->exprBindings[k].type == MS_EXPRESSION)
950 {
951 msTokenizeExpression(
952 &(layer->class[i]->labels[l]->exprBindings[k]),
953 layer->items, &(layer->numitems));
954 }
955 }
956
957 /* label expression */
958 if(layer->class[i]->labels[l]->expression.type == MS_EXPRESSION) msTokenizeExpression(&(layer->class[i]->labels[l]->expression), layer->items, &(layer->numitems));
959
960 /* label text */
961 if(layer->class[i]->labels[l]->text.type == MS_EXPRESSION || (layer->class[i]->labels[l]->text.string && strchr(layer->class[i]->labels[l]->text.string,'[') != NULL && strchr(layer->class[i]->labels[l]->text.string,']') != NULL))
962 msTokenizeExpression(&(layer->class[i]->labels[l]->text), layer->items, &(layer->numitems));
963 }
964
965 /* class text */
966 if(layer->class[i]->text.type == MS_EXPRESSION || (layer->class[i]->text.string && strchr(layer->class[i]->text.string,'[') != NULL && strchr(layer->class[i]->text.string,']') != NULL))
967 msTokenizeExpression(&(layer->class[i]->text), layer->items, &(layer->numitems));
968 }
969
970 /* layer filter */
971 if(layer->filter.type == MS_EXPRESSION) msTokenizeExpression(&(layer->filter), layer->items, &(layer->numitems));
972
973 /* cluster expressions */
974 if(layer->cluster.group.type == MS_EXPRESSION) msTokenizeExpression(&(layer->cluster.group), layer->items, &(layer->numitems));
975 if(layer->cluster.filter.type == MS_EXPRESSION) msTokenizeExpression(&(layer->cluster.filter), layer->items, &(layer->numitems));
976
977 /* utfdata */
978 if(layer->utfdata.type == MS_EXPRESSION || (layer->utfdata.string && strchr(layer->utfdata.string,'[') != NULL && strchr(layer->utfdata.string,']') != NULL)) {
979 msTokenizeExpression(&(layer->utfdata), layer->items, &(layer->numitems));
980 }
981
982 if(metadata) {
983 char **tokens;
984 int n = 0;
985 int j;
986 int bFound = 0;
987
988 tokens = msStringSplit(metadata, ',', &n);
989 if(tokens) {
990 for(i=0; i<n; i++) {
991 bFound = 0;
992 for(j=0; j<layer->numitems; j++) {
993 if(strcmp(tokens[i], layer->items[j]) == 0) {
994 bFound = 1;
995 break;
996 }
997 }
998
999 if(!bFound) {
1000 layer->numitems++;
1001 layer->items = (char **)msSmallRealloc(layer->items, sizeof(char *)*(layer->numitems));
1002 layer->items[layer->numitems-1] = msStrdup(tokens[i]);
1003 }
1004 }
1005 msFreeCharArray(tokens, n);
1006 }
1007 }
1008
1009 /* populate the iteminfo array */
1010 if(layer->numitems == 0)
1011 return(MS_SUCCESS);
1012
1013 return(msLayerInitItemInfo(layer));
1014 }
1015
1016 /*
1017 ** A helper function to set the items to be retrieved with a particular shape. Unused at the moment but will be used
1018 ** from within MapScript. Should not need modification.
1019 */
msLayerSetItems(layerObj * layer,char ** items,int numitems)1020 int msLayerSetItems(layerObj *layer, char **items, int numitems)
1021 {
1022 int i;
1023 /* Cleanup any previous item selection */
1024 msLayerFreeItemInfo(layer);
1025 if(layer->items) {
1026 msFreeCharArray(layer->items, layer->numitems);
1027 layer->items = NULL;
1028 layer->numitems = 0;
1029 }
1030
1031 /* now allocate and set the layer item parameters */
1032 layer->items = (char **)malloc(sizeof(char *)*numitems);
1033 MS_CHECK_ALLOC(layer->items, sizeof(char *)*numitems, MS_FAILURE);
1034
1035 for(i=0; i<numitems; i++)
1036 layer->items[i] = msStrdup(items[i]);
1037 layer->numitems = numitems;
1038
1039 /* populate the iteminfo array */
1040 return(msLayerInitItemInfo(layer));
1041
1042 return(MS_SUCCESS);
1043 }
1044
1045 /*
1046 ** Fills a classObj with style info from the specified shape. This is used
1047 ** with STYLEITEM AUTO when rendering shapes.
1048 ** For optimal results, this should be called immediately after
1049 ** GetNextShape() or GetShape() so that the shape doesn't have to be read
1050 ** twice.
1051 **
1052 */
msLayerGetAutoStyle(mapObj * map,layerObj * layer,classObj * c,shapeObj * shape)1053 int msLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c, shapeObj* shape)
1054 {
1055 if ( ! layer->vtable) {
1056 int rv = msInitializeVirtualTable(layer);
1057 if (rv != MS_SUCCESS)
1058 return rv;
1059 }
1060 return layer->vtable->LayerGetAutoStyle(map, layer, c, shape);
1061 }
1062
1063 /*
1064 ** Fills a classObj with style info from the specified attribute. This is used
1065 ** with STYLEITEM "attribute" when rendering shapes.
1066 **
1067 */
msLayerGetFeatureStyle(mapObj * map,layerObj * layer,classObj * c,shapeObj * shape)1068 int msLayerGetFeatureStyle(mapObj *map, layerObj *layer, classObj *c, shapeObj* shape)
1069 {
1070 char* stylestring = NULL;
1071 if (layer->styleitem && layer->styleitemindex >=0) {
1072 stylestring = msStrdup(shape->values[layer->styleitemindex]);
1073 }
1074 else if (strncasecmp(layer->styleitem,"javascript://",13) == 0) {
1075 #ifdef USE_V8_MAPSCRIPT
1076 char *filename = layer->styleitem+13;
1077
1078 if (!map->v8context) {
1079 msV8CreateContext(map);
1080 if (!map->v8context)
1081 {
1082 msSetError(MS_V8ERR, "Unable to create v8 context.", "msLayerGetFeatureStyle()");
1083 return MS_FAILURE;
1084 }
1085 }
1086
1087 if (*filename == '\0') {
1088 msSetError(MS_V8ERR, "Invalid javascript filename: \"%s\".", "msLayerGetFeatureStyle()", layer->styleitem);
1089 return MS_FAILURE;
1090 }
1091
1092 stylestring = msV8GetFeatureStyle(map, filename, layer, shape);
1093 #else
1094 msSetError(MS_V8ERR, "V8 Javascript support is not available.", "msLayerGetFeatureStyle()");
1095 return MS_FAILURE;
1096 #endif
1097 }
1098 else { /* unknown styleitem */
1099 return MS_FAILURE;
1100 }
1101
1102 /* try to find out the current style format */
1103 if (!stylestring)
1104 return MS_FAILURE;
1105
1106 if (strncasecmp(stylestring,"style",5) == 0) {
1107 resetClassStyle(c);
1108 c->layer = layer;
1109 if (msMaybeAllocateClassStyle(c, 0)) {
1110 free(stylestring);
1111 return(MS_FAILURE);
1112 }
1113
1114 msUpdateStyleFromString(c->styles[0], stylestring, MS_FALSE);
1115 if(c->styles[0]->symbolname) {
1116 if((c->styles[0]->symbol = msGetSymbolIndex(&(map->symbolset), c->styles[0]->symbolname, MS_TRUE)) == -1) {
1117 msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class of layer %s.", "msLayerGetFeatureStyle()",
1118 c->styles[0]->symbolname, layer->name);
1119 free(stylestring);
1120 return MS_FAILURE;
1121 }
1122 }
1123 } else if (strncasecmp(stylestring,"class",5) == 0) {
1124 if (strcasestr(stylestring, " style ") != NULL) {
1125 /* reset style if stylestring contains style definitions */
1126 resetClassStyle(c);
1127 c->layer = layer;
1128 }
1129 msUpdateClassFromString(c, stylestring, MS_FALSE);
1130 } else if (strncasecmp(stylestring,"pen",3) == 0 || strncasecmp(stylestring,"brush",5) == 0 ||
1131 strncasecmp(stylestring,"symbol",6) == 0 || strncasecmp(stylestring,"label",5) == 0) {
1132 msOGRUpdateStyleFromString(map, layer, c, stylestring);
1133 } else {
1134 resetClassStyle(c);
1135 }
1136
1137 free(stylestring);
1138 return MS_SUCCESS;
1139 }
1140
1141
1142 /*
1143 Returns the number of inline feature of a layer
1144 */
msLayerGetNumFeatures(layerObj * layer)1145 int msLayerGetNumFeatures(layerObj *layer)
1146 {
1147 int need_to_close = MS_FALSE, result = -1;
1148
1149 if (!msLayerIsOpen(layer)) {
1150 if (msLayerOpen(layer) != MS_SUCCESS)
1151 return result;
1152 need_to_close = MS_TRUE;
1153 }
1154
1155 if (!layer->vtable) {
1156 int rv = msInitializeVirtualTable(layer);
1157 if (rv != MS_SUCCESS)
1158 return result;
1159 }
1160
1161 result = layer->vtable->LayerGetNumFeatures(layer);
1162
1163 if (need_to_close)
1164 msLayerClose(layer);
1165
1166 return(result);
1167 }
1168
1169 void
msLayerSetProcessingKey(layerObj * layer,const char * key,const char * value)1170 msLayerSetProcessingKey( layerObj *layer, const char *key, const char *value)
1171
1172 {
1173 int len = strlen(key);
1174 int i;
1175 char *directive = NULL;
1176
1177 if( value != NULL ) {
1178 directive = (char *) msSmallMalloc(strlen(key)+strlen(value)+2);
1179 sprintf( directive, "%s=%s", key, value );
1180 }
1181
1182 for( i = 0; i < layer->numprocessing; i++ ) {
1183 if( strncasecmp( key, layer->processing[i], len ) == 0
1184 && layer->processing[i][len] == '=' ) {
1185 free( layer->processing[i] );
1186
1187 /*
1188 ** Either replace the existing entry with a new one or
1189 ** clear the entry.
1190 */
1191 if( directive != NULL )
1192 layer->processing[i] = directive;
1193 else {
1194 layer->processing[i] = layer->processing[layer->numprocessing-1];
1195 layer->processing[layer->numprocessing-1] = NULL;
1196 layer->numprocessing--;
1197 }
1198 return;
1199 }
1200 }
1201
1202 /* otherwise add the directive at the end. */
1203
1204 if( directive != NULL ) {
1205 msLayerAddProcessing( layer, directive );
1206 free( directive );
1207 }
1208 }
1209
msLayerSubstituteProcessing(layerObj * layer,const char * from,const char * to)1210 void msLayerSubstituteProcessing( layerObj *layer, const char *from, const char *to ) {
1211 int i;
1212 for( i = 0; i < layer->numprocessing; i++ ) {
1213 layer->processing[i] = msCaseReplaceSubstring(layer->processing[i], from, to);
1214 }
1215 }
1216
msLayerAddProcessing(layerObj * layer,const char * directive)1217 void msLayerAddProcessing( layerObj *layer, const char *directive )
1218
1219 {
1220 layer->numprocessing++;
1221 if( layer->numprocessing == 1 )
1222 layer->processing = (char **) msSmallMalloc(2*sizeof(char *));
1223 else
1224 layer->processing = (char **) msSmallRealloc(layer->processing, sizeof(char*) * (layer->numprocessing+1) );
1225 layer->processing[layer->numprocessing-1] = msStrdup(directive);
1226 layer->processing[layer->numprocessing] = NULL;
1227 }
1228
msLayerGetProcessing(layerObj * layer,int proc_index)1229 char *msLayerGetProcessing( layerObj *layer, int proc_index)
1230 {
1231 if (proc_index < 0 || proc_index >= layer->numprocessing) {
1232 msSetError(MS_CHILDERR, "Invalid processing index.", "msLayerGetProcessing()");
1233 return NULL;
1234 } else {
1235 return layer->processing[proc_index];
1236 }
1237 }
1238
msLayerGetProcessingKey(layerObj * layer,const char * key)1239 char *msLayerGetProcessingKey( layerObj *layer, const char *key )
1240 {
1241 int i, len = strlen(key);
1242
1243 for( i = 0; i < layer->numprocessing; i++ ) {
1244 if( strncasecmp(layer->processing[i],key,len) == 0
1245 && layer->processing[i][len] == '=' )
1246 return layer->processing[i] + len + 1;
1247 }
1248
1249 return NULL;
1250 }
1251
1252
1253 /************************************************************************/
1254 /* msLayerGetMaxFeaturesToDraw */
1255 /* */
1256 /* Check to see if maxfeaturestodraw is set as a metadata or an */
1257 /* output format option. Used for vector layers to limit the */
1258 /* number of fatures rendered. */
1259 /************************************************************************/
msLayerGetMaxFeaturesToDraw(layerObj * layer,outputFormatObj * format)1260 int msLayerGetMaxFeaturesToDraw(layerObj *layer, outputFormatObj *format)
1261 {
1262 int nMaxFeatures = -1;
1263 const char *pszTmp = NULL;
1264 if (layer) {
1265 nMaxFeatures = layer->maxfeatures;
1266 pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
1267 if (pszTmp)
1268 nMaxFeatures = atoi(pszTmp);
1269 else {
1270 pszTmp = msLookupHashTable(&layer->map->web.metadata, "maxfeaturestodraw");
1271 if (pszTmp)
1272 nMaxFeatures = atoi(pszTmp);
1273 }
1274 }
1275 if(format) {
1276 if (nMaxFeatures < 0)
1277 nMaxFeatures = atoi(msGetOutputFormatOption( format, "maxfeaturestodraw", "-1"));
1278 }
1279
1280 return nMaxFeatures;
1281
1282 }
msLayerClearProcessing(layerObj * layer)1283 int msLayerClearProcessing( layerObj *layer )
1284 {
1285 if (layer->numprocessing > 0) {
1286 msFreeCharArray( layer->processing, layer->numprocessing );
1287 layer->processing = NULL;
1288 layer->numprocessing = 0;
1289 }
1290 return layer->numprocessing;
1291 }
1292
1293
1294 int
makeTimeFilter(layerObj * lp,const char * timestring,const char * timefield,const int addtimebacktics)1295 makeTimeFilter(layerObj *lp,
1296 const char *timestring,
1297 const char *timefield,
1298 const int addtimebacktics)
1299 {
1300
1301 char **atimes, **tokens = NULL;
1302 int numtimes,i, ntmp = 0;
1303 char *pszBuffer = NULL;
1304 int bOnlyExistingFilter = 0;
1305
1306 if (!lp || !timestring || !timefield)
1307 return MS_FALSE;
1308
1309 /* parse the time string. We support dicrete times (eg 2004-09-21), */
1310 /* multiple times (2004-09-21, 2004-09-22, ...) */
1311 /* and range(s) (2004-09-21/2004-09-25, 2004-09-27/2004-09-29) */
1312
1313 if (strstr(timestring, ",") == NULL &&
1314 strstr(timestring, "/") == NULL) { /* discrete time */
1315 /*
1316 if(lp->filteritem) free(lp->filteritem);
1317 lp->filteritem = msStrdup(timefield);
1318 if (&lp->filter)
1319 msFreeExpression(&lp->filter);
1320 */
1321
1322 /* if the filter is set and it's a sting type, concatenate it with
1323 the time. If not just free it */
1324 if (lp->filter.string && lp->filter.type == MS_STRING) {
1325 pszBuffer = msStringConcatenate(pszBuffer, "((");
1326 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
1327 pszBuffer = msStringConcatenate(pszBuffer, ") and ");
1328 } else {
1329 msFreeExpression(&lp->filter);
1330 }
1331
1332 pszBuffer = msStringConcatenate(pszBuffer, "(");
1333 if (addtimebacktics)
1334 pszBuffer = msStringConcatenate(pszBuffer, "`");
1335
1336 if (addtimebacktics)
1337 pszBuffer = msStringConcatenate(pszBuffer, "[");
1338 pszBuffer = msStringConcatenate(pszBuffer, (char *)timefield);
1339 if (addtimebacktics)
1340 pszBuffer = msStringConcatenate(pszBuffer, "]");
1341 if (addtimebacktics)
1342 pszBuffer = msStringConcatenate(pszBuffer, "`");
1343
1344
1345 pszBuffer = msStringConcatenate(pszBuffer, " = ");
1346 if (addtimebacktics)
1347 pszBuffer = msStringConcatenate(pszBuffer, "`");
1348 else
1349 pszBuffer = msStringConcatenate(pszBuffer, "'");
1350
1351 pszBuffer = msStringConcatenate(pszBuffer, (char *)timestring);
1352 if (addtimebacktics)
1353 pszBuffer = msStringConcatenate(pszBuffer, "`");
1354 else
1355 pszBuffer = msStringConcatenate(pszBuffer, "'");
1356
1357 pszBuffer = msStringConcatenate(pszBuffer, ")");
1358
1359 /* if there was a filter, It was concatenate with an And ans should be closed*/
1360 if(lp->filter.string && lp->filter.type == MS_STRING) {
1361 pszBuffer = msStringConcatenate(pszBuffer, ")");
1362 }
1363
1364 msLoadExpressionString(&lp->filter, pszBuffer);
1365
1366 if (pszBuffer)
1367 msFree(pszBuffer);
1368
1369 return MS_TRUE;
1370 }
1371
1372 atimes = msStringSplit(timestring, ',', &numtimes);
1373 if (atimes == NULL || numtimes < 1) {
1374 msFreeCharArray(atimes,numtimes);
1375 return MS_FALSE;
1376 }
1377
1378 if (lp->filter.string && lp->filter.type == MS_STRING) {
1379 pszBuffer = msStringConcatenate(pszBuffer, "((");
1380 pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
1381 pszBuffer = msStringConcatenate(pszBuffer, ") and ");
1382 /*this flag is used to indicate that the buffer contains only the
1383 existing filter. It is set to 0 when time filter parts are
1384 added to the buffer */
1385 bOnlyExistingFilter = 1;
1386 } else
1387 msFreeExpression(&lp->filter);
1388
1389 /* check to see if we have ranges by parsing the first entry */
1390 tokens = msStringSplit(atimes[0], '/', &ntmp);
1391 if (ntmp == 2) { /* ranges */
1392 msFreeCharArray(tokens, ntmp);
1393 for (i=0; i<numtimes; i++) {
1394 tokens = msStringSplit(atimes[i], '/', &ntmp);
1395 if (ntmp == 2) {
1396 if (pszBuffer && strlen(pszBuffer) > 0 && bOnlyExistingFilter == 0)
1397 pszBuffer = msStringConcatenate(pszBuffer, " OR ");
1398 else
1399 pszBuffer = msStringConcatenate(pszBuffer, "(");
1400
1401 bOnlyExistingFilter = 0;
1402
1403 pszBuffer = msStringConcatenate(pszBuffer, "(");
1404 if (addtimebacktics)
1405 pszBuffer = msStringConcatenate(pszBuffer, "`");
1406
1407 if (addtimebacktics)
1408 pszBuffer = msStringConcatenate(pszBuffer, "[");
1409 pszBuffer = msStringConcatenate(pszBuffer, (char *)timefield);
1410 if (addtimebacktics)
1411 pszBuffer = msStringConcatenate(pszBuffer, "]");
1412
1413 if (addtimebacktics)
1414 pszBuffer = msStringConcatenate(pszBuffer, "`");
1415
1416 pszBuffer = msStringConcatenate(pszBuffer, " >= ");
1417 if (addtimebacktics)
1418 pszBuffer = msStringConcatenate(pszBuffer, "`");
1419 else
1420 pszBuffer = msStringConcatenate(pszBuffer, "'");
1421
1422 pszBuffer = msStringConcatenate(pszBuffer, tokens[0]);
1423 if (addtimebacktics)
1424 pszBuffer = msStringConcatenate(pszBuffer, "`");
1425 else
1426 pszBuffer = msStringConcatenate(pszBuffer, "'");
1427 pszBuffer = msStringConcatenate(pszBuffer, " AND ");
1428
1429 if (addtimebacktics)
1430 pszBuffer = msStringConcatenate(pszBuffer, "`");
1431
1432 if (addtimebacktics)
1433 pszBuffer = msStringConcatenate(pszBuffer, "[");
1434 pszBuffer = msStringConcatenate(pszBuffer, (char *)timefield);
1435 if (addtimebacktics)
1436 pszBuffer = msStringConcatenate(pszBuffer, "]");
1437 if (addtimebacktics)
1438 pszBuffer = msStringConcatenate(pszBuffer, "`");
1439
1440 pszBuffer = msStringConcatenate(pszBuffer, " <= ");
1441
1442 if (addtimebacktics)
1443 pszBuffer = msStringConcatenate(pszBuffer, "`");
1444 else
1445 pszBuffer = msStringConcatenate(pszBuffer, "'");
1446 pszBuffer = msStringConcatenate(pszBuffer, tokens[1]);
1447 if (addtimebacktics)
1448 pszBuffer = msStringConcatenate(pszBuffer, "`");
1449 else
1450 pszBuffer = msStringConcatenate(pszBuffer, "'");
1451 pszBuffer = msStringConcatenate(pszBuffer, ")");
1452 }
1453
1454 msFreeCharArray(tokens, ntmp);
1455 }
1456 if (pszBuffer && strlen(pszBuffer) > 0 && bOnlyExistingFilter == 0)
1457 pszBuffer = msStringConcatenate(pszBuffer, ")");
1458 } else if (ntmp == 1) { /* multiple times */
1459 msFreeCharArray(tokens, ntmp);
1460 pszBuffer = msStringConcatenate(pszBuffer, "(");
1461 for (i=0; i<numtimes; i++) {
1462 if (i > 0)
1463 pszBuffer = msStringConcatenate(pszBuffer, " OR ");
1464
1465 pszBuffer = msStringConcatenate(pszBuffer, "(");
1466 if (addtimebacktics)
1467 pszBuffer = msStringConcatenate(pszBuffer, "`");
1468
1469 if (addtimebacktics)
1470 pszBuffer = msStringConcatenate(pszBuffer, "[");
1471 pszBuffer = msStringConcatenate(pszBuffer, (char *)timefield);
1472 if (addtimebacktics)
1473 pszBuffer = msStringConcatenate(pszBuffer, "]");
1474
1475 if (addtimebacktics)
1476 pszBuffer = msStringConcatenate(pszBuffer, "`");
1477
1478 pszBuffer = msStringConcatenate(pszBuffer, " = ");
1479
1480 if (addtimebacktics)
1481 pszBuffer = msStringConcatenate(pszBuffer, "`");
1482 else
1483 pszBuffer = msStringConcatenate(pszBuffer, "'");
1484 pszBuffer = msStringConcatenate(pszBuffer, atimes[i]);
1485 if (addtimebacktics)
1486 pszBuffer = msStringConcatenate(pszBuffer, "`");
1487 else
1488 pszBuffer = msStringConcatenate(pszBuffer, "'");
1489 pszBuffer = msStringConcatenate(pszBuffer, ")");
1490 }
1491 pszBuffer = msStringConcatenate(pszBuffer, ")");
1492 } else {
1493 msFreeCharArray(tokens, ntmp);
1494 msFreeCharArray(atimes, numtimes);
1495 msFree(pszBuffer);
1496 return MS_FALSE;
1497 }
1498
1499 msFreeCharArray(atimes, numtimes);
1500
1501 /* load the string to the filter */
1502 if (pszBuffer && strlen(pszBuffer) > 0) {
1503 if(lp->filter.string && lp->filter.type == MS_STRING)
1504 pszBuffer = msStringConcatenate(pszBuffer, ")");
1505 /*
1506 if(lp->filteritem)
1507 free(lp->filteritem);
1508 lp->filteritem = msStrdup(timefield);
1509 */
1510
1511 msLoadExpressionString(&lp->filter, pszBuffer);
1512
1513 }
1514 msFree(pszBuffer);
1515 return MS_TRUE;
1516 }
1517
1518 /**
1519 set the filter parameter for a time filter
1520 **/
1521
msLayerSetTimeFilter(layerObj * lp,const char * timestring,const char * timefield)1522 int msLayerSetTimeFilter(layerObj *lp, const char *timestring,
1523 const char *timefield)
1524 {
1525 if ( ! lp->vtable) {
1526 int rv = msInitializeVirtualTable(lp);
1527 if (rv != MS_SUCCESS)
1528 return rv;
1529 }
1530 return lp->vtable->LayerSetTimeFilter(lp, timestring, timefield);
1531 }
1532
1533 int
msLayerMakeBackticsTimeFilter(layerObj * lp,const char * timestring,const char * timefield)1534 msLayerMakeBackticsTimeFilter(layerObj *lp, const char *timestring,
1535 const char *timefield)
1536 {
1537 return makeTimeFilter(lp, timestring, timefield, MS_TRUE);
1538 }
1539
1540 int
msLayerMakePlainTimeFilter(layerObj * lp,const char * timestring,const char * timefield)1541 msLayerMakePlainTimeFilter(layerObj *lp, const char *timestring,
1542 const char *timefield)
1543 {
1544 return makeTimeFilter(lp, timestring, timefield, MS_FALSE);
1545 }
1546
1547
1548 /*
1549 * Dummies / default actions for layers
1550 */
LayerDefaultInitItemInfo(layerObj * layer)1551 int LayerDefaultInitItemInfo(layerObj *layer)
1552 {
1553 return MS_SUCCESS;
1554 }
1555
LayerDefaultFreeItemInfo(layerObj * layer)1556 void LayerDefaultFreeItemInfo(layerObj *layer)
1557 {
1558 return;
1559 }
1560
LayerDefaultOpen(layerObj * layer)1561 int LayerDefaultOpen(layerObj *layer)
1562 {
1563 return MS_FAILURE;
1564 }
1565
LayerDefaultIsOpen(layerObj * layer)1566 int LayerDefaultIsOpen(layerObj *layer)
1567 {
1568 return MS_FALSE;
1569 }
1570
LayerDefaultWhichShapes(layerObj * layer,rectObj rect,int isQuery)1571 int LayerDefaultWhichShapes(layerObj *layer, rectObj rect, int isQuery)
1572 {
1573 return MS_SUCCESS;
1574 }
1575
LayerDefaultNextShape(layerObj * layer,shapeObj * shape)1576 int LayerDefaultNextShape(layerObj *layer, shapeObj *shape)
1577 {
1578 return MS_FAILURE;
1579 }
1580
LayerDefaultGetShape(layerObj * layer,shapeObj * shape,resultObj * record)1581 int LayerDefaultGetShape(layerObj *layer, shapeObj *shape, resultObj *record)
1582 {
1583 return MS_FAILURE;
1584 }
1585
LayerDefaultGetShapeCount(layerObj * layer,rectObj rect,projectionObj * rectProjection)1586 int LayerDefaultGetShapeCount(layerObj *layer, rectObj rect, projectionObj *rectProjection)
1587 {
1588 int status;
1589 shapeObj shape, searchshape;
1590 int nShapeCount = 0;
1591 rectObj searchrect = rect;
1592 reprojectionObj* reprojector = NULL;
1593
1594 msInitShape(&searchshape);
1595 msRectToPolygon(searchrect, &searchshape);
1596
1597 if( rectProjection != NULL )
1598 {
1599 if(layer->project && msProjectionsDiffer(&(layer->projection), rectProjection))
1600 msProjectRect(rectProjection, &(layer->projection), &searchrect); /* project the searchrect to source coords */
1601 else
1602 layer->project = MS_FALSE;
1603 }
1604
1605 status = msLayerWhichShapes(layer, searchrect, MS_TRUE) ;
1606 if( status == MS_FAILURE )
1607 {
1608 msFreeShape(&searchshape);
1609 return -1;
1610 }
1611 else if( status == MS_DONE )
1612 {
1613 msFreeShape(&searchshape);
1614 return 0;
1615 }
1616
1617 msInitShape(&shape);
1618 while((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS)
1619 {
1620 if( rectProjection != NULL )
1621 {
1622 if(layer->project && msProjectionsDiffer(&(layer->projection), rectProjection))
1623 {
1624 if( reprojector == NULL )
1625 reprojector = msProjectCreateReprojector(&(layer->projection), rectProjection);
1626 if( reprojector )
1627 msProjectShapeEx(reprojector, &shape);
1628 }
1629 else
1630 layer->project = MS_FALSE;
1631
1632 if(msRectContained(&shape.bounds, &rect) == MS_TRUE) { /* if the whole shape is in, don't intersect */
1633 status = MS_TRUE;
1634 } else {
1635 switch(shape.type) { /* make sure shape actually intersects the qrect (ADD FUNCTIONS SPECIFIC TO RECTOBJ) */
1636 case MS_SHAPE_POINT:
1637 status = msIntersectMultipointPolygon(&shape, &searchshape);
1638 break;
1639 case MS_SHAPE_LINE:
1640 status = msIntersectPolylinePolygon(&shape, &searchshape);
1641 break;
1642 case MS_SHAPE_POLYGON:
1643 status = msIntersectPolygons(&shape, &searchshape);
1644 break;
1645 default:
1646 break;
1647 }
1648 }
1649 }
1650 else
1651 status = MS_TRUE;
1652
1653 if( status == MS_TRUE )
1654 nShapeCount++ ;
1655 msFreeShape(&shape);
1656 if(layer->maxfeatures > 0 && layer->maxfeatures == nShapeCount)
1657 break;
1658 }
1659
1660 msFreeShape(&searchshape);
1661 msProjectDestroyReprojector(reprojector);
1662
1663 return nShapeCount;
1664 }
1665
LayerDefaultClose(layerObj * layer)1666 int LayerDefaultClose(layerObj *layer)
1667 {
1668 return MS_SUCCESS;
1669 }
1670
LayerDefaultGetItems(layerObj * layer)1671 int LayerDefaultGetItems(layerObj *layer)
1672 {
1673 return MS_SUCCESS; /* returning no items is legit */
1674 }
1675
1676 int
msLayerApplyCondSQLFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)1677 msLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
1678 int iLayerIndex)
1679 {
1680 return FLTLayerApplyCondSQLFilterToLayer(psNode, map, iLayerIndex);
1681 }
1682
msLayerSupportsPaging(layerObj * layer)1683 int msLayerSupportsPaging(layerObj *layer)
1684 {
1685 if (layer &&
1686 ((layer->connectiontype == MS_ORACLESPATIAL) ||
1687 (layer->connectiontype == MS_POSTGIS)) )
1688 return MS_TRUE;
1689
1690 return MS_FALSE;
1691 }
1692
1693 int msLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex);
1694
1695 /*
1696 * msLayerSupportsSorting()
1697 *
1698 * Returns MS_TRUE if the layer supports sorting/ordering.
1699 */
msLayerSupportsSorting(layerObj * layer)1700 int msLayerSupportsSorting(layerObj *layer)
1701 {
1702 if (layer && (
1703 (layer->connectiontype == MS_OGR) || (layer->connectiontype == MS_POSTGIS) || (layer->connectiontype == MS_ORACLESPATIAL) || ((layer->connectiontype == MS_PLUGIN) && (strstr(layer->plugin_library,"msplugin_oracle") != NULL)) || ((layer->connectiontype == MS_PLUGIN) && (strstr(layer->plugin_library,"msplugin_mssql2008") != NULL))
1704 )
1705 )
1706 return MS_TRUE;
1707
1708 return MS_FALSE;
1709 }
1710
1711 /*
1712 * msLayerSetSort()
1713 *
1714 * Copy the sortBy clause passed as an argument into the layer sortBy member.
1715 */
msLayerSetSort(layerObj * layer,const sortByClause * sortBy)1716 void msLayerSetSort(layerObj *layer, const sortByClause* sortBy)
1717 {
1718 int i;
1719 for(i=0;i<layer->sortBy.nProperties;i++)
1720 msFree(layer->sortBy.properties[i].item);
1721 msFree(layer->sortBy.properties);
1722
1723 layer->sortBy.nProperties = sortBy->nProperties;
1724 layer->sortBy.properties = (sortByProperties*) msSmallMalloc(sortBy->nProperties * sizeof(sortByProperties));
1725 for(i=0;i<layer->sortBy.nProperties;i++) {
1726 layer->sortBy.properties[i].item = msStrdup(sortBy->properties[i].item);
1727 layer->sortBy.properties[i].sortOrder = sortBy->properties[i].sortOrder;
1728 }
1729 }
1730
1731 /*
1732 * msLayerBuildSQLOrderBy()
1733 *
1734 * Returns the content of a SQL ORDER BY clause from the sortBy member of
1735 * the layer. The string does not contain the "ORDER BY" keywords itself.
1736 */
msLayerBuildSQLOrderBy(layerObj * layer)1737 char* msLayerBuildSQLOrderBy(layerObj *layer)
1738 {
1739 char* strOrderBy = NULL;
1740 if( layer->sortBy.nProperties > 0 ) {
1741 int i;
1742 for(i=0;i<layer->sortBy.nProperties;i++) {
1743 char* escaped = msLayerEscapePropertyName(layer, layer->sortBy.properties[i].item);
1744 if( i > 0 )
1745 strOrderBy = msStringConcatenate(strOrderBy, ", ");
1746 strOrderBy = msStringConcatenate(strOrderBy, escaped);
1747 if( layer->sortBy.properties[i].sortOrder == SORT_DESC )
1748 strOrderBy = msStringConcatenate(strOrderBy, " DESC");
1749 msFree(escaped);
1750 }
1751 }
1752 return strOrderBy;
1753 }
1754
1755 int
msLayerApplyPlainFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)1756 msLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
1757 {
1758 return FLTLayerApplyPlainFilterToLayer(psNode, map, iLayerIndex);
1759 }
1760
msLayerGetPaging(layerObj * layer)1761 int msLayerGetPaging(layerObj *layer)
1762 {
1763 if ( ! layer->vtable) {
1764 int rv = msInitializeVirtualTable(layer);
1765 if (rv != MS_SUCCESS) {
1766 msSetError(MS_MISCERR, "Unable to initialize virtual table", "msLayerGetPaging()");
1767 return MS_FAILURE;
1768 }
1769 }
1770 return layer->vtable->LayerGetPaging(layer);
1771 }
1772
msLayerEnablePaging(layerObj * layer,int value)1773 void msLayerEnablePaging(layerObj *layer, int value)
1774 {
1775 if ( ! layer->vtable) {
1776 int rv = msInitializeVirtualTable(layer);
1777 if (rv != MS_SUCCESS) {
1778 msSetError(MS_MISCERR, "Unable to initialize virtual table", "msLayerEnablePaging()");
1779 return;
1780 }
1781 }
1782 layer->vtable->LayerEnablePaging(layer, value);
1783 }
1784
LayerDefaultGetExtent(layerObj * layer,rectObj * extent)1785 int LayerDefaultGetExtent(layerObj *layer, rectObj *extent)
1786 {
1787 return MS_FAILURE;
1788 }
1789
LayerDefaultGetAutoStyle(mapObj * map,layerObj * layer,classObj * c,shapeObj * shape)1790 int LayerDefaultGetAutoStyle(mapObj *map, layerObj *layer, classObj *c, shapeObj *shape)
1791 {
1792 msSetError(MS_MISCERR, "'STYLEITEM AUTO' not supported for this data source.", "msLayerGetAutoStyle()");
1793 return MS_FAILURE;
1794 }
1795
LayerDefaultCloseConnection(layerObj * layer)1796 int LayerDefaultCloseConnection(layerObj *layer)
1797 {
1798 return MS_SUCCESS;
1799 }
1800
LayerDefaultCreateItems(layerObj * layer,const int nt)1801 int LayerDefaultCreateItems(layerObj *layer, const int nt)
1802 {
1803 if (nt > 0) {
1804 layer->items = (char **)calloc(nt, sizeof(char *)); /* should be more than enough space */
1805 MS_CHECK_ALLOC(layer->items, sizeof(char *), MS_FAILURE);
1806
1807 layer->numitems = 0;
1808 }
1809 return MS_SUCCESS;
1810 }
1811
LayerDefaultGetNumFeatures(layerObj * layer)1812 int LayerDefaultGetNumFeatures(layerObj *layer)
1813 {
1814 rectObj extent;
1815 int status;
1816 int result;
1817 shapeObj shape;
1818
1819 /* calculate layer extent */
1820 if (!MS_VALID_EXTENT(layer->extent)) {
1821 if (msLayerGetExtent(layer, &extent) != MS_SUCCESS) {
1822 msSetError(MS_MISCERR, "Unable to get layer extent", "LayerDefaultGetNumFeatures()");
1823 return -1;
1824 }
1825 }
1826 else
1827 extent = layer->extent;
1828
1829 /* Cleanup any previous item selection */
1830 msLayerFreeItemInfo(layer);
1831 if (layer->items) {
1832 msFreeCharArray(layer->items, layer->numitems);
1833 layer->items = NULL;
1834 layer->numitems = 0;
1835 }
1836
1837 status = msLayerWhichShapes(layer, extent, MS_FALSE);
1838 if (status == MS_DONE) { /* no overlap */
1839 return 0;
1840 }
1841 else if (status != MS_SUCCESS) {
1842 return -1;
1843 }
1844
1845 result = 0;
1846 while ((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
1847 ++result;
1848 }
1849
1850 return result;
1851 }
1852
LayerDefaultAutoProjection(layerObj * layer,projectionObj * projection)1853 int LayerDefaultAutoProjection(layerObj *layer, projectionObj* projection)
1854 {
1855 msSetError(MS_MISCERR, "This data driver does not implement AUTO projection support", "LayerDefaultAutoProjection()");
1856 return MS_FAILURE;
1857 }
1858
LayerDefaultSupportsCommonFilters(layerObj * layer)1859 int LayerDefaultSupportsCommonFilters(layerObj *layer)
1860 {
1861 return MS_FALSE;
1862 }
1863
LayerDefaultTranslateFilter(layerObj * layer,expressionObj * filter,char * filteritem)1864 int LayerDefaultTranslateFilter(layerObj *layer, expressionObj *filter, char *filteritem)
1865 {
1866 if(!filter->string) return MS_SUCCESS; /* nothing to do, not an error */
1867
1868 msSetError(MS_MISCERR, "This data driver does not implement filter translation support", "LayerDefaultTranslateFilter()");
1869 return MS_FAILURE;
1870 }
1871
msLayerDefaultGetPaging(layerObj * layer)1872 int msLayerDefaultGetPaging(layerObj *layer)
1873 {
1874 return MS_FALSE;
1875 }
1876
msLayerDefaultEnablePaging(layerObj * layer,int value)1877 void msLayerDefaultEnablePaging(layerObj *layer, int value)
1878 {
1879 return;
1880 }
1881
1882 /************************************************************************/
1883 /* LayerDefaultEscapeSQLParam */
1884 /* */
1885 /* Default function used to escape strings and avoid sql */
1886 /* injection. Specific drivers should redefine if an escaping */
1887 /* function is available in the driver. */
1888 /************************************************************************/
LayerDefaultEscapeSQLParam(layerObj * layer,const char * pszString)1889 char *LayerDefaultEscapeSQLParam(layerObj *layer, const char* pszString)
1890 {
1891 char *pszEscapedStr=NULL;
1892 if (pszString) {
1893 int nSrcLen;
1894 char c;
1895 int i=0, j=0;
1896 nSrcLen = (int)strlen(pszString);
1897 pszEscapedStr = (char*) msSmallMalloc( 2 * nSrcLen + 1);
1898 for(i = 0, j = 0; i < nSrcLen; i++) {
1899 c = pszString[i];
1900 if (c == '\'') {
1901 pszEscapedStr[j++] = '\'';
1902 pszEscapedStr[j++] = '\'';
1903 } else if (c == '\\') {
1904 pszEscapedStr[j++] = '\\';
1905 pszEscapedStr[j++] = '\\';
1906 } else
1907 pszEscapedStr[j++] = c;
1908 }
1909 pszEscapedStr[j] = 0;
1910 }
1911 return pszEscapedStr;
1912 }
1913
1914 /************************************************************************/
1915 /* LayerDefaultEscapePropertyName */
1916 /* */
1917 /* Return the property name in a properly escaped and quoted form. */
1918 /************************************************************************/
LayerDefaultEscapePropertyName(layerObj * layer,const char * pszString)1919 char *LayerDefaultEscapePropertyName(layerObj *layer, const char* pszString)
1920 {
1921 char* pszEscapedStr=NULL;
1922 int i, j = 0;
1923
1924 if (layer && pszString && strlen(pszString) > 0) {
1925 int nLength = strlen(pszString);
1926
1927 pszEscapedStr = (char*) msSmallMalloc( 1 + 2 * nLength + 1 + 1);
1928 pszEscapedStr[j++] = '"';
1929
1930 for (i=0; i<nLength; i++) {
1931 char c = pszString[i];
1932 if (c == '"') {
1933 pszEscapedStr[j++] = '"';
1934 pszEscapedStr[j++] ='"';
1935 } else if (c == '\\') {
1936 pszEscapedStr[j++] = '\\';
1937 pszEscapedStr[j++] = '\\';
1938 } else
1939 pszEscapedStr[j++] = c;
1940 }
1941 pszEscapedStr[j++] = '"';
1942 pszEscapedStr[j++] = 0;
1943
1944 }
1945 return pszEscapedStr;
1946 }
1947
1948
1949 /*
1950 * msConnectLayer
1951 *
1952 * This will connect layer object to the new layer type.
1953 * Caller is responsible to close previous layer correctly.
1954 * For Internal types the library_str is ignored, for PLUGIN it's
1955 * define what plugin to use. Returns MS_FAILURE or MS_SUCCESS.
1956 */
msConnectLayer(layerObj * layer,const int connectiontype,const char * library_str)1957 int msConnectLayer(layerObj *layer,
1958 const int connectiontype,
1959 const char *library_str)
1960 {
1961 layer->connectiontype = connectiontype;
1962 /* For internal types, library_str is ignored */
1963 if (connectiontype == MS_PLUGIN) {
1964 int rv;
1965 msFree(layer->plugin_library);
1966 msFree(layer->plugin_library_original);
1967
1968 layer->plugin_library_original = msStrdup(library_str);
1969 rv = msBuildPluginLibraryPath(&layer->plugin_library,
1970 layer->plugin_library_original,
1971 layer->map);
1972 if (rv != MS_SUCCESS) {
1973 return rv;
1974 }
1975 }
1976 return msInitializeVirtualTable(layer) ;
1977 }
1978
populateVirtualTable(layerVTableObj * vtable)1979 static int populateVirtualTable(layerVTableObj *vtable)
1980 {
1981 assert(vtable != NULL);
1982
1983 vtable->LayerSupportsCommonFilters = LayerDefaultSupportsCommonFilters;
1984 vtable->LayerTranslateFilter = LayerDefaultTranslateFilter;
1985
1986 vtable->LayerInitItemInfo = LayerDefaultInitItemInfo;
1987 vtable->LayerFreeItemInfo = LayerDefaultFreeItemInfo;
1988 vtable->LayerOpen = LayerDefaultOpen;
1989 vtable->LayerIsOpen = LayerDefaultIsOpen;
1990 vtable->LayerWhichShapes = LayerDefaultWhichShapes;
1991
1992 vtable->LayerNextShape = LayerDefaultNextShape;
1993 /* vtable->LayerResultsGetShape = LayerDefaultResultsGetShape; */
1994 vtable->LayerGetShape = LayerDefaultGetShape;
1995 vtable->LayerGetShapeCount = LayerDefaultGetShapeCount;
1996 vtable->LayerClose = LayerDefaultClose;
1997 vtable->LayerGetItems = LayerDefaultGetItems;
1998 vtable->LayerGetExtent = LayerDefaultGetExtent;
1999
2000 vtable->LayerGetAutoStyle = LayerDefaultGetAutoStyle;
2001 vtable->LayerCloseConnection = LayerDefaultCloseConnection;
2002 vtable->LayerSetTimeFilter = msLayerMakePlainTimeFilter;
2003
2004 vtable->LayerApplyFilterToLayer = msLayerApplyPlainFilterToLayer;
2005
2006 vtable->LayerCreateItems = LayerDefaultCreateItems;
2007
2008 vtable->LayerGetNumFeatures = LayerDefaultGetNumFeatures;
2009
2010 vtable->LayerGetAutoProjection = LayerDefaultAutoProjection;
2011
2012 vtable->LayerEscapeSQLParam = LayerDefaultEscapeSQLParam;
2013
2014 vtable->LayerEscapePropertyName = LayerDefaultEscapePropertyName;
2015
2016 vtable->LayerEnablePaging = msLayerDefaultEnablePaging;
2017 vtable->LayerGetPaging = msLayerDefaultGetPaging;
2018
2019 return MS_SUCCESS;
2020 }
2021
createVirtualTable(layerVTableObj ** vtable)2022 static int createVirtualTable(layerVTableObj **vtable)
2023 {
2024 *vtable = malloc(sizeof(**vtable));
2025 MS_CHECK_ALLOC(*vtable, sizeof(**vtable), MS_FAILURE);
2026
2027 return populateVirtualTable(*vtable);
2028 }
2029
destroyVirtualTable(layerVTableObj ** vtable)2030 static int destroyVirtualTable(layerVTableObj **vtable)
2031 {
2032 memset(*vtable, 0, sizeof(**vtable));
2033 msFree(*vtable);
2034 *vtable = NULL;
2035 return MS_SUCCESS;
2036 }
2037
msInitializeVirtualTable(layerObj * layer)2038 int msInitializeVirtualTable(layerObj *layer)
2039 {
2040 if (layer->vtable) {
2041 destroyVirtualTable(&layer->vtable);
2042 }
2043 createVirtualTable(&layer->vtable);
2044
2045 if(layer->features && layer->connectiontype != MS_GRATICULE )
2046 layer->connectiontype = MS_INLINE;
2047
2048 if(layer->tileindex && layer->connectiontype == MS_SHAPEFILE)
2049 layer->connectiontype = MS_TILED_SHAPEFILE;
2050
2051 if(layer->type == MS_LAYER_RASTER && layer->connectiontype != MS_WMS
2052 && layer->connectiontype != MS_KERNELDENSITY)
2053 layer->connectiontype = MS_RASTER;
2054
2055 switch(layer->connectiontype) {
2056 case(MS_INLINE):
2057 return(msINLINELayerInitializeVirtualTable(layer));
2058 break;
2059 case(MS_SHAPEFILE):
2060 return(msSHPLayerInitializeVirtualTable(layer));
2061 break;
2062 case(MS_TILED_SHAPEFILE):
2063 return(msTiledSHPLayerInitializeVirtualTable(layer));
2064 break;
2065 case(MS_OGR):
2066 return(msOGRLayerInitializeVirtualTable(layer));
2067 break;
2068 case(MS_POSTGIS):
2069 return(msPostGISLayerInitializeVirtualTable(layer));
2070 break;
2071 case(MS_WMS):
2072 /* WMS should be treated as a raster layer */
2073 return(msRASTERLayerInitializeVirtualTable(layer));
2074 break;
2075 case(MS_KERNELDENSITY):
2076 /* KERNELDENSITY should be treated as a raster layer */
2077 return(msRASTERLayerInitializeVirtualTable(layer));
2078 break;
2079 case(MS_ORACLESPATIAL):
2080 return(msOracleSpatialLayerInitializeVirtualTable(layer));
2081 break;
2082 case(MS_WFS):
2083 return(msWFSLayerInitializeVirtualTable(layer));
2084 break;
2085 case(MS_GRATICULE):
2086 return(msGraticuleLayerInitializeVirtualTable(layer));
2087 break;
2088 case(MS_RASTER):
2089 return(msRASTERLayerInitializeVirtualTable(layer));
2090 break;
2091 case(MS_PLUGIN):
2092 return(msPluginLayerInitializeVirtualTable(layer));
2093 break;
2094 case(MS_UNION):
2095 return(msUnionLayerInitializeVirtualTable(layer));
2096 break;
2097 case(MS_UVRASTER):
2098 return(msUVRASTERLayerInitializeVirtualTable(layer));
2099 break;
2100 case(MS_CONTOUR):
2101 return(msContourLayerInitializeVirtualTable(layer));
2102 break;
2103 default:
2104 msSetError(MS_MISCERR, "Unknown connectiontype, it was %d", "msInitializeVirtualTable()", layer->connectiontype);
2105 return MS_FAILURE;
2106 break;
2107 }
2108
2109 /* not reached */
2110 return MS_FAILURE;
2111 }
2112
2113 /*
2114 * INLINE: Virtual table functions
2115 */
2116
2117 typedef struct {
2118 rectObj searchrect;
2119 int is_relative; /* relative coordinates? */
2120 }
2121 msINLINELayerInfo;
2122
msINLINELayerIsOpen(layerObj * layer)2123 int msINLINELayerIsOpen(layerObj *layer)
2124 {
2125 if (layer->layerinfo)
2126 return(MS_TRUE);
2127 else
2128 return(MS_FALSE);
2129 }
2130
msINLINECreateLayerInfo(void)2131 msINLINELayerInfo *msINLINECreateLayerInfo(void)
2132 {
2133 msINLINELayerInfo *layerinfo = msSmallMalloc(sizeof(msINLINELayerInfo));
2134 layerinfo->searchrect.minx = -1.0;
2135 layerinfo->searchrect.miny = -1.0;
2136 layerinfo->searchrect.maxx = -1.0;
2137 layerinfo->searchrect.maxy = -1.0;
2138 layerinfo->is_relative = MS_FALSE;
2139 return layerinfo;
2140 }
2141
msINLINELayerOpen(layerObj * layer)2142 int msINLINELayerOpen(layerObj *layer)
2143 {
2144 msINLINELayerInfo *layerinfo;
2145
2146 if (layer->layerinfo) {
2147 if (layer->debug) {
2148 msDebug("msINLINELayerOpen: Layer is already open!\n");
2149 }
2150 return MS_SUCCESS; /* already open */
2151 }
2152
2153 /*
2154 ** Initialize the layerinfo
2155 **/
2156 layerinfo = msINLINECreateLayerInfo();
2157
2158 layer->currentfeature = layer->features; /* point to the begining of the feature list */
2159
2160 layer->layerinfo = (void*)layerinfo;
2161
2162 return(MS_SUCCESS);
2163 }
2164
msINLINELayerClose(layerObj * layer)2165 int msINLINELayerClose(layerObj *layer)
2166 {
2167 if (layer->layerinfo) {
2168 free(layer->layerinfo);
2169 layer->layerinfo = NULL;
2170 }
2171
2172 return MS_SUCCESS;
2173 }
2174
msINLINELayerWhichShapes(layerObj * layer,rectObj rect,int isQuery)2175 int msINLINELayerWhichShapes(layerObj *layer, rectObj rect, int isQuery)
2176 {
2177 msINLINELayerInfo *layerinfo = NULL;
2178 layerinfo = (msINLINELayerInfo*) layer->layerinfo;
2179
2180 layerinfo->searchrect = rect;
2181 layerinfo->is_relative = (layer->transform != MS_FALSE && layer->transform != MS_TRUE);
2182
2183 return MS_SUCCESS;
2184 }
2185
2186 /* Author: Cristoph Spoerri and Sean Gillies */
msINLINELayerGetShape(layerObj * layer,shapeObj * shape,resultObj * record)2187 int msINLINELayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record)
2188 {
2189 int i=0;
2190 featureListNodeObjPtr current;
2191
2192 int shapeindex = record->shapeindex; /* only index necessary */
2193
2194 current = layer->features;
2195 while (current!=NULL && i!=shapeindex) {
2196 i++;
2197 current = current->next;
2198 }
2199 if (current == NULL) {
2200 msSetError(MS_SHPERR, "No inline feature with this index.", "msINLINELayerGetShape()");
2201 return MS_FAILURE;
2202 }
2203
2204 if (msCopyShape(&(current->shape), shape) != MS_SUCCESS) {
2205 msSetError(MS_SHPERR, "Cannot retrieve inline shape. There some problem with the shape", "msINLINELayerGetShape()");
2206 return MS_FAILURE;
2207 }
2208 /* check for the expected size of the values array */
2209 if (layer->numitems > shape->numvalues) {
2210 shape->values = (char **)msSmallRealloc(shape->values, sizeof(char *)*(layer->numitems));
2211 for (i = shape->numvalues; i < layer->numitems; i++)
2212 shape->values[i] = msStrdup("");
2213 }
2214 msComputeBounds(shape);
2215 return MS_SUCCESS;
2216 }
2217
msINLINELayerNextShape(layerObj * layer,shapeObj * shape)2218 int msINLINELayerNextShape(layerObj *layer, shapeObj *shape)
2219 {
2220 msINLINELayerInfo *layerinfo = NULL;
2221 shapeObj * s;
2222
2223 layerinfo = (msINLINELayerInfo*) layer->layerinfo;
2224
2225 while (1) {
2226
2227 if( ! (layer->currentfeature)) {
2228 /* out of features */
2229 return(MS_DONE);
2230 }
2231
2232 s = &(layer->currentfeature->shape);
2233 layer->currentfeature = layer->currentfeature->next;
2234 msComputeBounds(s);
2235
2236 if (layerinfo->is_relative || msRectOverlap(&s->bounds, &layerinfo->searchrect)) {
2237
2238 msCopyShape(s, shape);
2239
2240 /* check for the expected size of the values array */
2241 if (layer->numitems > shape->numvalues) {
2242 int i;
2243 shape->values = (char **)msSmallRealloc(shape->values, sizeof(char *)*(layer->numitems));
2244 for (i = shape->numvalues; i < layer->numitems; i++)
2245 shape->values[i] = msStrdup("");
2246 shape->numvalues = layer->numitems;
2247 }
2248
2249 break;
2250 }
2251
2252 }
2253
2254 return(MS_SUCCESS);
2255 }
2256
msINLINELayerGetNumFeatures(layerObj * layer)2257 int msINLINELayerGetNumFeatures(layerObj *layer)
2258 {
2259 int i = 0;
2260 featureListNodeObjPtr current;
2261
2262 current = layer->features;
2263 while (current != NULL) {
2264 i++;
2265 current = current->next;
2266 }
2267 return i;
2268 }
2269
2270
2271
2272 /*
2273 Returns an escaped string
2274 */
msLayerEscapeSQLParam(layerObj * layer,const char * pszString)2275 char *msLayerEscapeSQLParam(layerObj *layer, const char*pszString)
2276 {
2277 if ( ! layer->vtable) {
2278 int rv = msInitializeVirtualTable(layer);
2279 if (rv != MS_SUCCESS)
2280 return "";
2281 }
2282 return layer->vtable->LayerEscapeSQLParam(layer, pszString);
2283 }
2284
msLayerEscapePropertyName(layerObj * layer,const char * pszString)2285 char *msLayerEscapePropertyName(layerObj *layer, const char*pszString)
2286 {
2287 if ( ! layer->vtable) {
2288 int rv = msInitializeVirtualTable(layer);
2289 if (rv != MS_SUCCESS)
2290 return "";
2291 }
2292 return layer->vtable->LayerEscapePropertyName(layer, pszString);
2293 }
2294
2295
2296 int
msINLINELayerInitializeVirtualTable(layerObj * layer)2297 msINLINELayerInitializeVirtualTable(layerObj *layer)
2298 {
2299 assert(layer != NULL);
2300 assert(layer->vtable != NULL);
2301
2302 /* layer->vtable->LayerInitItemInfo, use default */
2303 /* layer->vtable->LayerFreeItemInfo, use default */
2304 layer->vtable->LayerOpen = msINLINELayerOpen;
2305 layer->vtable->LayerIsOpen = msINLINELayerIsOpen;
2306 layer->vtable->LayerWhichShapes = msINLINELayerWhichShapes;
2307 layer->vtable->LayerNextShape = msINLINELayerNextShape;
2308 /* layer->vtable->LayerGetShapeCount, use default */
2309 layer->vtable->LayerGetShape = msINLINELayerGetShape;
2310 layer->vtable->LayerClose = msINLINELayerClose;
2311 /* layer->vtable->LayerGetItems, use default */
2312
2313 /*
2314 * Original code contained following
2315 * TODO: need to compute extents
2316 */
2317 /* layer->vtable->LayerGetExtent, use default */
2318
2319 /* layer->vtable->LayerGetAutoStyle, use default */
2320 /* layer->vtable->LayerCloseConnection, use default */
2321 layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
2322
2323 /* layer->vtable->LayerApplyFilterToLayer, use default */
2324
2325 /* layer->vtable->LayerCreateItems, use default */
2326 layer->vtable->LayerGetNumFeatures = msINLINELayerGetNumFeatures;
2327
2328 /*layer->vtable->LayerEscapeSQLParam, use default*/
2329 /*layer->vtable->LayerEscapePropertyName, use default*/
2330
2331 /* layer->vtable->LayerEnablePaging, use default */
2332 /* layer->vtable->LayerGetPaging, use default */
2333
2334 return MS_SUCCESS;
2335 }
2336
2337
2338