• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 21745274292

06 Feb 2026 09:17AM UTC coverage: 41.785%. First build
21745274292

push

github

geographika
Check CONFIG for an ONLINERESOURCE

2 of 3 new or added lines in 1 file covered. (66.67%)

62969 of 150697 relevant lines covered (41.79%)

25326.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.04
/src/maputil.c
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 <math.h>
35
#include <time.h>
36

37
#include "mapserver.h"
38
#include "maptime.h"
39
#include "mapthread.h"
40
#include "mapcopy.h"
41
#include "mapows.h"
42

43
#include "gdal.h"
44
#include "cpl_conv.h"
45

46
#if defined(_WIN32) && !defined(__CYGWIN__)
47
#include <windows.h>
48
#include <tchar.h>
49
#include <fcntl.h>
50
#include <io.h>
51
#include <process.h>
52
#endif
53

54
#ifdef USE_RSVG
55
#include <glib-object.h>
56
#endif
57
#ifdef USE_GEOS
58
#include <geos_c.h>
59
#endif
60

61
extern char *msyystring_buffer;
62
extern int msyylex_destroy(void);
63
extern int yyparse(parseObj *);
64

65
int msScaleInBounds(double scale, double minscale, double maxscale) {
114,541✔
66
  if (scale > 0) {
114,541✔
67
    if (maxscale != -1 && scale >= maxscale)
113,264✔
68
      return MS_FALSE;
69
    if (minscale != -1 && scale < minscale)
112,613✔
70
      return MS_FALSE;
2✔
71
  }
72
  return MS_TRUE;
73
}
74

75
/*
76
** Helper functions to convert from strings to other types or objects.
77
*/
78
static int bindIntegerAttribute(int *attribute, const char *value) {
44✔
79
  if (!value || strlen(value) == 0)
44✔
80
    return MS_FAILURE;
81
  *attribute =
44✔
82
      MS_NINT(atof(value)); /*use atof instead of atoi as a fix for bug 2394*/
44✔
83
  return MS_SUCCESS;
44✔
84
}
85

86
static int bindDoubleAttribute(double *attribute, const char *value) {
87
  if (!value || strlen(value) == 0)
23,589✔
88
    return MS_FAILURE;
89
  *attribute = atof(value);
23,589✔
90
  return MS_SUCCESS;
23,589✔
91
}
92

93
static int bindColorAttribute(colorObj *attribute, const char *value) {
30✔
94
  int len;
95

96
  if (!value || ((len = strlen(value)) == 0))
30✔
97
    return MS_FAILURE;
98

99
  if (value[0] == '#' && (len == 7 || len == 9)) { /* got a hex color */
30✔
100
    char hex[2];
101

102
    hex[0] = value[1];
22✔
103
    hex[1] = value[2];
22✔
104
    attribute->red = msHexToInt(hex);
22✔
105
    hex[0] = value[3];
22✔
106
    hex[1] = value[4];
22✔
107
    attribute->green = msHexToInt(hex);
22✔
108
    hex[0] = value[5];
22✔
109
    hex[1] = value[6];
22✔
110
    attribute->blue = msHexToInt(hex);
22✔
111
    if (len == 9) {
22✔
112
      hex[0] = value[7];
3✔
113
      hex[1] = value[8];
3✔
114
      attribute->alpha = msHexToInt(hex);
3✔
115
    }
116
    return MS_SUCCESS;
117
  } else { /* try a space delimited string */
118
    char **tokens = NULL;
119
    int numtokens = 0;
8✔
120

121
    tokens = msStringSplit(value, ' ', &numtokens);
8✔
122
    if (tokens == NULL || numtokens != 3) {
8✔
123
      msFreeCharArray(tokens, numtokens);
×
124
      return MS_FAILURE; /* punt */
×
125
    }
126

127
    attribute->red = atoi(tokens[0]);
8✔
128
    attribute->green = atoi(tokens[1]);
8✔
129
    attribute->blue = atoi(tokens[2]);
8✔
130
    msFreeCharArray(tokens, numtokens);
8✔
131

132
    return MS_SUCCESS;
8✔
133
  }
134

135
  return MS_FAILURE; /* shouldn't get here */
136
}
137

138
static void bindStyle(layerObj *layer, shapeObj *shape, styleObj *style,
65,964✔
139
                      int drawmode) {
140
  int applyOpacity = MS_FALSE;
141
  assert(MS_DRAW_FEATURES(drawmode));
142
  if (style->numbindings > 0) {
65,964✔
143
    applyOpacity = MS_TRUE;
144
    if (style->bindings[MS_STYLE_BINDING_SYMBOL].index != -1) {
20,186✔
145
      style->symbol = msGetSymbolIndex(
918✔
146
          &(layer->map->symbolset),
459✔
147
          shape->values[style->bindings[MS_STYLE_BINDING_SYMBOL].index],
459✔
148
          MS_TRUE);
149
      if (style->symbol == -1)
459✔
150
        style->symbol =
×
151
            0; /* a reasonable default (perhaps should throw an error?) */
152
    }
153
    if (style->bindings[MS_STYLE_BINDING_ANGLE].index != -1) {
20,186✔
154
      style->angle = 360.0;
19,917✔
155
      bindDoubleAttribute(
19,917✔
156
          &style->angle,
157
          shape->values[style->bindings[MS_STYLE_BINDING_ANGLE].index]);
19,917✔
158
    }
159
    if (style->bindings[MS_STYLE_BINDING_SIZE].index != -1) {
20,186✔
160
      style->size = 1;
1,356✔
161
      bindDoubleAttribute(
1,356✔
162
          &style->size,
163
          shape->values[style->bindings[MS_STYLE_BINDING_SIZE].index]);
1,356✔
164
    }
165
    if (style->bindings[MS_STYLE_BINDING_WIDTH].index != -1) {
20,186✔
166
      style->width = 1;
12✔
167
      bindDoubleAttribute(
12✔
168
          &style->width,
169
          shape->values[style->bindings[MS_STYLE_BINDING_WIDTH].index]);
12✔
170
    }
171
    if (style->bindings[MS_STYLE_BINDING_COLOR].index != -1 &&
20,186✔
172
        !MS_DRAW_QUERY(drawmode)) {
19✔
173
      MS_INIT_COLOR(style->color, -1, -1, -1, 255);
19✔
174
      bindColorAttribute(
19✔
175
          &style->color,
176
          shape->values[style->bindings[MS_STYLE_BINDING_COLOR].index]);
19✔
177
    }
178
    if (style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index != -1 &&
20,186✔
179
        !MS_DRAW_QUERY(drawmode)) {
×
180
      MS_INIT_COLOR(style->outlinecolor, -1, -1, -1, 255);
×
181
      bindColorAttribute(
×
182
          &style->outlinecolor,
183
          shape->values[style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].index]);
×
184
    }
185
    if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index != -1) {
20,186✔
186
      style->outlinewidth = 1;
12✔
187
      bindDoubleAttribute(
12✔
188
          &style->outlinewidth,
189
          shape->values[style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].index]);
12✔
190
    }
191
    if (style->bindings[MS_STYLE_BINDING_OPACITY].index != -1) {
20,186✔
192
      style->opacity = 100;
×
193
      bindIntegerAttribute(
×
194
          &style->opacity,
195
          shape->values[style->bindings[MS_STYLE_BINDING_OPACITY].index]);
×
196
    }
197
    if (style->bindings[MS_STYLE_BINDING_OFFSET_X].index != -1) {
20,186✔
198
      style->offsetx = 0;
×
199
      bindDoubleAttribute(
×
200
          &style->offsetx,
201
          shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_X].index]);
×
202
    }
203
    if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].index != -1) {
20,186✔
204
      style->offsety = 0;
×
205
      bindDoubleAttribute(
×
206
          &style->offsety,
207
          shape->values[style->bindings[MS_STYLE_BINDING_OFFSET_Y].index]);
×
208
    }
209
    if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].index != -1) {
20,186✔
210
      style->polaroffsetpixel = 0;
1,146✔
211
      bindDoubleAttribute(
1,146✔
212
          &style->polaroffsetpixel,
213
          shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL]
1,146✔
214
                            .index]);
1,146✔
215
    }
216
    if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].index != -1) {
20,186✔
217
      style->polaroffsetangle = 0;
1,146✔
218
      bindDoubleAttribute(
1,146✔
219
          &style->polaroffsetangle,
220
          shape->values[style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE]
1,146✔
221
                            .index]);
1,146✔
222
    }
223
  }
224
  if (style->nexprbindings > 0) {
65,964✔
225
    applyOpacity = MS_TRUE;
226
    if (style->exprBindings[MS_STYLE_BINDING_OFFSET_X].type == MS_EXPRESSION) {
38✔
227
      style->offsetx = msEvalDoubleExpression(
2✔
228
          &(style->exprBindings[MS_STYLE_BINDING_OFFSET_X]), shape);
229
    }
230
    if (style->exprBindings[MS_STYLE_BINDING_OFFSET_Y].type == MS_EXPRESSION) {
38✔
231
      style->offsety = msEvalDoubleExpression(
2✔
232
          &(style->exprBindings[MS_STYLE_BINDING_OFFSET_Y]), shape);
233
    }
234
    if (style->exprBindings[MS_STYLE_BINDING_ANGLE].type == MS_EXPRESSION) {
38✔
235
      style->angle = msEvalDoubleExpression(
2✔
236
          &(style->exprBindings[MS_STYLE_BINDING_ANGLE]), shape);
237
    }
238
    if (style->exprBindings[MS_STYLE_BINDING_SIZE].type == MS_EXPRESSION) {
38✔
239
      style->size = msEvalDoubleExpression(
12✔
240
          &(style->exprBindings[MS_STYLE_BINDING_SIZE]), shape);
241
    }
242
    if (style->exprBindings[MS_STYLE_BINDING_WIDTH].type == MS_EXPRESSION) {
38✔
243
      style->width = msEvalDoubleExpression(
7✔
244
          &(style->exprBindings[MS_STYLE_BINDING_WIDTH]), shape);
245
    }
246
    if (style->exprBindings[MS_STYLE_BINDING_OPACITY].type == MS_EXPRESSION) {
38✔
247
      style->opacity =
8✔
248
          100 * msEvalDoubleExpression(
8✔
249
                    &(style->exprBindings[MS_STYLE_BINDING_OPACITY]), shape);
250
    }
251
    if (style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR].type ==
38✔
252
        MS_EXPRESSION) {
253
      char *txt = msEvalTextExpression(
2✔
254
          &(style->exprBindings[MS_STYLE_BINDING_OUTLINECOLOR]), shape);
255
      bindColorAttribute(&style->outlinecolor, txt);
2✔
256
      msFree(txt);
2✔
257
    }
258
    if (style->exprBindings[MS_STYLE_BINDING_COLOR].type == MS_EXPRESSION) {
38✔
259
      char *txt = msEvalTextExpression(
3✔
260
          &(style->exprBindings[MS_STYLE_BINDING_COLOR]), shape);
261
      bindColorAttribute(&style->color, txt);
3✔
262
      msFree(txt);
3✔
263
    }
264
  }
265

266
  if (applyOpacity == MS_TRUE && style->opacity < 100) {
65,964✔
267
    int alpha;
268
    alpha = MS_NINT(style->opacity * 2.55);
30✔
269
    style->color.alpha = alpha;
30✔
270
    style->outlinecolor.alpha = alpha;
30✔
271
    style->mincolor.alpha = alpha;
30✔
272
    style->maxcolor.alpha = alpha;
30✔
273
  }
274
}
65,964✔
275

276
static void bindLabel(layerObj *layer, shapeObj *shape, labelObj *label,
34,865✔
277
                      int drawmode) {
278
  int i;
279
  assert(MS_DRAW_LABELS(drawmode));
280

281
  /* check the label styleObj's (TODO: do we need to use querymapMode here? */
282
  for (i = 0; i < label->numstyles; i++) {
62,446✔
283
    /* force MS_DRAWMODE_FEATURES for label styles */
284
    bindStyle(layer, shape, label->styles[i], drawmode | MS_DRAWMODE_FEATURES);
27,581✔
285
  }
286

287
  if (label->numbindings > 0) {
34,865✔
288
    if (label->bindings[MS_LABEL_BINDING_ANGLE].index != -1) {
34✔
289
      label->angle = 0.0;
×
290
      bindDoubleAttribute(
×
291
          &label->angle,
292
          shape->values[label->bindings[MS_LABEL_BINDING_ANGLE].index]);
×
293
    }
294

295
    if (label->bindings[MS_LABEL_BINDING_SIZE].index != -1) {
34✔
296
      label->size = 1;
4✔
297
      bindIntegerAttribute(
4✔
298
          &label->size,
299
          shape->values[label->bindings[MS_LABEL_BINDING_SIZE].index]);
4✔
300
    }
301

302
    if (label->bindings[MS_LABEL_BINDING_COLOR].index != -1) {
34✔
303
      MS_INIT_COLOR(label->color, -1, -1, -1, 255);
4✔
304
      bindColorAttribute(
4✔
305
          &label->color,
306
          shape->values[label->bindings[MS_LABEL_BINDING_COLOR].index]);
4✔
307
    }
308

309
    if (label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index != -1) {
34✔
310
      MS_INIT_COLOR(label->outlinecolor, -1, -1, -1, 255);
×
311
      bindColorAttribute(
×
312
          &label->outlinecolor,
313
          shape->values[label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].index]);
×
314
    }
315

316
    if (label->bindings[MS_LABEL_BINDING_FONT].index != -1) {
34✔
317
      msFree(label->font);
×
318
      label->font =
×
319
          msStrdup(shape->values[label->bindings[MS_LABEL_BINDING_FONT].index]);
×
320
    }
321

322
    if (label->bindings[MS_LABEL_BINDING_PRIORITY].index != -1) {
34✔
323
      label->priority = MS_DEFAULT_LABEL_PRIORITY;
10✔
324
      bindIntegerAttribute(
10✔
325
          &label->priority,
326
          shape->values[label->bindings[MS_LABEL_BINDING_PRIORITY].index]);
10✔
327
    }
328

329
    if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index != -1) {
34✔
330
      label->shadowsizex = 1;
×
331
      bindIntegerAttribute(
×
332
          &label->shadowsizex,
333
          shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].index]);
×
334
    }
335
    if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index != -1) {
34✔
336
      label->shadowsizey = 1;
×
337
      bindIntegerAttribute(
×
338
          &label->shadowsizey,
339
          shape->values[label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].index]);
×
340
    }
341

342
    if (label->bindings[MS_LABEL_BINDING_OFFSET_X].index != -1) {
34✔
343
      label->offsetx = 0;
10✔
344
      bindIntegerAttribute(
10✔
345
          &label->offsetx,
346
          shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_X].index]);
10✔
347
    }
348

349
    if (label->bindings[MS_LABEL_BINDING_OFFSET_Y].index != -1) {
34✔
350
      label->offsety = 0;
10✔
351
      bindIntegerAttribute(
10✔
352
          &label->offsety,
353
          shape->values[label->bindings[MS_LABEL_BINDING_OFFSET_Y].index]);
10✔
354
    }
355

356
    if (label->bindings[MS_LABEL_BINDING_ALIGN].index != -1) {
34✔
357
      int tmpAlign = 0;
10✔
358
      bindIntegerAttribute(
10✔
359
          &tmpAlign,
360
          shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]);
10✔
361
      if (tmpAlign != 0) { /* is this test sufficient? */
10✔
362
        label->align = tmpAlign;
×
363
      } else { /* Integer binding failed, look for strings like cc, ul, lr,
364
                  etc... */
365
        if (strlen(
10✔
366
                shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index]) >=
10✔
367
            4) {
368
          char *va =
369
              shape->values[label->bindings[MS_LABEL_BINDING_ALIGN].index];
370
          if (!strncasecmp(va, "center", 5))
10✔
371
            label->align = MS_ALIGN_CENTER;
3✔
372
          else if (!strncasecmp(va, "left", 4))
7✔
373
            label->align = MS_ALIGN_LEFT;
4✔
374
          else if (!strncasecmp(va, "right", 5))
3✔
375
            label->align = MS_ALIGN_RIGHT;
3✔
376
        }
377
      }
378
    }
379

380
    if (label->bindings[MS_LABEL_BINDING_POSITION].index != -1) {
34✔
381
      int tmpPosition = 0;
×
382
      bindIntegerAttribute(
×
383
          &tmpPosition,
384
          shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index]);
×
385
      if (tmpPosition != 0) { /* is this test sufficient? */
×
386
        label->position = tmpPosition;
×
387
      } else { /* Integer binding failed, look for strings like cc, ul, lr,
388
                  etc... */
389
        if (strlen(shape->values[label->bindings[MS_LABEL_BINDING_POSITION]
×
390
                                     .index]) == 2) {
×
391
          char *vp =
392
              shape->values[label->bindings[MS_LABEL_BINDING_POSITION].index];
393
          if (!strncasecmp(vp, "ul", 2))
×
394
            label->position = MS_UL;
×
395
          else if (!strncasecmp(vp, "lr", 2))
×
396
            label->position = MS_LR;
×
397
          else if (!strncasecmp(vp, "ur", 2))
×
398
            label->position = MS_UR;
×
399
          else if (!strncasecmp(vp, "ll", 2))
×
400
            label->position = MS_LL;
×
401
          else if (!strncasecmp(vp, "cr", 2))
×
402
            label->position = MS_CR;
×
403
          else if (!strncasecmp(vp, "cl", 2))
×
404
            label->position = MS_CL;
×
405
          else if (!strncasecmp(vp, "uc", 2))
×
406
            label->position = MS_UC;
×
407
          else if (!strncasecmp(vp, "lc", 2))
×
408
            label->position = MS_LC;
×
409
          else if (!strncasecmp(vp, "cc", 2))
×
410
            label->position = MS_CC;
×
411
        }
412
      }
413
    }
414
  }
415
  if (label->nexprbindings > 0) {
34,865✔
416
    if (label->exprBindings[MS_LABEL_BINDING_ANGLE].type == MS_EXPRESSION) {
17✔
417
      label->angle = msEvalDoubleExpression(
2✔
418
          &(label->exprBindings[MS_LABEL_BINDING_ANGLE]), shape);
419
    }
420
    if (label->exprBindings[MS_LABEL_BINDING_SIZE].type == MS_EXPRESSION) {
17✔
421
      label->size = msEvalDoubleExpression(
3✔
422
          &(label->exprBindings[MS_LABEL_BINDING_SIZE]), shape);
423
    }
424
    if (label->exprBindings[MS_LABEL_BINDING_COLOR].type == MS_EXPRESSION) {
17✔
425
      char *txt = msEvalTextExpression(
1✔
426
          &(label->exprBindings[MS_LABEL_BINDING_COLOR]), shape);
427
      bindColorAttribute(&label->color, txt);
1✔
428
      msFree(txt);
1✔
429
    }
430
    if (label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR].type ==
17✔
431
        MS_EXPRESSION) {
432
      char *txt = msEvalTextExpression(
1✔
433
          &(label->exprBindings[MS_LABEL_BINDING_OUTLINECOLOR]), shape);
434
      bindColorAttribute(&label->outlinecolor, txt);
1✔
435
      msFree(txt);
1✔
436
    }
437
    if (label->exprBindings[MS_LABEL_BINDING_PRIORITY].type == MS_EXPRESSION) {
17✔
438
      label->priority = msEvalDoubleExpression(
10✔
439
          &(label->exprBindings[MS_LABEL_BINDING_PRIORITY]), shape);
440
    }
441
  }
442
}
34,865✔
443

444
/*
445
** Function to bind various layer properties to shape attributes.
446
*/
447
int msBindLayerToShape(layerObj *layer, shapeObj *shape, int drawmode) {
30,470✔
448
  int i, j;
449

450
  if (!layer || !shape)
30,470✔
451
    return MS_FAILURE;
452

453
  for (i = 0; i < layer->numclasses; i++) {
67,244✔
454
    /* check the styleObj's */
455
    if (MS_DRAW_FEATURES(drawmode)) {
36,774✔
456
      for (j = 0; j < layer->class[i] -> numstyles; j++) {
75,147✔
457
        bindStyle(layer, shape, layer->class[i] -> styles[j], drawmode);
38,383✔
458
      }
459
    }
460

461
    /* check the labelObj's */
462
    if (MS_DRAW_LABELS(drawmode)) {
36,774✔
463
      for (j = 0; j < layer->class[i] -> numlabels; j++) {
40,510✔
464
        bindLabel(layer, shape, layer->class[i] -> labels[j], drawmode);
34,865✔
465
      }
466
    }
467
  } /* next classObj */
468

469
  return MS_SUCCESS;
470
}
471

472
/*
473
 * Used to get red, green, blue integers separately based upon the color index
474
 */
475
int getRgbColor(mapObj *map, int i, int *r, int *g, int *b) {
×
476
  /* check index range */
477
  int status = 1;
478
  *r = *g = *b = -1;
×
479
  if ((i > 0) && (i <= map->palette.numcolors)) {
×
480
    *r = map->palette.colors[i - 1].red;
×
481
    *g = map->palette.colors[i - 1].green;
×
482
    *b = map->palette.colors[i - 1].blue;
×
483
    status = 0;
484
  }
485
  return status;
×
486
}
487

488
static int searchContextForTag(mapObj *map, char **ltags, char *tag,
11,997✔
489
                               char *context, int requires) {
490
  int i;
491

492
  if (!context)
11,997✔
493
    return MS_FAILURE;
494

495
  /*  printf("\tin searchContextForTag, searching %s for %s\n", context, tag);
496
   */
497

498
  if (strstr(context, tag) != NULL)
15✔
499
    return MS_SUCCESS; /* found the tag */
500

501
  /* check referenced layers for the tag too */
502
  for (i = 0; i < map->numlayers; i++) {
75✔
503
    if (strstr(context, ltags[i]) != NULL) { /* need to check this layer */
60✔
504
      if (requires == MS_TRUE) {
5✔
505
        if (searchContextForTag(map, ltags, tag, GET_LAYER(map, i)->requires,
5✔
506
                                MS_TRUE) == MS_SUCCESS)
507
          return MS_SUCCESS;
508
      } else {
509
        if (searchContextForTag(map, ltags, tag,
×
510
                                GET_LAYER(map, i)->labelrequires,
×
511
                                MS_FALSE) == MS_SUCCESS)
512
          return MS_SUCCESS;
513
      }
514
    }
515
  }
516

517
  return MS_FAILURE;
518
}
519

520
/*
521
** Function to take a look at all layers with REQUIRES/LABELREQUIRES set to make
522
*sure there are no
523
** recursive context requirements set (e.g. layer1 requires layer2 and layer2
524
*requires layer1). This
525
** is bug 1059.
526
*/
527
int msValidateContexts(mapObj *map) {
1,299✔
528
  int i;
529
  char **ltags;
530
  int status = MS_SUCCESS;
531

532
  ltags = (char **)msSmallMalloc(map->numlayers * sizeof(char *));
1,299✔
533
  for (i = 0; i < map->numlayers; i++) {
7,295✔
534
    if (GET_LAYER(map, i)->name == NULL) {
5,996✔
535
      ltags[i] = msStrdup("[NULL]");
95✔
536
    } else {
537
      ltags[i] = (char *)msSmallMalloc(
11,802✔
538
          sizeof(char) * strlen(GET_LAYER(map, i)->name) + 3);
5,901✔
539
      sprintf(ltags[i], "[%s]", GET_LAYER(map, i)->name);
5,901✔
540
    }
541
  }
542

543
  /* check each layer's REQUIRES and LABELREQUIRES parameters */
544
  for (i = 0; i < map->numlayers; i++) {
7,295✔
545
    /* printf("working on layer %s, looking for references to %s\n",
546
     * GET_LAYER(map, i)->name, ltags[i]); */
547
    if (searchContextForTag(map, ltags, ltags[i], GET_LAYER(map, i)->requires,
5,996✔
548
                            MS_TRUE) == MS_SUCCESS) {
549
      msSetError(MS_PARSEERR,
×
550
                 "Recursion error found for REQUIRES parameter for layer %s.",
551
                 "msValidateContexts", GET_LAYER(map, i)->name);
552
      status = MS_FAILURE;
553
      break;
×
554
    }
555
    if (searchContextForTag(map, ltags, ltags[i],
5,996✔
556
                            GET_LAYER(map, i)->labelrequires,
557
                            MS_FALSE) == MS_SUCCESS) {
558
      msSetError(
×
559
          MS_PARSEERR,
560
          "Recursion error found for LABELREQUIRES parameter for layer %s.",
561
          "msValidateContexts", GET_LAYER(map, i)->name);
562
      status = MS_FAILURE;
563
      break;
×
564
    }
565
    /* printf("done layer %s\n", GET_LAYER(map, i)->name); */
566
  }
567

568
  /* clean up */
569
  msFreeCharArray(ltags, map->numlayers);
1,299✔
570

571
  return status;
1,299✔
572
}
573

574
int msEvalContext(mapObj *map, layerObj *layer, char *context) {
6,638✔
575
  int i, status;
576
  char *tag = NULL;
577
  tokenListNodeObjPtr token = NULL;
578

579
  expressionObj e;
580
  parseObj p;
581

582
  if (!context)
6,638✔
583
    return (MS_TRUE);
584

585
  /* initialize a temporary expression (e) */
586
  msInitExpression(&e);
5✔
587

588
  e.string = msStrdup(context);
5✔
589
  e.type = MS_EXPRESSION; /* todo */
5✔
590

591
  for (i = 0; i < map->numlayers; i++) { /* step through all the layers */
25✔
592
    if (layer->index == i)
20✔
593
      continue; /* skip the layer in question */
5✔
594
    if (GET_LAYER(map, i)->name == NULL)
15✔
595
      continue; /* Layer without name cannot be used in contexts */
×
596

597
    tag = (char *)msSmallMalloc(sizeof(char) * strlen(GET_LAYER(map, i)->name) +
15✔
598
                                3);
599
    sprintf(tag, "[%s]", GET_LAYER(map, i)->name);
15✔
600

601
    if (strstr(e.string, tag)) {
15✔
602
      if (msLayerIsVisible(map, (GET_LAYER(map, i))))
3✔
603
        e.string = msReplaceSubstring(e.string, tag, "1");
1✔
604
      else
605
        e.string = msReplaceSubstring(e.string, tag, "0");
2✔
606
    }
607

608
    free(tag);
15✔
609
  }
610

611
  msTokenizeExpression(&e, NULL, NULL);
5✔
612

613
  /*
614
   * We'll check for binding tokens in the token list. Since there is no shape
615
   * to bind to, the parser will crash if any are present.
616
   * This is one way to catch references to non-existent layers.
617
   */
618
  for (token = e.tokens; token; token = token->next) {
11✔
619
    if (token->token == MS_TOKEN_BINDING_DOUBLE ||
8✔
620
        token->token == MS_TOKEN_BINDING_STRING ||
6✔
621
        token->token == MS_TOKEN_BINDING_TIME) {
622
      msSetError(MS_PARSEERR,
2✔
623
                 "A non-existent layer is referenced in a LAYER REQUIRES or "
624
                 "LABELREQUIRES expression: %s",
625
                 "msEvalContext()", e.string);
626
      msFreeExpression(&e);
2✔
627
      return MS_FALSE;
2✔
628
    }
629
  }
630

631
  p.shape = NULL;
3✔
632
  p.expr = &e;
3✔
633
  p.expr->curtoken = p.expr->tokens; /* reset */
3✔
634
  p.type = MS_PARSE_TYPE_BOOLEAN;
3✔
635

636
  status = yyparse(&p);
3✔
637

638
  msFreeExpression(&e);
3✔
639

640
  if (status != 0) {
3✔
641
    msSetError(MS_PARSEERR, "Failed to parse context", "msEvalContext");
×
642
    return MS_FALSE; /* error in parse */
×
643
  }
644

645
  return p.result.intval;
3✔
646
}
647

648
/* msEvalExpression()
649
 *
650
 * Evaluates a mapserver expression for a given set of attribute values and
651
 * returns the result of the expression (MS_TRUE or MS_FALSE)
652
 * May also return MS_FALSE in case of parsing errors or invalid expressions
653
 * (check the error stack if you care)
654
 *
655
 */
656
int msEvalExpression(layerObj *layer, shapeObj *shape,
136,547✔
657
                     expressionObj *expression, int itemindex) {
658
  if (MS_STRING_IS_NULL_OR_EMPTY(expression->string))
136,547✔
659
    return MS_TRUE; /* NULL or empty expressions are ALWAYS true */
660
  if (expression->native_string != NULL)
53,355✔
661
    return MS_TRUE; /* expressions that are evaluated natively are ALWAYS true
662
                     */
663

664
  switch (expression->type) {
51,449✔
665
  case (MS_STRING):
5,137✔
666
    if (itemindex == -1) {
5,137✔
667
      msSetError(MS_MISCERR,
×
668
                 "Cannot evaluate expression, no item index defined.",
669
                 "msEvalExpression()");
670
      return MS_FALSE;
×
671
    }
672
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
5,137✔
673
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
674
      return MS_FALSE;
×
675
    }
676
    if (expression->flags & MS_EXP_INSENSITIVE) {
5,137✔
677
      if (strcasecmp(expression->string, shape->values[itemindex]) == 0)
117✔
678
        return MS_TRUE; /* got a match */
679
    } else {
680
      if (strcmp(expression->string, shape->values[itemindex]) == 0)
5,020✔
681
        return MS_TRUE; /* got a match */
682
    }
683
    break;
684
  case (MS_LIST):
22✔
685
    if (itemindex == -1) {
22✔
686
      msSetError(MS_MISCERR,
×
687
                 "Cannot evaluate expression, no item index defined.",
688
                 "msEvalExpression()");
689
      return MS_FALSE;
×
690
    }
691
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
22✔
692
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
693
      return MS_FALSE;
×
694
    }
695
    {
696
      char *start, *end;
697
      int value_len = strlen(shape->values[itemindex]);
22✔
698
      start = expression->string;
699
      while ((end = strchr(start, ',')) != NULL) {
103✔
700
        if (value_len == end - start &&
83✔
701
            !strncmp(start, shape->values[itemindex], end - start))
9✔
702
          return MS_TRUE;
703
        start = end + 1;
81✔
704
      }
705
      if (!strcmp(start, shape->values[itemindex]))
20✔
706
        return MS_TRUE;
707
    }
708
    break;
709
  case (MS_EXPRESSION): {
45,261✔
710
    int status;
711
    parseObj p;
712

713
    p.shape = shape;
45,261✔
714
    p.expr = expression;
45,261✔
715
    p.expr->curtoken = p.expr->tokens; /* reset */
45,261✔
716
    p.type = MS_PARSE_TYPE_BOOLEAN;
45,261✔
717

718
    status = yyparse(&p);
45,261✔
719

720
    if (status != 0) {
45,261✔
721
      msSetError(MS_PARSEERR, "Failed to parse expression: %s",
28✔
722
                 "msEvalExpression", expression->string);
723
      return MS_FALSE;
28✔
724
    }
725

726
    return p.result.intval;
45,233✔
727
  }
728
  case (MS_REGEX):
1,029✔
729
    if (itemindex == -1) {
1,029✔
730
      msSetError(MS_MISCERR,
×
731
                 "Cannot evaluate expression, no item index defined.",
732
                 "msEvalExpression()");
733
      return MS_FALSE;
×
734
    }
735
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
1,029✔
736
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
737
      return MS_FALSE;
×
738
    }
739

740
    if (MS_STRING_IS_NULL_OR_EMPTY(shape->values[itemindex]) == MS_TRUE)
1,029✔
741
      return MS_FALSE;
742

743
    if (!expression->compiled) {
1,029✔
744
      if (expression->flags & MS_EXP_INSENSITIVE) {
31✔
745
        if (ms_regcomp(&(expression->regex), expression->string,
1✔
746
                       MS_REG_EXTENDED | MS_REG_NOSUB | MS_REG_ICASE) !=
747
            0) { /* compile the expression */
748
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
749
                     "msEvalExpression()");
750
          return MS_FALSE;
×
751
        }
752
      } else {
753
        if (ms_regcomp(&(expression->regex), expression->string,
30✔
754
                       MS_REG_EXTENDED | MS_REG_NOSUB) !=
755
            0) { /* compile the expression */
756
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
757
                     "msEvalExpression()");
758
          return MS_FALSE;
×
759
        }
760
      }
761
      expression->compiled = MS_TRUE;
31✔
762
    }
763

764
    if (ms_regexec(&(expression->regex), shape->values[itemindex], 0, NULL,
1,029✔
765
                   0) == 0)
766
      return MS_TRUE; /* got a match */
767
    break;
768
  }
769

770
  return MS_FALSE;
771
}
772

773
int *msAllocateValidClassGroups(layerObj *lp, int *nclasses) {
144✔
774
  int *classgroup = NULL;
775
  int nvalidclass = 0, i = 0;
776

777
  if (!lp || !lp->classgroup || lp->numclasses <= 0 || !nclasses)
144✔
778
    return NULL;
779

780
  classgroup = (int *)msSmallMalloc(sizeof(int) * lp->numclasses);
144✔
781
  nvalidclass = 0;
782
  for (i = 0; i < lp->numclasses; i++) {
440✔
783
    if (lp->class[i] -> group && strcasecmp(lp->class[i] -> group,
296✔
784
                                            lp -> classgroup) == 0) {
296✔
785
      classgroup[nvalidclass] = i;
148✔
786
      nvalidclass++;
148✔
787
    }
788
  }
789
  if (nvalidclass > 0) {
144✔
790
    classgroup = (int *)msSmallRealloc(classgroup, sizeof(int) * nvalidclass);
144✔
791
    *nclasses = nvalidclass;
144✔
792
    return classgroup;
144✔
793
  }
794

795
  if (classgroup)
×
796
    msFree(classgroup);
×
797

798
  return NULL;
799
}
800

801
int msShapeGetClass(layerObj *layer, mapObj *map, shapeObj *shape,
7,113✔
802
                    int *classgroup, int numclasses) {
803
  return msShapeGetNextClass(-1, layer, map, shape, classgroup, numclasses);
7,113✔
804
}
805

806
int msShapeGetNextClass(int currentclass, layerObj *layer, mapObj *map,
70,807✔
807
                        shapeObj *shape, int *classgroup, int numclasses) {
808
  int i, iclass;
809

810
  if (currentclass < 0)
70,807✔
811
    currentclass = -1;
812

813
  if (layer->numclasses > 0) {
70,807✔
814
    if (classgroup == NULL || numclasses <= 0)
67,240✔
815
      numclasses = layer->numclasses;
816

817
    for (i = currentclass + 1; i < numclasses; i++) {
74,151✔
818
      if (classgroup)
41,762✔
819
        iclass = classgroup[i];
2,709✔
820
      else
821
        iclass = i;
822

823
      if (iclass < 0 || iclass >= layer->numclasses)
41,762✔
824
        continue; /* this should never happen but just in case */
×
825

826
      if (!msScaleInBounds(map->scaledenom,
41,762✔
827
                           layer->class[iclass] -> minscaledenom,
828
                           layer -> class[iclass] -> maxscaledenom)) {
41,762✔
829
        continue;
651✔
830
      }
831

832
      /* verify the minfeaturesize */
833
      if ((shape->type == MS_SHAPE_LINE || shape->type == MS_SHAPE_POLYGON) &&
41,111✔
834
          (layer->class[iclass] -> minfeaturesize > 0)) {
20,846✔
835
        double minfeaturesize =
836
            Pix2LayerGeoref(map, layer, layer->class[iclass] -> minfeaturesize);
×
837
        if (msShapeCheckSize(shape, minfeaturesize) == MS_FALSE)
×
838
          continue; /* skip this one, next class */
×
839
      }
840

841
      if (layer->class[iclass] -> status != MS_DELETE &&
82,222✔
842
                                      msEvalExpression(
41,111✔
843
                                          layer, shape,
844
                                          &(layer->class[iclass] -> expression),
845
                                          layer->classitemindex) == MS_TRUE) {
846
        if (layer->class[iclass] -> isfallback && currentclass != -1) {
34,851✔
847
          // Class is not applicable if it is flagged as fallback (<ElseFilter/>
848
          // tag in SLD) but other classes have been applied before.
849
          return -1;
850
        } else {
851
          return (iclass);
852
        }
853
      }
854
    }
855
  }
856

857
  return (-1); /* no match */
858
}
859

860
static char *msEvalTextExpressionInternal(expressionObj *expr, shapeObj *shape,
4,353✔
861
                                          int bJSonEscape) {
862
  char *result = NULL;
863

864
  if (!expr->string)
4,353✔
865
    return result; /* nothing to do */
866

867
  switch (expr->type) {
4,337✔
868
  case (MS_STRING): {
3,641✔
869
    char *target = NULL;
870
    char *pszEscaped;
871
    tokenListNodeObjPtr node = NULL;
872
    tokenListNodeObjPtr nextNode = NULL;
873

874
    result = msStrdup(expr->string);
3,641✔
875

876
    node = expr->tokens;
3,641✔
877
    if (node) {
3,641✔
878
      while (node != NULL) {
8,522✔
879
        nextNode = node->next;
5,086✔
880
        if (node->token == MS_TOKEN_BINDING_DOUBLE ||
5,086✔
881
            node->token == MS_TOKEN_BINDING_INTEGER ||
882
            node->token == MS_TOKEN_BINDING_STRING ||
5,086✔
883
            node->token == MS_TOKEN_BINDING_TIME) {
884
          target =
885
              (char *)msSmallMalloc(strlen(node->tokenval.bindval.item) + 3);
3,441✔
886
          sprintf(target, "[%s]", node->tokenval.bindval.item);
3,441✔
887
          if (bJSonEscape)
3,441✔
888
            pszEscaped =
889
                msEscapeJSonString(shape->values[node->tokenval.bindval.index]);
5✔
890
          else
891
            pszEscaped = msStrdup(shape->values[node->tokenval.bindval.index]);
3,436✔
892
          result = msReplaceSubstring(result, target, pszEscaped);
3,441✔
893
          msFree(pszEscaped);
3,441✔
894
          msFree(target);
3,441✔
895
        }
896
        node = nextNode;
897
      }
898
    }
899
    if (!strlen(result)) {
3,641✔
900
      msFree(result);
3✔
901
      result = NULL;
902
    }
903
  } break;
904
  case (MS_EXPRESSION): {
696✔
905
    int status;
906
    parseObj p;
907

908
    p.shape = shape;
696✔
909
    p.expr = expr;
696✔
910
    p.expr->curtoken = p.expr->tokens; /* reset */
696✔
911
    p.type = MS_PARSE_TYPE_STRING;
696✔
912

913
    status = yyparse(&p);
696✔
914

915
    if (status != 0) {
696✔
916
      msSetError(MS_PARSEERR, "Failed to process text expression: %s",
×
917
                 "msEvalTextExpression", expr->string);
918
      return NULL;
×
919
    }
920

921
    result = p.result.strval;
696✔
922
    break;
696✔
923
  }
924
  default:
925
    break;
926
  }
927
  if (result && !strlen(result)) {
4,334✔
928
    msFree(result);
24✔
929
    result = NULL;
930
  }
931
  return result;
932
}
933

934
char *msEvalTextExpressionJSonEscape(expressionObj *expr, shapeObj *shape) {
21✔
935
  return msEvalTextExpressionInternal(expr, shape, MS_TRUE);
21✔
936
}
937

938
char *msEvalTextExpression(expressionObj *expr, shapeObj *shape) {
4,332✔
939
  return msEvalTextExpressionInternal(expr, shape, MS_FALSE);
4,332✔
940
}
941

942
double msEvalDoubleExpression(expressionObj *expression, shapeObj *shape) {
48✔
943
  double value;
944
  int status;
945
  parseObj p;
946
  p.shape = shape;
48✔
947
  p.expr = expression;
48✔
948
  p.expr->curtoken = p.expr->tokens; /* reset */
48✔
949
  p.type = MS_PARSE_TYPE_STRING;
48✔
950
  status = yyparse(&p);
48✔
951
  if (status != 0) {
48✔
952
    msSetError(MS_PARSEERR, "Failed to parse expression: %s", "bindStyle",
×
953
               expression->string);
954
    value = 0.0;
955
  } else {
956
    value = atof(p.result.strval);
48✔
957
    msFree(p.result.strval);
48✔
958
  }
959
  return value;
48✔
960
}
961

962
char *msShapeGetLabelAnnotation(layerObj *layer, shapeObj *shape,
9,528✔
963
                                labelObj *lbl) {
964
  assert(shape && lbl);
965
  if (lbl->text.string) {
9,528✔
966
    return msEvalTextExpression(&(lbl->text), shape);
3,568✔
967
  } else if (layer->class[shape->classindex] -> text.string) {
5,960✔
968
    return msEvalTextExpression(&(layer->class[shape->classindex] -> text),
757✔
969
                                shape);
970
  } else {
971
    if (shape->values && layer->labelitemindex >= 0 &&
5,203✔
972
        shape->values[layer->labelitemindex] &&
338✔
973
        strlen(shape->values[layer->labelitemindex]))
974
      return msStrdup(shape->values[layer->labelitemindex]);
338✔
975
    else if (shape->text)
4,865✔
976
      return msStrdup(
1,480✔
977
          shape->text); /* last resort but common with iniline features */
978
  }
979
  return NULL;
980
}
981

982
int msGetLabelStatus(mapObj *map, layerObj *layer, shapeObj *shape,
34,340✔
983
                     labelObj *lbl) {
984
  assert(layer && lbl);
985
  if (!msScaleInBounds(map->scaledenom, lbl->minscaledenom, lbl->maxscaledenom))
34,340✔
986
    return MS_OFF;
987
  if (msEvalExpression(layer, shape, &(lbl->expression),
34,340✔
988
                       layer->labelitemindex) != MS_TRUE)
989
    return MS_OFF;
990
  /* TODO: check for minfeaturesize here ? */
991
  return MS_ON;
992
}
993

994
/* Check if the shape is enough big to be drawn with the
995
   layer::minfeaturesize setting. The minfeaturesize parameter should be
996
   the value in geo ref (not in pixel) and should have been multiplied by
997
   the resolution factor.
998
 */
999
int msShapeCheckSize(shapeObj *shape, double minfeaturesize) {
×
1000
  double dx = (shape->bounds.maxx - shape->bounds.minx);
×
1001
  double dy = (shape->bounds.maxy - shape->bounds.miny);
×
1002

1003
  if (pow(minfeaturesize, 2.0) > (pow(dx, 2.0) + pow(dy, 2.0)))
×
1004
    return MS_FALSE;
×
1005

1006
  return MS_TRUE;
1007
}
1008

1009
/*
1010
** Adjusts an image size in one direction to fit an extent.
1011
*/
1012
int msAdjustImage(rectObj rect, int *width, int *height) {
×
1013
  if (*width == -1 && *height == -1) {
×
1014
    msSetError(MS_MISCERR, "Cannot calculate both image height and width.",
×
1015
               "msAdjustImage()");
1016
    return (-1);
×
1017
  }
1018

1019
  if (*width > 0)
×
1020
    *height =
×
1021
        MS_NINT((rect.maxy - rect.miny) / ((rect.maxx - rect.minx) / (*width)));
×
1022
  else
1023
    *width = MS_NINT((rect.maxx - rect.minx) /
×
1024
                     ((rect.maxy - rect.miny) / (*height)));
1025

1026
  return (0);
1027
}
1028

1029
/*
1030
** Make sure extent fits image window to be created. Returns cellsize of output
1031
*image.
1032
*/
1033
double msAdjustExtent(rectObj *rect, int width, int height) {
2,226✔
1034
  double cellsize, ox, oy;
1035

1036
  if (width == 1 || height == 1)
2,226✔
1037
    return 0;
1038

1039
  cellsize = MS_MAX(MS_CELLSIZE(rect->minx, rect->maxx, width),
2,226✔
1040
                    MS_CELLSIZE(rect->miny, rect->maxy, height));
1041

1042
  if (cellsize <= 0) /* avoid division by zero errors */
2,226✔
1043
    return (0);
1044

1045
  ox = MS_MAX(((width - 1) - (rect->maxx - rect->minx) / cellsize) / 2,
2,103✔
1046
              0); /* these were width-1 and height-1 */
1047
  oy = MS_MAX(((height - 1) - (rect->maxy - rect->miny) / cellsize) / 2, 0);
2,103✔
1048

1049
  rect->minx = rect->minx - ox * cellsize;
2,103✔
1050
  rect->miny = rect->miny - oy * cellsize;
2,103✔
1051
  rect->maxx = rect->maxx + ox * cellsize;
2,103✔
1052
  rect->maxy = rect->maxy + oy * cellsize;
2,103✔
1053

1054
  return (cellsize);
2,103✔
1055
}
1056

1057
/*
1058
** Rect must always contain a portion of bounds. If not, rect is
1059
** shifted to overlap by overlay percent. The dimensions of rect do
1060
** not change but placement relative to bounds can.
1061
*/
1062
int msConstrainExtent(rectObj *bounds, rectObj *rect, double overlay) {
×
1063
  /* check left edge, and if necessary the right edge of bounds */
1064
  if (rect->maxx <= bounds->minx) {
×
1065
    const double offset = overlay * (rect->maxx - rect->minx);
×
1066
    rect->minx += offset; /* shift right */
×
1067
    rect->maxx += offset;
×
1068
  } else if (rect->minx >= bounds->maxx) {
×
1069
    const double offset = overlay * (rect->maxx - rect->minx);
×
1070
    rect->minx -= offset; /* shift left */
×
1071
    rect->maxx -= offset;
×
1072
  }
1073

1074
  /* check top edge, and if necessary the bottom edge of bounds */
1075
  if (rect->maxy <= bounds->miny) {
×
1076
    const double offset = overlay * (rect->maxy - rect->miny);
×
1077
    rect->miny -= offset; /* shift down */
×
1078
    rect->maxy -= offset;
×
1079
  } else if (rect->miny >= bounds->maxy) {
×
1080
    const double offset = overlay * (rect->maxy - rect->miny);
×
1081
    rect->miny += offset; /* shift up */
×
1082
    rect->maxy += offset;
×
1083
  }
1084

1085
  return (MS_SUCCESS);
×
1086
}
1087

1088
/*
1089
** Generic function to save an image to a file.
1090
**
1091
** Note that map may be NULL. If it is set, then it is used for two things:
1092
** - Deal with relative imagepaths (compute absolute path relative to map path)
1093
** - Extract the georeferenced extents and coordinate system
1094
**   of the map for writing out with the image when appropriate
1095
**   (primarily this means via msSaveImageGDAL() to something like GeoTIFF).
1096
**
1097
** The filename is NULL when the image is supposed to be written to stdout.
1098
*/
1099

1100
int msSaveImage(mapObj *map, imageObj *img, const char *filename) {
1,339✔
1101
  int nReturnVal = MS_FAILURE;
1102
  char szPath[MS_MAXPATHLEN];
1103
  struct mstimeval starttime = {0}, endtime = {0};
1,339✔
1104

1105
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,339✔
1106
    msGettimeofday(&starttime, NULL);
4✔
1107
  }
1108

1109
  if (img) {
1,339✔
1110
    if (MS_DRIVER_GDAL(img->format)) {
1,339✔
1111
      if (map != NULL && filename != NULL)
210✔
1112
        nReturnVal = msSaveImageGDAL(
188✔
1113
            map, img, msBuildPath(szPath, map->mappath, filename));
188✔
1114
      else
1115
        nReturnVal = msSaveImageGDAL(map, img, filename);
22✔
1116
    } else
1117

1118
        if (MS_RENDERER_PLUGIN(img->format)) {
1,129✔
1119
      rendererVTableObj *renderer = img->format->vtable;
1,129✔
1120
      FILE *stream = NULL;
1121
      if (filename) {
1,129✔
1122
        if (map)
611✔
1123
          stream = fopen(msBuildPath(szPath, map->mappath, filename), "wb");
567✔
1124
        else
1125
          stream = fopen(filename, "wb");
44✔
1126

1127
        if (!stream) {
611✔
1128
          msSetError(MS_IOERR, "Failed to create output file (%s).",
×
1129
                     "msSaveImage()", (map ? szPath : filename));
1130
          return MS_FAILURE;
×
1131
        }
1132

1133
      } else {
1134
        if (msIO_needBinaryStdout() == MS_FAILURE)
518✔
1135
          return MS_FAILURE;
1136
        stream = stdout;
518✔
1137
      }
1138

1139
      if (renderer->supports_pixel_buffer) {
1,129✔
1140
        rasterBufferObj data;
1141
        if (renderer->getRasterBufferHandle(img, &data) != MS_SUCCESS) {
962✔
1142
          if (stream != stdout)
×
1143
            fclose(stream);
×
1144
          return MS_FAILURE;
×
1145
        }
1146

1147
        nReturnVal = msSaveRasterBuffer(map, &data, stream, img->format);
962✔
1148
      } else {
1149
        nReturnVal = renderer->saveImage(img, map, stream, img->format);
167✔
1150
      }
1151
      if (stream != stdout)
1,129✔
1152
        fclose(stream);
611✔
1153

1154
    } else if (MS_DRIVER_IMAGEMAP(img->format))
×
1155
      nReturnVal = msSaveImageIM(img, filename, img->format);
×
1156
    else
1157
      msSetError(MS_MISCERR, "Unknown image type", "msSaveImage()");
×
1158
  }
1159

1160
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,339✔
1161
    msGettimeofday(&endtime, NULL);
4✔
1162
    msDebug("msSaveImage(%s) total time: %.3fs\n",
4✔
1163
            (filename ? filename : "stdout"),
1164
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
4✔
1165
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
4✔
1166
  }
1167

1168
  return nReturnVal;
1169
}
1170

1171
/*
1172
** Generic function to save an image to a byte array.
1173
** - the return value is the pointer to the byte array
1174
** - size_ptr contains the number of bytes returned
1175
** - format: the desired output format
1176
**
1177
** The caller is responsible to free the returned array
1178
** The function returns NULL if the output format is not supported.
1179
*/
1180

1181
unsigned char *msSaveImageBuffer(imageObj *image, int *size_ptr,
15✔
1182
                                 outputFormatObj *format) {
1183
  *size_ptr = 0;
15✔
1184
  if (MS_RENDERER_PLUGIN(image->format)) {
15✔
1185
    rasterBufferObj data;
1186
    rendererVTableObj *renderer = image->format->vtable;
15✔
1187
    if (renderer->supports_pixel_buffer) {
15✔
1188
      bufferObj buffer;
1189
      msBufferInit(&buffer);
15✔
1190
      const int status = renderer->getRasterBufferHandle(image, &data);
15✔
1191
      if (MS_UNLIKELY(status == MS_FAILURE)) {
15✔
1192
        return NULL;
1193
      }
1194
      msSaveRasterBufferToBuffer(&data, &buffer, format);
15✔
1195
      *size_ptr = buffer.size;
15✔
1196
      return buffer.data;
15✔
1197
      /* don't free the bufferObj as we don't own the bytes anymore */
1198
    } else {
1199
      /* check if the renderer supports native buffer output */
1200
      if (renderer->saveImageBuffer)
×
1201
        return renderer->saveImageBuffer(image, size_ptr, format);
×
1202

1203
      msSetError(MS_MISCERR, "Unsupported image type", "msSaveImageBuffer()");
×
1204
      return NULL;
×
1205
    }
1206
  }
1207
  msSetError(MS_MISCERR, "Unsupported image type", "msSaveImage()");
×
1208
  return NULL;
×
1209
}
1210

1211
/**
1212
 * Generic function to free the imageObj
1213
 */
1214
void msFreeImage(imageObj *image) {
2,059✔
1215
  if (image) {
2,059✔
1216
    if (MS_RENDERER_PLUGIN(image->format)) {
2,059✔
1217
      rendererVTableObj *renderer = image->format->vtable;
1,752✔
1218
      tileCacheObj *next, *cur = image->tilecache;
1,752✔
1219
      while (cur) {
1,789✔
1220
        msFreeImage(cur->image);
37✔
1221
        next = cur->next;
37✔
1222
        free(cur);
37✔
1223
        cur = next;
1224
      }
1225
      image->ntiles = 0;
1,752✔
1226
      renderer->freeImage(image);
1,752✔
1227
    } else if (MS_RENDERER_IMAGEMAP(image->format))
307✔
1228
      msFreeImageIM(image);
×
1229
    else if (MS_RENDERER_RAWDATA(image->format))
307✔
1230
      msFree(image->img.raw_16bit);
307✔
1231
    else
1232
      msSetError(MS_MISCERR, "Unknown image type", "msFreeImage()");
×
1233

1234
    if (image->imagepath)
2,059✔
1235
      free(image->imagepath);
1,749✔
1236
    if (image->imageurl)
2,059✔
1237
      free(image->imageurl);
1,749✔
1238

1239
    if (--image->format->refcount < 1)
2,059✔
1240
      msFreeOutputFormat(image->format);
526✔
1241

1242
    image->imagepath = NULL;
1243
    image->imageurl = NULL;
1244

1245
    msFree(image->img_mask);
2,059✔
1246
    image->img_mask = NULL;
1247

1248
    msFree(image);
2,059✔
1249
  }
1250
}
2,059✔
1251

1252
/*
1253
** Return an array containing all the layer's index given a group name.
1254
** If nothing is found, NULL is returned. The nCount is initialized
1255
** to the number of elements in the returned array.
1256
** Note : the caller of the function should free the array.
1257
*/
1258
int *msGetLayersIndexByGroup(mapObj *map, char *groupname, int *pnCount) {
1✔
1259
  int i;
1260
  int iLayer = 0;
1261
  int *aiIndex;
1262

1263
  if (!groupname || !map || !pnCount) {
1✔
1264
    return NULL;
1265
  }
1266

1267
  aiIndex = (int *)msSmallMalloc(sizeof(int) * map->numlayers);
1✔
1268

1269
  for (i = 0; i < map->numlayers; i++) {
3✔
1270
    if (!GET_LAYER(map, i)->group) /* skip it */
2✔
1271
      continue;
2✔
1272
    if (strcmp(groupname, GET_LAYER(map, i)->group) == 0) {
×
1273
      aiIndex[iLayer] = i;
×
1274
      iLayer++;
×
1275
    }
1276
  }
1277

1278
  if (iLayer == 0) {
1✔
1279
    free(aiIndex);
1✔
1280
    aiIndex = NULL;
1281
    *pnCount = 0;
1✔
1282
  } else {
1283
    aiIndex = (int *)msSmallRealloc(aiIndex, sizeof(int) * iLayer);
×
1284
    *pnCount = iLayer;
×
1285
  }
1286

1287
  return aiIndex;
1288
}
1289

1290
/* ==================================================================== */
1291
/*      Measured shape utility functions.                               */
1292
/* ==================================================================== */
1293

1294
/************************************************************************/
1295
/*        pointObj *msGetPointUsingMeasure(shapeObj *shape, double m)   */
1296
/*                                                                      */
1297
/*      Using a measured value get the XY location it corresponds        */
1298
/*      to.                                                             */
1299
/*                                                                      */
1300
/************************************************************************/
1301
pointObj *msGetPointUsingMeasure(shapeObj *shape, double m) {
×
1302
  pointObj *point = NULL;
1303
  int bFound = 0;
1304
  double dfFirstPointX = 0;
1305
  double dfFirstPointY = 0;
1306
  double dfFirstPointM = 0;
1307
  double dfSecondPointX = 0;
1308
  double dfSecondPointY = 0;
1309
  double dfSecondPointM = 0;
1310

1311
  if (shape && shape->numlines > 0) {
×
1312
    /* -------------------------------------------------------------------- */
1313
    /*      check fir the first value (min) and the last value(max) to      */
1314
    /*      see if the m is contained between these min and max.            */
1315
    /* -------------------------------------------------------------------- */
1316
    const lineObj *lineFirst = &(shape->line[0]);
×
1317
    const double dfMin = lineFirst->point[0].m;
×
1318
    const lineObj *lineLast = &(shape->line[shape->numlines - 1]);
×
1319
    const double dfMax = lineLast->point[lineLast->numpoints - 1].m;
×
1320

1321
    if (m >= dfMin && m <= dfMax) {
×
1322
      for (int i = 0; i < shape->numlines; i++) {
×
1323
        const lineObj *line = &(shape->line[i]);
×
1324

1325
        for (int j = 0; j < line->numpoints; j++) {
×
1326
          const double dfCurrentM = line->point[j].m;
×
1327
          if (dfCurrentM > m) {
×
1328
            bFound = 1;
1329

1330
            dfSecondPointX = line->point[j].x;
×
1331
            dfSecondPointY = line->point[j].y;
×
1332
            dfSecondPointM = line->point[j].m;
1333

1334
            /* --------------------------------------------------------------------
1335
             */
1336
            /*      get the previous node xy values. */
1337
            /* --------------------------------------------------------------------
1338
             */
1339
            if (j > 0) { /* not the first point of the line */
×
1340
              dfFirstPointX = line->point[j - 1].x;
×
1341
              dfFirstPointY = line->point[j - 1].y;
×
1342
              dfFirstPointM = line->point[j - 1].m;
×
1343
            } else { /* get last point of previous line */
1344
              dfFirstPointX = shape->line[i - 1].point[0].x;
×
1345
              dfFirstPointY = shape->line[i - 1].point[0].y;
×
1346
              dfFirstPointM = shape->line[i - 1].point[0].m;
×
1347
            }
1348
            break;
1349
          }
1350
        }
1351
      }
1352
    }
1353

1354
    if (!bFound)
×
1355
      return NULL;
1356

1357
    /* -------------------------------------------------------------------- */
1358
    /*      extrapolate the m value to get t he xy coordinate.              */
1359
    /* -------------------------------------------------------------------- */
1360

1361
    double dfFactor = 0;
1362
    if (dfFirstPointM != dfSecondPointM)
×
1363
      dfFactor = (m - dfFirstPointM) / (dfSecondPointM - dfFirstPointM);
×
1364

1365
    point = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1366

1367
    point->x = dfFirstPointX + (dfFactor * (dfSecondPointX - dfFirstPointX));
×
1368
    point->y = dfFirstPointY + (dfFactor * (dfSecondPointY - dfFirstPointY));
×
1369
    point->m = dfFirstPointM + (dfFactor * (dfSecondPointM - dfFirstPointM));
×
1370

1371
    return point;
×
1372
  }
1373

1374
  return NULL;
1375
}
1376

1377
/************************************************************************/
1378
/*       IntersectionPointLinepointObj *p, pointObj *a, pointObj *b)    */
1379
/*                                                                      */
1380
/*      Returns a point object corresponding to the intersection of     */
1381
/*      point p and a line formed of 2 points : a and b.                */
1382
/*                                                                      */
1383
/*      Alorith base on :                                               */
1384
/*      http://www.faqs.org/faqs/graphics/algorithms-faq/               */
1385
/*                                                                      */
1386
/*      Subject 1.02:How do I find the distance from a point to a line? */
1387
/*                                                                      */
1388
/*          Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By).*/
1389
/*          Let P be the point of perpendicular projection of C on AB.  The
1390
 * parameter*/
1391
/*          r, which indicates P's position along AB, is computed by the dot
1392
 * product */
1393
/*          of AC and AB divided by the square of the length of AB:     */
1394
/*                                                                      */
1395
/*          (1)     AC dot AB                                           */
1396
/*              r = ---------                                           */
1397
/*                  ||AB||^2                                            */
1398
/*                                                                      */
1399
/*          r has the following meaning:                                */
1400
/*                                                                      */
1401
/*              r=0      P = A                                          */
1402
/*              r=1      P = B                                          */
1403
/*              r<0      P is on the backward extension of AB           */
1404
/*              r>1      P is on the forward extension of AB            */
1405
/*              0<r<1    P is interior to AB                            */
1406
/*                                                                      */
1407
/*          The length of a line segment in d dimensions, AB is computed by:*/
1408
/*                                                                      */
1409
/*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 + ... + (Bd-Ad)^2)      */
1410
/*                                                                      */
1411
/*          so in 2D:                                                   */
1412
/*                                                                      */
1413
/*              L = sqrt( (Bx-Ax)^2 + (By-Ay)^2 )                       */
1414
/*                                                                      */
1415
/*          and the dot product of two vectors in d dimensions, U dot V is
1416
 * computed:*/
1417
/*                                                                      */
1418
/*              D = (Ux * Vx) + (Uy * Vy) + ... + (Ud * Vd)             */
1419
/*                                                                      */
1420
/*          so in 2D:                                                   */
1421
/*                                                                      */
1422
/*              D = (Ux * Vx) + (Uy * Vy)                               */
1423
/*                                                                      */
1424
/*          So (1) expands to:                                          */
1425
/*                                                                      */
1426
/*                  (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay)                     */
1427
/*              r = -------------------------------                     */
1428
/*                                L^2                                   */
1429
/*                                                                      */
1430
/*          The point P can then be found:                              */
1431
/*                                                                      */
1432
/*              Px = Ax + r(Bx-Ax)                                      */
1433
/*              Py = Ay + r(By-Ay)                                      */
1434
/*                                                                      */
1435
/*          And the distance from A to P = r*L.                         */
1436
/*                                                                      */
1437
/*          Use another parameter s to indicate the location along PC, with the
1438
 */
1439
/*          following meaning:                                          */
1440
/*                 s<0      C is left of AB                             */
1441
/*                 s>0      C is right of AB                            */
1442
/*                 s=0      C is on AB                                  */
1443
/*                                                                      */
1444
/*          Compute s as follows:                                       */
1445
/*                                                                      */
1446
/*                  (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)                       */
1447
/*              s = -----------------------------                       */
1448
/*                              L^2                                     */
1449
/*                                                                      */
1450
/*                                                                      */
1451
/*          Then the distance from C to P = |s|*L.                      */
1452
/*                                                                      */
1453
/************************************************************************/
1454
pointObj *msIntersectionPointLine(pointObj *p, pointObj *a, pointObj *b) {
×
1455
  pointObj *result = NULL;
1456

1457
  if (p && a && b) {
×
1458
    const double L =
1459
        sqrt(((b->x - a->x) * (b->x - a->x)) + ((b->y - a->y) * (b->y - a->y)));
×
1460

1461
    double r = 0;
1462
    if (L != 0)
×
1463
      r = ((p->x - a->x) * (b->x - a->x) + (p->y - a->y) * (b->y - a->y)) /
×
1464
          (L * L);
×
1465

1466
    result = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1467
    /* -------------------------------------------------------------------- */
1468
    /*      We want to make sure that the point returned is on the line     */
1469
    /*                                                                      */
1470
    /*              r=0      P = A                                          */
1471
    /*              r=1      P = B                                          */
1472
    /*              r<0      P is on the backward extension of AB           */
1473
    /*              r>1      P is on the forward extension of AB            */
1474
    /*                    0<r<1    P is interior to AB                      */
1475
    /* -------------------------------------------------------------------- */
1476
    if (r < 0) {
×
1477
      result->x = a->x;
×
1478
      result->y = a->y;
×
1479
    } else if (r > 1) {
×
1480
      result->x = b->x;
×
1481
      result->y = b->y;
×
1482
    } else {
1483
      result->x = a->x + r * (b->x - a->x);
×
1484
      result->y = a->y + r * (b->y - a->y);
×
1485
    }
1486
    result->m = 0;
×
1487
  }
1488

1489
  return result;
×
1490
}
1491

1492
/************************************************************************/
1493
/*         pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj   */
1494
/*      *point)                                                         */
1495
/*                                                                      */
1496
/*      Calculate the intersection point betwwen the point and the      */
1497
/*      shape and return the Measured value at the intersection.        */
1498
/************************************************************************/
1499
pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj *point) {
×
1500
  pointObj oFirst = {0, 0, 0, 0};
×
1501
  pointObj oSecond = {0, 0, 0, 0};
×
1502

1503
  if (shape && point) {
×
1504
    double dfMinDist = HUGE_VAL;
1505
    for (int i = 0; i < shape->numlines; i++) {
×
1506
      lineObj line = shape->line[i];
×
1507
      /* -------------------------------------------------------------------- */
1508
      /*      for each line (2 consecutive lines) get the distance between    */
1509
      /*      the line and the point and determine which line segment is      */
1510
      /*      the closeset to the point.                                      */
1511
      /* -------------------------------------------------------------------- */
1512
      for (int j = 0; j < line.numpoints - 1; j++) {
×
1513
        const double dfDist =
1514
            msDistancePointToSegment(point, &line.point[j], &line.point[j + 1]);
×
1515
        if (dfDist < dfMinDist) {
×
1516
          oFirst.x = line.point[j].x;
×
1517
          oFirst.y = line.point[j].y;
×
1518
          oFirst.m = line.point[j].m;
×
1519

1520
          oSecond.x = line.point[j + 1].x;
×
1521
          oSecond.y = line.point[j + 1].y;
×
1522
          oSecond.m = line.point[j + 1].m;
×
1523

1524
          dfMinDist = dfDist;
1525
        }
1526
      }
1527
    }
1528
    /* -------------------------------------------------------------------- */
1529
    /*      once we have the nearest segment, look for the x,y location     */
1530
    /*      which is the nearest intersection between the line and the      */
1531
    /*      point.                                                          */
1532
    /* -------------------------------------------------------------------- */
1533
    pointObj *poIntersectionPt =
1534
        msIntersectionPointLine(point, &oFirst, &oSecond);
×
1535
    if (poIntersectionPt) {
×
1536
      const double dfDistTotal =
1537
          sqrt(((oSecond.x - oFirst.x) * (oSecond.x - oFirst.x)) +
×
1538
               ((oSecond.y - oFirst.y) * (oSecond.y - oFirst.y)));
×
1539

1540
      const double dfDistToIntersection =
1541
          sqrt(((poIntersectionPt->x - oFirst.x) *
×
1542
                (poIntersectionPt->x - oFirst.x)) +
1543
               ((poIntersectionPt->y - oFirst.y) *
×
1544
                (poIntersectionPt->y - oFirst.y)));
1545

1546
      const double dfFactor = dfDistToIntersection / dfDistTotal;
×
1547

1548
      poIntersectionPt->m = oFirst.m + (oSecond.m - oFirst.m) * dfFactor;
×
1549
      return poIntersectionPt;
×
1550
    }
1551
  }
1552
  return NULL;
1553
}
1554

1555
/* ==================================================================== */
1556
/*   End   Measured shape utility functions.                            */
1557
/* ==================================================================== */
1558

1559
char **msGetAllGroupNames(mapObj *map, int *numTok) {
×
1560
  char **papszGroups = NULL;
1561

1562
  assert(map);
1563
  *numTok = 0;
×
1564

1565
  if (!map->layerorder) {
×
1566
    map->layerorder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
1567

1568
    /*
1569
     * Initiate to default order
1570
     */
1571
    for (int i = 0; i < map->numlayers; i++)
×
1572
      map->layerorder[i] = i;
×
1573
  }
1574

1575
  if (map->numlayers > 0) {
×
1576
    const int nCount = map->numlayers;
1577
    papszGroups = (char **)msSmallMalloc(sizeof(char *) * nCount);
×
1578

1579
    for (int i = 0; i < nCount; i++)
×
1580
      papszGroups[i] = NULL;
×
1581

1582
    for (int i = 0; i < nCount; i++) {
×
1583
      layerObj *lp;
1584
      lp = (GET_LAYER(map, map->layerorder[i]));
×
1585

1586
      int bFound = 0;
1587
      if (lp->group && lp->status != MS_DELETE) {
×
1588
        for (int j = 0; j < *numTok; j++) {
×
1589
          if (papszGroups[j] && strcmp(lp->group, papszGroups[j]) == 0) {
×
1590
            bFound = 1;
1591
            break;
1592
          }
1593
        }
1594
        if (!bFound) {
×
1595
          /* New group... add to the list of groups found */
1596
          papszGroups[(*numTok)] = msStrdup(lp->group);
×
1597
          (*numTok)++;
×
1598
        }
1599
      }
1600
    }
1601
  }
1602

1603
  return papszGroups;
×
1604
}
1605

1606
/************************************************************************/
1607
/*                         msForceTmpFileBase()                         */
1608
/************************************************************************/
1609

1610
static int tmpCount = 0;
1611
static char *ForcedTmpBase = NULL;
1612

1613
void msForceTmpFileBase(const char *new_base) {
2,657✔
1614
  /* -------------------------------------------------------------------- */
1615
  /*      Clear previous setting, if any.                                 */
1616
  /* -------------------------------------------------------------------- */
1617
  if (ForcedTmpBase != NULL) {
2,657✔
1618
    free(ForcedTmpBase);
×
1619
    ForcedTmpBase = NULL;
×
1620
  }
1621

1622
  tmpCount = -1;
2,657✔
1623

1624
  if (new_base == NULL)
2,657✔
1625
    return;
1626

1627
  /* -------------------------------------------------------------------- */
1628
  /*      Record new base.                                                */
1629
  /* -------------------------------------------------------------------- */
1630
  ForcedTmpBase = msStrdup(new_base);
×
1631
  tmpCount = 0;
×
1632
}
1633

1634
/**********************************************************************
1635
 *                          msTmpFile()
1636
 *
1637
 * Generate a Unique temporary file.
1638
 *
1639
 * Returns char* which must be freed by caller.
1640
 **********************************************************************/
1641
char *msTmpFile(mapObj *map, const char *mappath, const char *tmppath,
265✔
1642
                const char *ext) {
1643
  char szPath[MS_MAXPATHLEN];
1644
  const char *fullFname;
1645
  char *tmpFileName; /* big enough for time + pid + ext */
1646
  char *tmpBase = NULL;
1647

1648
  tmpBase = msTmpPath(map, mappath, tmppath);
265✔
1649
  tmpFileName = msTmpFilename(ext);
265✔
1650

1651
  fullFname = msBuildPath(szPath, tmpBase, tmpFileName);
265✔
1652

1653
  free(tmpFileName);
265✔
1654
  free(tmpBase);
265✔
1655

1656
  if (fullFname)
265✔
1657
    return msStrdup(fullFname);
265✔
1658

1659
  return NULL;
1660
}
1661

1662
/**********************************************************************
1663
 *                          msTmpPath()
1664
 *
1665
 * Return the temporary path based on the platform.
1666
 *
1667
 * Returns char* which must be freed by caller.
1668
 **********************************************************************/
1669
char *msTmpPath(mapObj *map, const char *mappath, const char *tmppath) {
275✔
1670
  char szPath[MS_MAXPATHLEN];
1671
  const char *fullPath;
1672
  const char *tmpBase;
1673
  const char *ms_temppath;
1674
#ifdef _WIN32
1675
  DWORD dwRetVal = 0;
1676
  TCHAR lpTempPathBuffer[MAX_PATH];
1677
#endif
1678

1679
  if (ForcedTmpBase != NULL)
275✔
1680
    tmpBase = ForcedTmpBase;
1681
  else if (tmppath != NULL)
275✔
1682
    tmpBase = tmppath;
1683
  else if ((ms_temppath = CPLGetConfigOption("MS_TEMPPATH", NULL)) != NULL)
38✔
1684
    tmpBase = ms_temppath;
1685
  else if (map && map->web.temppath)
38✔
1686
    tmpBase = map->web.temppath;
1687
  else { /* default paths */
1688
#ifndef _WIN32
1689
    tmpBase = "/tmp/";
1690
#else
1691
    dwRetVal = GetTempPath(MAX_PATH,          /* length of the buffer */
1692
                           lpTempPathBuffer); /* buffer for path */
1693
    if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
1694
      tmpBase = "C:\\";
1695
    } else {
1696
      tmpBase = (char *)lpTempPathBuffer;
1697
    }
1698
#endif
1699
  }
1700

1701
  fullPath = msBuildPath(szPath, mappath, tmpBase);
275✔
1702
  return msStrdup(fullPath);
275✔
1703
}
1704

1705
/**********************************************************************
1706
 *                          msTmpFilename()
1707
 *
1708
 * Generate a Unique temporary filename.
1709
 *
1710
 * Returns char* which must be freed by caller.
1711
 **********************************************************************/
1712
char *msTmpFilename(const char *ext) {
266✔
1713
  char *tmpFname;
1714
  int tmpFnameBufsize;
1715
  char *fullFname;
1716
  char tmpId[128]; /* big enough for time + pid + ext */
1717

1718
  snprintf(tmpId, sizeof(tmpId), "%lx_%x", (long)time(NULL), (int)getpid());
266✔
1719

1720
  if (ext == NULL)
266✔
1721
    ext = "";
1722
  tmpFnameBufsize = strlen(tmpId) + 10 + strlen(ext) + 1;
266✔
1723
  tmpFname = (char *)msSmallMalloc(tmpFnameBufsize);
266✔
1724

1725
  msAcquireLock(TLOCK_TMPFILE);
266✔
1726
  snprintf(tmpFname, tmpFnameBufsize, "%s_%x.%s", tmpId, tmpCount++, ext);
266✔
1727
  msReleaseLock(TLOCK_TMPFILE);
266✔
1728

1729
  fullFname = msStrdup(tmpFname);
266✔
1730
  free(tmpFname);
266✔
1731

1732
  return fullFname;
266✔
1733
}
1734

1735
/**
1736
 *  Generic function to Initialize an image object.
1737
 */
1738
imageObj *msImageCreate(int width, int height, outputFormatObj *format,
839✔
1739
                        char *imagepath, char *imageurl, double resolution,
1740
                        double defresolution, colorObj *bg) {
1741
  imageObj *image = NULL;
1742
  if (MS_RENDERER_PLUGIN(format)) {
839✔
1743

1744
    image = format->vtable->createImage(width, height, format, bg);
532✔
1745
    if (image == NULL) {
532✔
1746
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1747
                 "msImageCreate()");
1748
      return NULL;
×
1749
    }
1750

1751
    image->format = format;
532✔
1752
    format->refcount++;
532✔
1753

1754
    image->width = width;
532✔
1755
    image->height = height;
532✔
1756
    image->imagepath = NULL;
532✔
1757
    image->imageurl = NULL;
532✔
1758
    image->tilecache = NULL;
532✔
1759
    image->ntiles = 0;
532✔
1760
    image->resolution = resolution;
532✔
1761
    image->resolutionfactor = resolution / defresolution;
532✔
1762

1763
    if (imagepath)
532✔
1764
      image->imagepath = msStrdup(imagepath);
351✔
1765
    if (imageurl)
532✔
1766
      image->imageurl = msStrdup(imageurl);
351✔
1767
  } else if (MS_RENDERER_RAWDATA(format)) {
307✔
1768
    if (format->imagemode != MS_IMAGEMODE_INT16 &&
307✔
1769
        format->imagemode != MS_IMAGEMODE_FLOAT32 &&
307✔
1770
        format->imagemode != MS_IMAGEMODE_FLOAT64 &&
212✔
1771
        format->imagemode != MS_IMAGEMODE_BYTE) {
1772
      msSetError(MS_IMGERR,
×
1773
                 "Attempt to use illegal imagemode with rawdata renderer.",
1774
                 "msImageCreate()");
1775
      return NULL;
×
1776
    }
1777

1778
    image = (imageObj *)calloc(1, sizeof(imageObj));
307✔
1779
    if (image == NULL) {
307✔
1780
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1781
                 "msImageCreate()");
1782
      return NULL;
×
1783
    }
1784

1785
    if (format->imagemode == MS_IMAGEMODE_INT16)
307✔
1786
      image->img.raw_16bit = (short *)msSmallCalloc(
38✔
1787
          sizeof(short), ((size_t)width) * height * format->bands);
38✔
1788
    else if (format->imagemode == MS_IMAGEMODE_FLOAT32)
269✔
1789
      image->img.raw_float = (float *)msSmallCalloc(
53✔
1790
          sizeof(float), ((size_t)width) * height * format->bands);
53✔
1791
    else if (format->imagemode == MS_IMAGEMODE_FLOAT64)
216✔
1792
      image->img.raw_double = (double *)msSmallCalloc(
4✔
1793
          sizeof(double), ((size_t)width) * height * format->bands);
4✔
1794
    else if (format->imagemode == MS_IMAGEMODE_BYTE)
1795
      image->img.raw_byte = (unsigned char *)msSmallCalloc(
212✔
1796
          sizeof(unsigned char), ((size_t)width) * height * format->bands);
212✔
1797

1798
    if (image->img.raw_16bit == NULL) {
307✔
1799
      msFree(image);
×
1800
      msSetError(MS_IMGERR,
×
1801
                 "Attempt to allocate raw image failed, out of memory.",
1802
                 "msImageCreate()");
1803
      return NULL;
×
1804
    }
1805

1806
    image->img_mask = msAllocBitArray(width * height);
307✔
1807

1808
    image->format = format;
307✔
1809
    format->refcount++;
307✔
1810

1811
    image->width = width;
307✔
1812
    image->height = height;
307✔
1813
    image->imagepath = NULL;
307✔
1814
    image->imageurl = NULL;
307✔
1815
    image->resolution = resolution;
307✔
1816
    image->resolutionfactor = resolution / defresolution;
307✔
1817

1818
    if (imagepath)
307✔
1819
      image->imagepath = msStrdup(imagepath);
178✔
1820
    if (imageurl)
307✔
1821
      image->imageurl = msStrdup(imageurl);
178✔
1822

1823
    /* initialize to requested nullvalue if there is one */
1824
    const char *nullvalue =
1825
        msGetOutputFormatOption(image->format, "NULLVALUE", NULL);
307✔
1826
    if (nullvalue != NULL) {
307✔
1827
      int i = image->width * image->height * format->bands;
34✔
1828
      if (atof(nullvalue) == 0.0)
34✔
1829
        /* nothing to do */;
1830
      else if (format->imagemode == MS_IMAGEMODE_INT16) {
34✔
1831
        short nv = atoi(nullvalue);
31✔
1832
        for (; i > 0;)
31,941✔
1833
          image->img.raw_16bit[--i] = nv;
31,910✔
1834
      } else if (format->imagemode == MS_IMAGEMODE_FLOAT32) {
3✔
1835
        float nv = atof(nullvalue);
3✔
1836
        for (; i > 0;)
135✔
1837
          image->img.raw_float[--i] = nv;
132✔
1838
      } else if (format->imagemode == MS_IMAGEMODE_FLOAT64) {
×
1839
        double nv = atof(nullvalue);
1840
        for (; i > 0;)
×
1841
          image->img.raw_double[--i] = nv;
×
1842
      } else if (format->imagemode == MS_IMAGEMODE_BYTE) {
×
1843
        unsigned char nv = (unsigned char)atoi(nullvalue);
×
1844

1845
        memset(image->img.raw_byte, nv, i);
×
1846
      }
1847
    }
1848
  } else if (MS_RENDERER_IMAGEMAP(format)) {
×
1849
    image = msImageCreateIM(width, height, format, imagepath, imageurl,
×
1850
                            resolution, defresolution);
1851
  } else {
1852
    msSetError(MS_MISCERR,
×
1853
               "Unsupported renderer requested, unable to initialize image.",
1854
               "msImageCreate()");
1855
    return NULL;
×
1856
  }
1857

1858
  if (!image) {
215✔
1859
    msSetError(MS_IMGERR, "Unable to initialize image.", "msImageCreate()");
×
1860
    return NULL;
×
1861
  }
1862
  image->refpt.x = image->refpt.y = 0;
839✔
1863
  return image;
839✔
1864
}
1865

1866
/**
1867
 * Generic function to transform a point.
1868
 *
1869
 */
1870
void msTransformPoint(pointObj *point, rectObj *extent, double cellsize,
23,397✔
1871
                      imageObj *image) {
1872
  double invcellsize;
1873
  /*We should probably have a function defined at all the renders*/
1874
  if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
23,397✔
1875
    if (image->format->renderer == MS_RENDER_WITH_KML) {
23,397✔
1876
      return;
1877
    }
1878
  }
1879
  invcellsize = 1.0 / cellsize;
23,370✔
1880
  point->x = MS_MAP2IMAGE_X_IC_DBL(point->x, extent->minx, invcellsize);
23,370✔
1881
  point->y = MS_MAP2IMAGE_Y_IC_DBL(point->y, extent->maxy, invcellsize);
23,370✔
1882
}
1883

1884
/*
1885
** Helper functions supplied as part of bug #2868 solution. Consider moving
1886
*these to
1887
** mapprimitive.c for more general use.
1888
*/
1889

1890
/* vector difference */
1891
static pointObj point_diff(const pointObj a, const pointObj b) {
1892
  pointObj retv;
1893
  retv.x = a.x - b.x;
24✔
1894
  retv.y = a.y - b.y;
24✔
1895
  retv.z = a.z - b.z;
24✔
1896
  retv.m = a.m - b.m;
24✔
1897
  return retv;
24✔
1898
}
1899

1900
/* vector sum */
1901
static pointObj point_sum(const pointObj a, const pointObj b) {
1902
  pointObj retv;
1903
  retv.x = a.x + b.x;
28✔
1904
  retv.y = a.y + b.y;
28✔
1905
  retv.z = a.z + b.z;
28✔
1906
  retv.m = a.m + b.m;
28✔
1907
  return retv;
24✔
1908
}
1909

1910
/* vector multiply */
1911
static pointObj point_mul(const pointObj a, double b) {
1912
  pointObj retv;
1913
  retv.x = a.x * b;
28✔
1914
  retv.y = a.y * b;
28✔
1915
  retv.z = a.z * b;
28✔
1916
  retv.m = a.m * b;
4✔
1917
  return retv;
1918
}
1919

1920
/* vector ??? */
1921
static double point_abs2(const pointObj a) {
1922
  return a.x * a.x + a.y * a.y + a.z * a.z + a.m * a.m;
24✔
1923
}
1924

1925
/* vector normal */
1926
static pointObj point_norm(const pointObj a) {
24✔
1927
  double lenmul;
1928
  pointObj retv;
1929
  int norm_vector;
1930

1931
  norm_vector = a.x == 0 && a.y == 0;
24✔
1932
  norm_vector = norm_vector && a.z == 0 && a.m == 0;
×
1933

1934
  if (norm_vector)
1935
    return a;
×
1936

1937
  lenmul =
24✔
1938
      1.0 / sqrt(point_abs2(a)); /* this seems to be the costly operation */
24✔
1939

1940
  retv.x = a.x * lenmul;
24✔
1941
  retv.y = a.y * lenmul;
24✔
1942
  retv.z = a.z * lenmul;
24✔
1943
  retv.m = a.m * lenmul;
24✔
1944

1945
  return retv;
24✔
1946
}
1947

1948
/* rotate a vector 90 degrees */
1949
static pointObj point_rotz90(const pointObj a) {
1950
  double nx = -1.0 * a.y, ny = a.x;
24✔
1951
  pointObj retv = a;
1952
  retv.x = nx;
1953
  retv.y = ny;
1954
  return retv;
1955
}
1956

1957
/* vector cross product (warning: z and m dimensions are ignored!) */
1958
static double point_cross(const pointObj a, const pointObj b) {
1959
  return a.x * b.y - a.y * b.x;
20✔
1960
}
1961

1962
shapeObj *msOffsetCurve(shapeObj *p, double offset) {
79✔
1963
  shapeObj *ret;
1964
  int i, j, first, idx, ok = 0;
1965
#if defined USE_GEOS && (GEOS_VERSION_MAJOR > 3 ||                             \
1966
                         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3))
1967
  ret = msGEOSOffsetCurve(p, offset);
79✔
1968
  /* GEOS curve offsetting can fail sometimes, we continue with our own
1969
   implementation if that is the case.*/
1970
  if (ret)
79✔
1971
    return ret;
1972
  /* clear error raised by geos in this case */
1973
  msResetErrorList();
4✔
1974
#endif
1975
  /*
1976
  ** For offset corner point calculation 1/sin() is used
1977
  ** to avoid 1/0 division (and long spikes) we define a
1978
  ** limit for sin().
1979
  */
1980
#define CURVE_SIN_LIMIT 0.3
1981
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
4✔
1982
  msInitShape(ret);
4✔
1983
  ret->numlines = p->numlines;
4✔
1984
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
4✔
1985
  for (i = 0; i < ret->numlines; i++) {
12✔
1986
    ret->line[i].numpoints = p->line[i].numpoints;
8✔
1987
    ret->line[i].point =
8✔
1988
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
8✔
1989
  }
1990
  for (i = 0; i < p->numlines; i++) {
12✔
1991
    pointObj old_diffdir, old_offdir;
1992
    if (p->line[i].numpoints < 2) {
8✔
1993
      ret->line[i].numpoints = 0;
4✔
1994
      continue; /* skip degenerate points */
1995
    }
1996
    ok = 1;
1997
    /* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see
1998
     * that it is not an error to do so, and prints a warning */
1999
    old_offdir.x = old_offdir.y = old_diffdir.x = old_diffdir.y = 0;
2000

2001
    idx = 0;
2002
    first = 1;
2003

2004
    /* saved metrics of the last processed point */
2005
    pointObj old_pt = p->line[i].point[0];
4✔
2006
    for (j = 1; j < p->line[i].numpoints; j++) {
28✔
2007
      const pointObj pt = p->line[i].point[j]; /* place of the point */
24✔
2008
      const pointObj diffdir =
2009
          point_norm(point_diff(pt, old_pt)); /* direction of the line */
24✔
2010
      const pointObj offdir =
2011
          point_rotz90(diffdir); /* direction where the distance between the
2012
                                    line and the offset is measured */
2013
      pointObj offpt; /* this will be the corner point of the offset line */
2014

2015
      /* offset line points computation */
2016
      if (first == 1) { /* first point */
24✔
2017
        first = 0;
2018
        offpt = point_sum(old_pt, point_mul(offdir, offset));
2019
      } else { /* middle points */
2020
        /* curve is the angle of the last and the current line's direction
2021
         * (supplementary angle of the shape's inner angle) */
2022
        double sin_curve = point_cross(diffdir, old_diffdir);
2023
        double cos_curve = point_cross(old_offdir, diffdir);
2024
        if ((-1.0) * CURVE_SIN_LIMIT < sin_curve &&
20✔
2025
            sin_curve < CURVE_SIN_LIMIT) {
2026
          /* do not calculate 1/sin, instead use a corner point approximation:
2027
           * average of the last and current offset direction and length */
2028

2029
          /*
2030
          ** TODO: fair for obtuse inner angles, however, positive and negative
2031
          ** acute inner angles would need special handling - similar to LINECAP
2032
          ** to avoid drawing of long spikes
2033
          */
2034
          offpt = point_sum(
×
2035
              old_pt, point_mul(point_sum(offdir, old_offdir), 0.5 * offset));
2036
        } else {
2037
          double base_shift = -1.0 * (1.0 + cos_curve) / sin_curve;
20✔
2038
          offpt = point_sum(
2039
              old_pt,
2040
              point_mul(point_sum(point_mul(diffdir, base_shift), offdir),
2041
                        offset));
2042
        }
2043
      }
2044
      ret->line[i].point[idx] = offpt;
24✔
2045
      idx++;
24✔
2046
      old_pt = pt;
2047
      old_diffdir = diffdir;
2048
      old_offdir = offdir;
2049
    }
2050

2051
    /* last point */
2052
    if (first == 0) {
4✔
2053
      pointObj offpt = point_sum(old_pt, point_mul(old_offdir, offset));
2054
      ret->line[i].point[idx] = offpt;
4✔
2055
      idx++;
4✔
2056
    }
2057

2058
    if (idx != p->line[i].numpoints) {
4✔
2059
      /* printf("shouldn't happen :(\n"); */
2060
      ret->line[i].numpoints = idx;
×
2061
      ret->line =
×
2062
          msSmallRealloc(ret->line, ret->line[i].numpoints * sizeof(pointObj));
×
2063
    }
2064
  }
2065
  if (!ok)
4✔
2066
    ret->numlines = 0; /* all lines where degenerate */
×
2067
  return ret;
2068
}
2069

2070
shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety) {
208✔
2071
  int i, j;
2072
  shapeObj *ret;
2073
  if (offsety == MS_STYLE_SINGLE_SIDED_OFFSET) { /* complex calculations */
208✔
2074
    return msOffsetCurve(p, offsetx);
55✔
2075
  } else if (offsety == MS_STYLE_DOUBLE_SIDED_OFFSET) {
153✔
2076
    shapeObj *tmp1;
2077
    ret = msOffsetCurve(p, offsetx / 2.0);
12✔
2078
    tmp1 = msOffsetCurve(p, -offsetx / 2.0);
12✔
2079
    for (i = 0; i < tmp1->numlines; i++) {
24✔
2080
      msAddLineDirectly(ret, tmp1->line + i);
12✔
2081
    }
2082
    msFreeShape(tmp1);
12✔
2083
    free(tmp1);
12✔
2084
    return ret;
12✔
2085
  }
2086

2087
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
141✔
2088
  msInitShape(ret);
141✔
2089
  ret->numlines = p->numlines;
141✔
2090
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
141✔
2091
  for (i = 0; i < ret->numlines; i++) {
290✔
2092
    ret->line[i].numpoints = p->line[i].numpoints;
149✔
2093
    ret->line[i].point =
149✔
2094
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
149✔
2095
  }
2096

2097
  for (i = 0; i < p->numlines; i++) {
290✔
2098
    for (j = 0; j < p->line[i].numpoints; j++) {
1,656✔
2099
      ret->line[i].point[j].x = p->line[i].point[j].x + offsetx;
1,507✔
2100
      ret->line[i].point[j].y = p->line[i].point[j].y + offsety;
1,507✔
2101
    }
2102
  }
2103

2104
  return ret;
2105
}
2106

2107
/*
2108
-------------------------------------------------------------------------------
2109
 msSetup()
2110

2111
 Contributed by Jerry Pisk in bug 1203.  Heads off potential race condition
2112
 in initializing GD font cache with multiple threads.  Should be called from
2113
 mapscript module initialization code.
2114
-------------------------------------------------------------------------------
2115
*/
2116

2117
int msSetup() {
2,668✔
2118
#ifdef _WIN32
2119
  const char *maxfiles = CPLGetConfigOption("MS_MAX_OPEN_FILES", NULL);
2120
  if (maxfiles) {
2121
    int res = _getmaxstdio();
2122
    if (res < 2048) {
2123
      res = _setmaxstdio(atoi(maxfiles));
2124
      assert(res != -1);
2125
    }
2126
  }
2127
#endif
2128

2129
#ifdef USE_THREAD
2130
  msThreadInit();
2,668✔
2131
#endif
2132

2133
  /* Use PROJ_DATA/PROJ_LIB env vars if set */
2134
  msProjDataInitFromEnv();
2,668✔
2135

2136
  /* Use MS_ERRORFILE and MS_DEBUGLEVEL env vars if set */
2137
  if (msDebugInitFromEnv() != MS_SUCCESS)
2,668✔
2138
    return MS_FAILURE;
2139

2140
#ifdef USE_GEOS
2141
  msGEOSSetup();
2,668✔
2142
#endif
2143

2144
#ifdef USE_RSVG
2145
#if !GLIB_CHECK_VERSION(2, 35, 0)
2146
  g_type_init();
2147
#endif
2148
#endif
2149

2150
  msFontCacheSetup();
2,668✔
2151

2152
  return MS_SUCCESS;
2,668✔
2153
}
2154

2155
/* This is intended to be a function to cleanup anything that "hangs around"
2156
   when all maps are destroyed, like Registered GDAL drivers, and so forth. */
2157
#ifndef NDEBUG
2158
#if defined(USE_LIBXML2)
2159
#include "maplibxml2.h"
2160
#endif
2161
#endif
2162
void msCleanup() {
2,657✔
2163
  msForceTmpFileBase(NULL);
2,657✔
2164
  msConnPoolFinalCleanup();
2,657✔
2165
  /* Lexer string parsing variable */
2166
  if (msyystring_buffer != NULL) {
2,657✔
2167
    msFree(msyystring_buffer);
2,653✔
2168
    msyystring_buffer = NULL;
2,653✔
2169
  }
2170
  msyylex_destroy();
2,657✔
2171

2172
  msOGRCleanup();
2,657✔
2173
  msGDALCleanup();
2,657✔
2174

2175
  /* Release both GDAL and OGR resources */
2176
  msAcquireLock(TLOCK_GDAL);
2,657✔
2177
  /* Cleanup some GDAL global resources in particular */
2178
  GDALDestroy();
2,657✔
2179
  msReleaseLock(TLOCK_GDAL);
2,657✔
2180

2181
  msSetPROJ_DATA(NULL, NULL);
2,657✔
2182
  msProjectionContextPoolCleanup();
2,657✔
2183

2184
#if defined(USE_CURL)
2185
  msHTTPCleanup();
2,657✔
2186
#endif
2187

2188
#ifdef USE_GEOS
2189
  msGEOSCleanup();
2,657✔
2190
#endif
2191

2192
/* make valgrind happy on debug code */
2193
#ifndef NDEBUG
2194
#ifdef USE_CAIRO
2195
  msCairoCleanup();
2196
#endif
2197
#if defined(USE_LIBXML2)
2198
  xmlCleanupParser();
2199
#endif
2200
#endif
2201

2202
  msFontCacheCleanup();
2,657✔
2203

2204
  msTimeCleanup();
2,657✔
2205

2206
  msIO_Cleanup();
2,657✔
2207

2208
  msResetErrorList();
2,657✔
2209

2210
  /* Close/cleanup log/debug output. Keep this at the very end. */
2211
  msDebugCleanup();
2,657✔
2212

2213
  /* Clean up the vtable factory */
2214
  msPluginFreeVirtualTableFactory();
2,657✔
2215
}
2,657✔
2216

2217
/************************************************************************/
2218
/*                            msAlphaBlend()                            */
2219
/*                                                                      */
2220
/*      Function to overlay/blend an RGBA value into an existing        */
2221
/*      RGBA value using the Porter-Duff "over" operator.               */
2222
/*      Primarily intended for use with rasterBufferObj                 */
2223
/*      raster rendering.  The "src" is the overlay value, and "dst"    */
2224
/*      is the existing value being overlaid. dst is expected to be     */
2225
/*      premultiplied, but the source should not be.                    */
2226
/*                                                                      */
2227
/*      NOTE: alpha_dst may be NULL.                                    */
2228
/************************************************************************/
2229

2230
void msAlphaBlend(unsigned char red_src, unsigned char green_src,
314,744✔
2231
                  unsigned char blue_src, unsigned char alpha_src,
2232
                  unsigned char *red_dst, unsigned char *green_dst,
2233
                  unsigned char *blue_dst, unsigned char *alpha_dst) {
2234
  /* -------------------------------------------------------------------- */
2235
  /*      Simple cases we want to handle fast.                            */
2236
  /* -------------------------------------------------------------------- */
2237
  if (alpha_src == 0)
314,744✔
2238
    return;
2239

2240
  if (alpha_src == 255) {
314,744✔
2241
    *red_dst = red_src;
151,548✔
2242
    *green_dst = green_src;
151,548✔
2243
    *blue_dst = blue_src;
151,548✔
2244
    if (alpha_dst)
151,548✔
2245
      *alpha_dst = 255;
216✔
2246
    return;
151,548✔
2247
  }
2248

2249
  /* -------------------------------------------------------------------- */
2250
  /*      Premultiple alpha for source values now.                        */
2251
  /* -------------------------------------------------------------------- */
2252
  red_src = red_src * alpha_src / 255;
163,196✔
2253
  green_src = green_src * alpha_src / 255;
163,196✔
2254
  blue_src = blue_src * alpha_src / 255;
163,196✔
2255

2256
  /* -------------------------------------------------------------------- */
2257
  /*      Another pretty fast case if there is nothing in the             */
2258
  /*      destination to mix with.                                        */
2259
  /* -------------------------------------------------------------------- */
2260
  if (alpha_dst && *alpha_dst == 0) {
163,196✔
2261
    *red_dst = red_src;
96,608✔
2262
    *green_dst = green_src;
96,608✔
2263
    *blue_dst = blue_src;
96,608✔
2264
    *alpha_dst = alpha_src;
96,608✔
2265
    return;
96,608✔
2266
  }
2267

2268
  /* -------------------------------------------------------------------- */
2269
  /*      Cases with actual blending.                                     */
2270
  /* -------------------------------------------------------------------- */
2271
  if (!alpha_dst || *alpha_dst == 255) {
19,845✔
2272
    int weight_dst = 256 - alpha_src;
62,619✔
2273

2274
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
62,619✔
2275
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
62,619✔
2276
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
62,619✔
2277
  } else {
2278
    int weight_dst = (256 - alpha_src);
3,969✔
2279

2280
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
3,969✔
2281
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
3,969✔
2282
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
3,969✔
2283

2284
    *alpha_dst = (256 * alpha_src + *alpha_dst * weight_dst) >> 8;
3,969✔
2285
  }
2286
}
2287

2288
/************************************************************************/
2289
/*                           msAlphaBlendPM()                           */
2290
/*                                                                      */
2291
/*      Same as msAlphaBlend() except that the source RGBA is           */
2292
/*      assumed to already be premultiplied.                            */
2293
/************************************************************************/
2294

2295
void msAlphaBlendPM(unsigned char red_src, unsigned char green_src,
75,020✔
2296
                    unsigned char blue_src, unsigned char alpha_src,
2297
                    unsigned char *red_dst, unsigned char *green_dst,
2298
                    unsigned char *blue_dst, unsigned char *alpha_dst) {
2299
  /* -------------------------------------------------------------------- */
2300
  /*      Simple cases we want to handle fast.                            */
2301
  /* -------------------------------------------------------------------- */
2302
  if (alpha_src == 0)
75,020✔
2303
    return;
2304

2305
  if (alpha_src == 255) {
74,990✔
2306
    *red_dst = red_src;
19,981✔
2307
    *green_dst = green_src;
19,981✔
2308
    *blue_dst = blue_src;
19,981✔
2309
    if (alpha_dst)
19,981✔
2310
      *alpha_dst = 255;
19,981✔
2311
    return;
19,981✔
2312
  }
2313

2314
  /* -------------------------------------------------------------------- */
2315
  /*      Another pretty fast case if there is nothing in the             */
2316
  /*      destination to mix with.                                        */
2317
  /* -------------------------------------------------------------------- */
2318
  if (alpha_dst && *alpha_dst == 0) {
55,009✔
2319
    *red_dst = red_src;
15,214✔
2320
    *green_dst = green_src;
15,214✔
2321
    *blue_dst = blue_src;
15,214✔
2322
    *alpha_dst = alpha_src;
15,214✔
2323
    return;
15,214✔
2324
  }
2325

2326
  /* -------------------------------------------------------------------- */
2327
  /*      Cases with actual blending.                                     */
2328
  /* -------------------------------------------------------------------- */
2329
  if (!alpha_dst || *alpha_dst == 255) {
×
2330
    int weight_dst = 255 - alpha_src;
39,795✔
2331

2332
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
39,795✔
2333
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
39,795✔
2334
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
39,795✔
2335
  } else {
2336
    int weight_dst = (255 - alpha_src);
×
2337

2338
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
×
2339
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
×
2340
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
×
2341

2342
    *alpha_dst = (255 * alpha_src + *alpha_dst * weight_dst) >> 8;
×
2343
  }
2344
}
2345

2346
void msRGBtoHSL(colorObj *rgb, double *h, double *s, double *l) {
2,586✔
2347
  double r = rgb->red / 255.0, g = rgb->green / 255.0, b = rgb->blue / 255.0;
2,586✔
2348
  double maxv = MS_MAX(MS_MAX(r, g), b), minv = MS_MIN(MS_MIN(r, g), b);
7,758✔
2349
  double d = maxv - minv;
2,586✔
2350

2351
  *h = 0, *s = 0;
2,586✔
2352
  *l = (maxv + minv) / 2;
2,586✔
2353

2354
  if (maxv != minv) {
2,586✔
2355
    *s = *l > 0.5 ? d / (2 - maxv - minv) : d / (maxv + minv);
2,586✔
2356
    if (maxv == r) {
2,586✔
2357
      *h = (g - b) / d + (g < b ? 6 : 0);
2,226✔
2358
    } else if (maxv == g) {
1,473✔
2359
      *h = (b - r) / d + 2;
283✔
2360
    } else if (maxv == b) {
1,190✔
2361
      *h = (r - g) / d + 4;
1,190✔
2362
    }
2363
    *h /= 6;
2,586✔
2364
  }
2365
}
2,586✔
2366

2367
static double hue_to_rgb(double p, double q, double t) {
3,879✔
2368
  if (t < 0)
3,879✔
2369
    t += 1;
669✔
2370
  if (t > 1)
3,879✔
2371
    t -= 1;
×
2372
  if (t < 0.1666666666666666)
3,879✔
2373
    return p + (q - p) * 6 * t;
585✔
2374
  if (t < 0.5)
3,294✔
2375
    return q;
2376
  if (t < 0.6666666666666666)
2,001✔
2377
    return p + (q - p) * (0.666666666666 - t) * 6;
584✔
2378
  return p;
2379
}
2380

2381
void msHSLtoRGB(double h, double s, double l, colorObj *rgb) {
1,293✔
2382
  double r, g, b;
2383

2384
  if (s == 0) {
1,293✔
2385
    r = g = b = l;
2386
  } else {
2387

2388
    double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1,293✔
2389
    double p = 2 * l - q;
1,293✔
2390
    r = hue_to_rgb(p, q, h + 0.33333333333333333);
1,293✔
2391
    g = hue_to_rgb(p, q, h);
1,293✔
2392
    b = hue_to_rgb(p, q, h - 0.33333333333333333);
1,293✔
2393
  }
2394
  rgb->red = r * 255;
1,293✔
2395
  rgb->green = g * 255;
1,293✔
2396
  rgb->blue = b * 255;
1,293✔
2397
}
1,293✔
2398

2399
/*
2400
 RFC 24: check if the parent pointer is NULL and raise an error otherwise
2401
*/
2402
int msCheckParentPointer(void *p, const char *objname) {
4,342✔
2403
  if (p == NULL) {
4,342✔
2404
    if (objname != NULL) {
×
2405
      msSetError(MS_NULLPARENTERR, "The %s parent object is null",
×
2406
                 "msCheckParentPointer()", objname);
2407
    } else {
2408
      msSetError(MS_NULLPARENTERR, "The parent object is null",
×
2409
                 "msCheckParentPointer()");
2410
    }
2411
    return MS_FAILURE;
×
2412
  }
2413
  return MS_SUCCESS;
2414
}
2415

2416
void msBufferInit(bufferObj *buffer) {
207✔
2417
  buffer->data = NULL;
207✔
2418
  buffer->size = 0;
207✔
2419
  buffer->available = 0;
207✔
2420
  buffer->_next_allocation_size = MS_DEFAULT_BUFFER_ALLOC;
207✔
2421
}
207✔
2422

2423
void msBufferResize(bufferObj *buffer, size_t target_size) {
240✔
2424
  while (buffer->available <= target_size) {
493✔
2425
    buffer->data = msSmallRealloc(
506✔
2426
        buffer->data, buffer->available + buffer->_next_allocation_size);
253✔
2427
    buffer->available += buffer->_next_allocation_size;
253✔
2428
    buffer->_next_allocation_size *= 2;
253✔
2429
  }
2430
}
240✔
2431

2432
void msBufferAppend(bufferObj *buffer, void *data, size_t length) {
13,190✔
2433
  if (buffer->available < buffer->size + length) {
13,190✔
2434
    msBufferResize(buffer, buffer->size + length);
238✔
2435
  }
2436
  memcpy(&(buffer->data[buffer->size]), data, length);
13,190✔
2437
  buffer->size += length;
13,190✔
2438
}
13,190✔
2439

2440
void msBufferFree(bufferObj *buffer) {
192✔
2441
  if (buffer->available > 0)
192✔
2442
    free(buffer->data);
192✔
2443
}
192✔
2444

2445
void msFreeRasterBuffer(rasterBufferObj *b) {
342✔
2446
  switch (b->type) {
342✔
2447
  case MS_BUFFER_BYTE_RGBA:
342✔
2448
    msFree(b->data.rgba.pixels);
342✔
2449
    b->data.rgba.pixels = NULL;
342✔
2450
    break;
342✔
2451
  case MS_BUFFER_BYTE_PALETTE:
×
2452
    msFree(b->data.palette.pixels);
×
2453
    msFree(b->data.palette.palette);
×
2454
    b->data.palette.pixels = NULL;
×
2455
    b->data.palette.palette = NULL;
×
2456
    break;
×
2457
  }
2458
}
342✔
2459

2460
/*
2461
** Issue #3043: Layer extent comparison short circuit.
2462
**
2463
** msExtentsOverlap()
2464
**
2465
** Returns MS_TRUE if map extent and layer extent overlap,
2466
** MS_FALSE if they are disjoint, and MS_UNKNOWN if there is
2467
** not enough info to calculate a deterministic answer.
2468
**
2469
*/
2470
int msExtentsOverlap(mapObj *map, layerObj *layer) {
4,710✔
2471
  rectObj map_extent;
2472
  rectObj layer_extent;
2473

2474
  /* No extent info? Nothing we can do, return MS_UNKNOWN. */
2475
  if ((map->extent.minx == -1) && (map->extent.miny == -1) &&
4,710✔
2476
      (map->extent.maxx == -1) && (map->extent.maxy == -1))
2✔
2477
    return MS_UNKNOWN;
2478
  if ((layer->extent.minx == -1) && (layer->extent.miny == -1) &&
4,710✔
2479
      (layer->extent.maxx == -1) && (layer->extent.maxy == -1))
4,588✔
2480
    return MS_UNKNOWN;
2481

2482
  /* No map projection? Let someone else sort this out. */
2483
  if (!(map->projection.numargs > 0))
122✔
2484
    return MS_UNKNOWN;
2485

2486
  /* No layer projection? Perform naive comparison, because they are
2487
  ** in the same projection. */
2488
  if (!(layer->projection.numargs > 0))
109✔
2489
    return msRectOverlap(&(map->extent), &(layer->extent));
×
2490

2491
  /* In the case where map and layer projections are identical, and the */
2492
  /* bounding boxes don't cross the dateline, do simple rectangle comparison */
2493
  if (map->extent.minx < map->extent.maxx &&
109✔
2494
      layer->extent.minx < layer->extent.maxx &&
218✔
2495
      !msProjectionsDiffer(&(map->projection), &(layer->projection))) {
109✔
2496
    return msRectOverlap(&(map->extent), &(layer->extent));
21✔
2497
  }
2498

2499
  /* We need to transform our rectangles for comparison,
2500
  ** so we will work with copies and leave the originals intact. */
2501
  MS_COPYRECT(&map_extent, &(map->extent));
88✔
2502
  MS_COPYRECT(&layer_extent, &(layer->extent));
88✔
2503

2504
  /* Transform map extents into geographics for comparison. */
2505
  if (msProjectRect(&(map->projection), &(map->latlon), &map_extent))
88✔
2506
    return MS_UNKNOWN;
2507

2508
  /* Transform layer extents into geographics for comparison. */
2509
  if (msProjectRect(&(layer->projection), &(map->latlon), &layer_extent))
88✔
2510
    return MS_UNKNOWN;
2511

2512
  /* Simple case? Return simple answer. */
2513
  if (map_extent.minx < map_extent.maxx &&
88✔
2514
      layer_extent.minx < layer_extent.maxx)
88✔
2515
    return msRectOverlap(&(map_extent), &(layer_extent));
88✔
2516

2517
  /* Uh oh, one of the rects crosses the dateline!
2518
  ** Let someone else handle it. */
2519
  return MS_UNKNOWN;
2520
}
2521

2522
/************************************************************************/
2523
/*                             msSmallMalloc()                          */
2524
/************************************************************************/
2525

2526
/* Safe version of malloc(). This function is taken from gdal/cpl. */
2527

2528
void *msSmallMalloc(size_t nSize) {
414,855✔
2529
  void *pReturn;
2530

2531
  if (MS_UNLIKELY(nSize == 0))
414,855✔
2532
    return NULL;
2533

2534
  pReturn = malloc(nSize);
414,609✔
2535
  if (MS_UNLIKELY(pReturn == NULL)) {
414,609✔
2536
    msIO_fprintf(stderr,
×
2537
                 "msSmallMalloc(): Out of memory allocating %ld bytes.\n",
2538
                 (long)nSize);
2539
    exit(1);
×
2540
  }
2541

2542
  return pReturn;
2543
}
2544

2545
/************************************************************************/
2546
/*                             msSmallRealloc()                         */
2547
/************************************************************************/
2548

2549
/* Safe version of realloc(). This function is taken from gdal/cpl. */
2550

2551
void *msSmallRealloc(void *pData, size_t nNewSize) {
23,982✔
2552
  void *pReturn;
2553

2554
  if (MS_UNLIKELY(nNewSize == 0))
23,982✔
2555
    return NULL;
2556

2557
  pReturn = realloc(pData, nNewSize);
23,982✔
2558

2559
  if (MS_UNLIKELY(pReturn == NULL)) {
23,982✔
2560
    msIO_fprintf(stderr,
×
2561
                 "msSmallRealloc(): Out of memory allocating %ld bytes.\n",
2562
                 (long)nNewSize);
2563
    exit(1);
×
2564
  }
2565

2566
  return pReturn;
2567
}
2568

2569
/************************************************************************/
2570
/*                             msSmallCalloc()                         */
2571
/************************************************************************/
2572

2573
/* Safe version of calloc(). This function is taken from gdal/cpl. */
2574

2575
void *msSmallCalloc(size_t nCount, size_t nSize) {
26,627✔
2576
  void *pReturn;
2577

2578
  if (MS_UNLIKELY(nSize * nCount == 0))
26,627✔
2579
    return NULL;
2580

2581
  pReturn = calloc(nCount, nSize);
24,311✔
2582
  if (MS_UNLIKELY(pReturn == NULL)) {
24,311✔
2583
    msIO_fprintf(stderr,
×
2584
                 "msSmallCalloc(): Out of memory allocating %ld bytes.\n",
2585
                 (long)(nCount * nSize));
2586
    exit(1);
×
2587
  }
2588

2589
  return pReturn;
2590
}
2591

2592
/*
2593
** msBuildOnlineResource()
2594
**
2595
** Try to build the online resource (mapserv URL) for this service.
2596
** "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)$(PATH_INFO)?"
2597
** (+append the map=... param if it was explicitly passed in QUERY_STRING)
2598
**
2599
** Returns a newly allocated string that should be freed by the caller or
2600
** NULL in case of error.
2601
*/
2602
char *msBuildOnlineResource(mapObj *map, cgiRequestObj *req) {
43✔
2603
  (void)map; // unused parameter
2604

2605
  // first check if a value has been set in the
2606
  // CONFIG file
2607
  const char *config_online_resource =
2608
      CPLGetConfigOption("MS_ONLINERESOURCE", NULL);
43✔
2609

2610
  if (config_online_resource) {
43✔
NEW
2611
    return msStrdup(config_online_resource);
×
2612
  }
2613

2614
  char *online_resource = NULL;
2615
  const char *value, *protocol = "http", *mapparam = NULL;
2616
  char **hostname_array = NULL;
2617
  int mapparam_len = 0, hostname_array_len = 0;
43✔
2618

2619
  const char *hostname = getenv("HTTP_X_FORWARDED_HOST");
43✔
2620
  if (!hostname)
43✔
2621
    hostname = getenv("SERVER_NAME");
43✔
2622
  else {
2623
    if (strchr(hostname, ',')) {
×
2624
      hostname_array = msStringSplit(hostname, ',', &hostname_array_len);
×
2625
      hostname = hostname_array[0];
×
2626
    }
2627
  }
2628

2629
  const char *port = getenv("HTTP_X_FORWARDED_PORT");
43✔
2630
  if (!port)
43✔
2631
    port = getenv("SERVER_PORT");
43✔
2632

2633
  const char *pathinfo = getenv("PATH_INFO");
43✔
2634
  if (!pathinfo) {
43✔
2635
    pathinfo = "";
2636
  }
2637

2638
  const char *script = getenv("SCRIPT_NAME");
43✔
2639

2640
  /* HTTPS is set by Apache to "on" in an HTTPS server ... if not set */
2641
  /* then check SERVER_PORT: 443 is the default https port. */
2642
  if (((value = getenv("HTTPS")) && strcasecmp(value, "on") == 0) ||
86✔
2643
      ((value = getenv("SERVER_PORT")) && atoi(value) == 443)) {
43✔
2644
    protocol = "https";
2645
  }
2646
  if ((value = getenv("HTTP_X_FORWARDED_PROTO"))) {
43✔
2647
    protocol = value;
2648
  }
2649

2650
  /* If map=.. was explicitly set then we'll include it in onlineresource
2651
   */
2652
  if (req->type == MS_GET_REQUEST) {
43✔
2653
    int i;
2654
    for (i = 0; i < req->NumParams; i++) {
86✔
2655
      if (strcasecmp(req->ParamNames[i], "map") == 0) {
43✔
2656
        mapparam = req->ParamValues[i];
×
2657
        mapparam_len = strlen(mapparam) + 5; /* +5 for "map="+"&" */
×
2658
        break;
×
2659
      }
2660
    }
2661
  }
2662

2663
  if (hostname && port && script) {
43✔
2664
    size_t buffer_size;
2665
    buffer_size = strlen(hostname) + strlen(port) + strlen(pathinfo) +
×
2666
                  strlen(script) + mapparam_len +
×
2667
                  11; /* 11 comes from
2668
                         https://[host]:[port][scriptname][pathinfo]?[map]\0,
2669
                         i.e. "https://:?\0" */
2670
    online_resource = (char *)msSmallMalloc(buffer_size);
×
2671
    if ((atoi(port) == 80 && strcmp(protocol, "http") == 0) ||
×
2672
        (atoi(port) == 443 && strcmp(protocol, "https") == 0))
×
2673
      snprintf(online_resource, buffer_size, "%s://%s%s%s?", protocol, hostname,
2674
               script, pathinfo);
2675
    else
2676
      snprintf(online_resource, buffer_size, "%s://%s:%s%s%s?", protocol,
2677
               hostname, port, script, pathinfo);
2678

2679
    if (mapparam) {
×
2680
      int baselen;
2681
      baselen = strlen(online_resource);
×
2682
      snprintf(online_resource + baselen, buffer_size - baselen, "map=%s&",
×
2683
               mapparam);
2684
    }
2685
  } else {
2686
    msSetError(MS_CGIERR, "Impossible to establish server URL.",
43✔
2687
               "msBuildOnlineResource()");
2688
    return NULL;
43✔
2689
  }
2690
  if (hostname_array) {
×
2691
    msFreeCharArray(hostname_array, hostname_array_len);
×
2692
  }
2693

2694
  return online_resource;
2695
}
2696

2697
/************************************************************************/
2698
/*                             msIntegerInArray()                        */
2699
/************************************************************************/
2700

2701
/* Check if a integer is in a array */
2702
int msIntegerInArray(const int value, int *array, int numelements) {
6,940✔
2703
  int i;
2704
  for (i = 0; i < numelements; ++i) {
28,407✔
2705
    if (value == array[i])
28,331✔
2706
      return MS_TRUE;
2707
  }
2708
  return MS_FALSE;
2709
}
2710

2711
/************************************************************************
2712
 *                            msMapSetProjections                       *
2713
 *                                                                      *
2714
 *   Ensure that all the layers in the map file have a projection       *
2715
 *   by copying the map-level projection to all layers than have no     *
2716
 *   projection.                                                        *
2717
 ************************************************************************/
2718

2719
int msMapSetLayerProjections(mapObj *map) {
278✔
2720

2721
  char *mapProjStr = NULL;
2722
  int i;
2723

2724
  if (map->projection.numargs <= 0) {
278✔
2725
    msSetError(MS_WMSERR,
×
2726
               "Cannot set new SRS on a map that doesn't "
2727
               "have any projection set. Please make sure your mapfile "
2728
               "has a PROJECTION defined at the top level.",
2729
               "msTileSetProjectionst()");
2730
    return (MS_FAILURE);
×
2731
  }
2732

2733
  for (i = 0; i < map->numlayers; i++) {
1,374✔
2734
    layerObj *lp = GET_LAYER(map, i);
1,096✔
2735
    /* This layer is turned on and needs a projection? */
2736
    if (lp->projection.numargs <= 0 && lp->status != MS_OFF &&
1,096✔
2737
        lp->transform == MS_TRUE) {
306✔
2738

2739
      /* Fetch main map projection string only now that we need it */
2740
      if (mapProjStr == NULL)
306✔
2741
        mapProjStr = msGetProjectionString(&(map->projection));
141✔
2742

2743
      /* Set the projection to the map file projection */
2744
      if (msLoadProjectionString(&(lp->projection), mapProjStr) != 0) {
306✔
2745
        msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2746
                   "msMapSetLayerProjections()");
2747
        return (MS_FAILURE);
×
2748
      }
2749
      lp->project = MS_TRUE;
306✔
2750
      if (lp->connection &&
306✔
2751
          IS_THIRDPARTY_LAYER_CONNECTIONTYPE(lp->connectiontype)) {
43✔
2752
        char **reflayers;
2753
        int numreflayers, j;
2754
        reflayers = msStringSplit(lp->connection, ',', &numreflayers);
1✔
2755
        for (j = 0; j < numreflayers; j++) {
2✔
2756
          int *lidx, nlidx;
2757
          /* first check layers referenced by group name */
2758
          lidx = msGetLayersIndexByGroup(map, reflayers[i], &nlidx);
1✔
2759
          if (lidx) {
1✔
2760
            int k;
2761
            for (k = 0; k < nlidx; k++) {
×
2762
              layerObj *glp = GET_LAYER(map, lidx[k]);
×
2763
              if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
×
2764

2765
                /* Set the projection to the map file projection */
2766
                if (msLoadProjectionString(&(glp->projection), mapProjStr) !=
×
2767
                    0) {
2768
                  msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2769
                             "msMapSetLayerProjections()");
2770
                  return (MS_FAILURE);
×
2771
                }
2772
                glp->project = MS_TRUE;
×
2773
              }
2774
            }
2775
            free(lidx);
×
2776
          } else {
2777
            /* group name did not match, check by layer name */
2778
            int layer_idx = msGetLayerIndex(map, lp->connection);
1✔
2779
            layerObj *glp = GET_LAYER(map, layer_idx);
1✔
2780
            if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
1✔
2781

2782
              /* Set the projection to the map file projection */
2783
              if (msLoadProjectionString(&(glp->projection), mapProjStr) != 0) {
1✔
2784
                msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2785
                           "msMapSetLayerProjections()");
2786
                return (MS_FAILURE);
×
2787
              }
2788
              glp->project = MS_TRUE;
1✔
2789
            }
2790
          }
2791
        }
2792
        msFreeCharArray(reflayers, numreflayers);
1✔
2793
      }
2794
    }
2795
  }
2796
  msFree(mapProjStr);
278✔
2797
  return (MS_SUCCESS);
278✔
2798
}
2799

2800
/************************************************************************
2801
 *                    msMapSetLanguageSpecificConnection                *
2802
 *                                                                      *
2803
 *   Override DATA and CONNECTION of each layer with their specific     *
2804
 *  variant for the specified language.                                 *
2805
 ************************************************************************/
2806

2807
void msMapSetLanguageSpecificConnection(mapObj *map,
67✔
2808
                                        const char *validated_language) {
2809
  int i;
2810
  for (i = 0; i < map->numlayers; i++) {
702✔
2811
    layerObj *layer = GET_LAYER(map, i);
635✔
2812
    if (layer->data)
635✔
2813
      layer->data =
524✔
2814
          msCaseReplaceSubstring(layer->data, "%language%", validated_language);
524✔
2815
    if (layer->connection)
635✔
2816
      layer->connection = msCaseReplaceSubstring(
×
2817
          layer->connection, "%language%", validated_language);
2818
  }
2819
}
67✔
2820

2821
/* Generalize a shape based of the tolerance.
2822
   Ref: http://trac.osgeo.org/gdal/ticket/966 */
2823
shapeObj *msGeneralize(shapeObj *shape, double tolerance) {
12✔
2824
  lineObj newLine = {0, NULL};
12✔
2825
  const double sqTolerance = tolerance * tolerance;
12✔
2826

2827
  shapeObj *newShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
12✔
2828
  msInitShape(newShape);
12✔
2829
  msCopyShape(shape, newShape);
12✔
2830

2831
  if (shape->numlines < 1)
12✔
2832
    return newShape;
2833

2834
  /* Clean shape */
2835
  for (int i = 0; i < newShape->numlines; i++)
24✔
2836
    free(newShape->line[i].point);
12✔
2837
  newShape->numlines = 0;
12✔
2838
  if (newShape->line)
12✔
2839
    free(newShape->line);
12✔
2840

2841
  msAddLine(newShape, &newLine);
12✔
2842

2843
  if (shape->line[0].numpoints == 0) {
12✔
2844
    return newShape;
2845
  }
2846

2847
  msAddPointToLine(&newShape->line[0], &shape->line[0].point[0]);
12✔
2848
  double dX0 = shape->line[0].point[0].x;
12✔
2849
  double dY0 = shape->line[0].point[0].y;
12✔
2850

2851
  for (int i = 1; i < shape->line[0].numpoints; i++) {
80✔
2852
    double dX1 = shape->line[0].point[i].x;
68✔
2853
    double dY1 = shape->line[0].point[i].y;
68✔
2854

2855
    const double dX = dX1 - dX0;
68✔
2856
    const double dY = dY1 - dY0;
68✔
2857
    const double dSqDist = dX * dX + dY * dY;
68✔
2858
    if (i == shape->line[0].numpoints - 1 || dSqDist >= sqTolerance) {
68✔
2859
      pointObj p;
2860
      p.x = dX1;
44✔
2861
      p.y = dY1;
44✔
2862

2863
      /* Keep this point (always keep the last point) */
2864
      msAddPointToLine(&newShape->line[0], &p);
44✔
2865
      dX0 = dX1;
2866
      dY0 = dY1;
2867
    }
2868
  }
2869

2870
  return newShape;
2871
}
2872

2873
void msSetLayerOpacity(layerObj *layer, int opacity) {
1✔
2874
  if (!layer->compositer) {
1✔
2875
    layer->compositer = msSmallMalloc(sizeof(LayerCompositer));
1✔
2876
    initLayerCompositer(layer->compositer);
1✔
2877
  }
2878
  layer->compositer->opacity = opacity;
1✔
2879
}
1✔
2880

2881
void msReplaceFreeableStr(char **ppszStr, char *pszNewStr) {
14,068✔
2882
  msFree(*ppszStr);
14,068✔
2883
  *ppszStr = pszNewStr;
14,068✔
2884
}
14,068✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc