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

geographika / mapserver / 17823520225

18 Sep 2025 08:57AM UTC coverage: 41.442% (-0.02%) from 41.466%
17823520225

push

github

geographika
Use full path for CONFIG test

62112 of 149877 relevant lines covered (41.44%)

25354.57 hits per line

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

72.89
/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) {
71,865✔
66
  if (scale > 0) {
71,865✔
67
    if (maxscale != -1 && scale >= maxscale)
71,743✔
68
      return MS_FALSE;
69
    if (minscale != -1 && scale < minscale)
71,743✔
70
      return MS_FALSE;
×
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,449✔
88
    return MS_FAILURE;
89
  *attribute = atof(value);
23,449✔
90
  return MS_SUCCESS;
23,449✔
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,077✔
139
                      int drawmode) {
140
  int applyOpacity = MS_FALSE;
141
  assert(MS_DRAW_FEATURES(drawmode));
142
  if (style->numbindings > 0) {
65,077✔
143
    applyOpacity = MS_TRUE;
144
    if (style->bindings[MS_STYLE_BINDING_SYMBOL].index != -1) {
20,046✔
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,046✔
154
      style->angle = 360.0;
19,777✔
155
      bindDoubleAttribute(
19,777✔
156
          &style->angle,
157
          shape->values[style->bindings[MS_STYLE_BINDING_ANGLE].index]);
19,777✔
158
    }
159
    if (style->bindings[MS_STYLE_BINDING_SIZE].index != -1) {
20,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,046✔
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,077✔
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,077✔
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,077✔
275

276
static void bindLabel(layerObj *layer, shapeObj *shape, labelObj *label,
34,851✔
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,432✔
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,851✔
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,851✔
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,851✔
443

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

450
  if (!layer || !shape)
29,569✔
451
    return MS_FAILURE;
452

453
  for (i = 0; i < layer->numclasses; i++) {
65,442✔
454
    /* check the styleObj's */
455
    if (MS_DRAW_FEATURES(drawmode)) {
35,873✔
456
      for (j = 0; j < layer->class[i] -> numstyles; j++) {
73,359✔
457
        bindStyle(layer, shape, layer->class[i] -> styles[j], drawmode);
37,496✔
458
      }
459
    }
460

461
    /* check the labelObj's */
462
    if (MS_DRAW_LABELS(drawmode)) {
35,873✔
463
      for (j = 0; j < layer->class[i] -> numlabels; j++) {
40,482✔
464
        bindLabel(layer, shape, layer->class[i] -> labels[j], drawmode);
34,851✔
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,913✔
489
                               char *context, int requires) {
490
  int i;
491

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

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

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

501
  /* check referenced layers for the tag too */
502
  for (i = 0; i < map->numlayers; i++) {
9✔
503
    if (strstr(context, ltags[i]) != NULL) { /* need to check this layer */
6✔
504
      if (requires == MS_TRUE) {
3✔
505
        if (searchContextForTag(map, ltags, tag, GET_LAYER(map, i)->requires,
3✔
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,288✔
528
  int i;
529
  char **ltags;
530
  int status = MS_SUCCESS;
531

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

543
  /* check each layer's REQUIRES and LABELREQUIRES parameters */
544
  for (i = 0; i < map->numlayers; i++) {
7,243✔
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,955✔
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,955✔
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,288✔
570

571
  return status;
1,288✔
572
}
573

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

578
  expressionObj e;
579
  parseObj p;
580

581
  if (!context)
6,589✔
582
    return (MS_TRUE);
583

584
  /* initialize a temporary expression (e) */
585
  msInitExpression(&e);
3✔
586

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

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

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

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

607
    free(tag);
3✔
608
  }
609

610
  msTokenizeExpression(&e, NULL, NULL);
3✔
611

612
  p.shape = NULL;
3✔
613
  p.expr = &e;
3✔
614
  p.expr->curtoken = p.expr->tokens; /* reset */
3✔
615
  p.type = MS_PARSE_TYPE_BOOLEAN;
3✔
616

617
  status = yyparse(&p);
3✔
618

619
  msFreeExpression(&e);
3✔
620

621
  if (status != 0) {
3✔
622
    msSetError(MS_PARSEERR, "Failed to parse context", "msEvalContext");
×
623
    return MS_FALSE; /* error in parse */
×
624
  }
625

626
  return p.result.intval;
3✔
627
}
628

629
/* msEvalExpression()
630
 *
631
 * Evaluates a mapserver expression for a given set of attribute values and
632
 * returns the result of the expression (MS_TRUE or MS_FALSE)
633
 * May also return MS_FALSE in case of parsing errors or invalid expressions
634
 * (check the error stack if you care)
635
 *
636
 */
637
int msEvalExpression(layerObj *layer, shapeObj *shape,
129,003✔
638
                     expressionObj *expression, int itemindex) {
639
  if (MS_STRING_IS_NULL_OR_EMPTY(expression->string))
129,003✔
640
    return MS_TRUE; /* NULL or empty expressions are ALWAYS true */
641
  if (expression->native_string != NULL)
53,346✔
642
    return MS_TRUE; /* expressions that are evaluated natively are ALWAYS true
643
                     */
644

645
  switch (expression->type) {
51,440✔
646
  case (MS_STRING):
5,137✔
647
    if (itemindex == -1) {
5,137✔
648
      msSetError(MS_MISCERR,
×
649
                 "Cannot evaluate expression, no item index defined.",
650
                 "msEvalExpression()");
651
      return MS_FALSE;
×
652
    }
653
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
5,137✔
654
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
655
      return MS_FALSE;
×
656
    }
657
    if (expression->flags & MS_EXP_INSENSITIVE) {
5,137✔
658
      if (strcasecmp(expression->string, shape->values[itemindex]) == 0)
117✔
659
        return MS_TRUE; /* got a match */
660
    } else {
661
      if (strcmp(expression->string, shape->values[itemindex]) == 0)
5,020✔
662
        return MS_TRUE; /* got a match */
663
    }
664
    break;
665
  case (MS_LIST):
22✔
666
    if (itemindex == -1) {
22✔
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) {
22✔
673
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
674
      return MS_FALSE;
×
675
    }
676
    {
677
      char *start, *end;
678
      int value_len = strlen(shape->values[itemindex]);
22✔
679
      start = expression->string;
680
      while ((end = strchr(start, ',')) != NULL) {
103✔
681
        if (value_len == end - start &&
83✔
682
            !strncmp(start, shape->values[itemindex], end - start))
9✔
683
          return MS_TRUE;
684
        start = end + 1;
81✔
685
      }
686
      if (!strcmp(start, shape->values[itemindex]))
20✔
687
        return MS_TRUE;
688
    }
689
    break;
690
  case (MS_EXPRESSION): {
45,252✔
691
    int status;
692
    parseObj p;
693

694
    p.shape = shape;
45,252✔
695
    p.expr = expression;
45,252✔
696
    p.expr->curtoken = p.expr->tokens; /* reset */
45,252✔
697
    p.type = MS_PARSE_TYPE_BOOLEAN;
45,252✔
698

699
    status = yyparse(&p);
45,252✔
700

701
    if (status != 0) {
45,252✔
702
      msSetError(MS_PARSEERR, "Failed to parse expression: %s",
28✔
703
                 "msEvalExpression", expression->string);
704
      return MS_FALSE;
28✔
705
    }
706

707
    return p.result.intval;
45,224✔
708
  }
709
  case (MS_REGEX):
1,029✔
710
    if (itemindex == -1) {
1,029✔
711
      msSetError(MS_MISCERR,
×
712
                 "Cannot evaluate expression, no item index defined.",
713
                 "msEvalExpression()");
714
      return MS_FALSE;
×
715
    }
716
    if (itemindex >= layer->numitems || itemindex >= shape->numvalues) {
1,029✔
717
      msSetError(MS_MISCERR, "Invalid item index.", "msEvalExpression()");
×
718
      return MS_FALSE;
×
719
    }
720

721
    if (MS_STRING_IS_NULL_OR_EMPTY(shape->values[itemindex]) == MS_TRUE)
1,029✔
722
      return MS_FALSE;
723

724
    if (!expression->compiled) {
1,029✔
725
      if (expression->flags & MS_EXP_INSENSITIVE) {
31✔
726
        if (ms_regcomp(&(expression->regex), expression->string,
1✔
727
                       MS_REG_EXTENDED | MS_REG_NOSUB | MS_REG_ICASE) !=
728
            0) { /* compile the expression */
729
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
730
                     "msEvalExpression()");
731
          return MS_FALSE;
×
732
        }
733
      } else {
734
        if (ms_regcomp(&(expression->regex), expression->string,
30✔
735
                       MS_REG_EXTENDED | MS_REG_NOSUB) !=
736
            0) { /* compile the expression */
737
          msSetError(MS_REGEXERR, "Invalid regular expression.",
×
738
                     "msEvalExpression()");
739
          return MS_FALSE;
×
740
        }
741
      }
742
      expression->compiled = MS_TRUE;
31✔
743
    }
744

745
    if (ms_regexec(&(expression->regex), shape->values[itemindex], 0, NULL,
1,029✔
746
                   0) == 0)
747
      return MS_TRUE; /* got a match */
748
    break;
749
  }
750

751
  return MS_FALSE;
752
}
753

754
int *msAllocateValidClassGroups(layerObj *lp, int *nclasses) {
139✔
755
  int *classgroup = NULL;
756
  int nvalidclass = 0, i = 0;
757

758
  if (!lp || !lp->classgroup || lp->numclasses <= 0 || !nclasses)
139✔
759
    return NULL;
760

761
  classgroup = (int *)msSmallMalloc(sizeof(int) * lp->numclasses);
139✔
762
  nvalidclass = 0;
763
  for (i = 0; i < lp->numclasses; i++) {
430✔
764
    if (lp->class[i] -> group && strcasecmp(lp->class[i] -> group,
291✔
765
                                            lp -> classgroup) == 0) {
291✔
766
      classgroup[nvalidclass] = i;
143✔
767
      nvalidclass++;
143✔
768
    }
769
  }
770
  if (nvalidclass > 0) {
139✔
771
    classgroup = (int *)msSmallRealloc(classgroup, sizeof(int) * nvalidclass);
139✔
772
    *nclasses = nvalidclass;
139✔
773
    return classgroup;
139✔
774
  }
775

776
  if (classgroup)
×
777
    msFree(classgroup);
×
778

779
  return NULL;
780
}
781

782
int msShapeGetClass(layerObj *layer, mapObj *map, shapeObj *shape,
7,065✔
783
                    int *classgroup, int numclasses) {
784
  return msShapeGetNextClass(-1, layer, map, shape, classgroup, numclasses);
7,065✔
785
}
786

787
int msShapeGetNextClass(int currentclass, layerObj *layer, mapObj *map,
67,764✔
788
                        shapeObj *shape, int *classgroup, int numclasses) {
789
  int i, iclass;
790

791
  if (currentclass < 0)
67,764✔
792
    currentclass = -1;
793

794
  if (layer->numclasses > 0) {
67,764✔
795
    if (classgroup == NULL || numclasses <= 0)
64,238✔
796
      numclasses = layer->numclasses;
797

798
    for (i = currentclass + 1; i < numclasses; i++) {
71,148✔
799
      if (classgroup)
40,249✔
800
        iclass = classgroup[i];
2,698✔
801
      else
802
        iclass = i;
803

804
      if (iclass < 0 || iclass >= layer->numclasses)
40,249✔
805
        continue; /* this should never happen but just in case */
×
806

807
      if (map->scaledenom > 0) { /* verify scaledenom here  */
40,249✔
808
        if ((layer->class[iclass] -> maxscaledenom > 0) &&
39,094✔
809
            (map->scaledenom > layer->class[iclass] -> maxscaledenom))
810
          continue; /* can skip this one, next class */
651✔
811
        if ((layer->class[iclass] -> minscaledenom > 0) &&
38,443✔
812
            (map->scaledenom <= layer->class[iclass] -> minscaledenom))
813
          continue; /* can skip this one, next class */
×
814
      }
815

816
      /* verify the minfeaturesize */
817
      if ((shape->type == MS_SHAPE_LINE || shape->type == MS_SHAPE_POLYGON) &&
39,598✔
818
          (layer->class[iclass] -> minfeaturesize > 0)) {
19,509✔
819
        double minfeaturesize =
820
            Pix2LayerGeoref(map, layer, layer->class[iclass] -> minfeaturesize);
×
821
        if (msShapeCheckSize(shape, minfeaturesize) == MS_FALSE)
×
822
          continue; /* skip this one, next class */
×
823
      }
824

825
      if (layer->class[iclass] -> status != MS_DELETE &&
79,196✔
826
                                      msEvalExpression(
39,598✔
827
                                          layer, shape,
828
                                          &(layer->class[iclass] -> expression),
829
                                          layer->classitemindex) == MS_TRUE) {
830
        if (layer->class[iclass] -> isfallback && currentclass != -1) {
33,339✔
831
          // Class is not applicable if it is flagged as fallback (<ElseFilter/>
832
          // tag in SLD) but other classes have been applied before.
833
          return -1;
834
        } else {
835
          return (iclass);
836
        }
837
      }
838
    }
839
  }
840

841
  return (-1); /* no match */
842
}
843

844
static char *msEvalTextExpressionInternal(expressionObj *expr, shapeObj *shape,
4,339✔
845
                                          int bJSonEscape) {
846
  char *result = NULL;
847

848
  if (!expr->string)
4,339✔
849
    return result; /* nothing to do */
850

851
  switch (expr->type) {
4,323✔
852
  case (MS_STRING): {
3,637✔
853
    char *target = NULL;
854
    char *pszEscaped;
855
    tokenListNodeObjPtr node = NULL;
856
    tokenListNodeObjPtr nextNode = NULL;
857

858
    result = msStrdup(expr->string);
3,637✔
859

860
    node = expr->tokens;
3,637✔
861
    if (node) {
3,637✔
862
      while (node != NULL) {
8,522✔
863
        nextNode = node->next;
5,086✔
864
        if (node->token == MS_TOKEN_BINDING_DOUBLE ||
5,086✔
865
            node->token == MS_TOKEN_BINDING_INTEGER ||
866
            node->token == MS_TOKEN_BINDING_STRING ||
5,086✔
867
            node->token == MS_TOKEN_BINDING_TIME) {
868
          target =
869
              (char *)msSmallMalloc(strlen(node->tokenval.bindval.item) + 3);
3,441✔
870
          sprintf(target, "[%s]", node->tokenval.bindval.item);
3,441✔
871
          if (bJSonEscape)
3,441✔
872
            pszEscaped =
873
                msEscapeJSonString(shape->values[node->tokenval.bindval.index]);
5✔
874
          else
875
            pszEscaped = msStrdup(shape->values[node->tokenval.bindval.index]);
3,436✔
876
          result = msReplaceSubstring(result, target, pszEscaped);
3,441✔
877
          msFree(pszEscaped);
3,441✔
878
          msFree(target);
3,441✔
879
        }
880
        node = nextNode;
881
      }
882
    }
883
    if (!strlen(result)) {
3,637✔
884
      msFree(result);
3✔
885
      result = NULL;
886
    }
887
  } break;
888
  case (MS_EXPRESSION): {
686✔
889
    int status;
890
    parseObj p;
891

892
    p.shape = shape;
686✔
893
    p.expr = expr;
686✔
894
    p.expr->curtoken = p.expr->tokens; /* reset */
686✔
895
    p.type = MS_PARSE_TYPE_STRING;
686✔
896

897
    status = yyparse(&p);
686✔
898

899
    if (status != 0) {
686✔
900
      msSetError(MS_PARSEERR, "Failed to process text expression: %s",
×
901
                 "msEvalTextExpression", expr->string);
902
      return NULL;
×
903
    }
904

905
    result = p.result.strval;
686✔
906
    break;
686✔
907
  }
908
  default:
909
    break;
910
  }
911
  if (result && !strlen(result)) {
4,320✔
912
    msFree(result);
24✔
913
    result = NULL;
914
  }
915
  return result;
916
}
917

918
char *msEvalTextExpressionJSonEscape(expressionObj *expr, shapeObj *shape) {
21✔
919
  return msEvalTextExpressionInternal(expr, shape, MS_TRUE);
21✔
920
}
921

922
char *msEvalTextExpression(expressionObj *expr, shapeObj *shape) {
4,318✔
923
  return msEvalTextExpressionInternal(expr, shape, MS_FALSE);
4,318✔
924
}
925

926
double msEvalDoubleExpression(expressionObj *expression, shapeObj *shape) {
48✔
927
  double value;
928
  int status;
929
  parseObj p;
930
  p.shape = shape;
48✔
931
  p.expr = expression;
48✔
932
  p.expr->curtoken = p.expr->tokens; /* reset */
48✔
933
  p.type = MS_PARSE_TYPE_STRING;
48✔
934
  status = yyparse(&p);
48✔
935
  if (status != 0) {
48✔
936
    msSetError(MS_PARSEERR, "Failed to parse expression: %s", "bindStyle",
×
937
               expression->string);
938
    value = 0.0;
939
  } else {
940
    value = atof(p.result.strval);
48✔
941
    msFree(p.result.strval);
48✔
942
  }
943
  return value;
48✔
944
}
945

946
char *msShapeGetLabelAnnotation(layerObj *layer, shapeObj *shape,
9,514✔
947
                                labelObj *lbl) {
948
  assert(shape && lbl);
949
  if (lbl->text.string) {
9,514✔
950
    return msEvalTextExpression(&(lbl->text), shape);
3,568✔
951
  } else if (layer->class[shape->classindex] -> text.string) {
5,946✔
952
    return msEvalTextExpression(&(layer->class[shape->classindex] -> text),
743✔
953
                                shape);
954
  } else {
955
    if (shape->values && layer->labelitemindex >= 0 &&
5,203✔
956
        shape->values[layer->labelitemindex] &&
338✔
957
        strlen(shape->values[layer->labelitemindex]))
958
      return msStrdup(shape->values[layer->labelitemindex]);
338✔
959
    else if (shape->text)
4,865✔
960
      return msStrdup(
1,480✔
961
          shape->text); /* last resort but common with iniline features */
962
  }
963
  return NULL;
964
}
965

966
int msGetLabelStatus(mapObj *map, layerObj *layer, shapeObj *shape,
34,326✔
967
                     labelObj *lbl) {
968
  assert(layer && lbl);
969
  if (!msScaleInBounds(map->scaledenom, lbl->minscaledenom, lbl->maxscaledenom))
34,326✔
970
    return MS_OFF;
971
  if (msEvalExpression(layer, shape, &(lbl->expression),
34,326✔
972
                       layer->labelitemindex) != MS_TRUE)
973
    return MS_OFF;
974
  /* TODO: check for minfeaturesize here ? */
975
  return MS_ON;
976
}
977

978
/* Check if the shape is enough big to be drawn with the
979
   layer::minfeaturesize setting. The minfeaturesize parameter should be
980
   the value in geo ref (not in pixel) and should have been multiplied by
981
   the resolution factor.
982
 */
983
int msShapeCheckSize(shapeObj *shape, double minfeaturesize) {
×
984
  double dx = (shape->bounds.maxx - shape->bounds.minx);
×
985
  double dy = (shape->bounds.maxy - shape->bounds.miny);
×
986

987
  if (pow(minfeaturesize, 2.0) > (pow(dx, 2.0) + pow(dy, 2.0)))
×
988
    return MS_FALSE;
×
989

990
  return MS_TRUE;
991
}
992

993
/*
994
** Adjusts an image size in one direction to fit an extent.
995
*/
996
int msAdjustImage(rectObj rect, int *width, int *height) {
×
997
  if (*width == -1 && *height == -1) {
×
998
    msSetError(MS_MISCERR, "Cannot calculate both image height and width.",
×
999
               "msAdjustImage()");
1000
    return (-1);
×
1001
  }
1002

1003
  if (*width > 0)
×
1004
    *height =
×
1005
        MS_NINT((rect.maxy - rect.miny) / ((rect.maxx - rect.minx) / (*width)));
×
1006
  else
1007
    *width = MS_NINT((rect.maxx - rect.minx) /
×
1008
                     ((rect.maxy - rect.miny) / (*height)));
1009

1010
  return (0);
1011
}
1012

1013
/*
1014
** Make sure extent fits image window to be created. Returns cellsize of output
1015
*image.
1016
*/
1017
double msAdjustExtent(rectObj *rect, int width, int height) {
2,180✔
1018
  double cellsize, ox, oy;
1019

1020
  if (width == 1 || height == 1)
2,180✔
1021
    return 0;
1022

1023
  cellsize = MS_MAX(MS_CELLSIZE(rect->minx, rect->maxx, width),
2,180✔
1024
                    MS_CELLSIZE(rect->miny, rect->maxy, height));
1025

1026
  if (cellsize <= 0) /* avoid division by zero errors */
2,180✔
1027
    return (0);
1028

1029
  ox = MS_MAX(((width - 1) - (rect->maxx - rect->minx) / cellsize) / 2,
2,057✔
1030
              0); /* these were width-1 and height-1 */
1031
  oy = MS_MAX(((height - 1) - (rect->maxy - rect->miny) / cellsize) / 2, 0);
2,057✔
1032

1033
  rect->minx = rect->minx - ox * cellsize;
2,057✔
1034
  rect->miny = rect->miny - oy * cellsize;
2,057✔
1035
  rect->maxx = rect->maxx + ox * cellsize;
2,057✔
1036
  rect->maxy = rect->maxy + oy * cellsize;
2,057✔
1037

1038
  return (cellsize);
2,057✔
1039
}
1040

1041
/*
1042
** Rect must always contain a portion of bounds. If not, rect is
1043
** shifted to overlap by overlay percent. The dimensions of rect do
1044
** not change but placement relative to bounds can.
1045
*/
1046
int msConstrainExtent(rectObj *bounds, rectObj *rect, double overlay) {
×
1047
  /* check left edge, and if necessary the right edge of bounds */
1048
  if (rect->maxx <= bounds->minx) {
×
1049
    const double offset = overlay * (rect->maxx - rect->minx);
×
1050
    rect->minx += offset; /* shift right */
×
1051
    rect->maxx += offset;
×
1052
  } else if (rect->minx >= bounds->maxx) {
×
1053
    const double offset = overlay * (rect->maxx - rect->minx);
×
1054
    rect->minx -= offset; /* shift left */
×
1055
    rect->maxx -= offset;
×
1056
  }
1057

1058
  /* check top edge, and if necessary the bottom edge of bounds */
1059
  if (rect->maxy <= bounds->miny) {
×
1060
    const double offset = overlay * (rect->maxy - rect->miny);
×
1061
    rect->miny -= offset; /* shift down */
×
1062
    rect->maxy -= offset;
×
1063
  } else if (rect->miny >= bounds->maxy) {
×
1064
    const double offset = overlay * (rect->maxy - rect->miny);
×
1065
    rect->miny += offset; /* shift up */
×
1066
    rect->maxy += offset;
×
1067
  }
1068

1069
  return (MS_SUCCESS);
×
1070
}
1071

1072
/*
1073
** Generic function to save an image to a file.
1074
**
1075
** Note that map may be NULL. If it is set, then it is used for two things:
1076
** - Deal with relative imagepaths (compute absolute path relative to map path)
1077
** - Extract the georeferenced extents and coordinate system
1078
**   of the map for writing out with the image when appropriate
1079
**   (primarily this means via msSaveImageGDAL() to something like GeoTIFF).
1080
**
1081
** The filename is NULL when the image is supposed to be written to stdout.
1082
*/
1083

1084
int msSaveImage(mapObj *map, imageObj *img, const char *filename) {
1,328✔
1085
  int nReturnVal = MS_FAILURE;
1086
  char szPath[MS_MAXPATHLEN];
1087
  struct mstimeval starttime = {0}, endtime = {0};
1,328✔
1088

1089
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,328✔
1090
    msGettimeofday(&starttime, NULL);
4✔
1091
  }
1092

1093
  if (img) {
1,328✔
1094
    if (MS_DRIVER_GDAL(img->format)) {
1,328✔
1095
      if (map != NULL && filename != NULL)
210✔
1096
        nReturnVal = msSaveImageGDAL(
188✔
1097
            map, img, msBuildPath(szPath, map->mappath, filename));
188✔
1098
      else
1099
        nReturnVal = msSaveImageGDAL(map, img, filename);
22✔
1100
    } else
1101

1102
        if (MS_RENDERER_PLUGIN(img->format)) {
1,118✔
1103
      rendererVTableObj *renderer = img->format->vtable;
1,118✔
1104
      FILE *stream = NULL;
1105
      if (filename) {
1,118✔
1106
        if (map)
613✔
1107
          stream = fopen(msBuildPath(szPath, map->mappath, filename), "wb");
569✔
1108
        else
1109
          stream = fopen(filename, "wb");
44✔
1110

1111
        if (!stream) {
613✔
1112
          msSetError(MS_IOERR, "Failed to create output file (%s).",
×
1113
                     "msSaveImage()", (map ? szPath : filename));
1114
          return MS_FAILURE;
×
1115
        }
1116

1117
      } else {
1118
        if (msIO_needBinaryStdout() == MS_FAILURE)
505✔
1119
          return MS_FAILURE;
1120
        stream = stdout;
505✔
1121
      }
1122

1123
      if (renderer->supports_pixel_buffer) {
1,118✔
1124
        rasterBufferObj data;
1125
        if (renderer->getRasterBufferHandle(img, &data) != MS_SUCCESS) {
951✔
1126
          if (stream != stdout)
×
1127
            fclose(stream);
×
1128
          return MS_FAILURE;
×
1129
        }
1130

1131
        nReturnVal = msSaveRasterBuffer(map, &data, stream, img->format);
951✔
1132
      } else {
1133
        nReturnVal = renderer->saveImage(img, map, stream, img->format);
167✔
1134
      }
1135
      if (stream != stdout)
1,118✔
1136
        fclose(stream);
613✔
1137

1138
    } else if (MS_DRIVER_IMAGEMAP(img->format))
×
1139
      nReturnVal = msSaveImageIM(img, filename, img->format);
×
1140
    else
1141
      msSetError(MS_MISCERR, "Unknown image type", "msSaveImage()");
×
1142
  }
1143

1144
  if (map && map->debug >= MS_DEBUGLEVEL_TUNING) {
1,328✔
1145
    msGettimeofday(&endtime, NULL);
4✔
1146
    msDebug("msSaveImage(%s) total time: %.3fs\n",
4✔
1147
            (filename ? filename : "stdout"),
1148
            (endtime.tv_sec + endtime.tv_usec / 1.0e6) -
4✔
1149
                (starttime.tv_sec + starttime.tv_usec / 1.0e6));
4✔
1150
  }
1151

1152
  return nReturnVal;
1153
}
1154

1155
/*
1156
** Generic function to save an image to a byte array.
1157
** - the return value is the pointer to the byte array
1158
** - size_ptr contains the number of bytes returned
1159
** - format: the desired output format
1160
**
1161
** The caller is responsible to free the returned array
1162
** The function returns NULL if the output format is not supported.
1163
*/
1164

1165
unsigned char *msSaveImageBuffer(imageObj *image, int *size_ptr,
15✔
1166
                                 outputFormatObj *format) {
1167
  *size_ptr = 0;
15✔
1168
  if (MS_RENDERER_PLUGIN(image->format)) {
15✔
1169
    rasterBufferObj data;
1170
    rendererVTableObj *renderer = image->format->vtable;
15✔
1171
    if (renderer->supports_pixel_buffer) {
15✔
1172
      bufferObj buffer;
1173
      msBufferInit(&buffer);
15✔
1174
      const int status = renderer->getRasterBufferHandle(image, &data);
15✔
1175
      if (MS_UNLIKELY(status == MS_FAILURE)) {
15✔
1176
        return NULL;
1177
      }
1178
      msSaveRasterBufferToBuffer(&data, &buffer, format);
15✔
1179
      *size_ptr = buffer.size;
15✔
1180
      return buffer.data;
15✔
1181
      /* don't free the bufferObj as we don't own the bytes anymore */
1182
    } else {
1183
      /* check if the renderer supports native buffer output */
1184
      if (renderer->saveImageBuffer)
×
1185
        return renderer->saveImageBuffer(image, size_ptr, format);
×
1186

1187
      msSetError(MS_MISCERR, "Unsupported image type", "msSaveImageBuffer()");
×
1188
      return NULL;
×
1189
    }
1190
  }
1191
  msSetError(MS_MISCERR, "Unsupported image type", "msSaveImage()");
×
1192
  return NULL;
×
1193
}
1194

1195
/**
1196
 * Generic function to free the imageObj
1197
 */
1198
void msFreeImage(imageObj *image) {
2,026✔
1199
  if (image) {
2,026✔
1200
    if (MS_RENDERER_PLUGIN(image->format)) {
2,026✔
1201
      rendererVTableObj *renderer = image->format->vtable;
1,729✔
1202
      tileCacheObj *next, *cur = image->tilecache;
1,729✔
1203
      while (cur) {
1,766✔
1204
        msFreeImage(cur->image);
37✔
1205
        next = cur->next;
37✔
1206
        free(cur);
37✔
1207
        cur = next;
1208
      }
1209
      image->ntiles = 0;
1,729✔
1210
      renderer->freeImage(image);
1,729✔
1211
    } else if (MS_RENDERER_IMAGEMAP(image->format))
297✔
1212
      msFreeImageIM(image);
×
1213
    else if (MS_RENDERER_RAWDATA(image->format))
297✔
1214
      msFree(image->img.raw_16bit);
297✔
1215
    else
1216
      msSetError(MS_MISCERR, "Unknown image type", "msFreeImage()");
×
1217

1218
    if (image->imagepath)
2,026✔
1219
      free(image->imagepath);
1,738✔
1220
    if (image->imageurl)
2,026✔
1221
      free(image->imageurl);
1,738✔
1222

1223
    if (--image->format->refcount < 1)
2,026✔
1224
      msFreeOutputFormat(image->format);
518✔
1225

1226
    image->imagepath = NULL;
1227
    image->imageurl = NULL;
1228

1229
    msFree(image->img_mask);
2,026✔
1230
    image->img_mask = NULL;
1231

1232
    msFree(image);
2,026✔
1233
  }
1234
}
2,026✔
1235

1236
/*
1237
** Return an array containing all the layer's index given a group name.
1238
** If nothing is found, NULL is returned. The nCount is initialized
1239
** to the number of elements in the returned array.
1240
** Note : the caller of the function should free the array.
1241
*/
1242
int *msGetLayersIndexByGroup(mapObj *map, char *groupname, int *pnCount) {
1✔
1243
  int i;
1244
  int iLayer = 0;
1245
  int *aiIndex;
1246

1247
  if (!groupname || !map || !pnCount) {
1✔
1248
    return NULL;
1249
  }
1250

1251
  aiIndex = (int *)msSmallMalloc(sizeof(int) * map->numlayers);
1✔
1252

1253
  for (i = 0; i < map->numlayers; i++) {
3✔
1254
    if (!GET_LAYER(map, i)->group) /* skip it */
2✔
1255
      continue;
2✔
1256
    if (strcmp(groupname, GET_LAYER(map, i)->group) == 0) {
×
1257
      aiIndex[iLayer] = i;
×
1258
      iLayer++;
×
1259
    }
1260
  }
1261

1262
  if (iLayer == 0) {
1✔
1263
    free(aiIndex);
1✔
1264
    aiIndex = NULL;
1265
    *pnCount = 0;
1✔
1266
  } else {
1267
    aiIndex = (int *)msSmallRealloc(aiIndex, sizeof(int) * iLayer);
×
1268
    *pnCount = iLayer;
×
1269
  }
1270

1271
  return aiIndex;
1272
}
1273

1274
/* ==================================================================== */
1275
/*      Measured shape utility functions.                               */
1276
/* ==================================================================== */
1277

1278
/************************************************************************/
1279
/*        pointObj *msGetPointUsingMeasure(shapeObj *shape, double m)   */
1280
/*                                                                      */
1281
/*      Using a measured value get the XY location it corresponds        */
1282
/*      to.                                                             */
1283
/*                                                                      */
1284
/************************************************************************/
1285
pointObj *msGetPointUsingMeasure(shapeObj *shape, double m) {
×
1286
  pointObj *point = NULL;
1287
  int bFound = 0;
1288
  double dfFirstPointX = 0;
1289
  double dfFirstPointY = 0;
1290
  double dfFirstPointM = 0;
1291
  double dfSecondPointX = 0;
1292
  double dfSecondPointY = 0;
1293
  double dfSecondPointM = 0;
1294

1295
  if (shape && shape->numlines > 0) {
×
1296
    /* -------------------------------------------------------------------- */
1297
    /*      check fir the first value (min) and the last value(max) to      */
1298
    /*      see if the m is contained between these min and max.            */
1299
    /* -------------------------------------------------------------------- */
1300
    const lineObj *lineFirst = &(shape->line[0]);
×
1301
    const double dfMin = lineFirst->point[0].m;
×
1302
    const lineObj *lineLast = &(shape->line[shape->numlines - 1]);
×
1303
    const double dfMax = lineLast->point[lineLast->numpoints - 1].m;
×
1304

1305
    if (m >= dfMin && m <= dfMax) {
×
1306
      for (int i = 0; i < shape->numlines; i++) {
×
1307
        const lineObj *line = &(shape->line[i]);
×
1308

1309
        for (int j = 0; j < line->numpoints; j++) {
×
1310
          const double dfCurrentM = line->point[j].m;
×
1311
          if (dfCurrentM > m) {
×
1312
            bFound = 1;
1313

1314
            dfSecondPointX = line->point[j].x;
×
1315
            dfSecondPointY = line->point[j].y;
×
1316
            dfSecondPointM = line->point[j].m;
1317

1318
            /* --------------------------------------------------------------------
1319
             */
1320
            /*      get the previous node xy values. */
1321
            /* --------------------------------------------------------------------
1322
             */
1323
            if (j > 0) { /* not the first point of the line */
×
1324
              dfFirstPointX = line->point[j - 1].x;
×
1325
              dfFirstPointY = line->point[j - 1].y;
×
1326
              dfFirstPointM = line->point[j - 1].m;
×
1327
            } else { /* get last point of previous line */
1328
              dfFirstPointX = shape->line[i - 1].point[0].x;
×
1329
              dfFirstPointY = shape->line[i - 1].point[0].y;
×
1330
              dfFirstPointM = shape->line[i - 1].point[0].m;
×
1331
            }
1332
            break;
1333
          }
1334
        }
1335
      }
1336
    }
1337

1338
    if (!bFound)
×
1339
      return NULL;
1340

1341
    /* -------------------------------------------------------------------- */
1342
    /*      extrapolate the m value to get t he xy coordinate.              */
1343
    /* -------------------------------------------------------------------- */
1344

1345
    double dfFactor = 0;
1346
    if (dfFirstPointM != dfSecondPointM)
×
1347
      dfFactor = (m - dfFirstPointM) / (dfSecondPointM - dfFirstPointM);
×
1348

1349
    point = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1350

1351
    point->x = dfFirstPointX + (dfFactor * (dfSecondPointX - dfFirstPointX));
×
1352
    point->y = dfFirstPointY + (dfFactor * (dfSecondPointY - dfFirstPointY));
×
1353
    point->m = dfFirstPointM + (dfFactor * (dfSecondPointM - dfFirstPointM));
×
1354

1355
    return point;
×
1356
  }
1357

1358
  return NULL;
1359
}
1360

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

1441
  if (p && a && b) {
×
1442
    const double L =
1443
        sqrt(((b->x - a->x) * (b->x - a->x)) + ((b->y - a->y) * (b->y - a->y)));
×
1444

1445
    double r = 0;
1446
    if (L != 0)
×
1447
      r = ((p->x - a->x) * (b->x - a->x) + (p->y - a->y) * (b->y - a->y)) /
×
1448
          (L * L);
×
1449

1450
    result = (pointObj *)msSmallMalloc(sizeof(pointObj));
×
1451
    /* -------------------------------------------------------------------- */
1452
    /*      We want to make sure that the point returned is on the line     */
1453
    /*                                                                      */
1454
    /*              r=0      P = A                                          */
1455
    /*              r=1      P = B                                          */
1456
    /*              r<0      P is on the backward extension of AB           */
1457
    /*              r>1      P is on the forward extension of AB            */
1458
    /*                    0<r<1    P is interior to AB                      */
1459
    /* -------------------------------------------------------------------- */
1460
    if (r < 0) {
×
1461
      result->x = a->x;
×
1462
      result->y = a->y;
×
1463
    } else if (r > 1) {
×
1464
      result->x = b->x;
×
1465
      result->y = b->y;
×
1466
    } else {
1467
      result->x = a->x + r * (b->x - a->x);
×
1468
      result->y = a->y + r * (b->y - a->y);
×
1469
    }
1470
    result->m = 0;
×
1471
  }
1472

1473
  return result;
×
1474
}
1475

1476
/************************************************************************/
1477
/*         pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj   */
1478
/*      *point)                                                         */
1479
/*                                                                      */
1480
/*      Calculate the intersection point betwwen the point and the      */
1481
/*      shape and return the Measured value at the intersection.        */
1482
/************************************************************************/
1483
pointObj *msGetMeasureUsingPoint(shapeObj *shape, pointObj *point) {
×
1484
  pointObj oFirst = {0, 0, 0, 0};
×
1485
  pointObj oSecond = {0, 0, 0, 0};
×
1486

1487
  if (shape && point) {
×
1488
    double dfMinDist = HUGE_VAL;
1489
    for (int i = 0; i < shape->numlines; i++) {
×
1490
      lineObj line = shape->line[i];
×
1491
      /* -------------------------------------------------------------------- */
1492
      /*      for each line (2 consecutive lines) get the distance between    */
1493
      /*      the line and the point and determine which line segment is      */
1494
      /*      the closeset to the point.                                      */
1495
      /* -------------------------------------------------------------------- */
1496
      for (int j = 0; j < line.numpoints - 1; j++) {
×
1497
        const double dfDist =
1498
            msDistancePointToSegment(point, &line.point[j], &line.point[j + 1]);
×
1499
        if (dfDist < dfMinDist) {
×
1500
          oFirst.x = line.point[j].x;
×
1501
          oFirst.y = line.point[j].y;
×
1502
          oFirst.m = line.point[j].m;
×
1503

1504
          oSecond.x = line.point[j + 1].x;
×
1505
          oSecond.y = line.point[j + 1].y;
×
1506
          oSecond.m = line.point[j + 1].m;
×
1507

1508
          dfMinDist = dfDist;
1509
        }
1510
      }
1511
    }
1512
    /* -------------------------------------------------------------------- */
1513
    /*      once we have the nearest segment, look for the x,y location     */
1514
    /*      which is the nearest intersection between the line and the      */
1515
    /*      point.                                                          */
1516
    /* -------------------------------------------------------------------- */
1517
    pointObj *poIntersectionPt =
1518
        msIntersectionPointLine(point, &oFirst, &oSecond);
×
1519
    if (poIntersectionPt) {
×
1520
      const double dfDistTotal =
1521
          sqrt(((oSecond.x - oFirst.x) * (oSecond.x - oFirst.x)) +
×
1522
               ((oSecond.y - oFirst.y) * (oSecond.y - oFirst.y)));
×
1523

1524
      const double dfDistToIntersection =
1525
          sqrt(((poIntersectionPt->x - oFirst.x) *
×
1526
                (poIntersectionPt->x - oFirst.x)) +
1527
               ((poIntersectionPt->y - oFirst.y) *
×
1528
                (poIntersectionPt->y - oFirst.y)));
1529

1530
      const double dfFactor = dfDistToIntersection / dfDistTotal;
×
1531

1532
      poIntersectionPt->m = oFirst.m + (oSecond.m - oFirst.m) * dfFactor;
×
1533
      return poIntersectionPt;
×
1534
    }
1535
  }
1536
  return NULL;
1537
}
1538

1539
/* ==================================================================== */
1540
/*   End   Measured shape utility functions.                            */
1541
/* ==================================================================== */
1542

1543
char **msGetAllGroupNames(mapObj *map, int *numTok) {
×
1544
  char **papszGroups = NULL;
1545

1546
  assert(map);
1547
  *numTok = 0;
×
1548

1549
  if (!map->layerorder) {
×
1550
    map->layerorder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
1551

1552
    /*
1553
     * Initiate to default order
1554
     */
1555
    for (int i = 0; i < map->numlayers; i++)
×
1556
      map->layerorder[i] = i;
×
1557
  }
1558

1559
  if (map->numlayers > 0) {
×
1560
    const int nCount = map->numlayers;
1561
    papszGroups = (char **)msSmallMalloc(sizeof(char *) * nCount);
×
1562

1563
    for (int i = 0; i < nCount; i++)
×
1564
      papszGroups[i] = NULL;
×
1565

1566
    for (int i = 0; i < nCount; i++) {
×
1567
      layerObj *lp;
1568
      lp = (GET_LAYER(map, map->layerorder[i]));
×
1569

1570
      int bFound = 0;
1571
      if (lp->group && lp->status != MS_DELETE) {
×
1572
        for (int j = 0; j < *numTok; j++) {
×
1573
          if (papszGroups[j] && strcmp(lp->group, papszGroups[j]) == 0) {
×
1574
            bFound = 1;
1575
            break;
1576
          }
1577
        }
1578
        if (!bFound) {
×
1579
          /* New group... add to the list of groups found */
1580
          papszGroups[(*numTok)] = msStrdup(lp->group);
×
1581
          (*numTok)++;
×
1582
        }
1583
      }
1584
    }
1585
  }
1586

1587
  return papszGroups;
×
1588
}
1589

1590
/************************************************************************/
1591
/*                         msForceTmpFileBase()                         */
1592
/************************************************************************/
1593

1594
static int tmpCount = 0;
1595
static char *ForcedTmpBase = NULL;
1596

1597
void msForceTmpFileBase(const char *new_base) {
2,597✔
1598
  /* -------------------------------------------------------------------- */
1599
  /*      Clear previous setting, if any.                                 */
1600
  /* -------------------------------------------------------------------- */
1601
  if (ForcedTmpBase != NULL) {
2,597✔
1602
    free(ForcedTmpBase);
×
1603
    ForcedTmpBase = NULL;
×
1604
  }
1605

1606
  tmpCount = -1;
2,597✔
1607

1608
  if (new_base == NULL)
2,597✔
1609
    return;
1610

1611
  /* -------------------------------------------------------------------- */
1612
  /*      Record new base.                                                */
1613
  /* -------------------------------------------------------------------- */
1614
  ForcedTmpBase = msStrdup(new_base);
×
1615
  tmpCount = 0;
×
1616
}
1617

1618
/**********************************************************************
1619
 *                          msTmpFile()
1620
 *
1621
 * Generate a Unique temporary file.
1622
 *
1623
 * Returns char* which must be freed by caller.
1624
 **********************************************************************/
1625
char *msTmpFile(mapObj *map, const char *mappath, const char *tmppath,
261✔
1626
                const char *ext) {
1627
  char szPath[MS_MAXPATHLEN];
1628
  const char *fullFname;
1629
  char *tmpFileName; /* big enough for time + pid + ext */
1630
  char *tmpBase = NULL;
1631

1632
  tmpBase = msTmpPath(map, mappath, tmppath);
261✔
1633
  tmpFileName = msTmpFilename(ext);
261✔
1634

1635
  fullFname = msBuildPath(szPath, tmpBase, tmpFileName);
261✔
1636

1637
  free(tmpFileName);
261✔
1638
  free(tmpBase);
261✔
1639

1640
  if (fullFname)
261✔
1641
    return msStrdup(fullFname);
261✔
1642

1643
  return NULL;
1644
}
1645

1646
/**********************************************************************
1647
 *                          msTmpPath()
1648
 *
1649
 * Return the temporary path based on the platform.
1650
 *
1651
 * Returns char* which must be freed by caller.
1652
 **********************************************************************/
1653
char *msTmpPath(mapObj *map, const char *mappath, const char *tmppath) {
271✔
1654
  char szPath[MS_MAXPATHLEN];
1655
  const char *fullPath;
1656
  const char *tmpBase;
1657
  const char *ms_temppath;
1658
#ifdef _WIN32
1659
  DWORD dwRetVal = 0;
1660
  TCHAR lpTempPathBuffer[MAX_PATH];
1661
#endif
1662

1663
  if (ForcedTmpBase != NULL)
271✔
1664
    tmpBase = ForcedTmpBase;
1665
  else if (tmppath != NULL)
271✔
1666
    tmpBase = tmppath;
1667
  else if ((ms_temppath = CPLGetConfigOption("MS_TEMPPATH", NULL)) != NULL)
38✔
1668
    tmpBase = ms_temppath;
1669
  else if (map && map->web.temppath)
38✔
1670
    tmpBase = map->web.temppath;
1671
  else { /* default paths */
1672
#ifndef _WIN32
1673
    tmpBase = "/tmp/";
1674
#else
1675
    dwRetVal = GetTempPath(MAX_PATH,          /* length of the buffer */
1676
                           lpTempPathBuffer); /* buffer for path */
1677
    if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
1678
      tmpBase = "C:\\";
1679
    } else {
1680
      tmpBase = (char *)lpTempPathBuffer;
1681
    }
1682
#endif
1683
  }
1684

1685
  fullPath = msBuildPath(szPath, mappath, tmpBase);
271✔
1686
  return msStrdup(fullPath);
271✔
1687
}
1688

1689
/**********************************************************************
1690
 *                          msTmpFilename()
1691
 *
1692
 * Generate a Unique temporary filename.
1693
 *
1694
 * Returns char* which must be freed by caller.
1695
 **********************************************************************/
1696
char *msTmpFilename(const char *ext) {
262✔
1697
  char *tmpFname;
1698
  int tmpFnameBufsize;
1699
  char *fullFname;
1700
  char tmpId[128]; /* big enough for time + pid + ext */
1701

1702
  snprintf(tmpId, sizeof(tmpId), "%lx_%x", (long)time(NULL), (int)getpid());
262✔
1703

1704
  if (ext == NULL)
262✔
1705
    ext = "";
1706
  tmpFnameBufsize = strlen(tmpId) + 10 + strlen(ext) + 1;
262✔
1707
  tmpFname = (char *)msSmallMalloc(tmpFnameBufsize);
262✔
1708

1709
  msAcquireLock(TLOCK_TMPFILE);
262✔
1710
  snprintf(tmpFname, tmpFnameBufsize, "%s_%x.%s", tmpId, tmpCount++, ext);
262✔
1711
  msReleaseLock(TLOCK_TMPFILE);
262✔
1712

1713
  fullFname = msStrdup(tmpFname);
262✔
1714
  free(tmpFname);
262✔
1715

1716
  return fullFname;
262✔
1717
}
1718

1719
/**
1720
 *  Generic function to Initialize an image object.
1721
 */
1722
imageObj *msImageCreate(int width, int height, outputFormatObj *format,
817✔
1723
                        char *imagepath, char *imageurl, double resolution,
1724
                        double defresolution, colorObj *bg) {
1725
  imageObj *image = NULL;
1726
  if (MS_RENDERER_PLUGIN(format)) {
817✔
1727

1728
    image = format->vtable->createImage(width, height, format, bg);
520✔
1729
    if (image == NULL) {
520✔
1730
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1731
                 "msImageCreate()");
1732
      return NULL;
×
1733
    }
1734

1735
    image->format = format;
520✔
1736
    format->refcount++;
520✔
1737

1738
    image->width = width;
520✔
1739
    image->height = height;
520✔
1740
    image->imagepath = NULL;
520✔
1741
    image->imageurl = NULL;
520✔
1742
    image->tilecache = NULL;
520✔
1743
    image->ntiles = 0;
520✔
1744
    image->resolution = resolution;
520✔
1745
    image->resolutionfactor = resolution / defresolution;
520✔
1746

1747
    if (imagepath)
520✔
1748
      image->imagepath = msStrdup(imagepath);
351✔
1749
    if (imageurl)
520✔
1750
      image->imageurl = msStrdup(imageurl);
351✔
1751
  } else if (MS_RENDERER_RAWDATA(format)) {
297✔
1752
    if (format->imagemode != MS_IMAGEMODE_INT16 &&
297✔
1753
        format->imagemode != MS_IMAGEMODE_FLOAT32 &&
297✔
1754
        format->imagemode != MS_IMAGEMODE_BYTE) {
1755
      msSetError(MS_IMGERR,
×
1756
                 "Attempt to use illegal imagemode with rawdata renderer.",
1757
                 "msImageCreate()");
1758
      return NULL;
×
1759
    }
1760

1761
    image = (imageObj *)calloc(1, sizeof(imageObj));
297✔
1762
    if (image == NULL) {
297✔
1763
      msSetError(MS_MEMERR, "Unable to create new image object.",
×
1764
                 "msImageCreate()");
1765
      return NULL;
×
1766
    }
1767

1768
    if (format->imagemode == MS_IMAGEMODE_INT16)
297✔
1769
      image->img.raw_16bit = (short *)msSmallCalloc(
38✔
1770
          sizeof(short), ((size_t)width) * height * format->bands);
38✔
1771
    else if (format->imagemode == MS_IMAGEMODE_FLOAT32)
259✔
1772
      image->img.raw_float = (float *)msSmallCalloc(
47✔
1773
          sizeof(float), ((size_t)width) * height * format->bands);
47✔
1774
    else if (format->imagemode == MS_IMAGEMODE_BYTE)
1775
      image->img.raw_byte = (unsigned char *)msSmallCalloc(
212✔
1776
          sizeof(unsigned char), ((size_t)width) * height * format->bands);
212✔
1777

1778
    if (image->img.raw_16bit == NULL) {
297✔
1779
      msFree(image);
×
1780
      msSetError(MS_IMGERR,
×
1781
                 "Attempt to allocate raw image failed, out of memory.",
1782
                 "msImageCreate()");
1783
      return NULL;
×
1784
    }
1785

1786
    image->img_mask = msAllocBitArray(width * height);
297✔
1787

1788
    image->format = format;
297✔
1789
    format->refcount++;
297✔
1790

1791
    image->width = width;
297✔
1792
    image->height = height;
297✔
1793
    image->imagepath = NULL;
297✔
1794
    image->imageurl = NULL;
297✔
1795
    image->resolution = resolution;
297✔
1796
    image->resolutionfactor = resolution / defresolution;
297✔
1797

1798
    if (imagepath)
297✔
1799
      image->imagepath = msStrdup(imagepath);
178✔
1800
    if (imageurl)
297✔
1801
      image->imageurl = msStrdup(imageurl);
178✔
1802

1803
    /* initialize to requested nullvalue if there is one */
1804
    const char *nullvalue =
1805
        msGetOutputFormatOption(image->format, "NULLVALUE", NULL);
297✔
1806
    if (nullvalue != NULL) {
297✔
1807
      int i = image->width * image->height * format->bands;
34✔
1808
      if (atof(nullvalue) == 0.0)
34✔
1809
        /* nothing to do */;
1810
      else if (format->imagemode == MS_IMAGEMODE_INT16) {
34✔
1811
        short nv = atoi(nullvalue);
31✔
1812
        for (; i > 0;)
31,941✔
1813
          image->img.raw_16bit[--i] = nv;
31,910✔
1814
      } else if (format->imagemode == MS_IMAGEMODE_FLOAT32) {
3✔
1815
        float nv = atof(nullvalue);
3✔
1816
        for (; i > 0;)
135✔
1817
          image->img.raw_float[--i] = nv;
132✔
1818
      } else if (format->imagemode == MS_IMAGEMODE_BYTE) {
×
1819
        unsigned char nv = (unsigned char)atoi(nullvalue);
×
1820

1821
        memset(image->img.raw_byte, nv, i);
×
1822
      }
1823
    }
1824
  } else if (MS_RENDERER_IMAGEMAP(format)) {
×
1825
    image = msImageCreateIM(width, height, format, imagepath, imageurl,
×
1826
                            resolution, defresolution);
1827
  } else {
1828
    msSetError(MS_MISCERR,
×
1829
               "Unsupported renderer requested, unable to initialize image.",
1830
               "msImageCreate()");
1831
    return NULL;
×
1832
  }
1833

1834
  if (!image) {
203✔
1835
    msSetError(MS_IMGERR, "Unable to initialize image.", "msImageCreate()");
×
1836
    return NULL;
×
1837
  }
1838
  image->refpt.x = image->refpt.y = 0;
817✔
1839
  return image;
817✔
1840
}
1841

1842
/**
1843
 * Generic function to transform a point.
1844
 *
1845
 */
1846
void msTransformPoint(pointObj *point, rectObj *extent, double cellsize,
23,254✔
1847
                      imageObj *image) {
1848
  double invcellsize;
1849
  /*We should probably have a function defined at all the renders*/
1850
  if (image != NULL && MS_RENDERER_PLUGIN(image->format)) {
23,254✔
1851
    if (image->format->renderer == MS_RENDER_WITH_KML) {
23,254✔
1852
      return;
1853
    }
1854
  }
1855
  invcellsize = 1.0 / cellsize;
23,227✔
1856
  point->x = MS_MAP2IMAGE_X_IC_DBL(point->x, extent->minx, invcellsize);
23,227✔
1857
  point->y = MS_MAP2IMAGE_Y_IC_DBL(point->y, extent->maxy, invcellsize);
23,227✔
1858
}
1859

1860
/*
1861
** Helper functions supplied as part of bug #2868 solution. Consider moving
1862
*these to
1863
** mapprimitive.c for more general use.
1864
*/
1865

1866
/* vector difference */
1867
static pointObj point_diff(const pointObj a, const pointObj b) {
1868
  pointObj retv;
1869
  retv.x = a.x - b.x;
24✔
1870
  retv.y = a.y - b.y;
24✔
1871
  retv.z = a.z - b.z;
24✔
1872
  retv.m = a.m - b.m;
24✔
1873
  return retv;
24✔
1874
}
1875

1876
/* vector sum */
1877
static pointObj point_sum(const pointObj a, const pointObj b) {
1878
  pointObj retv;
1879
  retv.x = a.x + b.x;
28✔
1880
  retv.y = a.y + b.y;
28✔
1881
  retv.z = a.z + b.z;
28✔
1882
  retv.m = a.m + b.m;
28✔
1883
  return retv;
24✔
1884
}
1885

1886
/* vector multiply */
1887
static pointObj point_mul(const pointObj a, double b) {
1888
  pointObj retv;
1889
  retv.x = a.x * b;
28✔
1890
  retv.y = a.y * b;
28✔
1891
  retv.z = a.z * b;
28✔
1892
  retv.m = a.m * b;
4✔
1893
  return retv;
1894
}
1895

1896
/* vector ??? */
1897
static double point_abs2(const pointObj a) {
1898
  return a.x * a.x + a.y * a.y + a.z * a.z + a.m * a.m;
24✔
1899
}
1900

1901
/* vector normal */
1902
static pointObj point_norm(const pointObj a) {
24✔
1903
  double lenmul;
1904
  pointObj retv;
1905
  int norm_vector;
1906

1907
  norm_vector = a.x == 0 && a.y == 0;
24✔
1908
  norm_vector = norm_vector && a.z == 0 && a.m == 0;
×
1909

1910
  if (norm_vector)
1911
    return a;
×
1912

1913
  lenmul =
24✔
1914
      1.0 / sqrt(point_abs2(a)); /* this seems to be the costly operation */
24✔
1915

1916
  retv.x = a.x * lenmul;
24✔
1917
  retv.y = a.y * lenmul;
24✔
1918
  retv.z = a.z * lenmul;
24✔
1919
  retv.m = a.m * lenmul;
24✔
1920

1921
  return retv;
24✔
1922
}
1923

1924
/* rotate a vector 90 degrees */
1925
static pointObj point_rotz90(const pointObj a) {
1926
  double nx = -1.0 * a.y, ny = a.x;
24✔
1927
  pointObj retv = a;
1928
  retv.x = nx;
1929
  retv.y = ny;
1930
  return retv;
1931
}
1932

1933
/* vector cross product (warning: z and m dimensions are ignored!) */
1934
static double point_cross(const pointObj a, const pointObj b) {
1935
  return a.x * b.y - a.y * b.x;
20✔
1936
}
1937

1938
shapeObj *msOffsetCurve(shapeObj *p, double offset) {
79✔
1939
  shapeObj *ret;
1940
  int i, j, first, idx, ok = 0;
1941
#if defined USE_GEOS && (GEOS_VERSION_MAJOR > 3 ||                             \
1942
                         (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3))
1943
  ret = msGEOSOffsetCurve(p, offset);
79✔
1944
  /* GEOS curve offsetting can fail sometimes, we continue with our own
1945
   implementation if that is the case.*/
1946
  if (ret)
79✔
1947
    return ret;
1948
  /* clear error raised by geos in this case */
1949
  msResetErrorList();
4✔
1950
#endif
1951
  /*
1952
  ** For offset corner point calculation 1/sin() is used
1953
  ** to avoid 1/0 division (and long spikes) we define a
1954
  ** limit for sin().
1955
  */
1956
#define CURVE_SIN_LIMIT 0.3
1957
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
4✔
1958
  msInitShape(ret);
4✔
1959
  ret->numlines = p->numlines;
4✔
1960
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
4✔
1961
  for (i = 0; i < ret->numlines; i++) {
12✔
1962
    ret->line[i].numpoints = p->line[i].numpoints;
8✔
1963
    ret->line[i].point =
8✔
1964
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
8✔
1965
  }
1966
  for (i = 0; i < p->numlines; i++) {
12✔
1967
    pointObj old_diffdir, old_offdir;
1968
    if (p->line[i].numpoints < 2) {
8✔
1969
      ret->line[i].numpoints = 0;
4✔
1970
      continue; /* skip degenerate points */
1971
    }
1972
    ok = 1;
1973
    /* initialize old_offdir and old_diffdir, as gcc isn't smart enough to see
1974
     * that it is not an error to do so, and prints a warning */
1975
    old_offdir.x = old_offdir.y = old_diffdir.x = old_diffdir.y = 0;
1976

1977
    idx = 0;
1978
    first = 1;
1979

1980
    /* saved metrics of the last processed point */
1981
    pointObj old_pt = p->line[i].point[0];
4✔
1982
    for (j = 1; j < p->line[i].numpoints; j++) {
28✔
1983
      const pointObj pt = p->line[i].point[j]; /* place of the point */
24✔
1984
      const pointObj diffdir =
1985
          point_norm(point_diff(pt, old_pt)); /* direction of the line */
24✔
1986
      const pointObj offdir =
1987
          point_rotz90(diffdir); /* direction where the distance between the
1988
                                    line and the offset is measured */
1989
      pointObj offpt; /* this will be the corner point of the offset line */
1990

1991
      /* offset line points computation */
1992
      if (first == 1) { /* first point */
24✔
1993
        first = 0;
1994
        offpt = point_sum(old_pt, point_mul(offdir, offset));
1995
      } else { /* middle points */
1996
        /* curve is the angle of the last and the current line's direction
1997
         * (supplementary angle of the shape's inner angle) */
1998
        double sin_curve = point_cross(diffdir, old_diffdir);
1999
        double cos_curve = point_cross(old_offdir, diffdir);
2000
        if ((-1.0) * CURVE_SIN_LIMIT < sin_curve &&
20✔
2001
            sin_curve < CURVE_SIN_LIMIT) {
2002
          /* do not calculate 1/sin, instead use a corner point approximation:
2003
           * average of the last and current offset direction and length */
2004

2005
          /*
2006
          ** TODO: fair for obtuse inner angles, however, positive and negative
2007
          ** acute inner angles would need special handling - similar to LINECAP
2008
          ** to avoid drawing of long spikes
2009
          */
2010
          offpt = point_sum(
×
2011
              old_pt, point_mul(point_sum(offdir, old_offdir), 0.5 * offset));
2012
        } else {
2013
          double base_shift = -1.0 * (1.0 + cos_curve) / sin_curve;
20✔
2014
          offpt = point_sum(
2015
              old_pt,
2016
              point_mul(point_sum(point_mul(diffdir, base_shift), offdir),
2017
                        offset));
2018
        }
2019
      }
2020
      ret->line[i].point[idx] = offpt;
24✔
2021
      idx++;
24✔
2022
      old_pt = pt;
2023
      old_diffdir = diffdir;
2024
      old_offdir = offdir;
2025
    }
2026

2027
    /* last point */
2028
    if (first == 0) {
4✔
2029
      pointObj offpt = point_sum(old_pt, point_mul(old_offdir, offset));
2030
      ret->line[i].point[idx] = offpt;
4✔
2031
      idx++;
4✔
2032
    }
2033

2034
    if (idx != p->line[i].numpoints) {
4✔
2035
      /* printf("shouldn't happen :(\n"); */
2036
      ret->line[i].numpoints = idx;
×
2037
      ret->line =
×
2038
          msSmallRealloc(ret->line, ret->line[i].numpoints * sizeof(pointObj));
×
2039
    }
2040
  }
2041
  if (!ok)
4✔
2042
    ret->numlines = 0; /* all lines where degenerate */
×
2043
  return ret;
2044
}
2045

2046
shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety) {
208✔
2047
  int i, j;
2048
  shapeObj *ret;
2049
  if (offsety == MS_STYLE_SINGLE_SIDED_OFFSET) { /* complex calculations */
208✔
2050
    return msOffsetCurve(p, offsetx);
55✔
2051
  } else if (offsety == MS_STYLE_DOUBLE_SIDED_OFFSET) {
153✔
2052
    shapeObj *tmp1;
2053
    ret = msOffsetCurve(p, offsetx / 2.0);
12✔
2054
    tmp1 = msOffsetCurve(p, -offsetx / 2.0);
12✔
2055
    for (i = 0; i < tmp1->numlines; i++) {
24✔
2056
      msAddLineDirectly(ret, tmp1->line + i);
12✔
2057
    }
2058
    msFreeShape(tmp1);
12✔
2059
    free(tmp1);
12✔
2060
    return ret;
12✔
2061
  }
2062

2063
  ret = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
141✔
2064
  msInitShape(ret);
141✔
2065
  ret->numlines = p->numlines;
141✔
2066
  ret->line = (lineObj *)msSmallMalloc(sizeof(lineObj) * ret->numlines);
141✔
2067
  for (i = 0; i < ret->numlines; i++) {
290✔
2068
    ret->line[i].numpoints = p->line[i].numpoints;
149✔
2069
    ret->line[i].point =
149✔
2070
        (pointObj *)msSmallMalloc(sizeof(pointObj) * ret->line[i].numpoints);
149✔
2071
  }
2072

2073
  for (i = 0; i < p->numlines; i++) {
290✔
2074
    for (j = 0; j < p->line[i].numpoints; j++) {
1,656✔
2075
      ret->line[i].point[j].x = p->line[i].point[j].x + offsetx;
1,507✔
2076
      ret->line[i].point[j].y = p->line[i].point[j].y + offsety;
1,507✔
2077
    }
2078
  }
2079

2080
  return ret;
2081
}
2082

2083
/*
2084
-------------------------------------------------------------------------------
2085
 msSetup()
2086

2087
 Contributed by Jerry Pisk in bug 1203.  Heads off potential race condition
2088
 in initializing GD font cache with multiple threads.  Should be called from
2089
 mapscript module initialization code.
2090
-------------------------------------------------------------------------------
2091
*/
2092

2093
int msSetup() {
2,608✔
2094
#ifdef _WIN32
2095
  const char *maxfiles = CPLGetConfigOption("MS_MAX_OPEN_FILES", NULL);
2096
  if (maxfiles) {
2097
    int res = _getmaxstdio();
2098
    if (res < 2048) {
2099
      res = _setmaxstdio(atoi(maxfiles));
2100
      assert(res != -1);
2101
    }
2102
  }
2103
#endif
2104

2105
#ifdef USE_THREAD
2106
  msThreadInit();
2,608✔
2107
#endif
2108

2109
  /* Use PROJ_DATA/PROJ_LIB env vars if set */
2110
  msProjDataInitFromEnv();
2,608✔
2111

2112
  /* Use MS_ERRORFILE and MS_DEBUGLEVEL env vars if set */
2113
  if (msDebugInitFromEnv() != MS_SUCCESS)
2,608✔
2114
    return MS_FAILURE;
2115

2116
#ifdef USE_GEOS
2117
  msGEOSSetup();
2,608✔
2118
#endif
2119

2120
#ifdef USE_RSVG
2121
#if !GLIB_CHECK_VERSION(2, 35, 0)
2122
  g_type_init();
2123
#endif
2124
#endif
2125

2126
  msFontCacheSetup();
2,608✔
2127

2128
  return MS_SUCCESS;
2,608✔
2129
}
2130

2131
/* This is intended to be a function to cleanup anything that "hangs around"
2132
   when all maps are destroyed, like Registered GDAL drivers, and so forth. */
2133
#ifndef NDEBUG
2134
#if defined(USE_LIBXML2)
2135
#include "maplibxml2.h"
2136
#endif
2137
#endif
2138
void msCleanup() {
2,597✔
2139
  msForceTmpFileBase(NULL);
2,597✔
2140
  msConnPoolFinalCleanup();
2,597✔
2141
  /* Lexer string parsing variable */
2142
  if (msyystring_buffer != NULL) {
2,597✔
2143
    msFree(msyystring_buffer);
2,593✔
2144
    msyystring_buffer = NULL;
2,593✔
2145
  }
2146
  msyylex_destroy();
2,597✔
2147

2148
  msOGRCleanup();
2,597✔
2149
  msGDALCleanup();
2,597✔
2150

2151
  /* Release both GDAL and OGR resources */
2152
  msAcquireLock(TLOCK_GDAL);
2,597✔
2153
  /* Cleanup some GDAL global resources in particular */
2154
  GDALDestroy();
2,597✔
2155
  msReleaseLock(TLOCK_GDAL);
2,597✔
2156

2157
  msSetPROJ_DATA(NULL, NULL);
2,597✔
2158
  msProjectionContextPoolCleanup();
2,597✔
2159

2160
#if defined(USE_CURL)
2161
  msHTTPCleanup();
2,597✔
2162
#endif
2163

2164
#ifdef USE_GEOS
2165
  msGEOSCleanup();
2,597✔
2166
#endif
2167

2168
/* make valgrind happy on debug code */
2169
#ifndef NDEBUG
2170
#ifdef USE_CAIRO
2171
  msCairoCleanup();
2172
#endif
2173
#if defined(USE_LIBXML2)
2174
  xmlCleanupParser();
2175
#endif
2176
#endif
2177

2178
  msFontCacheCleanup();
2,597✔
2179

2180
  msTimeCleanup();
2,597✔
2181

2182
  msIO_Cleanup();
2,597✔
2183

2184
  msResetErrorList();
2,597✔
2185

2186
  /* Close/cleanup log/debug output. Keep this at the very end. */
2187
  msDebugCleanup();
2,597✔
2188

2189
  /* Clean up the vtable factory */
2190
  msPluginFreeVirtualTableFactory();
2,597✔
2191
}
2,597✔
2192

2193
/************************************************************************/
2194
/*                            msAlphaBlend()                            */
2195
/*                                                                      */
2196
/*      Function to overlay/blend an RGBA value into an existing        */
2197
/*      RGBA value using the Porter-Duff "over" operator.               */
2198
/*      Primarily intended for use with rasterBufferObj                 */
2199
/*      raster rendering.  The "src" is the overlay value, and "dst"    */
2200
/*      is the existing value being overlaid. dst is expected to be     */
2201
/*      premultiplied, but the source should not be.                    */
2202
/*                                                                      */
2203
/*      NOTE: alpha_dst may be NULL.                                    */
2204
/************************************************************************/
2205

2206
void msAlphaBlend(unsigned char red_src, unsigned char green_src,
313,466✔
2207
                  unsigned char blue_src, unsigned char alpha_src,
2208
                  unsigned char *red_dst, unsigned char *green_dst,
2209
                  unsigned char *blue_dst, unsigned char *alpha_dst) {
2210
  /* -------------------------------------------------------------------- */
2211
  /*      Simple cases we want to handle fast.                            */
2212
  /* -------------------------------------------------------------------- */
2213
  if (alpha_src == 0)
313,466✔
2214
    return;
2215

2216
  if (alpha_src == 255) {
313,466✔
2217
    *red_dst = red_src;
151,548✔
2218
    *green_dst = green_src;
151,548✔
2219
    *blue_dst = blue_src;
151,548✔
2220
    if (alpha_dst)
151,548✔
2221
      *alpha_dst = 255;
216✔
2222
    return;
151,548✔
2223
  }
2224

2225
  /* -------------------------------------------------------------------- */
2226
  /*      Premultiple alpha for source values now.                        */
2227
  /* -------------------------------------------------------------------- */
2228
  red_src = red_src * alpha_src / 255;
161,918✔
2229
  green_src = green_src * alpha_src / 255;
161,918✔
2230
  blue_src = blue_src * alpha_src / 255;
161,918✔
2231

2232
  /* -------------------------------------------------------------------- */
2233
  /*      Another pretty fast case if there is nothing in the             */
2234
  /*      destination to mix with.                                        */
2235
  /* -------------------------------------------------------------------- */
2236
  if (alpha_dst && *alpha_dst == 0) {
161,918✔
2237
    *red_dst = red_src;
95,330✔
2238
    *green_dst = green_src;
95,330✔
2239
    *blue_dst = blue_src;
95,330✔
2240
    *alpha_dst = alpha_src;
95,330✔
2241
    return;
95,330✔
2242
  }
2243

2244
  /* -------------------------------------------------------------------- */
2245
  /*      Cases with actual blending.                                     */
2246
  /* -------------------------------------------------------------------- */
2247
  if (!alpha_dst || *alpha_dst == 255) {
19,845✔
2248
    int weight_dst = 256 - alpha_src;
62,619✔
2249

2250
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
62,619✔
2251
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
62,619✔
2252
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
62,619✔
2253
  } else {
2254
    int weight_dst = (256 - alpha_src);
3,969✔
2255

2256
    *red_dst = (256 * red_src + *red_dst * weight_dst) >> 8;
3,969✔
2257
    *green_dst = (256 * green_src + *green_dst * weight_dst) >> 8;
3,969✔
2258
    *blue_dst = (256 * blue_src + *blue_dst * weight_dst) >> 8;
3,969✔
2259

2260
    *alpha_dst = (256 * alpha_src + *alpha_dst * weight_dst) >> 8;
3,969✔
2261
  }
2262
}
2263

2264
/************************************************************************/
2265
/*                           msAlphaBlendPM()                           */
2266
/*                                                                      */
2267
/*      Same as msAlphaBlend() except that the source RGBA is           */
2268
/*      assumed to already be premultiplied.                            */
2269
/************************************************************************/
2270

2271
void msAlphaBlendPM(unsigned char red_src, unsigned char green_src,
73,742✔
2272
                    unsigned char blue_src, unsigned char alpha_src,
2273
                    unsigned char *red_dst, unsigned char *green_dst,
2274
                    unsigned char *blue_dst, unsigned char *alpha_dst) {
2275
  /* -------------------------------------------------------------------- */
2276
  /*      Simple cases we want to handle fast.                            */
2277
  /* -------------------------------------------------------------------- */
2278
  if (alpha_src == 0)
73,742✔
2279
    return;
2280

2281
  if (alpha_src == 255) {
73,712✔
2282
    *red_dst = red_src;
19,981✔
2283
    *green_dst = green_src;
19,981✔
2284
    *blue_dst = blue_src;
19,981✔
2285
    if (alpha_dst)
19,981✔
2286
      *alpha_dst = 255;
19,981✔
2287
    return;
19,981✔
2288
  }
2289

2290
  /* -------------------------------------------------------------------- */
2291
  /*      Another pretty fast case if there is nothing in the             */
2292
  /*      destination to mix with.                                        */
2293
  /* -------------------------------------------------------------------- */
2294
  if (alpha_dst && *alpha_dst == 0) {
53,731✔
2295
    *red_dst = red_src;
15,214✔
2296
    *green_dst = green_src;
15,214✔
2297
    *blue_dst = blue_src;
15,214✔
2298
    *alpha_dst = alpha_src;
15,214✔
2299
    return;
15,214✔
2300
  }
2301

2302
  /* -------------------------------------------------------------------- */
2303
  /*      Cases with actual blending.                                     */
2304
  /* -------------------------------------------------------------------- */
2305
  if (!alpha_dst || *alpha_dst == 255) {
×
2306
    int weight_dst = 255 - alpha_src;
38,517✔
2307

2308
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
38,517✔
2309
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
38,517✔
2310
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
38,517✔
2311
  } else {
2312
    int weight_dst = (255 - alpha_src);
×
2313

2314
    *red_dst = (alpha_src * red_src + *red_dst * weight_dst) >> 8;
×
2315
    *green_dst = (alpha_src * green_src + *green_dst * weight_dst) >> 8;
×
2316
    *blue_dst = (alpha_src * blue_src + *blue_dst * weight_dst) >> 8;
×
2317

2318
    *alpha_dst = (255 * alpha_src + *alpha_dst * weight_dst) >> 8;
×
2319
  }
2320
}
2321

2322
void msRGBtoHSL(colorObj *rgb, double *h, double *s, double *l) {
2,586✔
2323
  double r = rgb->red / 255.0, g = rgb->green / 255.0, b = rgb->blue / 255.0;
2,586✔
2324
  double maxv = MS_MAX(MS_MAX(r, g), b), minv = MS_MIN(MS_MIN(r, g), b);
7,758✔
2325
  double d = maxv - minv;
2,586✔
2326

2327
  *h = 0, *s = 0;
2,586✔
2328
  *l = (maxv + minv) / 2;
2,586✔
2329

2330
  if (maxv != minv) {
2,586✔
2331
    *s = *l > 0.5 ? d / (2 - maxv - minv) : d / (maxv + minv);
2,586✔
2332
    if (maxv == r) {
2,586✔
2333
      *h = (g - b) / d + (g < b ? 6 : 0);
2,226✔
2334
    } else if (maxv == g) {
1,473✔
2335
      *h = (b - r) / d + 2;
283✔
2336
    } else if (maxv == b) {
1,190✔
2337
      *h = (r - g) / d + 4;
1,190✔
2338
    }
2339
    *h /= 6;
2,586✔
2340
  }
2341
}
2,586✔
2342

2343
static double hue_to_rgb(double p, double q, double t) {
3,879✔
2344
  if (t < 0)
3,879✔
2345
    t += 1;
669✔
2346
  if (t > 1)
3,879✔
2347
    t -= 1;
×
2348
  if (t < 0.1666666666666666)
3,879✔
2349
    return p + (q - p) * 6 * t;
585✔
2350
  if (t < 0.5)
3,294✔
2351
    return q;
2352
  if (t < 0.6666666666666666)
2,001✔
2353
    return p + (q - p) * (0.666666666666 - t) * 6;
584✔
2354
  return p;
2355
}
2356

2357
void msHSLtoRGB(double h, double s, double l, colorObj *rgb) {
1,293✔
2358
  double r, g, b;
2359

2360
  if (s == 0) {
1,293✔
2361
    r = g = b = l;
2362
  } else {
2363

2364
    double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1,293✔
2365
    double p = 2 * l - q;
1,293✔
2366
    r = hue_to_rgb(p, q, h + 0.33333333333333333);
1,293✔
2367
    g = hue_to_rgb(p, q, h);
1,293✔
2368
    b = hue_to_rgb(p, q, h - 0.33333333333333333);
1,293✔
2369
  }
2370
  rgb->red = r * 255;
1,293✔
2371
  rgb->green = g * 255;
1,293✔
2372
  rgb->blue = b * 255;
1,293✔
2373
}
1,293✔
2374

2375
/*
2376
 RFC 24: check if the parent pointer is NULL and raise an error otherwise
2377
*/
2378
int msCheckParentPointer(void *p, const char *objname) {
4,319✔
2379
  if (p == NULL) {
4,319✔
2380
    if (objname != NULL) {
×
2381
      msSetError(MS_NULLPARENTERR, "The %s parent object is null",
×
2382
                 "msCheckParentPointer()", objname);
2383
    } else {
2384
      msSetError(MS_NULLPARENTERR, "The parent object is null",
×
2385
                 "msCheckParentPointer()");
2386
    }
2387
    return MS_FAILURE;
×
2388
  }
2389
  return MS_SUCCESS;
2390
}
2391

2392
void msBufferInit(bufferObj *buffer) {
207✔
2393
  buffer->data = NULL;
207✔
2394
  buffer->size = 0;
207✔
2395
  buffer->available = 0;
207✔
2396
  buffer->_next_allocation_size = MS_DEFAULT_BUFFER_ALLOC;
207✔
2397
}
207✔
2398

2399
void msBufferResize(bufferObj *buffer, size_t target_size) {
240✔
2400
  while (buffer->available <= target_size) {
493✔
2401
    buffer->data = msSmallRealloc(
506✔
2402
        buffer->data, buffer->available + buffer->_next_allocation_size);
253✔
2403
    buffer->available += buffer->_next_allocation_size;
253✔
2404
    buffer->_next_allocation_size *= 2;
253✔
2405
  }
2406
}
240✔
2407

2408
void msBufferAppend(bufferObj *buffer, void *data, size_t length) {
13,190✔
2409
  if (buffer->available < buffer->size + length) {
13,190✔
2410
    msBufferResize(buffer, buffer->size + length);
238✔
2411
  }
2412
  memcpy(&(buffer->data[buffer->size]), data, length);
13,190✔
2413
  buffer->size += length;
13,190✔
2414
}
13,190✔
2415

2416
void msBufferFree(bufferObj *buffer) {
192✔
2417
  if (buffer->available > 0)
192✔
2418
    free(buffer->data);
192✔
2419
}
192✔
2420

2421
void msFreeRasterBuffer(rasterBufferObj *b) {
332✔
2422
  switch (b->type) {
332✔
2423
  case MS_BUFFER_BYTE_RGBA:
332✔
2424
    msFree(b->data.rgba.pixels);
332✔
2425
    b->data.rgba.pixels = NULL;
332✔
2426
    break;
332✔
2427
  case MS_BUFFER_BYTE_PALETTE:
×
2428
    msFree(b->data.palette.pixels);
×
2429
    msFree(b->data.palette.palette);
×
2430
    b->data.palette.pixels = NULL;
×
2431
    b->data.palette.palette = NULL;
×
2432
    break;
×
2433
  }
2434
}
332✔
2435

2436
/*
2437
** Issue #3043: Layer extent comparison short circuit.
2438
**
2439
** msExtentsOverlap()
2440
**
2441
** Returns MS_TRUE if map extent and layer extent overlap,
2442
** MS_FALSE if they are disjoint, and MS_UNKNOWN if there is
2443
** not enough info to calculate a deterministic answer.
2444
**
2445
*/
2446
int msExtentsOverlap(mapObj *map, layerObj *layer) {
4,675✔
2447
  rectObj map_extent;
2448
  rectObj layer_extent;
2449

2450
  /* No extent info? Nothing we can do, return MS_UNKNOWN. */
2451
  if ((map->extent.minx == -1) && (map->extent.miny == -1) &&
4,675✔
2452
      (map->extent.maxx == -1) && (map->extent.maxy == -1))
2✔
2453
    return MS_UNKNOWN;
2454
  if ((layer->extent.minx == -1) && (layer->extent.miny == -1) &&
4,675✔
2455
      (layer->extent.maxx == -1) && (layer->extent.maxy == -1))
4,543✔
2456
    return MS_UNKNOWN;
2457

2458
  /* No map projection? Let someone else sort this out. */
2459
  if (!(map->projection.numargs > 0))
132✔
2460
    return MS_UNKNOWN;
2461

2462
  /* No layer projection? Perform naive comparison, because they are
2463
  ** in the same projection. */
2464
  if (!(layer->projection.numargs > 0))
114✔
2465
    return msRectOverlap(&(map->extent), &(layer->extent));
×
2466

2467
  /* In the case where map and layer projections are identical, and the */
2468
  /* bounding boxes don't cross the dateline, do simple rectangle comparison */
2469
  if (map->extent.minx < map->extent.maxx &&
114✔
2470
      layer->extent.minx < layer->extent.maxx &&
228✔
2471
      !msProjectionsDiffer(&(map->projection), &(layer->projection))) {
114✔
2472
    return msRectOverlap(&(map->extent), &(layer->extent));
30✔
2473
  }
2474

2475
  /* We need to transform our rectangles for comparison,
2476
  ** so we will work with copies and leave the originals intact. */
2477
  MS_COPYRECT(&map_extent, &(map->extent));
84✔
2478
  MS_COPYRECT(&layer_extent, &(layer->extent));
84✔
2479

2480
  /* Transform map extents into geographics for comparison. */
2481
  if (msProjectRect(&(map->projection), &(map->latlon), &map_extent))
84✔
2482
    return MS_UNKNOWN;
2483

2484
  /* Transform layer extents into geographics for comparison. */
2485
  if (msProjectRect(&(layer->projection), &(map->latlon), &layer_extent))
84✔
2486
    return MS_UNKNOWN;
2487

2488
  /* Simple case? Return simple answer. */
2489
  if (map_extent.minx < map_extent.maxx &&
84✔
2490
      layer_extent.minx < layer_extent.maxx)
84✔
2491
    return msRectOverlap(&(map_extent), &(layer_extent));
84✔
2492

2493
  /* Uh oh, one of the rects crosses the dateline!
2494
  ** Let someone else handle it. */
2495
  return MS_UNKNOWN;
2496
}
2497

2498
/************************************************************************/
2499
/*                             msSmallMalloc()                          */
2500
/************************************************************************/
2501

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

2504
void *msSmallMalloc(size_t nSize) {
409,634✔
2505
  void *pReturn;
2506

2507
  if (MS_UNLIKELY(nSize == 0))
409,634✔
2508
    return NULL;
2509

2510
  pReturn = malloc(nSize);
409,388✔
2511
  if (MS_UNLIKELY(pReturn == NULL)) {
409,388✔
2512
    msIO_fprintf(stderr,
×
2513
                 "msSmallMalloc(): Out of memory allocating %ld bytes.\n",
2514
                 (long)nSize);
2515
    exit(1);
×
2516
  }
2517

2518
  return pReturn;
2519
}
2520

2521
/************************************************************************/
2522
/*                             msSmallRealloc()                         */
2523
/************************************************************************/
2524

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

2527
void *msSmallRealloc(void *pData, size_t nNewSize) {
21,830✔
2528
  void *pReturn;
2529

2530
  if (MS_UNLIKELY(nNewSize == 0))
21,830✔
2531
    return NULL;
2532

2533
  pReturn = realloc(pData, nNewSize);
21,830✔
2534

2535
  if (MS_UNLIKELY(pReturn == NULL)) {
21,830✔
2536
    msIO_fprintf(stderr,
×
2537
                 "msSmallRealloc(): Out of memory allocating %ld bytes.\n",
2538
                 (long)nNewSize);
2539
    exit(1);
×
2540
  }
2541

2542
  return pReturn;
2543
}
2544

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

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

2551
void *msSmallCalloc(size_t nCount, size_t nSize) {
26,928✔
2552
  void *pReturn;
2553

2554
  if (MS_UNLIKELY(nSize * nCount == 0))
26,928✔
2555
    return NULL;
2556

2557
  pReturn = calloc(nCount, nSize);
24,612✔
2558
  if (MS_UNLIKELY(pReturn == NULL)) {
24,612✔
2559
    msIO_fprintf(stderr,
×
2560
                 "msSmallCalloc(): Out of memory allocating %ld bytes.\n",
2561
                 (long)(nCount * nSize));
2562
    exit(1);
×
2563
  }
2564

2565
  return pReturn;
2566
}
2567

2568
/*
2569
** msBuildOnlineResource()
2570
**
2571
** Try to build the online resource (mapserv URL) for this service.
2572
** "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)$(PATH_INFO)?"
2573
** (+append the map=... param if it was explicitly passed in QUERY_STRING)
2574
**
2575
** Returns a newly allocated string that should be freed by the caller or
2576
** NULL in case of error.
2577
*/
2578
char *msBuildOnlineResource(mapObj *map, cgiRequestObj *req) {
1✔
2579
  (void)map;
2580
  char *online_resource = NULL;
2581
  const char *value, *hostname, *port, *script, *protocol = "http", *pathinfo,
2582
                                                *mapparam = NULL;
2583
  char **hostname_array = NULL;
2584
  int mapparam_len = 0, hostname_array_len = 0;
1✔
2585

2586
  hostname = getenv("HTTP_X_FORWARDED_HOST");
1✔
2587
  if (!hostname)
1✔
2588
    hostname = getenv("SERVER_NAME");
1✔
2589
  else {
2590
    if (strchr(hostname, ',')) {
×
2591
      hostname_array = msStringSplit(hostname, ',', &hostname_array_len);
×
2592
      hostname = hostname_array[0];
×
2593
    }
2594
  }
2595

2596
  port = getenv("HTTP_X_FORWARDED_PORT");
1✔
2597
  if (!port)
1✔
2598
    port = getenv("SERVER_PORT");
1✔
2599

2600
  pathinfo = getenv("PATH_INFO");
1✔
2601
  if (!pathinfo) {
1✔
2602
    pathinfo = "";
2603
  }
2604

2605
  script = getenv("SCRIPT_NAME");
1✔
2606

2607
  /* HTTPS is set by Apache to "on" in an HTTPS server ... if not set */
2608
  /* then check SERVER_PORT: 443 is the default https port. */
2609
  if (((value = getenv("HTTPS")) && strcasecmp(value, "on") == 0) ||
2✔
2610
      ((value = getenv("SERVER_PORT")) && atoi(value) == 443)) {
1✔
2611
    protocol = "https";
2612
  }
2613
  if ((value = getenv("HTTP_X_FORWARDED_PROTO"))) {
1✔
2614
    protocol = value;
2615
  }
2616

2617
  /* If map=.. was explicitly set then we'll include it in onlineresource
2618
   */
2619
  if (req->type == MS_GET_REQUEST) {
1✔
2620
    int i;
2621
    for (i = 0; i < req->NumParams; i++) {
2✔
2622
      if (strcasecmp(req->ParamNames[i], "map") == 0) {
1✔
2623
        mapparam = req->ParamValues[i];
×
2624
        mapparam_len = strlen(mapparam) + 5; /* +5 for "map="+"&" */
×
2625
        break;
×
2626
      }
2627
    }
2628
  }
2629

2630
  if (hostname && port && script) {
1✔
2631
    size_t buffer_size;
2632
    buffer_size = strlen(hostname) + strlen(port) + strlen(pathinfo) +
×
2633
                  strlen(script) + mapparam_len +
×
2634
                  11; /* 11 comes from
2635
                         https://[host]:[port][scriptname][pathinfo]?[map]\0,
2636
                         i.e. "https://:?\0" */
2637
    online_resource = (char *)msSmallMalloc(buffer_size);
×
2638
    if ((atoi(port) == 80 && strcmp(protocol, "http") == 0) ||
×
2639
        (atoi(port) == 443 && strcmp(protocol, "https") == 0))
×
2640
      snprintf(online_resource, buffer_size, "%s://%s%s%s?", protocol, hostname,
2641
               script, pathinfo);
2642
    else
2643
      snprintf(online_resource, buffer_size, "%s://%s:%s%s%s?", protocol,
2644
               hostname, port, script, pathinfo);
2645

2646
    if (mapparam) {
×
2647
      int baselen;
2648
      baselen = strlen(online_resource);
×
2649
      snprintf(online_resource + baselen, buffer_size - baselen, "map=%s&",
×
2650
               mapparam);
2651
    }
2652
  } else {
2653
    msSetError(MS_CGIERR, "Impossible to establish server URL.",
1✔
2654
               "msBuildOnlineResource()");
2655
    return NULL;
1✔
2656
  }
2657
  if (hostname_array) {
×
2658
    msFreeCharArray(hostname_array, hostname_array_len);
×
2659
  }
2660

2661
  return online_resource;
2662
}
2663

2664
/************************************************************************/
2665
/*                             msIntegerInArray()                        */
2666
/************************************************************************/
2667

2668
/* Check if a integer is in a array */
2669
int msIntegerInArray(const int value, int *array, int numelements) {
6,652✔
2670
  int i;
2671
  for (i = 0; i < numelements; ++i) {
26,735✔
2672
    if (value == array[i])
26,659✔
2673
      return MS_TRUE;
2674
  }
2675
  return MS_FALSE;
2676
}
2677

2678
/************************************************************************
2679
 *                            msMapSetProjections                       *
2680
 *                                                                      *
2681
 *   Ensure that all the layers in the map file have a projection       *
2682
 *   by copying the map-level projection to all layers than have no     *
2683
 *   projection.                                                        *
2684
 ************************************************************************/
2685

2686
int msMapSetLayerProjections(mapObj *map) {
269✔
2687

2688
  char *mapProjStr = NULL;
2689
  int i;
2690

2691
  if (map->projection.numargs <= 0) {
269✔
2692
    msSetError(MS_WMSERR,
×
2693
               "Cannot set new SRS on a map that doesn't "
2694
               "have any projection set. Please make sure your mapfile "
2695
               "has a PROJECTION defined at the top level.",
2696
               "msTileSetProjectionst()");
2697
    return (MS_FAILURE);
×
2698
  }
2699

2700
  for (i = 0; i < map->numlayers; i++) {
1,339✔
2701
    layerObj *lp = GET_LAYER(map, i);
1,070✔
2702
    /* This layer is turned on and needs a projection? */
2703
    if (lp->projection.numargs <= 0 && lp->status != MS_OFF &&
1,070✔
2704
        lp->transform == MS_TRUE) {
304✔
2705

2706
      /* Fetch main map projection string only now that we need it */
2707
      if (mapProjStr == NULL)
304✔
2708
        mapProjStr = msGetProjectionString(&(map->projection));
139✔
2709

2710
      /* Set the projection to the map file projection */
2711
      if (msLoadProjectionString(&(lp->projection), mapProjStr) != 0) {
304✔
2712
        msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2713
                   "msMapSetLayerProjections()");
2714
        return (MS_FAILURE);
×
2715
      }
2716
      lp->project = MS_TRUE;
304✔
2717
      if (lp->connection &&
304✔
2718
          IS_THIRDPARTY_LAYER_CONNECTIONTYPE(lp->connectiontype)) {
41✔
2719
        char **reflayers;
2720
        int numreflayers, j;
2721
        reflayers = msStringSplit(lp->connection, ',', &numreflayers);
1✔
2722
        for (j = 0; j < numreflayers; j++) {
2✔
2723
          int *lidx, nlidx;
2724
          /* first check layers referenced by group name */
2725
          lidx = msGetLayersIndexByGroup(map, reflayers[i], &nlidx);
1✔
2726
          if (lidx) {
1✔
2727
            int k;
2728
            for (k = 0; k < nlidx; k++) {
×
2729
              layerObj *glp = GET_LAYER(map, lidx[k]);
×
2730
              if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
×
2731

2732
                /* Set the projection to the map file projection */
2733
                if (msLoadProjectionString(&(glp->projection), mapProjStr) !=
×
2734
                    0) {
2735
                  msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2736
                             "msMapSetLayerProjections()");
2737
                  return (MS_FAILURE);
×
2738
                }
2739
                glp->project = MS_TRUE;
×
2740
              }
2741
            }
2742
            free(lidx);
×
2743
          } else {
2744
            /* group name did not match, check by layer name */
2745
            int layer_idx = msGetLayerIndex(map, lp->connection);
1✔
2746
            layerObj *glp = GET_LAYER(map, layer_idx);
1✔
2747
            if (glp->projection.numargs <= 0 && glp->transform == MS_TRUE) {
1✔
2748

2749
              /* Set the projection to the map file projection */
2750
              if (msLoadProjectionString(&(glp->projection), mapProjStr) != 0) {
1✔
2751
                msSetError(MS_CGIERR, "Unable to set projection on layer.",
×
2752
                           "msMapSetLayerProjections()");
2753
                return (MS_FAILURE);
×
2754
              }
2755
              glp->project = MS_TRUE;
1✔
2756
            }
2757
          }
2758
        }
2759
        msFreeCharArray(reflayers, numreflayers);
1✔
2760
      }
2761
    }
2762
  }
2763
  msFree(mapProjStr);
269✔
2764
  return (MS_SUCCESS);
269✔
2765
}
2766

2767
/************************************************************************
2768
 *                    msMapSetLanguageSpecificConnection                *
2769
 *                                                                      *
2770
 *   Override DATA and CONNECTION of each layer with their specific     *
2771
 *  variant for the specified language.                                 *
2772
 ************************************************************************/
2773

2774
void msMapSetLanguageSpecificConnection(mapObj *map,
67✔
2775
                                        const char *validated_language) {
2776
  int i;
2777
  for (i = 0; i < map->numlayers; i++) {
702✔
2778
    layerObj *layer = GET_LAYER(map, i);
635✔
2779
    if (layer->data)
635✔
2780
      layer->data =
524✔
2781
          msCaseReplaceSubstring(layer->data, "%language%", validated_language);
524✔
2782
    if (layer->connection)
635✔
2783
      layer->connection = msCaseReplaceSubstring(
×
2784
          layer->connection, "%language%", validated_language);
2785
  }
2786
}
67✔
2787

2788
/* Generalize a shape based of the tolerance.
2789
   Ref: http://trac.osgeo.org/gdal/ticket/966 */
2790
shapeObj *msGeneralize(shapeObj *shape, double tolerance) {
12✔
2791
  lineObj newLine = {0, NULL};
12✔
2792
  const double sqTolerance = tolerance * tolerance;
12✔
2793

2794
  shapeObj *newShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
12✔
2795
  msInitShape(newShape);
12✔
2796
  msCopyShape(shape, newShape);
12✔
2797

2798
  if (shape->numlines < 1)
12✔
2799
    return newShape;
2800

2801
  /* Clean shape */
2802
  for (int i = 0; i < newShape->numlines; i++)
24✔
2803
    free(newShape->line[i].point);
12✔
2804
  newShape->numlines = 0;
12✔
2805
  if (newShape->line)
12✔
2806
    free(newShape->line);
12✔
2807

2808
  msAddLine(newShape, &newLine);
12✔
2809

2810
  if (shape->line[0].numpoints == 0) {
12✔
2811
    return newShape;
2812
  }
2813

2814
  msAddPointToLine(&newShape->line[0], &shape->line[0].point[0]);
12✔
2815
  double dX0 = shape->line[0].point[0].x;
12✔
2816
  double dY0 = shape->line[0].point[0].y;
12✔
2817

2818
  for (int i = 1; i < shape->line[0].numpoints; i++) {
80✔
2819
    double dX1 = shape->line[0].point[i].x;
68✔
2820
    double dY1 = shape->line[0].point[i].y;
68✔
2821

2822
    const double dX = dX1 - dX0;
68✔
2823
    const double dY = dY1 - dY0;
68✔
2824
    const double dSqDist = dX * dX + dY * dY;
68✔
2825
    if (i == shape->line[0].numpoints - 1 || dSqDist >= sqTolerance) {
68✔
2826
      pointObj p;
2827
      p.x = dX1;
44✔
2828
      p.y = dY1;
44✔
2829

2830
      /* Keep this point (always keep the last point) */
2831
      msAddPointToLine(&newShape->line[0], &p);
44✔
2832
      dX0 = dX1;
2833
      dY0 = dY1;
2834
    }
2835
  }
2836

2837
  return newShape;
2838
}
2839

2840
void msSetLayerOpacity(layerObj *layer, int opacity) {
1✔
2841
  if (!layer->compositer) {
1✔
2842
    layer->compositer = msSmallMalloc(sizeof(LayerCompositer));
1✔
2843
    initLayerCompositer(layer->compositer);
1✔
2844
  }
2845
  layer->compositer->opacity = opacity;
1✔
2846
}
1✔
2847

2848
void msReplaceFreeableStr(char **ppszStr, char *pszNewStr) {
14,052✔
2849
  msFree(*ppszStr);
14,052✔
2850
  *ppszStr = pszNewStr;
14,052✔
2851
}
14,052✔
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