1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  Various utility functions ... a real hodgepodge.
6  * Author:   Steve Lime and the MapServer team.
7  *
8  * Notes: Some code (notably msAlphaBlend()) are directly derived from GD. See
9  * the mapserver/GD-COPYING file for the GD license.  Use of this code in this
10  * manner is compatible with the MapServer license.
11  *
12  ******************************************************************************
13  * Copyright (c) 1996-2005 Regents of the University of Minnesota.
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included in
23  * all copies of this Software or works derived from this Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  *****************************************************************************/
33 
34 #include <time.h>
35 
36 #include "mapserver.h"
37 #include "maptime.h"
38 #include "mapthread.h"
39 #include "mapcopy.h"
40 #include "mapows.h"
41 
42 #include "gdal.h"
43 
44 #if defined(_WIN32) && !defined(__CYGWIN__)
45 # include <windows.h>
46 # include <tchar.h>
47 # include <fcntl.h>
48 # include <io.h>
49 #include <process.h>
50 #endif
51 
52 #ifdef USE_RSVG
53 #include <glib-object.h>
54 #endif
55 #ifdef USE_GEOS
56 #include <geos_c.h>
57 #endif
58 
59 
60 extern char *msyystring_buffer;
61 extern int msyylex_destroy(void);
62 extern int yyparse(parseObj *);
63 
msScaleInBounds(double scale,double minscale,double maxscale)64 int msScaleInBounds(double scale, double minscale, double maxscale)
65 {
66   if(scale > 0) {
67     if(maxscale != -1 && scale >= maxscale) return MS_FALSE;
68     if(minscale != -1 && scale < minscale) return MS_FALSE;
69   }
70   return MS_TRUE;
71 }
72 
73 /*
74 ** Helper functions to convert from strings to other types or objects.
75 */
bindIntegerAttribute(int * attribute,const char * value)76 static int bindIntegerAttribute(int *attribute, const char *value)
77 {
78   if(!value || strlen(value) == 0) return MS_FAILURE;
79   *attribute = MS_NINT(atof(value)); /*use atof instead of atoi as a fix for bug 2394*/
80   return MS_SUCCESS;
81 }
82 
bindDoubleAttribute(double * attribute,const char * value)83 static int bindDoubleAttribute(double *attribute, const char *value)
84 {
85   if(!value || strlen(value) == 0) return MS_FAILURE;
86   *attribute = atof(value);
87   return MS_SUCCESS;
88 }
89 
bindColorAttribute(colorObj * attribute,const char * value)90 static int bindColorAttribute(colorObj *attribute, const char *value)
91 {
92   int len;
93 
94   if(!value || ((len = strlen(value)) == 0)) return MS_FAILURE;
95 
96   if(value[0] == '#' && (len == 7 || len == 9)) { /* got a hex color */
97     char hex[2];
98 
99     hex[0] = value[1];
100     hex[1] = value[2];
101     attribute->red = msHexToInt(hex);
102     hex[0] = value[3];
103     hex[1] = value[4];
104     attribute->green = msHexToInt(hex);
105     hex[0] = value[5];
106     hex[1] = value[6];
107     attribute->blue = msHexToInt(hex);
108     if(len == 9) {
109       hex[0] = value[7];
110       hex[1] = value[8];
111       attribute->alpha = msHexToInt(hex);
112     }
113     return MS_SUCCESS;
114   } else { /* try a space delimited string */
115     char **tokens=NULL;
116     int numtokens=0;
117 
118     tokens = msStringSplit(value, ' ', &numtokens);
119     if(tokens==NULL || numtokens != 3) {
120       msFreeCharArray(tokens, numtokens);
121       return MS_FAILURE; /* punt */
122     }
123 
124     attribute->red = atoi(tokens[0]);
125     attribute->green = atoi(tokens[1]);
126     attribute->blue = atoi(tokens[2]);
127     msFreeCharArray(tokens, numtokens);
128 
129     return MS_SUCCESS;
130   }
131 
132   return MS_FAILURE; /* shouldn't get here */
133 }
134 
bindStyle(layerObj * layer,shapeObj * shape,styleObj * style,int drawmode)135 static void bindStyle(layerObj *layer, shapeObj *shape, styleObj *style, int drawmode)
136 {
137   int applyOpacity = MS_FALSE;
138   assert(MS_DRAW_FEATURES(drawmode));
139   if(style->numbindings > 0) {
140     applyOpacity = MS_TRUE;
141     if(style->bindings[MS_STYLE_BINDING_SYMBOL].index != -1) {
142       style->symbol = msGetSymbolIndex(&(layer->map->symbolset), shape->values[style->bindings[MS_STYLE_BINDING_SYMBOL].index], MS_TRUE);
143       if(style->symbol == -1) style->symbol = 0; /* a reasonable default (perhaps should throw an error?) */
144     }
145     if(style->bindings[MS_STYLE_BINDING_ANGLE].index != -1) {
146       style->angle = 360.0;
147       bindDoubleAttribute(&style->angle, shape->values[style->bindings[MS_STYLE_BINDING_ANGLE].index]);
148     }
149     if(style->bindings[MS_STYLE_BINDING_SIZE].index != -1) {
150       style->size = 1;
151       bindDoubleAttribute(&style->size, shape->values[style->bindings[MS_STYLE_BINDING_SIZE].index]);
152     }
153     if(style->bindings[MS_STYLE_BINDING_WIDTH].index != -1) {
154       style->width = 1;
155       bindDoubleAttribute(&style->width, shape->values[style->bindings[MS_STYLE_BINDING_WIDTH].index]);
156     }
157     if(style->bindings[MS_STYLE_BINDING_COLOR].index != -1 && !MS_DRAW_QUERY(drawmode)) {
158       MS_INIT_COLOR(style->color, -1,-1,-1,255);
159       bindColorAttribute(&style->color, shape->values[style->bindings[MS_STYLE_BINDING_COLOR].index]);
160     }
161     if(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index != -1 && !MS_DRAW_QUERY(drawmode)) {
162       MS_INIT_COLOR(style->outlinecolor, -1,-1,-1,255);
163       bindColorAttribute(&style->outlinecolor, shape->values[style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index]);
164     }
165     if(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index != -1) {
166       style->outlinewidth = 1;
167       bindDoubleAttribute(&style->outlinewidth, shape->values[style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index]);
168     }
169     if(style->bindings[MS_STYLE_BINDING_OPACITY].index != -1) {
170       style->opacity = 100;
171       bindIntegerAttribute(&style->opacity, shape->values[style->bindings[MS_STYLE_BINDING_OPACITY].index]);
172     }
173     if(style->bindings[MS_STYLE_BINDING_OFFSET_X].index != -1) {
174       style->offsetx = 0;
175       bindDoubleAttribute(&style->offsetx, shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_X].index]);
176     }
177     if(style->bindings[MS_STYLE_BINDING_OFFSET_Y].index != -1) {
178       style->offsety = 0;
179       bindDoubleAttribute(&style->offsety, shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_Y].index]);
180     }
181     if(style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].index != -1) {
182       style->polaroffsetpixel = 0;
183       bindDoubleAttribute(&style->polaroffsetpixel, shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].index]);
184     }
185     if(style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].index != -1) {
186       style->polaroffsetangle = 0;
187       bindDoubleAttribute(&style->polaroffsetangle, shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].index]);
188     }
189   }
190   if (style->nexprbindings > 0)
191   {
192     applyOpacity = MS_TRUE;
193     if (style->exprBindings[MS_STYLE_BINDING_OFFSET_X].type == MS_EXPRESSION)
194     {
195       style->offsetx = msEvalDoubleExpression(
196           &(style->exprBindings[MS_STYLE_BINDING_OFFSET_X]),
197           shape);
198     }
199     if (style->exprBindings[MS_STYLE_BINDING_OFFSET_Y].type == MS_EXPRESSION)
200     {
201       style->offsety = msEvalDoubleExpression(
202           &(style->exprBindings[MS_STYLE_BINDING_OFFSET_Y]),
203           shape);
204     }
205     if (style->exprBindings[MS_STYLE_BINDING_ANGLE].type == MS_EXPRESSION)
206     {
207       style->angle = msEvalDoubleExpression(
208           &(style->exprBindings[MS_STYLE_BINDING_ANGLE]),
209           shape);
210     }
211     if (style->exprBindings[MS_STYLE_BINDING_SIZE].type == MS_EXPRESSION)
212     {
213       style->size = msEvalDoubleExpression(
214           &(style->exprBindings[MS_STYLE_BINDING_SIZE]),
215           shape);
216     }
217     if (style->exprBindings[MS_STYLE_BINDING_WIDTH].type == MS_EXPRESSION)
218     {
219       style->width = msEvalDoubleExpression(
220           &(style->exprBindings[MS_STYLE_BINDING_WIDTH]),
221           shape);
222     }
223     if (style->exprBindings[MS_STYLE_BINDING_OPACITY].type == MS_EXPRESSION)
224     {
225       style->opacity = 100 * msEvalDoubleExpression(
226           &(style->exprBindings[MS_STYLE_BINDING_OPACITY]),
227           shape);
228     }
229     if (style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].type == MS_EXPRESSION)
230     {
231       char* txt = msEvalTextExpression(
232             &(style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR]), shape);
233       bindColorAttribute(&style->outlinecolor, txt);
234       msFree(txt);
235     }
236     if (style->exprBindings[MS_STYLE_BINDING_COLOR].type == MS_EXPRESSION)
237     {
238       char* txt = msEvalTextExpression(
239             &(style->exprBindings[MS_STYLE_BINDING_COLOR]), shape);
240       bindColorAttribute(&style->color, txt);
241       msFree(txt);
242     }
243   }
244 
245   if(applyOpacity == MS_TRUE && (style->opacity < 100 || style->color.alpha != 255) ) {
246     int alpha;
247     alpha = MS_NINT(style->opacity*2.55);
248     style->color.alpha = alpha;
249     style->outlinecolor.alpha = alpha;
250     style->backgroundcolor.alpha = alpha;
251     style->mincolor.alpha = alpha;
252     style->maxcolor.alpha = alpha;
253   }
254 }
255 
bindLabel(layerObj * layer,shapeObj * shape,labelObj * label,int drawmode)256 static void bindLabel(layerObj *layer, shapeObj *shape, labelObj *label, int drawmode)
257 {
258   int i;
259   assert(MS_DRAW_LABELS(drawmode));
260 
261   /* check the label styleObj's (TODO: do we need to use querymapMode here? */
262   for(i=0; i<label->numstyles; i++) {
263     /* force MS_DRAWMODE_FEATURES for label styles */
264     bindStyle(layer, shape, label->styles[i], drawmode|MS_DRAWMODE_FEATURES);
265   }
266 
267   if(label->numbindings > 0) {
268     if(label->bindings[MS_LABEL_BINDING_ANGLE].index != -1) {
269       label->angle = 0.0;
270       bindDoubleAttribute(&label->angle, shape->values[label->bindings[MS_LABEL_BINDING_ANGLE].index]);
271     }
272 
273     if(label->bindings[MS_LABEL_BINDING_SIZE].index != -1) {
274       label->size = 1;
275       bindIntegerAttribute(&label->size, shape->values[label->bindings[MS_LABEL_BINDING_SIZE].index]);
276     }
277 
278     if(label->bindings[MS_LABEL_BINDING_COLOR].index != -1) {
279       MS_INIT_COLOR(label->color, -1,-1,-1,255);
280       bindColorAttribute(&label->color, shape->values[label->bindings[MS_LABEL_BINDING_COLOR].index]);
281     }
282 
283     if(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index != -1) {
284       MS_INIT_COLOR(label->outlinecolor, -1,-1,-1,255);
285       bindColorAttribute(&label->outlinecolor, shape->values[label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index]);
286     }
287 
288     if(label->bindings[MS_LABEL_BINDING_FONT].index != -1) {
289       msFree(label->font);
290       label->font = msStrdup(shape->values[label->bindings[MS_LABEL_BINDING_FONT].index]);
291     }
292 
293     if(label->bindings[MS_LABEL_BINDING_PRIORITY].index != -1) {
294       label->priority = MS_DEFAULT_LABEL_PRIORITY;
295       bindIntegerAttribute(&label->priority, shape->values[label->bindings[MS_LABEL_BINDING_PRIORITY].index]);
296     }
297 
298     if(label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index != -1) {
299       label->shadowsizex = 1;
300       bindIntegerAttribute(&label->shadowsizex, shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index]);
301     }
302     if(label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index != -1) {
303       label->shadowsizey = 1;
304       bindIntegerAttribute(&label->shadowsizey, shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index]);
305     }
306 
307     if(label->bindings[MS_LABEL_BINDING_OFFSET_X].index != -1) {
308       label->offsetx = 0;
309       bindIntegerAttribute(&label->offsetx, shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_X].index]);
310     }
311 
312     if(label->bindings[MS_LABEL_BINDING_OFFSET_Y].index != -1) {
313       label->offsety = 0;
314       bindIntegerAttribute(&label->offsety, shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_Y].index]);
315     }
316 
317     if(label->bindings[MS_LABEL_BINDING_ALIGN].index != -1) {
318       int tmpAlign = 0;
319       bindIntegerAttribute(&tmpAlign, shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]);
320       if(tmpAlign != 0) { /* is this test sufficient? */
321         label->align = tmpAlign;
322       } else { /* Integer binding failed, look for strings like cc, ul, lr, etc... */
323         if(strlen(shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]) >= 4) {
324           char *va = shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index];
325           if(!strncasecmp(va,"center",5))
326             label->align = MS_ALIGN_CENTER;
327           else if(!strncasecmp(va,"left",4))
328             label->align = MS_ALIGN_LEFT;
329           else if(!strncasecmp(va,"right",5))
330             label->align = MS_ALIGN_RIGHT;
331         }
332       }
333     }
334 
335     if(label->bindings[MS_LABEL_BINDING_POSITION].index != -1) {
336       int tmpPosition = 0;
337       bindIntegerAttribute(&tmpPosition, shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index]);
338       if(tmpPosition != 0) { /* is this test sufficient? */
339         label->position = tmpPosition;
340       } else { /* Integer binding failed, look for strings like cc, ul, lr, etc... */
341         if(strlen(shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index]) == 2) {
342           char *vp = shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index];
343           if(!strncasecmp(vp,"ul",2))
344             label->position = MS_UL;
345           else if(!strncasecmp(vp,"lr",2))
346             label->position = MS_LR;
347           else if(!strncasecmp(vp,"ur",2))
348             label->position = MS_UR;
349           else if(!strncasecmp(vp,"ll",2))
350             label->position = MS_LL;
351           else if(!strncasecmp(vp,"cr",2))
352             label->position = MS_CR;
353           else if(!strncasecmp(vp,"cl",2))
354             label->position = MS_CL;
355           else if(!strncasecmp(vp,"uc",2))
356             label->position = MS_UC;
357           else if(!strncasecmp(vp,"lc",2))
358             label->position = MS_LC;
359           else if(!strncasecmp(vp,"cc",2))
360             label->position = MS_CC;
361         }
362       }
363     }
364   }
365   if (label->nexprbindings > 0)
366   {
367     if (label->exprBindings[MS_LABEL_BINDING_ANGLE].type == MS_EXPRESSION)
368     {
369       label->angle = msEvalDoubleExpression(
370           &(label->exprBindings[MS_LABEL_BINDING_ANGLE]),
371           shape);
372     }
373     if (label->exprBindings[MS_LABEL_BINDING_SIZE].type == MS_EXPRESSION)
374     {
375       label->size = msEvalDoubleExpression(
376           &(label->exprBindings[MS_LABEL_BINDING_SIZE]),
377           shape);
378     }
379     if (label->exprBindings[MS_LABEL_BINDING_COLOR].type == MS_EXPRESSION)
380     {
381       char* txt = msEvalTextExpression(
382             &(label->exprBindings[MS_LABEL_BINDING_COLOR]), shape);
383       bindColorAttribute(&label->color, txt);
384       msFree(txt);
385     }
386     if (label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR].type == MS_EXPRESSION)
387     {
388       char* txt = msEvalTextExpression(
389             &(label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR]), shape);
390       bindColorAttribute(&label->outlinecolor, txt);
391       msFree(txt);
392     }
393   }
394 }
395 
396 /*
397 ** Function to bind various layer properties to shape attributes.
398 */
msBindLayerToShape(layerObj * layer,shapeObj * shape,int drawmode)399 int msBindLayerToShape(layerObj *layer, shapeObj *shape, int drawmode)
400 {
401   int i, j;
402 
403   if(!layer || !shape) return MS_FAILURE;
404 
405   for(i=0; i<layer->numclasses; i++) {
406     /* check the styleObj's */
407     if(MS_DRAW_FEATURES(drawmode)) {
408       for(j=0; j<layer->class[i]->numstyles; j++) {
409         bindStyle(layer, shape, layer->class[i]->styles[j], drawmode);
410       }
411     }
412 
413     /* check the labelObj's */
414     if(MS_DRAW_LABELS(drawmode)) {
415       for(j=0; j<layer->class[i]->numlabels; j++) {
416         bindLabel(layer, shape, layer->class[i]->labels[j], drawmode);
417       }
418     }
419   } /* next classObj */
420 
421   return MS_SUCCESS;
422 }
423 
424 /*
425  * Used to get red, green, blue integers separately based upon the color index
426  */
getRgbColor(mapObj * map,int i,int * r,int * g,int * b)427 int getRgbColor(mapObj *map,int i,int *r,int *g,int *b)
428 {
429   /* check index range */
430   int status=1;
431   *r=*g=*b=-1;
432   if ((i > 0 ) && (i <= map->palette.numcolors) ) {
433     *r=map->palette.colors[i-1].red;
434     *g=map->palette.colors[i-1].green;
435     *b=map->palette.colors[i-1].blue;
436     status=0;
437   }
438   return status;
439 }
440 
searchContextForTag(mapObj * map,char ** ltags,char * tag,char * context,int requires)441 static int searchContextForTag(mapObj *map, char **ltags, char *tag, char *context, int requires)
442 {
443   int i;
444 
445   if(!context) return MS_FAILURE;
446 
447   /*  printf("\tin searchContextForTag, searching %s for %s\n", context, tag); */
448 
449   if(strstr(context, tag) != NULL) return MS_SUCCESS; /* found the tag */
450 
451   /* check referenced layers for the tag too */
452   for(i=0; i<map->numlayers; i++) {
453     if(strstr(context, ltags[i]) != NULL) { /* need to check this layer */
454       if(requires == MS_TRUE) {
455         if(searchContextForTag(map, ltags, tag, GET_LAYER(map, i)->requires, MS_TRUE) == MS_SUCCESS) return MS_SUCCESS;
456       } else {
457         if(searchContextForTag(map, ltags, tag, GET_LAYER(map, i)->labelrequires, MS_FALSE) == MS_SUCCESS) return MS_SUCCESS;
458       }
459     }
460   }
461 
462   return MS_FAILURE;
463 }
464 
465 /*
466 ** Function to take a look at all layers with REQUIRES/LABELREQUIRES set to make sure there are no
467 ** recursive context requirements set (e.g. layer1 requires layer2 and layer2 requires layer1). This
468 ** is bug 1059.
469 */
msValidateContexts(mapObj * map)470 int msValidateContexts(mapObj *map)
471 {
472   int i;
473   char **ltags;
474   int status = MS_SUCCESS;
475 
476   ltags = (char **) msSmallMalloc(map->numlayers*sizeof(char *));
477   for(i=0; i<map->numlayers; i++) {
478     if(GET_LAYER(map, i)->name == NULL) {
479       ltags[i] = msStrdup("[NULL]");
480     } else {
481       ltags[i] = (char *) msSmallMalloc(sizeof(char)*strlen(GET_LAYER(map, i)->name) + 3);
482       sprintf(ltags[i], "[%s]", GET_LAYER(map, i)->name);
483     }
484   }
485 
486   /* check each layer's REQUIRES and LABELREQUIRES parameters */
487   for(i=0; i<map->numlayers; i++) {
488     /* printf("working on layer %s, looking for references to %s\n", GET_LAYER(map, i)->name, ltags[i]); */
489     if(searchContextForTag(map, ltags, ltags[i], GET_LAYER(map, i)->requires, MS_TRUE) == MS_SUCCESS) {
490       msSetError(MS_PARSEERR, "Recursion error found for REQUIRES parameter for layer %s.", "msValidateContexts", GET_LAYER(map, i)->name);
491       status = MS_FAILURE;
492       break;
493     }
494     if(searchContextForTag(map, ltags, ltags[i], GET_LAYER(map, i)->labelrequires, MS_FALSE) == MS_SUCCESS) {
495       msSetError(MS_PARSEERR, "Recursion error found for LABELREQUIRES parameter for layer %s.", "msValidateContexts", GET_LAYER(map, i)->name);
496       status = MS_FAILURE;
497       break;
498     }
499     /* printf("done layer %s\n", GET_LAYER(map, i)->name); */
500   }
501 
502   /* clean up */
503   msFreeCharArray(ltags, map->numlayers);
504 
505   return status;
506 }
507 
msEvalContext(mapObj * map,layerObj * layer,char * context)508 int msEvalContext(mapObj *map, layerObj *layer, char *context)
509 {
510   int i, status;
511   char *tag=NULL;
512 
513   expressionObj e;
514   parseObj p;
515 
516   if(!context) return(MS_TRUE);
517 
518   /* initialize a temporary expression (e) */
519   msInitExpression(&e);
520 
521   e.string = msStrdup(context);
522   e.type = MS_EXPRESSION; /* todo */
523 
524   for(i=0; i<map->numlayers; i++) { /* step through all the layers */
525     if(layer->index == i) continue; /* skip the layer in question */
526     if (GET_LAYER(map, i)->name == NULL) continue; /* Layer without name cannot be used in contexts */
527 
528     tag = (char *)msSmallMalloc(sizeof(char)*strlen(GET_LAYER(map, i)->name) + 3);
529     sprintf(tag, "[%s]", GET_LAYER(map, i)->name);
530 
531     if(strstr(e.string, tag)) {
532       if(msLayerIsVisible(map, (GET_LAYER(map, i))))
533         e.string = msReplaceSubstring(e.string, tag, "1");
534       else
535         e.string = msReplaceSubstring(e.string, tag, "0");
536     }
537 
538     free(tag);
539   }
540 
541   msTokenizeExpression(&e, NULL, NULL);
542 
543   p.shape = NULL;
544   p.expr = &e;
545   p.expr->curtoken = p.expr->tokens; /* reset */
546   p.type = MS_PARSE_TYPE_BOOLEAN;
547 
548   status = yyparse(&p);
549 
550   msFreeExpression(&e);
551 
552   if (status != 0) {
553     msSetError(MS_PARSEERR, "Failed to parse context", "msEvalContext");
554     return MS_FALSE; /* error in parse */
555   }
556 
557   return p.result.intval;
558 }
559 
560 /* msEvalExpression()
561  *
562  * Evaluates a mapserver expression for a given set of attribute values and
563  * returns the result of the expression (MS_TRUE or MS_FALSE)
564  * May also return MS_FALSE in case of parsing errors or invalid expressions
565  * (check the error stack if you care)
566  *
567  */
msEvalExpression(layerObj * layer,shapeObj * shape,expressionObj * expression,int itemindex)568 int msEvalExpression(layerObj *layer, shapeObj *shape, expressionObj *expression, int itemindex)
569 {
570   if(MS_STRING_IS_NULL_OR_EMPTY(expression->string)) return MS_TRUE; /* NULL or empty expressions are ALWAYS true */
571   if(expression->native_string != NULL) return MS_TRUE; /* expressions that are evaluated natively are ALWAYS true */
572 
573   switch(expression->type) {
574     case(MS_STRING):
575       if(itemindex == -1) {
576         msSetError(MS_MISCERR, "Cannot evaluate expression, no item index defined.", "msEvalExpression()");
577         return MS_FALSE;
578       }
579       if(itemindex >= layer->numitems || itemindex >= shape->numvalues) {
580         msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
581         return MS_FALSE;
582       }
583       if(expression->flags & MS_EXP_INSENSITIVE) {
584         if(strcasecmp(expression->string, shape->values[itemindex]) == 0) return MS_TRUE; /* got a match */
585       } else {
586         if(strcmp(expression->string, shape->values[itemindex]) == 0) return MS_TRUE; /* got a match */
587       }
588       break;
589     case(MS_LIST):
590       if(itemindex == -1) {
591         msSetError(MS_MISCERR, "Cannot evaluate expression, no item index defined.", "msEvalExpression()");
592         return MS_FALSE;
593       }
594       if(itemindex >= layer->numitems || itemindex >= shape->numvalues) {
595         msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
596         return MS_FALSE;
597       }
598       {
599         char *start,*end;
600         int value_len = strlen(shape->values[itemindex]);
601         start = expression->string;
602         while((end = strchr(start,',')) != NULL) {
603           if(value_len == end-start && !strncmp(start,shape->values[itemindex],end-start)) return MS_TRUE;
604           start = end+1;
605         }
606         if(!strcmp(start,shape->values[itemindex])) return MS_TRUE;
607       }
608       break;
609     case(MS_EXPRESSION): {
610       int status;
611       parseObj p;
612 
613       p.shape = shape;
614       p.expr = expression;
615       p.expr->curtoken = p.expr->tokens; /* reset */
616       p.type = MS_PARSE_TYPE_BOOLEAN;
617 
618       status = yyparse(&p);
619 
620       if (status != 0) {
621         msSetError(MS_PARSEERR, "Failed to parse expression: %s", "msEvalExpression", expression->string);
622         return MS_FALSE;
623       }
624 
625       return p.result.intval;
626       break;
627     }
628     case(MS_REGEX):
629       if(itemindex == -1) {
630         msSetError(MS_MISCERR, "Cannot evaluate expression, no item index defined.", "msEvalExpression()");
631         return MS_FALSE;
632       }
633       if(itemindex >= layer->numitems || itemindex >= shape->numvalues) {
634         msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
635         return MS_FALSE;
636       }
637 
638       if(MS_STRING_IS_NULL_OR_EMPTY(shape->values[itemindex]) == MS_TRUE) return MS_FALSE;
639 
640       if(!expression->compiled) {
641         if(expression->flags & MS_EXP_INSENSITIVE) {
642           if(ms_regcomp(&(expression->regex), expression->string, MS_REG_EXTENDED|MS_REG_NOSUB|MS_REG_ICASE) != 0) { /* compile the expression */
643             msSetError(MS_REGEXERR, "Invalid regular expression.", "msEvalExpression()");
644             return MS_FALSE;
645           }
646         } else {
647           if(ms_regcomp(&(expression->regex), expression->string, MS_REG_EXTENDED|MS_REG_NOSUB) != 0) { /* compile the expression */
648             msSetError(MS_REGEXERR, "Invalid regular expression.", "msEvalExpression()");
649             return MS_FALSE;
650           }
651         }
652         expression->compiled = MS_TRUE;
653       }
654 
655       if(ms_regexec(&(expression->regex), shape->values[itemindex], 0, NULL, 0) == 0) return MS_TRUE; /* got a match */
656       break;
657   }
658 
659   return MS_FALSE;
660 }
661 
msAllocateValidClassGroups(layerObj * lp,int * nclasses)662 int *msAllocateValidClassGroups(layerObj *lp, int *nclasses)
663 {
664   int *classgroup = NULL;
665   int nvalidclass = 0, i=0;
666 
667   if (!lp || !lp->classgroup || lp->numclasses <=0 || !nclasses)
668     return NULL;
669 
670   classgroup = (int *)msSmallMalloc(sizeof(int)*lp->numclasses);
671   nvalidclass = 0;
672   for (i=0; i<lp->numclasses; i++) {
673     if (lp->class[i]->group && strcasecmp(lp->class[i]->group, lp->classgroup) == 0) {
674       classgroup[nvalidclass] = i;
675       nvalidclass++;
676     }
677   }
678   if (nvalidclass > 0) {
679     classgroup = (int *)msSmallRealloc(classgroup, sizeof(int)*nvalidclass);
680     *nclasses = nvalidclass;
681     return classgroup;
682   }
683 
684   if (classgroup)
685     msFree(classgroup);
686 
687   return NULL;
688 
689 }
690 
msShapeGetClass(layerObj * layer,mapObj * map,shapeObj * shape,int * classgroup,int numclasses)691 int msShapeGetClass(layerObj *layer, mapObj *map, shapeObj *shape, int *classgroup, int numclasses)
692 {
693   return msShapeGetNextClass(-1, layer, map, shape, classgroup, numclasses);
694 }
695 
msShapeGetNextClass(int currentclass,layerObj * layer,mapObj * map,shapeObj * shape,int * classgroup,int numclasses)696 int msShapeGetNextClass(int currentclass, layerObj *layer, mapObj *map,
697     shapeObj *shape, int *classgroup, int numclasses)
698 {
699   int i, iclass;
700 
701   if (currentclass < 0)
702     currentclass = -1;
703 
704   if (layer->numclasses > 0) {
705     if (classgroup == NULL || numclasses <=0)
706       numclasses = layer->numclasses;
707 
708     for(i=currentclass+1; i<numclasses; i++) {
709       if (classgroup)
710         iclass = classgroup[i];
711       else
712         iclass = i;
713 
714       if (iclass < 0 || iclass >= layer->numclasses)
715         continue; /* this should never happen but just in case */
716 
717       if(map->scaledenom > 0) { /* verify scaledenom here  */
718         if((layer->class[iclass]->maxscaledenom > 0) && (map->scaledenom > layer->class[iclass]->maxscaledenom))
719           continue; /* can skip this one, next class */
720         if((layer->class[iclass]->minscaledenom > 0) && (map->scaledenom <= layer->class[iclass]->minscaledenom))
721           continue; /* can skip this one, next class */
722       }
723 
724       /* verify the minfeaturesize */
725       if ((shape->type == MS_SHAPE_LINE || shape->type == MS_SHAPE_POLYGON) && (layer->class[iclass]->minfeaturesize > 0)) {
726         double minfeaturesize = Pix2LayerGeoref(map, layer,
727                                                 layer->class[iclass]->minfeaturesize);
728         if (msShapeCheckSize(shape, minfeaturesize) == MS_FALSE)
729           continue; /* skip this one, next class */
730       }
731 
732       if(layer->class[iclass]->status != MS_DELETE && msEvalExpression(layer, shape, &(layer->class[iclass]->expression), layer->classitemindex) == MS_TRUE)
733       {
734         if (layer->class[iclass]->isfallback && currentclass != -1)
735         {
736           // Class is not applicable if it is flagged as fallback (<ElseFilter/> tag in SLD)
737           // but other classes have been applied before.
738           return -1;
739         }
740         else
741         {
742           return(iclass);
743         }
744       }
745     }
746   }
747 
748   return(-1); /* no match */
749 }
750 
751 static
msEvalTextExpressionInternal(expressionObj * expr,shapeObj * shape,int bJSonEscape)752 char *msEvalTextExpressionInternal(expressionObj *expr, shapeObj *shape, int bJSonEscape)
753 {
754   char *result=NULL;
755 
756   if(!expr->string) return result; /* nothing to do */
757 
758   switch(expr->type) {
759     case(MS_STRING): {
760       char *target=NULL;
761       char *pszEscaped;
762       tokenListNodeObjPtr node=NULL;
763       tokenListNodeObjPtr nextNode=NULL;
764 
765       result = msStrdup(expr->string);
766 
767       node = expr->tokens;
768       if(node) {
769         while(node != NULL) {
770           nextNode = node->next;
771           if(node->token == MS_TOKEN_BINDING_DOUBLE || node->token == MS_TOKEN_BINDING_INTEGER || node->token == MS_TOKEN_BINDING_STRING || node->token == MS_TOKEN_BINDING_TIME) {
772             target = (char *) msSmallMalloc(strlen(node->tokenval.bindval.item) + 3);
773             sprintf(target, "[%s]", node->tokenval.bindval.item);
774             if( bJSonEscape )
775                 pszEscaped = msEscapeJSonString(shape->values[node->tokenval.bindval.index]);
776             else
777                 pszEscaped = msStrdup(shape->values[node->tokenval.bindval.index]);
778             result = msReplaceSubstring(result, target, pszEscaped);
779             msFree(pszEscaped);
780             msFree(target);
781           }
782           node = nextNode;
783         }
784       }
785       if(!strlen(result)) {
786         msFree(result);
787         result = NULL;
788       }
789     }
790     break;
791     case(MS_EXPRESSION): {
792       int status;
793       parseObj p;
794 
795       p.shape = shape;
796       p.expr = expr;
797       p.expr->curtoken = p.expr->tokens; /* reset */
798       p.type = MS_PARSE_TYPE_STRING;
799 
800       status = yyparse(&p);
801 
802       if (status != 0) {
803         msSetError(MS_PARSEERR, "Failed to process text expression: %s", "msEvalTextExpression", expr->string);
804         return NULL;
805       }
806 
807       result = p.result.strval;
808       break;
809     }
810     default:
811       break;
812   }
813   if(result && !strlen(result)) {
814     msFree(result);
815     result = NULL;
816   }
817   return result;
818 }
819 
msEvalTextExpressionJSonEscape(expressionObj * expr,shapeObj * shape)820 char *msEvalTextExpressionJSonEscape(expressionObj *expr, shapeObj *shape)
821 {
822     return msEvalTextExpressionInternal(expr, shape, MS_TRUE);
823 }
824 
msEvalTextExpression(expressionObj * expr,shapeObj * shape)825 char *msEvalTextExpression(expressionObj *expr, shapeObj *shape)
826 {
827     return msEvalTextExpressionInternal(expr, shape, MS_FALSE);
828 }
829 
msEvalDoubleExpression(expressionObj * expression,shapeObj * shape)830 double msEvalDoubleExpression(expressionObj *expression, shapeObj *shape)
831 {
832   double value;
833   int status;
834   parseObj p;
835   p.shape = shape;
836   p.expr = expression;
837   p.expr->curtoken = p.expr->tokens; /* reset */
838   p.type = MS_PARSE_TYPE_STRING;
839   status = yyparse(&p);
840   if (status != 0) {
841     msSetError(MS_PARSEERR, "Failed to parse expression: %s",
842         "bindStyle", expression->string);
843     value = 0.0;
844   } else {
845     value = atof(p.result.strval);
846     msFree(p.result.strval);
847   }
848   return value;
849 }
850 
msShapeGetLabelAnnotation(layerObj * layer,shapeObj * shape,labelObj * lbl)851 char* msShapeGetLabelAnnotation(layerObj *layer, shapeObj *shape, labelObj *lbl) {
852   assert(shape && lbl);
853   if(lbl->text.string) {
854     return msEvalTextExpression(&(lbl->text), shape);
855   } else if(layer->class[shape->classindex]->text.string) {
856     return msEvalTextExpression(&(layer->class[shape->classindex]->text), shape);
857   } else {
858     if (shape->values && layer->labelitemindex >= 0 && shape->values[layer->labelitemindex] && strlen(shape->values[layer->labelitemindex]) )
859       return msStrdup(shape->values[layer->labelitemindex]);
860     else if(shape->text)
861       return msStrdup(shape->text); /* last resort but common with iniline features */
862   }
863   return NULL;
864 }
865 
msGetLabelStatus(mapObj * map,layerObj * layer,shapeObj * shape,labelObj * lbl)866 int msGetLabelStatus(mapObj *map, layerObj *layer, shapeObj *shape, labelObj *lbl) {
867   assert(layer && lbl);
868   if(!msScaleInBounds(map->scaledenom,lbl->minscaledenom,lbl->maxscaledenom))
869     return MS_OFF;
870   if(msEvalExpression(layer, shape, &(lbl->expression), layer->labelitemindex) != MS_TRUE)
871     return MS_OFF;
872   /* TODO: check for minfeaturesize here ? */
873   return MS_ON;
874 }
875 
876 /* Check if the shape is enough big to be drawn with the
877    layer::minfeaturesize setting. The minfeaturesize parameter should be
878    the value in geo ref (not in pixel) and should have been multiplied by
879    the resolution factor.
880  */
msShapeCheckSize(shapeObj * shape,double minfeaturesize)881 int msShapeCheckSize(shapeObj *shape, double minfeaturesize)
882 {
883   double dx = (shape->bounds.maxx-shape->bounds.minx);
884   double dy = (shape->bounds.maxy-shape->bounds.miny);
885 
886   if (pow(minfeaturesize,2.0) > (pow(dx,2.0)+pow(dy,2.0)))
887     return MS_FALSE;
888 
889   return MS_TRUE;
890 }
891 
892 /*
893 ** Adjusts an image size in one direction to fit an extent.
894 */
msAdjustImage(rectObj rect,int * width,int * height)895 int msAdjustImage(rectObj rect, int *width, int *height)
896 {
897   if(*width == -1 && *height == -1) {
898     msSetError(MS_MISCERR, "Cannot calculate both image height and width.", "msAdjustImage()");
899     return(-1);
900   }
901 
902   if(*width > 0)
903     *height = MS_NINT((rect.maxy - rect.miny)/((rect.maxx - rect.minx)/(*width)));
904   else
905     *width = MS_NINT((rect.maxx - rect.minx)/((rect.maxy - rect.miny)/(*height)));
906 
907   return(0);
908 }
909 
910 /*
911 ** Make sure extent fits image window to be created. Returns cellsize of output image.
912 */
msAdjustExtent(rectObj * rect,int width,int height)913 double msAdjustExtent(rectObj *rect, int width, int height)
914 {
915   double cellsize, ox, oy;
916 
917   if(width == 1 || height == 1)
918     return 0;
919 
920   cellsize = MS_MAX(MS_CELLSIZE(rect->minx, rect->maxx, width), MS_CELLSIZE(rect->miny, rect->maxy, height));
921 
922   if(cellsize <= 0) /* avoid division by zero errors */
923     return(0);
924 
925   ox = MS_MAX(((width-1) - (rect->maxx - rect->minx)/cellsize)/2,0); /* these were width-1 and height-1 */
926   oy = MS_MAX(((height-1) - (rect->maxy - rect->miny)/cellsize)/2,0);
927 
928   rect->minx = rect->minx - ox*cellsize;
929   rect->miny = rect->miny - oy*cellsize;
930   rect->maxx = rect->maxx + ox*cellsize;
931   rect->maxy = rect->maxy + oy*cellsize;
932 
933   return(cellsize);
934 }
935 
936 /*
937 ** Rect must always contain a portion of bounds. If not, rect is
938 ** shifted to overlap by overlay percent. The dimensions of rect do
939 ** not change but placement relative to bounds can.
940 */
msConstrainExtent(rectObj * bounds,rectObj * rect,double overlay)941 int msConstrainExtent(rectObj *bounds, rectObj *rect, double overlay)
942 {
943   double offset=0;
944 
945   /* check left edge, and if necessary the right edge of bounds */
946   if(rect->maxx <= bounds->minx) {
947     offset = overlay*(rect->maxx - rect->minx);
948     rect->minx += offset; /* shift right */
949     rect->maxx += offset;
950   } else if(rect->minx >= bounds->maxx) {
951     offset = overlay*(rect->maxx - rect->minx);
952     rect->minx -= offset; /* shift left */
953     rect->maxx -= offset;
954   }
955 
956   /* check top edge, and if necessary the bottom edge of bounds */
957   if(rect->maxy <= bounds->miny) {
958     offset = overlay*(rect->maxy - rect->miny);
959     rect->miny -= offset; /* shift down */
960     rect->maxy -= offset;
961   } else if(rect->miny >= bounds->maxy) {
962     offset = overlay*(rect->maxy - rect->miny);
963     rect->miny += offset; /* shift up */
964     rect->maxy += offset;
965   }
966 
967   return(MS_SUCCESS);
968 }
969 
970 /*
971 ** Generic function to save an image to a file.
972 **
973 ** Note that map may be NULL. If it is set, then it is used for two things:
974 ** - Deal with relative imagepaths (compute absolute path relative to map path)
975 ** - Extract the georeferenced extents and coordinate system
976 **   of the map for writing out with the image when appropriate
977 **   (primarily this means via msSaveImageGDAL() to something like GeoTIFF).
978 **
979 ** The filename is NULL when the image is supposed to be written to stdout.
980 */
981 
msSaveImage(mapObj * map,imageObj * img,const char * filename)982 int msSaveImage(mapObj *map, imageObj *img, const char *filename)
983 {
984   int nReturnVal = MS_FAILURE;
985   char szPath[MS_MAXPATHLEN];
986   struct mstimeval starttime={0}, endtime={0};
987 
988   if(map && map->debug >= MS_DEBUGLEVEL_TUNING) {
989     msGettimeofday(&starttime, NULL);
990   }
991 
992   if (img) {
993     if( MS_DRIVER_GDAL(img->format) ) {
994       if (map != NULL && filename != NULL )
995         nReturnVal = msSaveImageGDAL(map, img,
996                                      msBuildPath(szPath, map->mappath,
997                                          filename));
998       else
999         nReturnVal = msSaveImageGDAL(map, img, filename);
1000     } else
1001 
1002       if (MS_RENDERER_PLUGIN(img->format)) {
1003         rendererVTableObj *renderer = img->format->vtable;
1004         FILE *stream = NULL;
1005         if(filename) {
1006           if(map)
1007             stream = fopen(msBuildPath(szPath, map->mappath, filename),"wb");
1008           else
1009             stream = fopen(filename,"wb");
1010 
1011           if(!stream) {
1012             msSetError(MS_IOERR,
1013                        "Failed to create output file (%s).",
1014                        "msSaveImage()", (map?szPath:filename) );
1015             return MS_FAILURE;
1016           }
1017 
1018         } else {
1019           if ( msIO_needBinaryStdout() == MS_FAILURE )
1020             return MS_FAILURE;
1021           stream = stdout;
1022         }
1023 
1024         if(renderer->supports_pixel_buffer) {
1025           rasterBufferObj data;
1026           if(renderer->getRasterBufferHandle(img,&data) != MS_SUCCESS) {
1027             if( stream != stdout )
1028               fclose(stream);
1029             return MS_FAILURE;
1030           }
1031 
1032           nReturnVal = msSaveRasterBuffer(map,&data,stream,img->format );
1033         } else {
1034           nReturnVal = renderer->saveImage(img, map, stream, img->format);
1035         }
1036         if( stream != stdout )
1037           fclose(stream);
1038 
1039       } else if( MS_DRIVER_IMAGEMAP(img->format) )
1040         nReturnVal = msSaveImageIM(img, filename, img->format);
1041       else
1042         msSetError(MS_MISCERR, "Unknown image type",
1043                    "msSaveImage()");
1044   }
1045 
1046   if(map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1047     msGettimeofday(&endtime, NULL);
1048     msDebug("msSaveImage(%s) total time: %.3fs\n",
1049             (filename ? filename : "stdout"),
1050             (endtime.tv_sec+endtime.tv_usec/1.0e6)-
1051             (starttime.tv_sec+starttime.tv_usec/1.0e6) );
1052   }
1053 
1054   return nReturnVal;
1055 }
1056 
1057 /*
1058 ** Generic function to save an image to a byte array.
1059 ** - the return value is the pointer to the byte array
1060 ** - size_ptr contains the number of bytes returned
1061 ** - format: the desired output format
1062 **
1063 ** The caller is responsible to free the returned array
1064 ** The function returns NULL if the output format is not supported.
1065 */
1066 
msSaveImageBuffer(imageObj * image,int * size_ptr,outputFormatObj * format)1067 unsigned char *msSaveImageBuffer(imageObj* image, int *size_ptr, outputFormatObj *format)
1068 {
1069   int status = MS_SUCCESS;
1070   *size_ptr = 0;
1071   if( MS_RENDERER_PLUGIN(image->format)) {
1072     rasterBufferObj data;
1073     rendererVTableObj *renderer = image->format->vtable;
1074     if(renderer->supports_pixel_buffer) {
1075       bufferObj buffer;
1076       msBufferInit(&buffer);
1077       status = renderer->getRasterBufferHandle(image,&data);
1078       if(UNLIKELY(status == MS_FAILURE)) {
1079         return NULL;
1080       }
1081       msSaveRasterBufferToBuffer(&data,&buffer,format);
1082       *size_ptr = buffer.size;
1083       return buffer.data;
1084       /* don't free the bufferObj as we don't own the bytes anymore */
1085     } else {
1086       /* check if the renderer supports native buffer output */
1087       if (renderer->saveImageBuffer)
1088         return renderer->saveImageBuffer(image, size_ptr, format);
1089 
1090       msSetError(MS_MISCERR, "Unsupported image type", "msSaveImageBuffer()");
1091       return NULL;
1092     }
1093   }
1094   msSetError(MS_MISCERR, "Unsupported image type", "msSaveImage()");
1095   return NULL;
1096 }
1097 
1098 /**
1099  * Generic function to free the imageObj
1100  */
msFreeImage(imageObj * image)1101 void msFreeImage(imageObj *image)
1102 {
1103   if (image) {
1104     if(MS_RENDERER_PLUGIN(image->format)) {
1105       rendererVTableObj *renderer = image->format->vtable;
1106       tileCacheObj *next,*cur = image->tilecache;
1107       while(cur) {
1108         msFreeImage(cur->image);
1109         next = cur->next;
1110         free(cur);
1111         cur = next;
1112       }
1113       image->ntiles = 0;
1114       renderer->freeImage(image);
1115     } else if( MS_RENDERER_IMAGEMAP(image->format) )
1116       msFreeImageIM(image);
1117     else if( MS_RENDERER_RAWDATA(image->format) )
1118       msFree(image->img.raw_16bit);
1119     else
1120       msSetError(MS_MISCERR, "Unknown image type",
1121                  "msFreeImage()");
1122 
1123     if (image->imagepath)
1124       free(image->imagepath);
1125     if (image->imageurl)
1126       free(image->imageurl);
1127 
1128     if( --image->format->refcount < 1 )
1129       msFreeOutputFormat( image->format );
1130 
1131     image->imagepath = NULL;
1132     image->imageurl = NULL;
1133 
1134     msFree( image->img_mask );
1135     image->img_mask= NULL;
1136 
1137     msFree( image );
1138   }
1139 }
1140 
1141 /*
1142 ** Return an array containing all the layer's index given a group name.
1143 ** If nothing is found, NULL is returned. The nCount is initalized
1144 ** to the number of elements in the returned array.
1145 ** Note : the caller of the function should free the array.
1146 */
msGetLayersIndexByGroup(mapObj * map,char * groupname,int * pnCount)1147 int *msGetLayersIndexByGroup(mapObj *map, char *groupname, int *pnCount)
1148 {
1149   int         i;
1150   int         iLayer = 0;
1151   int         *aiIndex;
1152 
1153   if(!groupname || !map || !pnCount) {
1154     return NULL;
1155   }
1156 
1157   aiIndex = (int *)msSmallMalloc(sizeof(int) * map->numlayers);
1158 
1159   for(i=0; i<map->numlayers; i++) {
1160     if(!GET_LAYER(map, i)->group) /* skip it */
1161       continue;
1162     if(strcmp(groupname, GET_LAYER(map, i)->group) == 0) {
1163       aiIndex[iLayer] = i;
1164       iLayer++;
1165     }
1166   }
1167 
1168   if (iLayer == 0) {
1169     free(aiIndex);
1170     aiIndex = NULL;
1171     *pnCount = 0;
1172   } else {
1173     aiIndex = (int *)msSmallRealloc(aiIndex, sizeof(int)* iLayer);
1174     *pnCount = iLayer;
1175   }
1176 
1177   return aiIndex;
1178 }
1179 
1180 /* ==================================================================== */
1181 /*      Measured shape utility functions.                               */
1182 /* ==================================================================== */
1183 
1184 
1185 /************************************************************************/
1186 /*        pointObj *msGetPointUsingMeasure(shapeObj *shape, double m)   */
1187 /*                                                                      */
1188 /*      Using a measured value get the XY location it corresonds        */
1189 /*      to.                                                             */
1190 /*                                                                      */
1191 /************************************************************************/
msGetPointUsingMeasure(shapeObj * shape,double m)1192 pointObj *msGetPointUsingMeasure(shapeObj *shape, double m)
1193 {
1194 #ifdef USE_POINT_Z_M
1195   pointObj    *point = NULL;
1196   lineObj     line;
1197   double      dfMin = 0;
1198   double      dfMax = 0;
1199   int         i,j = 0;
1200   int         bFound = 0;
1201   double      dfFirstPointX = 0;
1202   double      dfFirstPointY = 0;
1203   double      dfFirstPointM = 0;
1204   double      dfSecondPointX = 0;
1205   double      dfSecondPointY = 0;
1206   double      dfSecondPointM = 0;
1207   double      dfCurrentM = 0;
1208   double      dfFactor = 0;
1209 
1210   if (shape &&  shape->numlines > 0) {
1211     /* -------------------------------------------------------------------- */
1212     /*      check fir the first value (min) and the last value(max) to      */
1213     /*      see if the m is contained between these min and max.            */
1214     /* -------------------------------------------------------------------- */
1215     line = shape->line[0];
1216     dfMin = line.point[0].m;
1217     line = shape->line[shape->numlines-1];
1218     dfMax = line.point[line.numpoints-1].m;
1219 
1220     if (m >= dfMin && m <= dfMax) {
1221       for (i=0; i<shape->numlines; i++) {
1222         line = shape->line[i];
1223 
1224         for (j=0; j<line.numpoints; j++) {
1225           dfCurrentM = line.point[j].m;
1226           if (dfCurrentM > m) {
1227             bFound = 1;
1228 
1229             dfSecondPointX = line.point[j].x;
1230             dfSecondPointY = line.point[j].y;
1231             dfSecondPointM = line.point[j].m;
1232 
1233             /* -------------------------------------------------------------------- */
1234             /*      get the previous node xy values.                                */
1235             /* -------------------------------------------------------------------- */
1236             if (j > 0) { /* not the first point of the line */
1237               dfFirstPointX = line.point[j-1].x;
1238               dfFirstPointY = line.point[j-1].y;
1239               dfFirstPointM = line.point[j-1].m;
1240             } else { /* get last point of previous line */
1241               dfFirstPointX = shape->line[i-1].point[0].x;
1242               dfFirstPointY = shape->line[i-1].point[0].y;
1243               dfFirstPointM = shape->line[i-1].point[0].m;
1244             }
1245             break;
1246           }
1247         }
1248       }
1249     }
1250 
1251     if (!bFound)
1252       return NULL;
1253 
1254     /* -------------------------------------------------------------------- */
1255     /*      extrapolate the m value to get t he xy coordinate.              */
1256     /* -------------------------------------------------------------------- */
1257 
1258     if (dfFirstPointM != dfSecondPointM)
1259       dfFactor = (m-dfFirstPointM)/(dfSecondPointM - dfFirstPointM);
1260     else
1261       dfFactor = 0;
1262 
1263     point = (pointObj *)msSmallMalloc(sizeof(pointObj));
1264 
1265     point->x = dfFirstPointX + (dfFactor * (dfSecondPointX - dfFirstPointX));
1266     point->y = dfFirstPointY +
1267                (dfFactor * (dfSecondPointY - dfFirstPointY));
1268     point->m = dfFirstPointM +
1269                (dfFactor * (dfSecondPointM - dfFirstPointM));
1270 
1271     return point;
1272   }
1273 
1274   return NULL;
1275 #else
1276   msSetError(MS_MISCERR,
1277              "The \"m\" parameter for points is unavailable in your build.",
1278              "msGetPointUsingMeasure()");
1279   return NULL;
1280 #endif /* USE_POINT_Z_M */
1281 }
1282 
1283 
1284 /************************************************************************/
1285 /*       IntersectionPointLinepointObj *p, pointObj *a, pointObj *b)    */
1286 /*                                                                      */
1287 /*      Retunrs a point object corresponding to the intersection of     */
1288 /*      point p and a line formed of 2 points : a and b.                */
1289 /*                                                                      */
1290 /*      Alorith base on :                                               */
1291 /*      http://www.faqs.org/faqs/graphics/algorithms-faq/               */
1292 /*                                                                      */
1293 /*      Subject 1.02:How do I find the distance from a point to a line? */
1294 /*                                                                      */
1295 /*          Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By).*/
1296 /*          Let P be the point of perpendicular projection of C on AB.  The parameter*/
1297 /*          r, which indicates P's position along AB, is computed by the dot product */
1298 /*          of AC and AB divided by the square of the length of AB:     */
1299 /*                                                                      */
1300 /*          (1)     AC dot AB                                           */
1301 /*              r = ---------                                           */
1302 /*                  ||AB||^2                                            */
1303 /*                                                                      */
1304 /*          r has the following meaning:                                */
1305 /*                                                                      */
1306 /*              r=0      P = A                                          */
1307 /*              r=1      P = B                                          */
1308 /*              r<0      P is on the backward extension of AB           */
1309 /*              r>1      P is on the forward extension of AB            */
1310 /*              0<r<1    P is interior to AB                            */
1311 /*                                                                      */
1312 /*          The length of a line segment in d dimensions, AB is computed by:*/
1313 /*                                                                      */
1314 /*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 + ... + (Bd-Ad)^2)      */
1315 /*                                                                      */
1316 /*          so in 2D:                                                   */
1317 /*                                                                      */
1318 /*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )                       */
1319 /*                                                                      */
1320 /*          and the dot product of two vectors in d dimensions, U dot V is computed:*/
1321 /*                                                                      */
1322 /*              D = (Ux * Vx) + (Uy * Vy) + ... + (Ud * Vd)             */
1323 /*                                                                      */
1324 /*          so in 2D:                                                   */
1325 /*                                                                      */
1326 /*              D = (Ux * Vx) + (Uy * Vy)                               */
1327 /*                                                                      */
1328 /*          So (1) expands to:                                          */
1329 /*                                                                      */
1330 /*                  (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)                     */
1331 /*              r = -------------------------------                     */
1332 /*                                L^2                                   */
1333 /*                                                                      */
1334 /*          The point P can then be found:                              */
1335 /*                                                                      */
1336 /*              Px = Ax + r(Bx-Ax)                                      */
1337 /*              Py = Ay + r(By-Ay)                                      */
1338 /*                                                                      */
1339 /*          And the distance from A to P = r*L.                         */
1340 /*                                                                      */
1341 /*          Use another parameter s to indicate the location along PC, with the */
1342 /*          following meaning:                                          */
1343 /*                 s<0      C is left of AB                             */
1344 /*                 s>0      C is right of AB                            */
1345 /*                 s=0      C is on AB                                  */
1346 /*                                                                      */
1347 /*          Compute s as follows:                                       */
1348 /*                                                                      */
1349 /*                  (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)                       */
1350 /*              s = -----------------------------                       */
1351 /*                              L^2                                     */
1352 /*                                                                      */
1353 /*                                                                      */
1354 /*          Then the distance from C to P = |s|*L.                      */
1355 /*                                                                      */
1356 /************************************************************************/
msIntersectionPointLine(pointObj * p,pointObj * a,pointObj * b)1357 pointObj *msIntersectionPointLine(pointObj *p, pointObj *a, pointObj *b)
1358 {
1359   double r = 0;
1360   double L = 0;
1361   pointObj *result = NULL;
1362 
1363   if (p && a && b) {
1364     L = sqrt(((b->x - a->x)*(b->x - a->x)) +
1365              ((b->y - a->y)*(b->y - a->y)));
1366 
1367     if (L != 0)
1368       r = ((p->x - a->x)*(b->x - a->x) + (p->y - a->y)*(b->y - a->y))/(L*L);
1369     else
1370       r = 0;
1371 
1372     result = (pointObj *)msSmallMalloc(sizeof(pointObj));
1373     /* -------------------------------------------------------------------- */
1374     /*      We want to make sure that the point returned is on the line     */
1375     /*                                                                      */
1376     /*              r=0      P = A                                          */
1377     /*              r=1      P = B                                          */
1378     /*              r<0      P is on the backward extension of AB           */
1379     /*              r>1      P is on the forward extension of AB            */
1380     /*                    0<r<1    P is interior to AB                      */
1381     /* -------------------------------------------------------------------- */
1382     if (r < 0) {
1383       result->x = a->x;
1384       result->y = a->y;
1385     } else if (r > 1) {
1386       result->x = b->x;
1387       result->y = b->y;
1388     } else {
1389       result->x = a->x + r*(b->x - a->x);
1390       result->y = a->y + r*(b->y - a->y);
1391     }
1392 #ifdef USE_POINT_Z_M
1393     result->m = 0;
1394 #endif
1395   }
1396 
1397   return result;
1398 }
1399 
1400 
1401 /************************************************************************/
1402 /*         pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj   */
1403 /*      *point)                                                         */
1404 /*                                                                      */
1405 /*      Calculate the intersection point betwwen the point and the      */
1406 /*      shape and return the Measured value at the intersection.        */
1407 /************************************************************************/
msGetMeasureUsingPoint(shapeObj * shape,pointObj * point)1408 pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj *point)
1409 {
1410   double      dfMinDist = 1e35;
1411   double      dfDist = 0;
1412   pointObj    oFirst;
1413   pointObj    oSecond;
1414   int         i, j = 0;
1415   lineObj     line;
1416   pointObj    *poIntersectionPt = NULL;
1417 #ifdef USE_POINT_Z_M
1418   double      dfFactor = 0;
1419   double      dfDistTotal, dfDistToIntersection = 0;
1420 #endif
1421 
1422   if (shape && point) {
1423     for (i=0; i<shape->numlines; i++) {
1424       line = shape->line[i];
1425       /* -------------------------------------------------------------------- */
1426       /*      for each line (2 consecutive lines) get the distance between    */
1427       /*      the line and the point and determine which line segment is      */
1428       /*      the closeset to the point.                                      */
1429       /* -------------------------------------------------------------------- */
1430       for (j=0; j<line.numpoints-1; j++) {
1431         dfDist = msDistancePointToSegment(point, &line.point[j], &line.point[j+1]);
1432         if (dfDist < dfMinDist) {
1433           oFirst.x = line.point[j].x;
1434           oFirst.y = line.point[j].y;
1435 #ifdef USE_POINT_Z_M
1436           oFirst.m = line.point[j].m;
1437 #endif
1438 
1439           oSecond.x =  line.point[j+1].x;
1440           oSecond.y =  line.point[j+1].y;
1441 #ifdef USE_POINT_Z_M
1442           oSecond.m =  line.point[j+1].m;
1443 #endif
1444 
1445           dfMinDist = dfDist;
1446         }
1447       }
1448     }
1449     /* -------------------------------------------------------------------- */
1450     /*      once we have the nearest segment, look for the x,y location     */
1451     /*      which is the nearest intersection between the line and the      */
1452     /*      point.                                                          */
1453     /* -------------------------------------------------------------------- */
1454     poIntersectionPt = msIntersectionPointLine(point, &oFirst, &oSecond);
1455     if (poIntersectionPt) {
1456 #ifdef USE_POINT_Z_M
1457       dfDistTotal = sqrt(((oSecond.x - oFirst.x)*(oSecond.x - oFirst.x)) +
1458                          ((oSecond.y - oFirst.y)*(oSecond.y - oFirst.y)));
1459 
1460       dfDistToIntersection = sqrt(((poIntersectionPt->x - oFirst.x)*
1461                                    (poIntersectionPt->x - oFirst.x)) +
1462                                   ((poIntersectionPt->y - oFirst.y)*
1463                                    (poIntersectionPt->y - oFirst.y)));
1464 
1465       dfFactor = dfDistToIntersection / dfDistTotal;
1466 
1467       poIntersectionPt->m = oFirst.m + (oSecond.m - oFirst.m)*dfFactor;
1468 #endif
1469 
1470       return poIntersectionPt;
1471     }
1472 
1473   }
1474   return NULL;
1475 }
1476 
1477 /* ==================================================================== */
1478 /*   End   Measured shape utility functions.                            */
1479 /* ==================================================================== */
1480 
1481 
msGetAllGroupNames(mapObj * map,int * numTok)1482 char **msGetAllGroupNames(mapObj *map, int *numTok)
1483 {
1484   char        **papszGroups = NULL;
1485   int         bFound = 0;
1486   int         nCount = 0;
1487   int         i = 0, j = 0;
1488 
1489   assert(map);
1490   *numTok = 0;
1491 
1492   if (!map->layerorder) {
1493     map->layerorder = (int*)msSmallMalloc(map->numlayers * sizeof(int));
1494 
1495     /*
1496      * Initiate to default order
1497      */
1498     for (i=0; i<map->numlayers; i++)
1499       map->layerorder[i] = i;
1500   }
1501 
1502   if (map->numlayers > 0) {
1503     nCount = map->numlayers;
1504     papszGroups = (char **)msSmallMalloc(sizeof(char *)*nCount);
1505 
1506     for (i=0; i<nCount; i++)
1507       papszGroups[i] = NULL;
1508 
1509     for (i=0; i<nCount; i++) {
1510       layerObj *lp;
1511       lp = (GET_LAYER(map, map->layerorder[i]));
1512 
1513       bFound = 0;
1514       if (lp->group && lp->status != MS_DELETE) {
1515         for (j=0; j<*numTok; j++) {
1516           if (papszGroups[j] &&
1517               strcmp(lp->group, papszGroups[j]) == 0) {
1518             bFound = 1;
1519             break;
1520           }
1521         }
1522         if (!bFound) {
1523           /* New group... add to the list of groups found */
1524           papszGroups[(*numTok)] = msStrdup(lp->group);
1525           (*numTok)++;
1526         }
1527       }
1528     }
1529 
1530   }
1531 
1532   return papszGroups;
1533 }
1534 
1535 /************************************************************************/
1536 /*                         msForceTmpFileBase()                         */
1537 /************************************************************************/
1538 
1539 static int tmpCount = 0;
1540 static char *ForcedTmpBase = NULL;
1541 
msForceTmpFileBase(const char * new_base)1542 void msForceTmpFileBase( const char *new_base )
1543 {
1544   /* -------------------------------------------------------------------- */
1545   /*      Clear previous setting, if any.                                 */
1546   /* -------------------------------------------------------------------- */
1547   if( ForcedTmpBase != NULL ) {
1548     free( ForcedTmpBase );
1549     ForcedTmpBase = NULL;
1550   }
1551 
1552   tmpCount = -1;
1553 
1554   if( new_base == NULL )
1555     return;
1556 
1557   /* -------------------------------------------------------------------- */
1558   /*      Record new base.                                                */
1559   /* -------------------------------------------------------------------- */
1560   ForcedTmpBase = msStrdup( new_base );
1561   tmpCount = 0;
1562 }
1563 
1564 /**********************************************************************
1565  *                          msTmpFile()
1566  *
1567  * Generate a Unique temporary file.
1568  *
1569  * Returns char* which must be freed by caller.
1570  **********************************************************************/
msTmpFile(mapObj * map,const char * mappath,const char * tmppath,const char * ext)1571 char *msTmpFile(mapObj *map, const char *mappath, const char *tmppath, const char *ext)
1572 {
1573   char szPath[MS_MAXPATHLEN];
1574   const char *fullFname;
1575   char *tmpFileName; /* big enough for time + pid + ext */
1576   char *tmpBase = NULL;
1577 
1578   tmpBase = msTmpPath(map, mappath, tmppath);
1579   tmpFileName = msTmpFilename(ext);
1580 
1581   fullFname = msBuildPath(szPath, tmpBase, tmpFileName);
1582 
1583   free(tmpFileName);
1584   free(tmpBase);
1585 
1586   if (fullFname)
1587     return msStrdup(fullFname);
1588 
1589   return NULL;
1590 }
1591 
1592 /**********************************************************************
1593  *                          msTmpPath()
1594  *
1595  * Return the temporary path based on the platform.
1596  *
1597  * Returns char* which must be freed by caller.
1598  **********************************************************************/
msTmpPath(mapObj * map,const char * mappath,const char * tmppath)1599 char *msTmpPath(mapObj *map, const char *mappath, const char *tmppath)
1600 {
1601   char szPath[MS_MAXPATHLEN];
1602   const char *fullPath;
1603   const char *tmpBase;
1604 #ifdef _WIN32
1605   DWORD dwRetVal = 0;
1606   TCHAR lpTempPathBuffer[MAX_PATH];
1607 #endif
1608 
1609   if( ForcedTmpBase != NULL )
1610     tmpBase = ForcedTmpBase;
1611   else if (tmppath != NULL)
1612     tmpBase = tmppath;
1613   else if (getenv("MS_TEMPPATH"))
1614     tmpBase = getenv("MS_TEMPPATH");
1615   else if (map && map->web.temppath)
1616     tmpBase = map->web.temppath;
1617   else { /* default paths */
1618 #ifndef _WIN32
1619     tmpBase = "/tmp/";
1620 #else
1621     dwRetVal =  GetTempPath(MAX_PATH,          /* length of the buffer */
1622                             lpTempPathBuffer); /* buffer for path */
1623     if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
1624       tmpBase = "C:\\";
1625     } else {
1626       tmpBase = (char*)lpTempPathBuffer;
1627     }
1628 #endif
1629   }
1630 
1631   fullPath = msBuildPath(szPath, mappath, tmpBase);
1632   return msStrdup(fullPath);
1633 }
1634 
1635 /**********************************************************************
1636  *                          msTmpFilename()
1637  *
1638  * Generate a Unique temporary filename.
1639  *
1640  * Returns char* which must be freed by caller.
1641  **********************************************************************/
msTmpFilename(const char * ext)1642 char *msTmpFilename(const char *ext)
1643 {
1644   char *tmpFname;
1645   int tmpFnameBufsize;
1646   char *fullFname;
1647   char tmpId[128]; /* big enough for time + pid + ext */
1648 
1649   snprintf(tmpId, sizeof(tmpId), "%lx_%x",(long)time(NULL),(int)getpid());
1650 
1651   if (ext == NULL)  ext = "";
1652   tmpFnameBufsize = strlen(tmpId) + 10 + strlen(ext) + 1;
1653   tmpFname = (char*)msSmallMalloc(tmpFnameBufsize);
1654 
1655   msAcquireLock( TLOCK_TMPFILE );
1656   snprintf(tmpFname, tmpFnameBufsize, "%s_%x.%s", tmpId, tmpCount++, ext);
1657   msReleaseLock( TLOCK_TMPFILE );
1658 
1659   fullFname = msStrdup(tmpFname);
1660   free(tmpFname);
1661 
1662   return fullFname;
1663 }
1664 
1665 /**
1666  *  Generic function to Initalize an image object.
1667  */
msImageCreate(int width,int height,outputFormatObj * format,char * imagepath,char * imageurl,double resolution,double defresolution,colorObj * bg)1668 imageObj *msImageCreate(int width, int height, outputFormatObj *format,
1669                         char *imagepath, char *imageurl, double resolution,
1670                         double defresolution, colorObj *bg)
1671 {
1672   imageObj *image = NULL;
1673   if(MS_RENDERER_PLUGIN(format)) {
1674 
1675     image = format->vtable->createImage(width,height,format,bg);
1676     if (image == NULL) {
1677       msSetError(MS_MEMERR, "Unable to create new image object.", "msImageCreate()");
1678       return NULL;
1679     }
1680 
1681     image->format = format;
1682     format->refcount++;
1683 
1684     image->width = width;
1685     image->height = height;
1686     image->imagepath = NULL;
1687     image->imageurl = NULL;
1688     image->tilecache = NULL;
1689     image->ntiles = 0;
1690     image->resolution = resolution;
1691     image->resolutionfactor = resolution/defresolution;
1692 
1693     if (imagepath)
1694       image->imagepath = msStrdup(imagepath);
1695     if (imageurl)
1696       image->imageurl = msStrdup(imageurl);
1697   } else if( MS_RENDERER_RAWDATA(format) ) {
1698     if( format->imagemode != MS_IMAGEMODE_INT16
1699         && format->imagemode != MS_IMAGEMODE_FLOAT32
1700         && format->imagemode != MS_IMAGEMODE_BYTE ) {
1701       msSetError(MS_IMGERR,
1702                  "Attempt to use illegal imagemode with rawdata renderer.",
1703                  "msImageCreate()" );
1704       return NULL;
1705     }
1706 
1707     image = (imageObj *)calloc(1,sizeof(imageObj));
1708     if (image == NULL) {
1709       msSetError(MS_MEMERR, "Unable to create new image object.", "msImageCreate()");
1710       return NULL;
1711     }
1712 
1713     if( format->imagemode == MS_IMAGEMODE_INT16 )
1714       image->img.raw_16bit = (short *)
1715                              msSmallCalloc(sizeof(short),width*height*format->bands);
1716     else if( format->imagemode == MS_IMAGEMODE_FLOAT32 )
1717       image->img.raw_float = (float *)
1718                              msSmallCalloc(sizeof(float),width*height*format->bands);
1719     else if( format->imagemode == MS_IMAGEMODE_BYTE )
1720       image->img.raw_byte = (unsigned char *)
1721                             msSmallCalloc(sizeof(unsigned char),width*height*format->bands);
1722 
1723     if( image->img.raw_16bit == NULL ) {
1724       msFree( image );
1725       msSetError(MS_IMGERR,
1726                  "Attempt to allocate raw image failed, out of memory.",
1727                  "msImageCreate()" );
1728       return NULL;
1729     }
1730 
1731     image->img_mask = msAllocBitArray( width*height );
1732 
1733     image->format = format;
1734     format->refcount++;
1735 
1736     image->width = width;
1737     image->height = height;
1738     image->imagepath = NULL;
1739     image->imageurl = NULL;
1740     image->resolution = resolution;
1741     image->resolutionfactor = resolution/defresolution;
1742 
1743     if (imagepath)
1744       image->imagepath = msStrdup(imagepath);
1745     if (imageurl)
1746       image->imageurl = msStrdup(imageurl);
1747 
1748     /* initialize to requested nullvalue if there is one */
1749     if( msGetOutputFormatOption(image->format,"NULLVALUE",NULL) != NULL ) {
1750       int i = image->width * image->height * format->bands;
1751       const char *nullvalue = msGetOutputFormatOption(image->format,
1752                               "NULLVALUE",NULL);
1753 
1754       if( atof(nullvalue) == 0.0 )
1755         /* nothing to do */;
1756       else if( format->imagemode == MS_IMAGEMODE_INT16 ) {
1757         short nv = atoi(nullvalue);
1758         for( ; i > 0; )
1759           image->img.raw_16bit[--i] = nv;
1760       } else if( format->imagemode == MS_IMAGEMODE_FLOAT32 ) {
1761         float nv = atof(nullvalue);
1762         for( ; i > 0; )
1763           image->img.raw_float[--i] = nv;
1764       } else if( format->imagemode == MS_IMAGEMODE_BYTE ) {
1765         unsigned char nv = (unsigned char) atoi(nullvalue);
1766 
1767         memset( image->img.raw_byte, nv, i );
1768       }
1769     }
1770   } else if( MS_RENDERER_IMAGEMAP(format) ) {
1771     image = msImageCreateIM(width, height, format,
1772                             imagepath, imageurl, resolution, defresolution);
1773   } else {
1774     msSetError(MS_MISCERR,
1775                "Unsupported renderer requested, unable to initialize image.",
1776                "msImageCreate()");
1777     return NULL;
1778   }
1779 
1780   if(!image)
1781     msSetError(MS_IMGERR, "Unable to initialize image.", "msImageCreate()");
1782   image->refpt.x = image->refpt.y = 0;
1783   return image;
1784 }
1785 
1786 
1787 /**
1788  * Generic function to transorm a point.
1789  *
1790  */
msTransformPoint(pointObj * point,rectObj * extent,double cellsize,imageObj * image)1791 void  msTransformPoint(pointObj *point, rectObj *extent, double cellsize,
1792                        imageObj *image)
1793 {
1794   double invcellsize;
1795   /*We should probabaly have a function defined at all the renders*/
1796   if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
1797     if(image->format->renderer == MS_RENDER_WITH_KML) {
1798       return;
1799     }
1800   }
1801   invcellsize = 1.0/cellsize;
1802   point->x = MS_MAP2IMAGE_X_IC_DBL(point->x, extent->minx, invcellsize);
1803   point->y = MS_MAP2IMAGE_Y_IC_DBL(point->y, extent->maxy, invcellsize);
1804 }
1805 
1806 
1807 /*
1808 ** Helper functions supplied as part of bug #2868 solution. Consider moving these to
1809 ** mapprimitive.c for more general use.
1810 */
1811 
1812 /* vector difference */
point_diff(const pointObj a,const pointObj b)1813 static pointObj point_diff(const pointObj a, const pointObj b)
1814 {
1815   pointObj retv;
1816   retv.x = a.x-b.x;
1817   retv.y = a.y-b.y;
1818 #ifdef USE_POINT_Z_M
1819   retv.z = a.z-b.z;
1820   retv.m = a.m-b.m;
1821 #endif
1822   return retv;
1823 }
1824 
1825 /* vector sum */
point_sum(const pointObj a,const pointObj b)1826 static pointObj point_sum(const pointObj a, const pointObj b)
1827 {
1828   pointObj retv;
1829   retv.x = a.x+b.x;
1830   retv.y = a.y+b.y;
1831 #ifdef USE_POINT_Z_M
1832   retv.z = a.z+b.z;
1833   retv.m = a.m+b.m;
1834 #endif
1835   return retv;
1836 }
1837 
1838 /* vector multiply */
point_mul(const pointObj a,double b)1839 static pointObj point_mul(const pointObj a, double b)
1840 {
1841   pointObj retv;
1842   retv.x = a.x*b;
1843   retv.y = a.y*b;
1844 #ifdef USE_POINT_Z_M
1845   retv.z = a.z*b;
1846   retv.m = a.m*b;
1847 #endif
1848   return retv;
1849 }
1850 
1851 /* vector ??? */
point_abs2(const pointObj a)1852 static double point_abs2(const pointObj a)
1853 {
1854 #ifdef USE_POINT_Z_M
1855   return a.x*a.x+a.y*a.y+a.z*a.z+a.m*a.m;
1856 #else
1857   return a.x*a.x+a.y*a.y;
1858 #endif
1859 }
1860 
1861 /* vector normal */
point_norm(const pointObj a)1862 static pointObj point_norm(const pointObj a)
1863 {
1864   double lenmul;
1865   pointObj retv;
1866   int norm_vector;
1867 
1868   norm_vector = a.x==0 && a.y==0;
1869 #ifdef USE_POINT_Z_M
1870   norm_vector = norm_vector && a.z==0 && a.m==0;
1871 #endif
1872   if (norm_vector)
1873     return a;
1874 
1875   lenmul=1.0/sqrt(point_abs2(a));  /* this seems to be the costly operation */
1876 
1877   retv.x = a.x*lenmul;
1878   retv.y = a.y*lenmul;
1879 #ifdef USE_POINT_Z_M
1880   retv.z = a.z*lenmul;
1881   retv.m = a.m*lenmul;
1882 #endif
1883 
1884   return retv;
1885 }
1886 
1887 /* rotate a vector 90 degrees */
point_rotz90(const pointObj a)1888 static pointObj point_rotz90(const pointObj a)
1889 {
1890   double nx=-1.0*a.y, ny=a.x;
1891   pointObj retv=a;
1892   retv.x=nx;
1893   retv.y=ny;
1894   return retv;
1895 }
1896 
1897 /* vector cross product (warning: z and m dimensions are ignored!) */
point_cross(const pointObj a,const pointObj b)1898 static double point_cross(const pointObj a, const pointObj b)
1899 {
1900   return a.x*b.y-a.y*b.x;
1901 }
1902 
msOffsetCurve(shapeObj * p,double offset)1903 shapeObj *msOffsetCurve(shapeObj *p, double offset)
1904 {
1905   shapeObj *ret;
1906   int i, j, first,idx,ok=0;
1907 #if defined USE_GEOS && (GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3))
1908   ret = msGEOSOffsetCurve(p,offset);
1909   /* GEOS curve offsetting can fail sometimes, we continue with our own implementation
1910    if that is the case.*/
1911   if(ret)
1912     return ret;
1913   /* clear error raised by geos in this case */
1914   msResetErrorList();
1915 #endif
1916   /*
1917   ** For offset corner point calculation 1/sin() is used
1918   ** to avoid 1/0 division (and long spikes) we define a
1919   ** limit for sin().
1920   */
1921 #define CURVE_SIN_LIMIT 0.3
1922   ret = (shapeObj*)msSmallMalloc(sizeof(shapeObj));
1923   msInitShape(ret);
1924   ret->numlines = p->numlines;
1925   ret->line=(lineObj*)msSmallMalloc(sizeof(lineObj)*ret->numlines);
1926   for(i=0; i<ret->numlines; i++) {
1927     ret->line[i].numpoints=p->line[i].numpoints;
1928     ret->line[i].point=(pointObj*)msSmallMalloc(sizeof(pointObj)*ret->line[i].numpoints);
1929   }
1930   for (i = 0; i < p->numlines; i++) {
1931     pointObj old_pt = {0}, old_diffdir, old_offdir;
1932     if(p->line[i].numpoints<2) {
1933       ret->line[i].numpoints = 0;
1934       continue; /* skip degenerate points */
1935     }
1936     ok = 1;
1937     /* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see that it
1938      * is not an error to do so, and prints a warning */
1939     old_offdir.x=old_offdir.y=old_diffdir.x=old_diffdir.y = 0;
1940 
1941     idx=0;
1942     first = 1;
1943 
1944     /* saved metrics of the last processed point */
1945     if (p->line[i].numpoints>0)
1946       old_pt=p->line[i].point[0];
1947     for(j=1; j<p->line[i].numpoints; j++) {
1948       const pointObj pt = p->line[i].point[j]; /* place of the point */
1949       const pointObj diffdir = point_norm(point_diff(pt,old_pt)); /* direction of the line */
1950       const pointObj offdir = point_rotz90(diffdir); /* direction where the distance between the line and the offset is measured */
1951       pointObj offpt; /* this will be the corner point of the offset line */
1952 
1953       /* offset line points computation */
1954       if(first == 1) { /* first point */
1955         first = 0;
1956         offpt = point_sum(old_pt,point_mul(offdir,offset));
1957       } else { /* middle points */
1958         /* curve is the angle of the last and the current line's direction (supplementary angle of the shape's inner angle) */
1959         double sin_curve = point_cross(diffdir,old_diffdir);
1960         double cos_curve = point_cross(old_offdir,diffdir);
1961         if ((-1.0)*CURVE_SIN_LIMIT < sin_curve && sin_curve < CURVE_SIN_LIMIT) {
1962           /* do not calculate 1/sin, instead use a corner point approximation: average of the last and current offset direction and length */
1963 
1964           /*
1965           ** TODO: fair for obtuse inner angles, however, positive and negative
1966           ** acute inner angles would need special handling - similar to LINECAP
1967           ** to avoid drawing of long spikes
1968           */
1969           offpt = point_sum(old_pt, point_mul(point_sum(offdir, old_offdir),0.5*offset));
1970         } else {
1971           double base_shift = -1.0*(1.0+cos_curve)/sin_curve;
1972           offpt = point_sum(old_pt, point_mul(point_sum(point_mul(diffdir,base_shift),offdir), offset));
1973         }
1974       }
1975       ret->line[i].point[idx]=offpt;
1976       idx++;
1977       old_pt=pt;
1978       old_diffdir=diffdir;
1979       old_offdir=offdir;
1980     }
1981 
1982     /* last point */
1983     if(first == 0) {
1984       pointObj offpt=point_sum(old_pt,point_mul(old_offdir,offset));
1985       ret->line[i].point[idx]=offpt;
1986       idx++;
1987     }
1988 
1989     if(idx != p->line[i].numpoints) {
1990       /* printf("shouldn't happen :(\n"); */
1991       ret->line[i].numpoints=idx;
1992       ret->line=msSmallRealloc(ret->line,ret->line[i].numpoints*sizeof(pointObj));
1993     }
1994   }
1995   if(!ok) ret->numlines = 0; /* all lines where degenerate */
1996   return ret;
1997 }
1998 
msOffsetPolyline(shapeObj * p,double offsetx,double offsety)1999 shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety)
2000 {
2001   int i, j;
2002   shapeObj *ret;
2003   if(offsety == MS_STYLE_SINGLE_SIDED_OFFSET) { /* complex calculations */
2004     return msOffsetCurve(p,offsetx);
2005   } else if(offsety == MS_STYLE_DOUBLE_SIDED_OFFSET) {
2006     shapeObj *tmp1;
2007     ret = msOffsetCurve(p,offsetx/2.0);
2008     tmp1 = msOffsetCurve(p, -offsetx/2.0);
2009     for(i=0;i<tmp1->numlines; i++) {
2010       msAddLineDirectly(ret,tmp1->line + i);
2011     }
2012     msFreeShape(tmp1);
2013     free(tmp1);
2014     return ret;
2015   }
2016 
2017   ret = (shapeObj*)msSmallMalloc(sizeof(shapeObj));
2018   msInitShape(ret);
2019   ret->numlines = p->numlines;
2020   ret->line=(lineObj*)msSmallMalloc(sizeof(lineObj)*ret->numlines);
2021   for(i=0; i<ret->numlines; i++) {
2022     ret->line[i].numpoints=p->line[i].numpoints;
2023     ret->line[i].point=(pointObj*)msSmallMalloc(sizeof(pointObj)*ret->line[i].numpoints);
2024   }
2025 
2026   for (i = 0; i < p->numlines; i++) {
2027     for(j=0; j<p->line[i].numpoints; j++) {
2028       ret->line[i].point[j].x=p->line[i].point[j].x+offsetx;
2029       ret->line[i].point[j].y=p->line[i].point[j].y+offsety;
2030     }
2031   }
2032 
2033   return ret;
2034 }
2035 
2036 /*
2037 -------------------------------------------------------------------------------
2038  msSetup()
2039 
2040  Contributed by Jerry Pisk in bug 1203.  Heads off potential race condition
2041  in initializing GD font cache with multiple threads.  Should be called from
2042  mapscript module initialization code.
2043 -------------------------------------------------------------------------------
2044 */
2045 
msSetup()2046 int msSetup()
2047 {
2048 #ifdef _WIN32
2049   char* maxfiles = getenv("MS_MAX_OPEN_FILES");
2050   if (maxfiles) {
2051     int res = _getmaxstdio();
2052     if (res < 2048) {
2053       res = _setmaxstdio(atoi(maxfiles));
2054       assert(res != -1);
2055     }
2056   }
2057 #endif
2058 
2059 #ifdef USE_THREAD
2060   msThreadInit();
2061 #endif
2062 
2063   /* Use PROJ_LIB env vars if set */
2064   msProjLibInitFromEnv();
2065 
2066   /* Use MS_ERRORFILE and MS_DEBUGLEVEL env vars if set */
2067   if (msDebugInitFromEnv() != MS_SUCCESS)
2068     return MS_FAILURE;
2069 
2070 #ifdef USE_GEOS
2071   msGEOSSetup();
2072 #endif
2073 
2074 #ifdef USE_RSVG
2075 #if !GLIB_CHECK_VERSION(2, 35, 0)
2076   g_type_init();
2077 #endif
2078 #endif
2079 
2080   msFontCacheSetup();
2081 
2082 
2083   return MS_SUCCESS;
2084 }
2085 
2086 /* This is intended to be a function to cleanup anything that "hangs around"
2087    when all maps are destroyed, like Registered GDAL drivers, and so forth. */
2088 #ifndef NDEBUG
2089 #if defined(USE_LIBXML2)
2090 #include "maplibxml2.h"
2091 #endif
2092 #endif
msCleanup()2093 void msCleanup()
2094 {
2095   msForceTmpFileBase( NULL );
2096   msConnPoolFinalCleanup();
2097   /* Lexer string parsing variable */
2098   if (msyystring_buffer != NULL) {
2099     msFree(msyystring_buffer);
2100     msyystring_buffer = NULL;
2101   }
2102   msyylex_destroy();
2103 
2104   msOGRCleanup();
2105   msGDALCleanup();
2106 
2107   /* Release both GDAL and OGR resources */
2108   msAcquireLock( TLOCK_GDAL );
2109 #if GDAL_VERSION_MAJOR >= 3 || (GDAL_VERSION_MAJOR == 2 && GDAL_VERSION_MINOR == 4)
2110   /* Cleanup some GDAL global resources in particular */
2111   GDALDestroy();
2112 #else
2113   GDALDestroyDriverManager();
2114 #endif
2115   msReleaseLock( TLOCK_GDAL );
2116 
2117 
2118 #if PROJ_VERSION_MAJOR < 6
2119 #  if PJ_VERSION >= 480
2120   pj_clear_initcache();
2121 #  endif
2122   pj_deallocate_grids();
2123 #endif
2124   msSetPROJ_LIB( NULL, NULL );
2125   msProjectionContextPoolCleanup();
2126 
2127 #if defined(USE_CURL)
2128   msHTTPCleanup();
2129 #endif
2130 
2131 #ifdef USE_GEOS
2132   msGEOSCleanup();
2133 #endif
2134 
2135 /* make valgrind happy on debug code */
2136 #ifndef NDEBUG
2137 #ifdef USE_CAIRO
2138   msCairoCleanup();
2139 #endif
2140 #if defined(USE_LIBXML2)
2141   xmlCleanupParser();
2142 #endif
2143 #endif
2144 
2145   msFontCacheCleanup();
2146 
2147   msTimeCleanup();
2148 
2149   msIO_Cleanup();
2150 
2151   msResetErrorList();
2152 
2153   /* Close/cleanup log/debug output. Keep this at the very end. */
2154   msDebugCleanup();
2155 
2156   /* Clean up the vtable factory */
2157   msPluginFreeVirtualTableFactory();
2158 }
2159 
2160 /************************************************************************/
2161 /*                            msAlphaBlend()                            */
2162 /*                                                                      */
2163 /*      Function to overlay/blend an RGBA value into an existing        */
2164 /*      RGBA value using the Porter-Duff "over" operator.               */
2165 /*      Primarily intended for use with rasterBufferObj                 */
2166 /*      raster rendering.  The "src" is the overlay value, and "dst"    */
2167 /*      is the existing value being overlaid. dst is expected to be     */
2168 /*      premultiplied, but the source should not be.                    */
2169 /*                                                                      */
2170 /*      NOTE: alpha_dst may be NULL.                                    */
2171 /************************************************************************/
2172 
msAlphaBlend(unsigned char red_src,unsigned char green_src,unsigned char blue_src,unsigned char alpha_src,unsigned char * red_dst,unsigned char * green_dst,unsigned char * blue_dst,unsigned char * alpha_dst)2173 void msAlphaBlend( unsigned char red_src, unsigned char green_src,
2174                    unsigned char blue_src, unsigned char alpha_src,
2175                    unsigned char *red_dst, unsigned char *green_dst,
2176                    unsigned char *blue_dst, unsigned char *alpha_dst )
2177 {
2178   /* -------------------------------------------------------------------- */
2179   /*      Simple cases we want to handle fast.                            */
2180   /* -------------------------------------------------------------------- */
2181   if( alpha_src == 0 )
2182     return;
2183 
2184   if( alpha_src == 255 ) {
2185     *red_dst = red_src;
2186     *green_dst = green_src;
2187     *blue_dst = blue_src;
2188     if( alpha_dst )
2189       *alpha_dst = 255;
2190     return;
2191   }
2192 
2193   /* -------------------------------------------------------------------- */
2194   /*      Premultiple alpha for source values now.                        */
2195   /* -------------------------------------------------------------------- */
2196   red_src   = red_src * alpha_src / 255;
2197   green_src = green_src * alpha_src / 255;
2198   blue_src  = blue_src * alpha_src / 255;
2199 
2200   /* -------------------------------------------------------------------- */
2201   /*      Another pretty fast case if there is nothing in the             */
2202   /*      destination to mix with.                                        */
2203   /* -------------------------------------------------------------------- */
2204   if( alpha_dst && *alpha_dst == 0) {
2205     *red_dst = red_src;
2206     *green_dst = green_src;
2207     *blue_dst = blue_src;
2208     *alpha_dst = alpha_src;
2209     return;
2210   }
2211 
2212   /* -------------------------------------------------------------------- */
2213   /*      Cases with actual blending.                                     */
2214   /* -------------------------------------------------------------------- */
2215   if(!alpha_dst || *alpha_dst == 255) {
2216     int weight_dst = 256 - alpha_src;
2217 
2218     *red_dst   = (256 * red_src   + *red_dst   * weight_dst) >> 8;
2219     *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
2220     *blue_dst  = (256 * blue_src  + *blue_dst  * weight_dst) >> 8;
2221   } else {
2222     int   weight_dst = (256 - alpha_src);
2223 
2224     *red_dst   = (256 * red_src   + *red_dst   * weight_dst) >> 8;
2225     *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
2226     *blue_dst  = (256 * blue_src  + *blue_dst  * weight_dst) >> 8;
2227 
2228     *alpha_dst = (256 * alpha_src + *alpha_dst * weight_dst) >> 8;
2229   }
2230 }
2231 
2232 /************************************************************************/
2233 /*                           msAlphaBlendPM()                           */
2234 /*                                                                      */
2235 /*      Same as msAlphaBlend() except that the source RGBA is           */
2236 /*      assumed to already be premultiplied.                            */
2237 /************************************************************************/
2238 
msAlphaBlendPM(unsigned char red_src,unsigned char green_src,unsigned char blue_src,unsigned char alpha_src,unsigned char * red_dst,unsigned char * green_dst,unsigned char * blue_dst,unsigned char * alpha_dst)2239 void msAlphaBlendPM( unsigned char red_src, unsigned char green_src,
2240                      unsigned char blue_src, unsigned char alpha_src,
2241                      unsigned char *red_dst, unsigned char *green_dst,
2242                      unsigned char *blue_dst, unsigned char *alpha_dst )
2243 {
2244   /* -------------------------------------------------------------------- */
2245   /*      Simple cases we want to handle fast.                            */
2246   /* -------------------------------------------------------------------- */
2247   if( alpha_src == 0 )
2248     return;
2249 
2250   if( alpha_src == 255 ) {
2251     *red_dst = red_src;
2252     *green_dst = green_src;
2253     *blue_dst = blue_src;
2254     if( alpha_dst )
2255       *alpha_dst = 255;
2256     return;
2257   }
2258 
2259   /* -------------------------------------------------------------------- */
2260   /*      Another pretty fast case if there is nothing in the             */
2261   /*      destination to mix with.                                        */
2262   /* -------------------------------------------------------------------- */
2263   if( alpha_dst && *alpha_dst == 0) {
2264     *red_dst = red_src;
2265     *green_dst = green_src;
2266     *blue_dst = blue_src;
2267     *alpha_dst = alpha_src;
2268     return;
2269   }
2270 
2271   /* -------------------------------------------------------------------- */
2272   /*      Cases with actual blending.                                     */
2273   /* -------------------------------------------------------------------- */
2274   if(!alpha_dst || *alpha_dst == 255) {
2275     int weight_dst = 255 - alpha_src;
2276 
2277     *red_dst   = (alpha_src * red_src   + *red_dst   * weight_dst) >> 8;
2278     *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
2279     *blue_dst  = (alpha_src * blue_src  + *blue_dst  * weight_dst) >> 8;
2280   } else {
2281     int   weight_dst = (255 - alpha_src);
2282 
2283     *red_dst   = (alpha_src * red_src   + *red_dst   * weight_dst) >> 8;
2284     *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
2285     *blue_dst  = (alpha_src * blue_src  + *blue_dst  * weight_dst) >> 8;
2286 
2287     *alpha_dst = (255 * alpha_src + *alpha_dst * weight_dst) >> 8;
2288   }
2289 }
2290 
msRGBtoHSL(colorObj * rgb,double * h,double * s,double * l)2291 void msRGBtoHSL(colorObj *rgb, double *h, double *s, double *l) {
2292   double r = rgb->red/255.0, g = rgb->green/255.0, b = rgb->blue/255.0;
2293   double maxv = MS_MAX(MS_MAX(r, g), b), minv = MS_MIN(MS_MIN(r, g), b);
2294   double d = maxv - minv;
2295 
2296   *h = 0, *s = 0;
2297   *l = (maxv + minv) / 2;
2298 
2299   if (maxv != minv)
2300   {
2301     *s = *l > 0.5 ? d / (2 - maxv - minv) : d / (maxv + minv);
2302     if (maxv == r) { *h = (g - b) / d + (g < b ? 6 : 0); }
2303     else if (maxv == g) { *h = (b - r) / d + 2; }
2304     else if (maxv == b) { *h = (r - g) / d + 4; }
2305     *h /= 6;
2306   }
2307 }
2308 
hue_to_rgb(double p,double q,double t)2309 static double hue_to_rgb(double p, double q, double t) {
2310   if(t < 0) t += 1;
2311   if(t > 1) t -= 1;
2312   if(t < 0.1666666666666666) return p + (q - p) * 6 * t;
2313   if(t < 0.5) return q;
2314   if(t < 0.6666666666666666) return p + (q - p) * (0.666666666666 - t) * 6;
2315   return p;
2316 }
2317 
msHSLtoRGB(double h,double s,double l,colorObj * rgb)2318 void msHSLtoRGB(double h, double s, double l, colorObj *rgb) {
2319   double r, g, b;
2320 
2321   if(s == 0){
2322     r = g = b = l;
2323   } else {
2324 
2325     double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
2326     double p = 2 * l - q;
2327     r = hue_to_rgb(p, q, h + 0.33333333333333333);
2328     g = hue_to_rgb(p, q, h);
2329     b = hue_to_rgb(p, q, h - 0.33333333333333333);
2330   }
2331   rgb->red = r * 255;
2332   rgb->green = g * 255;
2333   rgb->blue = b * 255;
2334 }
2335 
2336 /*
2337  RFC 24: check if the parent pointer is NULL and raise an error otherwise
2338 */
msCheckParentPointer(void * p,char * objname)2339 int msCheckParentPointer(void* p, char *objname)
2340 {
2341   char* msg=NULL;
2342   if (p == NULL) {
2343     if(objname != NULL) {
2344       msSetError(MS_NULLPARENTERR, "The %s parent object is null", "msCheckParentPointer()", msg);
2345     } else {
2346       msSetError(MS_NULLPARENTERR, "The parent object is null", "msCheckParentPointer()");
2347     }
2348     return MS_FAILURE;
2349   }
2350   return MS_SUCCESS;
2351 }
2352 
msBufferInit(bufferObj * buffer)2353 void msBufferInit(bufferObj *buffer)
2354 {
2355   buffer->data=NULL;
2356   buffer->size=0;
2357   buffer->available=0;
2358   buffer->_next_allocation_size = MS_DEFAULT_BUFFER_ALLOC;
2359 }
2360 
msBufferResize(bufferObj * buffer,size_t target_size)2361 void msBufferResize(bufferObj *buffer, size_t target_size)
2362 {
2363   while(buffer->available <= target_size) {
2364     buffer->data = msSmallRealloc(buffer->data,buffer->available+buffer->_next_allocation_size);
2365     buffer->available += buffer->_next_allocation_size;
2366     buffer->_next_allocation_size *= 2;
2367   }
2368 }
2369 
msBufferAppend(bufferObj * buffer,void * data,size_t length)2370 void msBufferAppend(bufferObj *buffer, void *data, size_t length)
2371 {
2372   if(buffer->available < buffer->size+length) {
2373     msBufferResize(buffer,buffer->size+length);
2374   }
2375   memcpy(&(buffer->data[buffer->size]),data,length);
2376   buffer->size += length;
2377 }
2378 
msBufferFree(bufferObj * buffer)2379 void msBufferFree(bufferObj *buffer)
2380 {
2381   if(buffer->available>0)
2382     free(buffer->data);
2383 }
2384 
2385 
msFreeRasterBuffer(rasterBufferObj * b)2386 void msFreeRasterBuffer(rasterBufferObj *b)
2387 {
2388   switch(b->type) {
2389     case MS_BUFFER_BYTE_RGBA:
2390       msFree(b->data.rgba.pixels);
2391       b->data.rgba.pixels = NULL;
2392       break;
2393     case MS_BUFFER_BYTE_PALETTE:
2394       msFree(b->data.palette.pixels);
2395       msFree(b->data.palette.palette);
2396       b->data.palette.pixels = NULL;
2397       b->data.palette.palette = NULL;
2398       break;
2399   }
2400 
2401 }
2402 
2403 /*
2404 ** Issue #3043: Layer extent comparison short circuit.
2405 **
2406 ** msExtentsOverlap()
2407 **
2408 ** Returns MS_TRUE if map extent and layer extent overlap,
2409 ** MS_FALSE if they are disjoint, and MS_UNKNOWN if there is
2410 ** not enough info to calculate a deterministic answer.
2411 **
2412 */
msExtentsOverlap(mapObj * map,layerObj * layer)2413 int msExtentsOverlap(mapObj *map, layerObj *layer)
2414 {
2415   rectObj map_extent;
2416   rectObj layer_extent;
2417 
2418   /* No extent info? Nothing we can do, return MS_UNKNOWN. */
2419   if( (map->extent.minx == -1) && (map->extent.miny == -1) && (map->extent.maxx == -1 ) && (map->extent.maxy == -1) ) return MS_UNKNOWN;
2420   if( (layer->extent.minx == -1) && (layer->extent.miny == -1) && (layer->extent.maxx == -1 ) && (layer->extent.maxy == -1) ) return MS_UNKNOWN;
2421 
2422   /* No map projection? Let someone else sort this out. */
2423   if( ! (map->projection.numargs > 0) )
2424     return MS_UNKNOWN;
2425 
2426   /* No layer projection? Perform naive comparison, because they are
2427   ** in the same projection. */
2428   if( ! (layer->projection.numargs > 0) )
2429     return msRectOverlap( &(map->extent), &(layer->extent) );
2430 
2431   /* In the case where map and layer projections are identical, and the */
2432   /* bounding boxes don't cross the dateline, do simple rectangle comparison */
2433   if( map->extent.minx < map->extent.maxx &&
2434       layer->extent.minx < layer->extent.maxx &&
2435       !msProjectionsDiffer(&(map->projection), &(layer->projection)) ) {
2436     return msRectOverlap( &(map->extent), &(layer->extent) );
2437   }
2438 
2439   /* We need to transform our rectangles for comparison,
2440   ** so we will work with copies and leave the originals intact. */
2441   MS_COPYRECT(&map_extent, &(map->extent) );
2442   MS_COPYRECT(&layer_extent, &(layer->extent) );
2443 
2444   /* Transform map extents into geographics for comparison. */
2445   if( msProjectRect(&(map->projection), &(map->latlon), &map_extent) )
2446     return MS_UNKNOWN;
2447 
2448   /* Transform layer extents into geographics for comparison. */
2449   if( msProjectRect(&(layer->projection), &(map->latlon), &layer_extent) )
2450     return MS_UNKNOWN;
2451 
2452   /* Simple case? Return simple answer. */
2453   if ( map_extent.minx < map_extent.maxx && layer_extent.minx < layer_extent.maxx )
2454     return msRectOverlap( &(map_extent), &(layer_extent) );
2455 
2456   /* Uh oh, one of the rects crosses the dateline!
2457   ** Let someone else handle it. */
2458   return MS_UNKNOWN;
2459 }
2460 
2461 /************************************************************************/
2462 /*                             msSmallMalloc()                          */
2463 /************************************************************************/
2464 
2465 /* Safe version of malloc(). This function is taken from gdal/cpl. */
2466 
msSmallMalloc(size_t nSize)2467 void *msSmallMalloc( size_t nSize )
2468 {
2469   void        *pReturn;
2470 
2471   if( UNLIKELY(nSize == 0) )
2472     return NULL;
2473 
2474   pReturn = malloc( nSize );
2475   if( UNLIKELY(pReturn == NULL) ) {
2476     msIO_fprintf(stderr, "msSmallMalloc(): Out of memory allocating %ld bytes.\n",
2477                  (long) nSize );
2478     exit(1);
2479   }
2480 
2481   return pReturn;
2482 }
2483 
2484 /************************************************************************/
2485 /*                             msSmallRealloc()                         */
2486 /************************************************************************/
2487 
2488 /* Safe version of realloc(). This function is taken from gdal/cpl. */
2489 
msSmallRealloc(void * pData,size_t nNewSize)2490 void * msSmallRealloc( void * pData, size_t nNewSize )
2491 {
2492   void        *pReturn;
2493 
2494   if ( UNLIKELY(nNewSize == 0) )
2495     return NULL;
2496 
2497   pReturn = realloc( pData, nNewSize );
2498 
2499   if( UNLIKELY(pReturn == NULL) ) {
2500     msIO_fprintf(stderr, "msSmallRealloc(): Out of memory allocating %ld bytes.\n",
2501                  (long)nNewSize );
2502     exit(1);
2503   }
2504 
2505   return pReturn;
2506 }
2507 
2508 /************************************************************************/
2509 /*                             msSmallCalloc()                         */
2510 /************************************************************************/
2511 
2512 /* Safe version of calloc(). This function is taken from gdal/cpl. */
2513 
msSmallCalloc(size_t nCount,size_t nSize)2514 void *msSmallCalloc( size_t nCount, size_t nSize )
2515 {
2516   void  *pReturn;
2517 
2518   if( UNLIKELY(nSize * nCount == 0) )
2519     return NULL;
2520 
2521   pReturn = calloc( nCount, nSize );
2522   if( UNLIKELY(pReturn == NULL) ) {
2523     msIO_fprintf(stderr, "msSmallCalloc(): Out of memory allocating %ld bytes.\n",
2524                  (long)(nCount*nSize));
2525     exit(1);
2526   }
2527 
2528   return pReturn;
2529 }
2530 
2531 /*
2532 ** msBuildOnlineResource()
2533 **
2534 ** Try to build the online resource (mapserv URL) for this service.
2535 ** "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)?"
2536 ** (+append the map=... param if it was explicitly passed in QUERY_STRING)
2537 **
2538 ** Returns a newly allocated string that should be freed by the caller or
2539 ** NULL in case of error.
2540 */
msBuildOnlineResource(mapObj * map,cgiRequestObj * req)2541 char *msBuildOnlineResource(mapObj *map, cgiRequestObj *req)
2542 {
2543   char *online_resource = NULL;
2544   const char *value, *hostname, *port, *script, *protocol="http", *mapparam=NULL;
2545   char **hostname_array = NULL;
2546   int mapparam_len = 0, hostname_array_len = 0;
2547 
2548   hostname = getenv("HTTP_X_FORWARDED_HOST");
2549   if(!hostname)
2550     hostname = getenv("SERVER_NAME");
2551   else {
2552     if(strchr(hostname,',')) {
2553       hostname_array = msStringSplit(hostname,',', &hostname_array_len);
2554       hostname = hostname_array[0];
2555     }
2556   }
2557 
2558   port = getenv("HTTP_X_FORWARDED_PORT");
2559   if(!port)
2560     port = getenv("SERVER_PORT");
2561 
2562   script = getenv("SCRIPT_NAME");
2563 
2564   /* HTTPS is set by Apache to "on" in an HTTPS server ... if not set */
2565   /* then check SERVER_PORT: 443 is the default https port. */
2566   if ( ((value=getenv("HTTPS")) && strcasecmp(value, "on") == 0) ||
2567        ((value=getenv("SERVER_PORT")) && atoi(value) == 443) ) {
2568     protocol = "https";
2569   }
2570   if ( (value=getenv("HTTP_X_FORWARDED_PROTO")) ) {
2571     protocol = value;
2572   }
2573 
2574   /* If map=.. was explicitly set then we'll include it in onlineresource
2575    */
2576   if (req->type == MS_GET_REQUEST) {
2577     int i;
2578     for(i=0; i<req->NumParams; i++) {
2579       if (strcasecmp(req->ParamNames[i], "map") == 0) {
2580         mapparam = req->ParamValues[i];
2581         mapparam_len = strlen(mapparam)+5; /* +5 for "map="+"&" */
2582         break;
2583       }
2584     }
2585   }
2586 
2587   if (hostname && port && script) {
2588     size_t buffer_size;
2589     buffer_size = strlen(hostname)+strlen(port)+strlen(script)+mapparam_len+11; /* 11 comes from https://[host]:[port][scriptname]?[map]\0, i.e. "https://:?\0" */
2590     online_resource = (char*)msSmallMalloc(buffer_size);
2591     if ((atoi(port) == 80 && strcmp(protocol, "http") == 0) ||
2592         (atoi(port) == 443 && strcmp(protocol, "https") == 0) )
2593       snprintf(online_resource, buffer_size, "%s://%s%s?", protocol, hostname, script);
2594     else
2595       snprintf(online_resource, buffer_size, "%s://%s:%s%s?", protocol, hostname, port, script);
2596 
2597     if (mapparam) {
2598       int baselen;
2599       baselen = strlen(online_resource);
2600       snprintf(online_resource+baselen, buffer_size-baselen, "map=%s&", mapparam);
2601     }
2602   } else {
2603     msSetError(MS_CGIERR, "Impossible to establish server URL.", "msBuildOnlineResource()");
2604     return NULL;
2605   }
2606   if(hostname_array) {
2607     msFreeCharArray(hostname_array, hostname_array_len);
2608   }
2609 
2610   return online_resource;
2611 }
2612 
2613 
2614 /************************************************************************/
2615 /*                             msIntegerInArray()                        */
2616 /************************************************************************/
2617 
2618 /* Check if a integer is in a array */
msIntegerInArray(const int value,int * array,int numelements)2619 int msIntegerInArray(const int value, int *array, int numelements)
2620 {
2621   int i;
2622   for (i=0; i<numelements; ++i) {
2623     if (value == array[i])
2624       return MS_TRUE;
2625   }
2626   return MS_FALSE;
2627 }
2628 
2629 
2630 /************************************************************************
2631  *                            msMapSetProjections                       *
2632  *                                                                      *
2633  *   Ensure that all the layers in the map file have a projection       *
2634  *   by copying the map-level projection to all layers than have no     *
2635  *   projection.                                                        *
2636  ************************************************************************/
2637 
msMapSetLayerProjections(mapObj * map)2638 int msMapSetLayerProjections(mapObj* map)
2639 {
2640 
2641   char *mapProjStr = NULL;
2642   int i;
2643 
2644   if (map->projection.numargs <= 0) {
2645     msSetError(MS_WMSERR, "Cannot set new SRS on a map that doesn't "
2646                "have any projection set. Please make sure your mapfile "
2647                "has a PROJECTION defined at the top level.",
2648                "msTileSetProjectionst()");
2649     return(MS_FAILURE);
2650   }
2651 
2652   for(i=0; i<map->numlayers; i++) {
2653     layerObj *lp = GET_LAYER(map,i);
2654     /* This layer is turned on and needs a projection? */
2655     if (lp->projection.numargs <= 0 &&
2656         lp->status != MS_OFF &&
2657         lp->transform == MS_TRUE) {
2658 
2659       /* Fetch main map projection string only now that we need it */
2660       if (mapProjStr == NULL)
2661         mapProjStr = msGetProjectionString(&(map->projection));
2662 
2663       /* Set the projection to the map file projection */
2664       if (msLoadProjectionString(&(lp->projection), mapProjStr) != 0) {
2665         msSetError(MS_CGIERR, "Unable to set projection on layer.", "msMapSetLayerProjections()");
2666         return(MS_FAILURE);
2667       }
2668       lp->project = MS_TRUE;
2669       if(lp->connection && IS_THIRDPARTY_LAYER_CONNECTIONTYPE(lp->connectiontype)) {
2670         char **reflayers;
2671         int numreflayers,j;
2672         reflayers = msStringSplit(lp->connection,',',&numreflayers);
2673         for(j=0; j<numreflayers; j++) {
2674           int *lidx, nlidx;
2675           /* first check layers referenced by group name */
2676           lidx = msGetLayersIndexByGroup(map, reflayers[i], &nlidx);
2677           if(lidx) {
2678             int k;
2679             for(k=0; k<nlidx; k++) {
2680               layerObj *glp = GET_LAYER(map,lidx[k]);
2681               if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
2682 
2683                 /* Set the projection to the map file projection */
2684                 if (msLoadProjectionString(&(glp->projection), mapProjStr) != 0) {
2685                   msSetError(MS_CGIERR, "Unable to set projection on layer.", "msMapSetLayerProjections()");
2686                   return(MS_FAILURE);
2687                 }
2688                 glp->project = MS_TRUE;
2689               }
2690             }
2691             free(lidx);
2692           } else {
2693             /* group name did not match, check by layer name */
2694             int layer_idx = msGetLayerIndex(map,lp->connection);
2695             layerObj *glp = GET_LAYER(map,layer_idx);
2696             if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
2697 
2698               /* Set the projection to the map file projection */
2699               if (msLoadProjectionString(&(glp->projection), mapProjStr) != 0) {
2700                 msSetError(MS_CGIERR, "Unable to set projection on layer.", "msMapSetLayerProjections()");
2701                 return(MS_FAILURE);
2702               }
2703               glp->project = MS_TRUE;
2704             }
2705           }
2706         }
2707         msFreeCharArray(reflayers, numreflayers);
2708       }
2709     }
2710   }
2711   msFree(mapProjStr);
2712   return(MS_SUCCESS);
2713 }
2714 
2715 
2716 /************************************************************************
2717  *                    msMapSetLanguageSpecificConnection                *
2718  *                                                                      *
2719  *   Override DATA and CONNECTION of each layer with their specific     *
2720  *  variant for the specified language.                                 *
2721  ************************************************************************/
2722 
msMapSetLanguageSpecificConnection(mapObj * map,const char * validated_language)2723 void msMapSetLanguageSpecificConnection(mapObj* map, const char* validated_language)
2724 {
2725     int i;
2726     for(i=0; i<map->numlayers; i++) {
2727       layerObj *layer = GET_LAYER(map, i);
2728       if(layer->data) layer->data = msCaseReplaceSubstring(layer->data, "%language%", validated_language);
2729       if(layer->connection) layer->connection = msCaseReplaceSubstring(layer->connection, "%language%", validated_language);
2730     }
2731 }
2732 
2733 /* Generalize a shape based of the tolerance.
2734    Ref: http://trac.osgeo.org/gdal/ticket/966 */
msGeneralize(shapeObj * shape,double tolerance)2735 shapeObj* msGeneralize(shapeObj *shape, double tolerance)
2736 {
2737   lineObj newLine = {0,NULL};
2738   const double sqTolerance = tolerance*tolerance;
2739 
2740   shapeObj* newShape = (shapeObj*)msSmallMalloc(sizeof(shapeObj));
2741   msInitShape(newShape);
2742   msCopyShape(shape, newShape);
2743 
2744   if (shape->numlines<1)
2745     return newShape;
2746 
2747   /* Clean shape */
2748   for (int i=0; i < newShape->numlines; i++)
2749     free(newShape->line[i].point);
2750   newShape->numlines = 0;
2751   if (newShape->line) free(newShape->line);
2752 
2753   msAddLine(newShape, &newLine);
2754 
2755   if (shape->line[0].numpoints==0) {
2756     return newShape;
2757   }
2758 
2759   msAddPointToLine(&newShape->line[0],
2760                    &shape->line[0].point[0]);
2761   double dX0 = shape->line[0].point[0].x;
2762   double dY0 = shape->line[0].point[0].y;
2763 
2764   for(int i=1; i<shape->line[0].numpoints; i++)
2765   {
2766       double dX1 = shape->line[0].point[i].x;
2767       double dY1 = shape->line[0].point[i].y;
2768 
2769       const double dX = dX1-dX0;
2770       const double dY = dY1-dY0;
2771       const double dSqDist = dX*dX + dY*dY;
2772       if (i == shape->line[0].numpoints-1 || dSqDist >= sqTolerance)
2773       {
2774           pointObj p;
2775           p.x = dX1;
2776           p.y = dY1;
2777 
2778           /* Keep this point (always keep the last point) */
2779           msAddPointToLine(&newShape->line[0],
2780                            &p);
2781           dX0 = dX1;
2782           dY0 = dY1;
2783         }
2784     }
2785 
2786   return newShape;
2787 }
2788 
msSetLayerOpacity(layerObj * layer,int opacity)2789 void msSetLayerOpacity(layerObj *layer, int opacity) {
2790   if(!layer->compositer) {
2791     layer->compositer = msSmallMalloc(sizeof(LayerCompositer));
2792     initLayerCompositer(layer->compositer);
2793   }
2794   layer->compositer->opacity = opacity;
2795 }
2796