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