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

geographika / mapserver / 17709373190

14 Sep 2025 09:32AM UTC coverage: 41.466% (+0.09%) from 41.375%
17709373190

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

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

70.34
/src/maplegend.c
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Legend generation.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 *****************************************************************************/
29

30
#include "mapserver.h"
31

32
#define PSF .8
33
#define VMARGIN 5 /* margin at top and bottom of legend graphic */
34
#define HMARGIN 5 /* margin at left and right of legend graphic */
35

36
static int msDrawGradientSymbol(rendererVTableObj *renderer,
2✔
37
                                imageObj *image_draw, double x_center,
38
                                double y_center, int width, int height,
39
                                styleObj *style) {
40
  unsigned char *r, *g, *b, *a;
41
  symbolObj symbol;
42
  rasterBufferObj *rb;
43
  symbolStyleObj symbolStyle;
44
  int ret;
45

46
  initSymbol(&symbol);
2✔
47
  rb = (rasterBufferObj *)calloc(1, sizeof(rasterBufferObj));
2✔
48
  symbol.pixmap_buffer = rb;
2✔
49
  rb->type = MS_BUFFER_BYTE_RGBA;
2✔
50
  rb->width = width;
2✔
51
  rb->height = height;
2✔
52
  rb->data.rgba.row_step = rb->width * 4;
2✔
53
  rb->data.rgba.pixel_step = 4;
2✔
54
  rb->data.rgba.pixels = (unsigned char *)malloc(sizeof(unsigned char) *
2✔
55
                                                 rb->width * rb->height * 4);
2✔
56
  b = rb->data.rgba.b = &rb->data.rgba.pixels[0];
2✔
57
  g = rb->data.rgba.g = &rb->data.rgba.pixels[1];
2✔
58
  r = rb->data.rgba.r = &rb->data.rgba.pixels[2];
2✔
59
  a = rb->data.rgba.a = &rb->data.rgba.pixels[3];
2✔
60
  for (unsigned j = 0; j < rb->height; j++) {
20✔
61
    for (unsigned i = 0; i < rb->width; i++) {
360✔
62
      msValueToRange(style,
342✔
63
                     style->minvalue + (double)i / rb->width *
342✔
64
                                           (style->maxvalue - style->minvalue),
342✔
65
                     MS_COLORSPACE_RGB);
66
      b[4 * (j * rb->width + i)] = style->color.blue;
342✔
67
      g[4 * (j * rb->width + i)] = style->color.green;
342✔
68
      r[4 * (j * rb->width + i)] = style->color.red;
342✔
69
      if (strncasecmp(image_draw->format->driver, "AGG/", 4) == 0) {
342✔
70
        a[4 * (j * rb->width + i)] = 255; // Apply alpha later
342✔
71
      } else {
72
        a[4 * (j * rb->width + i)] = style->color.alpha;
×
73
      }
74
    }
75
  }
76
  INIT_SYMBOL_STYLE(symbolStyle);
2✔
77
  symbolStyle.color = &style->color;
2✔
78
  ret = renderer->renderPixmapSymbol(image_draw, x_center, y_center, &symbol,
2✔
79
                                     &symbolStyle);
80
  msFreeSymbol(&symbol);
2✔
81
  return ret;
2✔
82
}
83

84
/*
85
 * generic function for drawing a legend icon. (added for bug #2348)
86
 * renderer specific drawing functions shouldn't be called directly, but through
87
 * this function
88
 */
89
int msDrawLegendIcon(mapObj *map, layerObj *lp, classObj *theclass, int width,
317✔
90
                     int height, imageObj *image, int dstX, int dstY,
91
                     int scale_independant, class_hittest *hittest) {
92
  int i, type, hasmarkersymbol, ret = MS_SUCCESS;
93
  double offset;
94
  double polygon_contraction =
95
      0.5; /* used to account for the width of a polygon's outline */
96
  shapeObj box, zigzag;
97
  lineObj box_line, zigzag_line;
98
  pointObj box_point[5], zigzag_point[4];
99
  pointObj marker;
100
  char szPath[MS_MAXPATHLEN];
101
  styleObj outline_style;
102
  imageObj *image_draw = image;
103
  rendererVTableObj *renderer;
104
  outputFormatObj *transFormat = NULL, *altFormat = NULL;
317✔
105
  const char *alternativeFormatString = NULL;
106

107
  if (!MS_RENDERER_PLUGIN(image->format)) {
317✔
108
    msSetError(MS_MISCERR, "unsupported image format", "msDrawLegendIcon()");
×
109
    return MS_FAILURE;
×
110
  }
111

112
  alternativeFormatString = msLayerGetProcessingKey(lp, "RENDERER");
317✔
113
  if (MS_RENDERER_PLUGIN(image_draw->format) &&
317✔
114
      alternativeFormatString != NULL &&
×
115
      (altFormat = msSelectOutputFormat(map, alternativeFormatString))) {
×
116
    msInitializeRendererVTable(altFormat);
×
117

118
    image_draw = msImageCreate(
×
119
        image->width, image->height, altFormat, image->imagepath,
120
        image->imageurl, map->resolution, map->defresolution, &map->imagecolor);
121
    image_draw->map = map;
×
122
    renderer = MS_IMAGE_RENDERER(image_draw);
×
123
  } else {
124
    renderer = MS_IMAGE_RENDERER(image_draw);
317✔
125
    if (lp->compositer && renderer->compositeRasterBuffer) {
317✔
126
      image_draw = msImageCreate(image->width, image->height, image->format,
×
127
                                 image->imagepath, image->imageurl,
128
                                 map->resolution, map->defresolution, NULL);
129
      if (!image_draw) {
×
130
        msSetError(MS_MISCERR,
×
131
                   "Unable to initialize temporary transparent image.",
132
                   "msDrawLegendIcon()");
133
        return (MS_FAILURE);
×
134
      }
135
      image_draw->map = map;
×
136
    }
137
  }
138

139
  if (renderer->supports_clipping && MS_VALID_COLOR(map->legend.outlinecolor)) {
317✔
140
    /* keep GD specific code here for now as it supports clipping */
141
    rectObj clip;
142
    clip.maxx = dstX + width - 1;
×
143
    clip.maxy = dstY + height - 1;
×
144
    clip.minx = dstX;
×
145
    clip.miny = dstY;
×
146
    renderer->setClip(image_draw, clip);
×
147
  }
148

149
  /* if the class has a keyimage, treat it as a point layer
150
   * (the keyimage will be treated there) */
151
  if (theclass->keyimage != NULL) {
317✔
152
    type = MS_LAYER_POINT;
153
  } else {
154
    /* some polygon layers may be better drawn using zigzag if there is no fill
155
     */
156
    type = lp->type;
316✔
157
    if (type == MS_LAYER_POLYGON) {
316✔
158
      type = MS_LAYER_LINE;
159
      for (i = 0; i < theclass->numstyles; i++) {
200✔
160
        if (MS_VALID_COLOR(theclass->styles[i]->color)) { /* there is a fill */
88✔
161
          type = MS_LAYER_POLYGON;
162
        }
163
        if (MS_VALID_COLOR(
88✔
164
                theclass->styles[i]->outlinecolor)) { /* there is an outline */
165
          polygon_contraction =
166
              MS_MAX(polygon_contraction, theclass->styles[i]->width / 2.0);
88✔
167
        }
168
      }
169
    }
170
  }
171

172
  /* initialize the box used for polygons and for outlines */
173
  msInitShape(&box);
317✔
174
  box.line = &box_line;
317✔
175
  box.numlines = 1;
317✔
176
  box.line[0].point = box_point;
317✔
177
  box.line[0].numpoints = 5;
317✔
178
  box.type = MS_SHAPE_POLYGON;
317✔
179

180
  box.line[0].point[0].x = dstX + polygon_contraction;
317✔
181
  box.line[0].point[0].y = dstY + polygon_contraction;
317✔
182
  box.line[0].point[1].x = dstX + width - polygon_contraction;
317✔
183
  box.line[0].point[1].y = dstY + polygon_contraction;
317✔
184
  box.line[0].point[2].x = dstX + width - polygon_contraction;
317✔
185
  box.line[0].point[2].y = dstY + height - polygon_contraction;
317✔
186
  box.line[0].point[3].x = dstX + polygon_contraction;
317✔
187
  box.line[0].point[3].y = dstY + height - polygon_contraction;
317✔
188
  box.line[0].point[4].x = box.line[0].point[0].x;
317✔
189
  box.line[0].point[4].y = box.line[0].point[0].y;
317✔
190

191
  /*
192
  ** now draw the appropriate color/symbol/size combination
193
  */
194

195
  /* Scalefactor will be infinity when SIZEUNITS is set in LAYER */
196
  if (lp->sizeunits != MS_PIXELS) {
317✔
197
    lp->scalefactor = 1.0;
16✔
198
  }
199

200
  switch (type) {
317✔
201
  case MS_LAYER_POINT:
65✔
202
    marker.x = dstX + MS_NINT(width / 2.0);
65✔
203
    marker.y = dstY + MS_NINT(height / 2.0);
65✔
204
    if (theclass->keyimage != NULL) {
65✔
205
      int symbolNum;
206
      styleObj imgStyle;
207
      symbolObj *symbol = NULL;
208
      symbolNum =
209
          msAddImageSymbol(&(map->symbolset), msBuildPath(szPath, map->mappath,
1✔
210
                                                          theclass->keyimage));
211
      if (symbolNum == -1) {
1✔
212
        msSetError(MS_IMGERR, "Failed to open legend key image",
×
213
                   "msCreateLegendIcon()");
214
        return (MS_FAILURE);
×
215
      }
216

217
      symbol = map->symbolset.symbol[symbolNum];
1✔
218

219
      initStyle(&imgStyle);
1✔
220
      /*set size so that symbol will be scaled properly #3296*/
221
      if (width / symbol->sizex < height / symbol->sizey)
1✔
222
        imgStyle.size = symbol->sizey * (width / symbol->sizex);
×
223
      else
224
        imgStyle.size = symbol->sizey * (height / symbol->sizey);
1✔
225

226
      if (imgStyle.size > imgStyle.maxsize)
1✔
227
        imgStyle.maxsize = imgStyle.size;
×
228

229
      imgStyle.symbol = symbolNum;
1✔
230
      ret = msDrawMarkerSymbol(map, image_draw, &marker, &imgStyle, 1.0);
1✔
231
      if (MS_UNLIKELY(ret == MS_FAILURE))
1✔
232
        goto legend_icon_cleanup;
×
233
      /* TO DO: we may want to handle this differently depending on the relative
234
       * size of the keyimage */
235
    } else {
236
      for (i = 0; i < theclass->numstyles; i++) {
101✔
237
        if (!scale_independant && map->scaledenom > 0) {
37✔
238
          styleObj *lp = theclass->styles[i];
24✔
239
          if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
24✔
240
            continue;
×
241
          if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
24✔
242
            continue;
×
243
        }
244
        if (hittest && hittest->stylehits[i].status == 0)
37✔
245
          continue;
×
246
        ret = msDrawMarkerSymbol(map, image_draw, &marker, theclass->styles[i],
37✔
247
                                 lp->scalefactor * image->resolutionfactor);
37✔
248
        if (MS_UNLIKELY(ret == MS_FAILURE))
37✔
249
          goto legend_icon_cleanup;
×
250
      }
251
    }
252
    break;
253
  case MS_LAYER_LINE:
162✔
254
    offset = 1;
255
    /* To set the offset, we only check the size/width parameter of the first
256
     * style */
257
    if (theclass->numstyles > 0) {
162✔
258
      if (theclass->styles[0]->symbol > 0 &&
114✔
259
          theclass->styles[0]->symbol < map->symbolset.numsymbols &&
×
260
          map->symbolset.symbol[theclass->styles[0]->symbol]->type !=
×
261
              MS_SYMBOL_SIMPLE)
262
        offset = theclass->styles[0]->size / 2;
×
263
      else
264
        offset = theclass->styles[0]->width / 2;
114✔
265
    }
266
    msInitShape(&zigzag);
162✔
267
    zigzag.line = &zigzag_line;
162✔
268
    zigzag.numlines = 1;
162✔
269
    zigzag.line[0].point = zigzag_point;
162✔
270
    zigzag.line[0].numpoints = 4;
162✔
271
    zigzag.type = MS_SHAPE_LINE;
162✔
272

273
    zigzag.line[0].point[0].x = dstX + offset;
162✔
274
    zigzag.line[0].point[0].y = dstY + height - offset;
162✔
275
    zigzag.line[0].point[1].x = dstX + MS_NINT(width / 3.0) - 1;
162✔
276
    zigzag.line[0].point[1].y = dstY + offset;
162✔
277
    zigzag.line[0].point[2].x = dstX + MS_NINT(2.0 * width / 3.0) - 1;
162✔
278
    zigzag.line[0].point[2].y = dstY + height - offset;
162✔
279
    zigzag.line[0].point[3].x = dstX + width - offset;
162✔
280
    zigzag.line[0].point[3].y = dstY + offset;
162✔
281

282
    for (i = 0; i < theclass->numstyles; i++) {
276✔
283
      if (!scale_independant && map->scaledenom > 0) {
114✔
284
        styleObj *lp = theclass->styles[i];
41✔
285
        if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
41✔
286
          continue;
×
287
        if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
41✔
288
          continue;
×
289
      }
290
      if (hittest && hittest->stylehits[i].status == 0)
114✔
291
        continue;
×
292
      if (theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE ||
114✔
293
          theclass->styles[i]->_geomtransform.type ==
294
              MS_GEOMTRANSFORM_LABELPOINT ||
×
295
          theclass->styles[i]->_geomtransform.type ==
296
              MS_GEOMTRANSFORM_LABELPOLY) {
297
        if (theclass->styles[i]->outlinewidth > 0) {
114✔
298
          /* Swap the style contents to render the outline first,
299
           * and then restore the style to render the interior of the line
300
           */
301
          msOutlineRenderingPrepareStyle(theclass->styles[i], map, lp, image);
12✔
302
          ret =
303
              msDrawLineSymbol(map, image_draw, &zigzag, theclass->styles[i],
12✔
304
                               lp->scalefactor * image_draw->resolutionfactor);
12✔
305
          msOutlineRenderingRestoreStyle(theclass->styles[i], map, lp, image);
12✔
306
          if (MS_UNLIKELY(ret == MS_FAILURE))
12✔
307
            goto legend_icon_cleanup;
×
308
        }
309
        ret = msDrawLineSymbol(map, image_draw, &zigzag, theclass->styles[i],
114✔
310
                               lp->scalefactor * image_draw->resolutionfactor);
114✔
311
        if (MS_UNLIKELY(ret == MS_FAILURE))
114✔
312
          goto legend_icon_cleanup;
×
313
      } else {
314
        if (theclass->styles[i]->outlinewidth > 0) {
×
315
          /* Swap the style contents to render the outline first,
316
           * and then restore the style to render the interior of the line
317
           */
318
          msOutlineRenderingPrepareStyle(theclass->styles[i], map, lp, image);
×
319
          ret = msDrawTransformedShape(
×
320
              map, image_draw, &zigzag, theclass->styles[i],
×
321
              lp->scalefactor * image_draw->resolutionfactor);
×
322
          msOutlineRenderingRestoreStyle(theclass->styles[i], map, lp, image);
×
323
          if (MS_UNLIKELY(ret == MS_FAILURE))
×
324
            goto legend_icon_cleanup;
×
325
        }
326
        ret = msDrawTransformedShape(
×
327
            map, image_draw, &zigzag, theclass->styles[i],
×
328
            lp->scalefactor * image_draw->resolutionfactor);
×
329
        if (MS_UNLIKELY(ret == MS_FAILURE))
×
330
          goto legend_icon_cleanup;
×
331
      }
332
    }
333

334
    break;
335
  case MS_LAYER_CIRCLE:
336
  case MS_LAYER_RASTER:
337
  case MS_LAYER_CHART:
338
  case MS_LAYER_POLYGON:
339
    for (i = 0; i < theclass->numstyles; i++) {
180✔
340
      if (!scale_independant && map->scaledenom > 0) {
90✔
341
        styleObj *lp = theclass->styles[i];
73✔
342
        if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
73✔
343
          continue;
×
344
        if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
73✔
345
          continue;
×
346
      }
347
      if (hittest && hittest->stylehits[i].status == 0)
90✔
348
        continue;
×
349
      if (theclass->styles[i]->_geomtransform.type == MS_GEOMTRANSFORM_NONE ||
90✔
350
          theclass->styles[i]->_geomtransform.type ==
351
              MS_GEOMTRANSFORM_LABELPOINT ||
×
352
          theclass->styles[i]->_geomtransform.type ==
353
              MS_GEOMTRANSFORM_LABELPOLY) {
354

355
        if (MS_VALID_COLOR(theclass->styles[i]->mincolor)) {
90✔
356
          ret = msDrawGradientSymbol(
2✔
357
              renderer, image_draw, dstX + width / 2., dstY + height / 2.0,
2✔
358
              width - 2 * polygon_contraction, height - 2 * polygon_contraction,
2✔
359
              theclass->styles[i]);
360
        } else {
361
          ret =
362
              msDrawShadeSymbol(map, image_draw, &box, theclass->styles[i],
88✔
363
                                lp->scalefactor * image_draw->resolutionfactor);
88✔
364
        }
365
        if (MS_UNLIKELY(ret == MS_FAILURE))
90✔
366
          goto legend_icon_cleanup;
×
367
      } else {
368
        ret = msDrawTransformedShape(map, image_draw, &box, theclass->styles[i],
×
369
                                     lp->scalefactor);
370
        if (MS_UNLIKELY(ret == MS_FAILURE))
×
371
          goto legend_icon_cleanup;
×
372
      }
373
    }
374
    break;
375
  default:
376
    return MS_FAILURE;
377
    break;
378
  } /* end symbol drawing */
379

380
  /* handle label styles */
381
  for (i = 0; i < theclass->numlabels; i++) {
467✔
382
    labelObj *l = theclass->labels[i];
150✔
383
    if (!scale_independant && map->scaledenom > 0) {
150✔
384
      if (msScaleInBounds(map->scaledenom, l->minscaledenom,
144✔
385
                          l->maxscaledenom)) {
386
        int j;
387
        for (j = 0; j < l->numstyles; j++) {
216✔
388
          styleObj *s = l->styles[j];
72✔
389
          marker.x = dstX + MS_NINT(width / 2.0);
72✔
390
          marker.y = dstY + MS_NINT(height / 2.0);
72✔
391
          if (s->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
72✔
392
            ret = msDrawMarkerSymbol(map, image_draw, &marker, s,
72✔
393
                                     lp->scalefactor);
394
            if (MS_UNLIKELY(ret == MS_FAILURE))
72✔
395
              goto legend_icon_cleanup;
×
396
          }
397
        }
398
      }
399
    }
400
  }
401

402
  /* handle "pure" text layers, i.e. layers with no symbology */
403
  hasmarkersymbol = 0;
404
  if (theclass->numstyles == 0) {
317✔
405
    for (i = 0; i < theclass->numlabels; i++) {
150✔
406
      labelObj *l = theclass->labels[i];
75✔
407
      if (!scale_independant && map->scaledenom > 0) {
75✔
408
        if (msScaleInBounds(map->scaledenom, l->minscaledenom,
72✔
409
                            l->maxscaledenom)) {
410
          int j;
411
          for (j = 0; j < l->numstyles; j++) {
108✔
412
            styleObj *s = l->styles[j];
36✔
413
            if (s->_geomtransform.type == MS_GEOMTRANSFORM_LABELPOINT) {
36✔
414
              hasmarkersymbol = 1;
415
            }
416
          }
417
        }
418
      }
419
    }
420
  } else {
421
    hasmarkersymbol = 1;
422
  }
423

424
  if (!hasmarkersymbol && theclass->numlabels > 0) {
75✔
425
    textSymbolObj ts;
426
    pointObj textstartpt;
427
    marker.x = dstX + MS_NINT(width / 2.0);
39✔
428
    marker.y = dstY + MS_NINT(height / 2.0);
39✔
429
    initTextSymbol(&ts);
39✔
430
    msPopulateTextSymbolForLabelAndString(
78✔
431
        &ts, theclass->labels[0], msStrdup("Az"),
39✔
432
        lp->scalefactor * image_draw->resolutionfactor,
39✔
433
        image_draw->resolutionfactor, duplicate_always);
434
    ts.label->size = height - 1;
39✔
435
    ts.rotation = 0;
39✔
436
    ret = msComputeTextPath(map, &ts);
39✔
437
    if (MS_UNLIKELY(ret == MS_FAILURE))
39✔
438
      goto legend_icon_cleanup;
×
439
    textstartpt = get_metrics(&marker, MS_CC, ts.textpath, 0, 0, 0, 0, NULL);
39✔
440
    ret = msDrawTextSymbol(map, image_draw, textstartpt, &ts);
39✔
441
    freeTextSymbol(&ts);
39✔
442
    if (MS_UNLIKELY(ret == MS_FAILURE))
39✔
443
      goto legend_icon_cleanup;
×
444
  }
445

446
  /* handle an outline if necessary */
447
  if (MS_VALID_COLOR(map->legend.outlinecolor)) {
317✔
448
    initStyle(&outline_style);
×
449
    outline_style.color = map->legend.outlinecolor;
×
450
    ret = msDrawLineSymbol(map, image_draw, &box, &outline_style,
×
451
                           image_draw->resolutionfactor);
452
    if (MS_UNLIKELY(ret == MS_FAILURE))
×
453
      goto legend_icon_cleanup;
×
454
    /* reset clipping rectangle */
455
    if (renderer->supports_clipping)
×
456
      renderer->resetClip(image_draw);
×
457
  }
458

459
  if (altFormat) {
317✔
460
    rendererVTableObj *renderer = MS_IMAGE_RENDERER(image);
×
461
    rendererVTableObj *altrenderer = MS_IMAGE_RENDERER(image_draw);
×
462
    rasterBufferObj rb;
463
    memset(&rb, 0, sizeof(rasterBufferObj));
464

465
    ret = altrenderer->getRasterBufferHandle(image_draw, &rb);
×
466
    if (MS_UNLIKELY(ret == MS_FAILURE))
×
467
      goto legend_icon_cleanup;
×
468
    ret = renderer->mergeRasterBuffer(
×
469
        image, &rb, ((lp->compositer) ? lp->compositer->opacity * 0.01 : 1.0),
×
470
        0, 0, 0, 0, rb.width, rb.height);
×
471
    if (MS_UNLIKELY(ret == MS_FAILURE))
×
472
      goto legend_icon_cleanup;
×
473
    /*
474
     * hack to work around bug #3834: if we have use an alternate renderer, the
475
     * symbolset may contain symbols that reference it. We want to remove those
476
     * references before the altFormat is destroyed to avoid a segfault and/or a
477
     * leak, and so the the main renderer doesn't pick the cache up thinking
478
     * it's for him.
479
     */
480
    for (i = 0; i < map->symbolset.numsymbols; i++) {
×
481
      if (map->symbolset.symbol[i] != NULL) {
×
482
        symbolObj *s = map->symbolset.symbol[i];
483
        if (s->renderer == altrenderer) {
×
484
          altrenderer->freeSymbol(s);
×
485
          s->renderer = NULL;
×
486
        }
487
      }
488
    }
489

490
  } else if (image != image_draw) {
317✔
491
    rendererVTableObj *renderer = MS_IMAGE_RENDERER(image_draw);
×
492
    rasterBufferObj rb;
493
    memset(&rb, 0, sizeof(rasterBufferObj));
494

495
    ret = renderer->getRasterBufferHandle(image_draw, &rb);
×
496
    if (MS_UNLIKELY(ret == MS_FAILURE))
×
497
      goto legend_icon_cleanup;
×
498
    ret = renderer->mergeRasterBuffer(
×
499
        image, &rb, ((lp->compositer) ? lp->compositer->opacity * 0.01 : 1.0),
×
500
        0, 0, 0, 0, rb.width, rb.height);
×
501
    if (MS_UNLIKELY(ret == MS_FAILURE))
×
502
      goto legend_icon_cleanup;
×
503

504
    /* deref and possibly free temporary transparent output format.  */
505
    msApplyOutputFormat(&transFormat, NULL, MS_NOOVERRIDE);
×
506
  }
507

508
legend_icon_cleanup:
317✔
509
  if (image != image_draw) {
317✔
510
    msFreeImage(image_draw);
×
511
  }
512
  return ret;
513
}
514

515
imageObj *msCreateLegendIcon(mapObj *map, layerObj *lp, classObj *class,
6✔
516
                             int width, int height, int scale_independant) {
517
  imageObj *image;
518
  outputFormatObj *format = NULL;
6✔
519

520
  rendererVTableObj *renderer = MS_MAP_RENDERER(map);
6✔
521

522
  if (!renderer) {
6✔
523
    msSetError(MS_MISCERR, "invalid map outputformat", "msCreateLegendIcon()");
×
524
    return (NULL);
×
525
  }
526

527
  /* ensure we have an image format representing the options for the legend */
528
  msApplyOutputFormat(&format, map->outputformat, map->legend.transparent);
6✔
529

530
  image = msImageCreate(width, height, format, map->web.imagepath,
6✔
531
                        map->web.imageurl, map->resolution, map->defresolution,
532
                        &(map->legend.imagecolor));
533

534
  /* drop this reference to output format */
535
  msApplyOutputFormat(&format, NULL, MS_NOOVERRIDE);
6✔
536

537
  if (image == NULL) {
6✔
538
    msSetError(MS_IMGERR, "Unable to initialize image.",
×
539
               "msCreateLegendIcon()");
540
    return (NULL);
×
541
  }
542
  image->map = map;
6✔
543

544
  /* Call drawLegendIcon with destination (0, 0) */
545
  /* Return an empty image if lp==NULL || class=NULL  */
546
  /* (If class is NULL draw the legend for all classes. Modifications done */
547
  /* Fev 2004 by AY) */
548
  if (lp) {
6✔
549
    if (class) {
6✔
550
      if (MS_UNLIKELY(MS_FAILURE ==
6✔
551
                      msDrawLegendIcon(map, lp, class, width, height, image, 0,
552
                                       0, scale_independant, NULL))) {
553
        msFreeImage(image);
×
554
        return NULL;
×
555
      }
556
    } else {
557
      for (int i = 0; i < lp->numclasses; i++) {
×
558
        if (MS_UNLIKELY(MS_FAILURE == msDrawLegendIcon(map, lp, lp->class[i],
×
559
                                                       width, height, image, 0,
560
                                                       0, scale_independant,
561
                                                       NULL))) {
562
          msFreeImage(image);
×
563
          return NULL;
×
564
        }
565
      }
566
    }
567
  }
568
  return image;
569
}
570

571
/*
572
 * Calculates the optimal size for the legend. If the optional layerObj
573
 * argument is given, the legend size will be calculated for only that
574
 * layer. Otherwise, the legend size is calculated for all layers that
575
 * are not MS_OFF or of MS_LAYER_QUERY type.
576
 *
577
 * Returns one of:
578
 *   MS_SUCCESS
579
 *   MS_FAILURE
580
 */
581
int msLegendCalcSize(mapObj *map, int scale_independent, int *size_x,
227✔
582
                     int *size_y, int *layer_index, int num_layers,
583
                     map_hittest *hittest, double resolutionfactor) {
584
  int i, j;
585
  int status, maxwidth = 0, nLegendItems = 0;
586
  char *text;
587
  layerObj *lp;
588
  rectObj rect;
589
  int current_layers = 0;
590

591
  /* reset sizes */
592
  *size_x = 0;
227✔
593
  *size_y = 0;
227✔
594

595
  /* enable scale-dependent calculations */
596
  if (!scale_independent) {
227✔
597
    map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height);
31✔
598
    status = msCalculateScale(map->extent, map->units, map->width, map->height,
31✔
599
                              map->resolution, &map->scaledenom);
600
    if (status != MS_SUCCESS)
31✔
601
      return MS_FAILURE;
602
  }
603

604
  /*
605
   * step through all map classes, and for each one that will be displayed
606
   * calculate the label size
607
   */
608
  if (layer_index != NULL && num_layers > 0)
227✔
609
    current_layers = num_layers;
610
  else
611
    current_layers = map->numlayers;
73✔
612

613
  for (i = 0; i < current_layers; i++) {
836✔
614
    int layerindex;
615
    if (layer_index != NULL && num_layers > 0)
609✔
616
      layerindex = layer_index[i];
250✔
617
    else
618
      layerindex = map->layerorder[i];
359✔
619

620
    lp = (GET_LAYER(map, layerindex));
609✔
621

622
    if ((lp->status == MS_OFF && (layer_index == NULL || num_layers <= 0)) ||
609✔
623
        (lp->type == MS_LAYER_QUERY)) /* skip it */
383✔
624
      continue;
226✔
625

626
    if (hittest && hittest->layerhits[layerindex].status == 0)
383✔
627
      continue;
1✔
628

629
    if (!scale_independent && map->scaledenom > 0) {
382✔
630
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
55✔
631
        continue;
×
632
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
55✔
633
        continue;
×
634
    }
635

636
    for (j = lp->numclasses - 1; j >= 0; j--) {
1,167✔
637
      textSymbolObj ts;
638
      text = lp->class[j]->title
785✔
639
                 ? lp->class[j]->title
640
                 : lp->class[j]->name; /* point to the right legend text, title
785✔
641
                                          takes precedence */
642
      if (!text)
785✔
643
        continue; /* skip it */
223✔
644

645
      /* skip the class if the classgroup is defined */
646
      if (lp->classgroup &&
784✔
647
          (lp->class[j] -> group == NULL || strcasecmp(lp->class[j] -> group,
436✔
648
                                                       lp -> classgroup) != 0))
649
        continue;
202✔
650

651
      /* verify class scale */
652
      if (!scale_independent && map->scaledenom > 0) {
582✔
653
        if ((lp->class[j] -> maxscaledenom > 0) &&
213✔
654
            (map->scaledenom > lp->class[j] -> maxscaledenom))
655
          continue;
×
656
        if ((lp->class[j] -> minscaledenom > 0) &&
213✔
657
            (map->scaledenom <= lp->class[j] -> minscaledenom))
658
          continue;
×
659
      }
660
      if (hittest && hittest->layerhits[layerindex].classhits[j].status == 0)
582✔
661
        continue;
20✔
662

663
      if (*text) {
562✔
664
        initTextSymbol(&ts);
560✔
665
        msPopulateTextSymbolForLabelAndString(&ts, &map->legend.label,
560✔
666
                                              msStrdup(text), resolutionfactor,
667
                                              resolutionfactor, 0);
668
        if (MS_UNLIKELY(MS_FAILURE == msGetTextSymbolSize(map, &ts, &rect))) {
560✔
669
          freeTextSymbol(&ts);
×
670
          return MS_FAILURE;
×
671
        }
672
        freeTextSymbol(&ts);
560✔
673

674
        maxwidth = MS_MAX(maxwidth, MS_NINT(rect.maxx - rect.minx));
560✔
675
        *size_y += MS_MAX(MS_NINT(rect.maxy - rect.miny), map->legend.keysizey);
560✔
676
      } else {
677
        *size_y += map->legend.keysizey;
2✔
678
      }
679
      nLegendItems++;
562✔
680
    }
681
  }
682

683
  /* Calculate the size of the legend: */
684
  /*   - account for the Y keyspacing */
685
  *size_y += (2 * VMARGIN) + ((nLegendItems - 1) * map->legend.keyspacingy);
227✔
686
  /*   - determine the legend width */
687
  *size_x =
227✔
688
      (2 * HMARGIN) + maxwidth + map->legend.keyspacingx + map->legend.keysizex;
227✔
689

690
  if (*size_y <= 0 || *size_x <= 0)
227✔
691
    return MS_FAILURE;
692

693
  return MS_SUCCESS;
694
}
695

696
/*
697
** Creates a GD image of a legend for a specific map. msDrawLegend()
698
** respects the current scale, and classes without a name are not
699
** added to the legend.
700
**
701
** scale_independent is used for WMS GetLegendGraphic. It should be set to
702
** MS_FALSE in most cases. If it is set to MS_TRUE then the layers' minscale
703
** and maxscale are ignored and layers that are currently out of scale still
704
** show up in the legend.
705
*/
706
imageObj *msDrawLegend(mapObj *map, int scale_independent,
73✔
707
                       map_hittest *hittest) {
708
  int i, j, ret = MS_SUCCESS; /* loop counters */
709
  pointObj pnt;
710
  int size_x, size_y = 0;
73✔
711
  layerObj *lp;
712
  rectObj rect;
713
  imageObj *image = NULL;
714
  outputFormatObj *format = NULL;
73✔
715
  char *text;
716

717
  struct legend_struct {
718
    int height;
719
    textSymbolObj ts;
720
    int layerindex, classindex;
721
    struct legend_struct *pred;
722
  };
723
  typedef struct legend_struct legendlabel;
724
  legendlabel *head = NULL;
725

726
  if (!MS_RENDERER_PLUGIN(map->outputformat)) {
73✔
727
    msSetError(MS_MISCERR, "unsupported output format", "msDrawLegend()");
×
728
    return NULL;
×
729
  }
730
  if (msValidateContexts(map) != MS_SUCCESS)
73✔
731
    return NULL; /* make sure there are no recursive REQUIRES or LABELREQUIRES
732
                    expressions */
733
  if (msLegendCalcSize(map, scale_independent, &size_x, &size_y, NULL, 0,
73✔
734
                       hittest,
735
                       map->resolution / map->defresolution) != MS_SUCCESS)
73✔
736
    return NULL;
737

738
  /*
739
   * step through all map classes, and for each one that will be displayed
740
   * keep a reference to its label size and text
741
   */
742

743
  for (i = 0; i < map->numlayers; i++) {
432✔
744
    lp = (GET_LAYER(map, map->layerorder[i]));
359✔
745

746
    if ((lp->status == MS_OFF) || (lp->type == MS_LAYER_QUERY)) /* skip it */
359✔
747
      continue;
226✔
748

749
    if (hittest && hittest->layerhits[map->layerorder[i]].status == 0)
133✔
750
      continue;
1✔
751

752
    if (!scale_independent && map->scaledenom > 0) {
132✔
753
      if ((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom))
55✔
754
        continue;
×
755
      if ((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom))
55✔
756
        continue;
×
757
    }
758

759
    if (!scale_independent && lp->maxscaledenom <= 0 &&
55✔
760
        lp->minscaledenom <= 0) {
55✔
761
      if ((lp->maxgeowidth > 0) &&
55✔
762
          ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth))
×
763
        continue;
×
764
      if ((lp->mingeowidth > 0) &&
55✔
765
          ((map->extent.maxx - map->extent.minx) < lp->mingeowidth))
×
766
        continue;
×
767
    }
768

769
    /* set the scale factor so that scale dependent symbols are drawn in the
770
     * legend with their default size */
771
    if (lp->sizeunits != MS_PIXELS) {
132✔
772
      map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height);
4✔
773
      lp->scalefactor =
4✔
774
          (msInchesPerUnit(lp->sizeunits, 0) / msInchesPerUnit(map->units, 0)) /
4✔
775
          map->cellsize;
4✔
776
    }
777

778
    for (j = lp->numclasses - 1; j >= 0; j--) {
498✔
779
      text = lp->class[j]->title
366✔
780
                 ? lp->class[j]->title
781
                 : lp->class[j]->name; /* point to the right legend text, title
366✔
782
                                          takes precedence */
783
      if (!text)
366✔
784
        continue; /* skip it */
1✔
785

786
      /* skip the class if the classgroup is defined */
787
      if (lp->classgroup &&
365✔
788
          (lp->class[j] -> group == NULL || strcasecmp(lp->class[j] -> group,
82✔
789
                                                       lp -> classgroup) != 0))
790
        continue;
34✔
791

792
      if (!scale_independent &&
331✔
793
          map->scaledenom > 0) { /* verify class scale here */
213✔
794
        if ((lp->class[j] -> maxscaledenom > 0) &&
213✔
795
            (map->scaledenom > lp->class[j] -> maxscaledenom))
796
          continue;
×
797
        if ((lp->class[j] -> minscaledenom > 0) &&
213✔
798
            (map->scaledenom <= lp->class[j] -> minscaledenom))
799
          continue;
×
800
      }
801

802
      if (hittest &&
331✔
803
          hittest->layerhits[map->layerorder[i]].classhits[j].status == 0) {
54✔
804
        continue;
20✔
805
      }
806

807
      legendlabel *cur = (legendlabel *)msSmallMalloc(sizeof(legendlabel));
311✔
808
      initTextSymbol(&cur->ts);
311✔
809
      if (*text) {
311✔
810
        msPopulateTextSymbolForLabelAndString(
309✔
811
            &cur->ts, &map->legend.label, msStrdup(text),
812
            map->resolution / map->defresolution,
813
            map->resolution / map->defresolution, 0);
309✔
814
        if (MS_UNLIKELY(MS_FAILURE == msComputeTextPath(map, &cur->ts))) {
309✔
815
          ret = MS_FAILURE;
816
          goto cleanup;
×
817
        }
818
        if (MS_UNLIKELY(MS_FAILURE ==
309✔
819
                        msGetTextSymbolSize(map, &cur->ts, &rect))) {
820
          ret = MS_FAILURE;
821
          goto cleanup;
×
822
        }
823
        cur->height =
309✔
824
            MS_MAX(MS_NINT(rect.maxy - rect.miny), map->legend.keysizey);
309✔
825
      } else {
826
        cur->height = map->legend.keysizey;
2✔
827
      }
828

829
      cur->classindex = j;
311✔
830
      cur->layerindex = map->layerorder[i];
311✔
831
      cur->pred = head;
311✔
832
      head = cur;
833
    }
834
  }
835

836
  /* ensure we have an image format representing the options for the legend. */
837
  msApplyOutputFormat(&format, map->outputformat, map->legend.transparent);
73✔
838

839
  /* initialize the legend image */
840
  image = msImageCreate(size_x, size_y, format, map->web.imagepath,
73✔
841
                        map->web.imageurl, map->resolution, map->defresolution,
842
                        &map->legend.imagecolor);
843
  if (!image) {
73✔
844
    msSetError(MS_MISCERR, "Unable to initialize image.", "msDrawLegend()");
×
845
    return NULL;
×
846
  }
847
  image->map = map;
73✔
848

849
  /* image =
850
   * renderer->createImage(size_x,size_y,format,&(map->legend.imagecolor)); */
851

852
  /* drop this reference to output format */
853
  msApplyOutputFormat(&format, NULL, MS_NOOVERRIDE);
73✔
854

855
  pnt.y = VMARGIN;
856
  pnt.x = HMARGIN + map->legend.keysizex + map->legend.keyspacingx;
73✔
857

858
  while (head) { /* head initially points on the last legend item, i.e. the one
384✔
859
                    that should be at the top */
860
    legendlabel *cur = head;
861
    class_hittest *ch = NULL;
862

863
    /* set the scale factor so that scale dependent symbols are drawn in the
864
     * legend with their default size */
865
    if (map->layers[cur->layerindex]->sizeunits != MS_PIXELS) {
311✔
866
      map->cellsize = msAdjustExtent(&(map->extent), map->width, map->height);
16✔
867
      map->layers[cur->layerindex]->scalefactor =
16✔
868
          (msInchesPerUnit(map->layers[cur->layerindex]->sizeunits, 0) /
16✔
869
           msInchesPerUnit(map->units, 0)) /
16✔
870
          map->cellsize;
16✔
871
    }
872
    if (hittest) {
311✔
873
      ch = &hittest->layerhits[cur->layerindex].classhits[cur->classindex];
34✔
874
    }
875
    ret = msDrawLegendIcon(map, map->layers[cur->layerindex],
311✔
876
                           map->layers[cur->layerindex]->class[cur->classindex],
311✔
877
                           map -> legend.keysizex, map->legend.keysizey, image,
878
                           HMARGIN, (int)pnt.y, scale_independent, ch);
879
    if (MS_UNLIKELY(ret != MS_SUCCESS))
311✔
880
      goto cleanup;
×
881

882
    pnt.y += cur->height;
311✔
883

884
    if (cur->ts.annotext) {
311✔
885
      pointObj textPnt = pnt;
309✔
886
      textPnt.y -= cur->ts.textpath->bounds.bbox.maxy;
309✔
887
      textPnt.y += map->legend.label.offsety;
309✔
888
      textPnt.x += map->legend.label.offsetx;
309✔
889
      ret = msDrawTextSymbol(map, image, textPnt, &cur->ts);
309✔
890
      if (MS_UNLIKELY(ret == MS_FAILURE))
309✔
891
        goto cleanup;
×
892
      /* Coverity Scan is confused by label refcount, and wrongly believe we */
893
      /* might free &map->legend.label, so make it clear we won't */
894
      freeTextSymbolEx(&cur->ts, MS_FALSE);
309✔
895
      MS_REFCNT_DECR(cur->ts.label);
309✔
896
    }
897

898
    pnt.y += map->legend.keyspacingy; /* bump y for next label */
311✔
899

900
    /* clean up */
901
    head = cur->pred;
311✔
902
    free(cur);
311✔
903
  } /* next legend */
904

905
cleanup:
73✔
906
  while (head) {
73✔
907
    legendlabel *cur = head;
908
    /* Coverity Scan is confused by label refcount, and wrongly believe we */
909
    /* might free &map->legend.label, so make it clear we won't */
910
    freeTextSymbolEx(&cur->ts, MS_FALSE);
×
911
    MS_REFCNT_DECR(cur->ts.label);
×
912
    head = cur->pred;
×
913
    free(cur);
×
914
  }
915
  if (MS_UNLIKELY(ret != MS_SUCCESS)) {
73✔
916
    if (image)
×
917
      msFreeImage(image);
×
918
    return NULL;
×
919
  }
920
  return (image);
921
}
922

923
/* TODO */
924
int msEmbedLegend(mapObj *map, imageObj *img) {
16✔
925
  pointObj point;
926
  imageObj *image = NULL;
927
  symbolObj *legendSymbol;
928
  char *imageType = NULL;
929
  const char *const LEGEND_SYMBOL_NAME = "legend";
930

931
  rendererVTableObj *renderer;
932

933
  int symbolIdx =
934
      msGetSymbolIndex(&(map->symbolset), LEGEND_SYMBOL_NAME, MS_FALSE);
16✔
935
  if (symbolIdx != -1)
16✔
936
    msRemoveSymbol(&(map->symbolset),
×
937
                   symbolIdx); /* solves some caching issues in AGG with
938
                                  long-running processes */
939

940
  if (msGrowSymbolSet(&map->symbolset) == NULL)
16✔
941
    return -1;
942
  symbolIdx = map->symbolset.numsymbols;
16✔
943
  legendSymbol = map->symbolset.symbol[symbolIdx];
16✔
944
  map->symbolset.numsymbols++;
16✔
945
  initSymbol(legendSymbol);
16✔
946

947
  if (!MS_RENDERER_PLUGIN(map->outputformat) ||
16✔
948
      !MS_MAP_RENDERER(map)->supports_pixel_buffer) {
16✔
949
    imageType = msStrdup(map->imagetype); /* save format */
×
950
    if MS_DRIVER_CAIRO (map->outputformat)
×
951
      map->outputformat = msSelectOutputFormat(map, "cairopng");
×
952
    else
953
      map->outputformat = msSelectOutputFormat(map, "png");
×
954

955
    msInitializeRendererVTable(map->outputformat);
×
956
  }
957
  renderer = MS_MAP_RENDERER(map);
16✔
958

959
  /* render the legend. */
960
  image = msDrawLegend(map, MS_FALSE, NULL);
16✔
961
  if (image == NULL) {
16✔
962
    msFree(imageType);
×
963
    return MS_FAILURE;
×
964
  }
965

966
  if (imageType) {
16✔
967
    map->outputformat =
×
968
        msSelectOutputFormat(map, imageType); /* restore format */
×
969
    msFree(imageType);
×
970
  }
971

972
  /* copy renderered legend image into symbol */
973
  legendSymbol->pixmap_buffer = calloc(1, sizeof(rasterBufferObj));
16✔
974
  MS_CHECK_ALLOC(legendSymbol->pixmap_buffer, sizeof(rasterBufferObj),
16✔
975
                 MS_FAILURE);
976

977
  if (MS_SUCCESS !=
16✔
978
      renderer->getRasterBufferCopy(image, legendSymbol->pixmap_buffer))
16✔
979
    return MS_FAILURE;
980
  legendSymbol->renderer = renderer;
16✔
981

982
  msFreeImage(image);
16✔
983

984
  if (!legendSymbol->pixmap_buffer)
16✔
985
    return (MS_FAILURE); /* something went wrong creating scalebar */
986

987
  legendSymbol->type = MS_SYMBOL_PIXMAP; /* initialize a few things */
16✔
988
  legendSymbol->name = msStrdup(LEGEND_SYMBOL_NAME);
16✔
989
  legendSymbol->sizex = legendSymbol->pixmap_buffer->width;
16✔
990
  legendSymbol->sizey = legendSymbol->pixmap_buffer->height;
16✔
991

992
  /* I'm not too sure this test is sufficient ... NFW. */
993
  /* if(map->legend.transparent == MS_ON) */
994
  /*  gdImageColorTransparent(legendSymbol->img_deprecated, 0); */
995

996
  switch (map->legend.position) {
16✔
997
  case (MS_LL):
×
998
    point.x = MS_NINT(legendSymbol->sizex / 2.0);
×
999
    point.y = map->height - MS_NINT(legendSymbol->sizey / 2.0);
×
1000
    break;
×
1001
  case (MS_LR):
16✔
1002
    point.x = map->width - MS_NINT(legendSymbol->sizex / 2.0);
16✔
1003
    point.y = map->height - MS_NINT(legendSymbol->sizey / 2.0);
16✔
1004
    break;
16✔
1005
  case (MS_LC):
×
1006
    point.x = MS_NINT(map->width / 2.0);
×
1007
    point.y = map->height - MS_NINT(legendSymbol->sizey / 2.0);
×
1008
    break;
×
1009
  case (MS_UR):
×
1010
    point.x = map->width - MS_NINT(legendSymbol->sizex / 2.0);
×
1011
    point.y = MS_NINT(legendSymbol->sizey / 2.0);
×
1012
    break;
×
1013
  case (MS_UL):
×
1014
    point.x = MS_NINT(legendSymbol->sizex / 2.0);
×
1015
    point.y = MS_NINT(legendSymbol->sizey / 2.0);
×
1016
    break;
×
1017
  case (MS_UC):
×
1018
    point.x = MS_NINT(map->width / 2.0);
×
1019
    point.y = MS_NINT(legendSymbol->sizey / 2.0);
×
1020
    break;
×
1021
  }
1022

1023
  const char *const EMBED_LEGEND_LAYER_NAME = "__embed__legend";
1024
  int layerIdx = msGetLayerIndex(map, EMBED_LEGEND_LAYER_NAME);
16✔
1025
  if (layerIdx == -1) {
16✔
1026
    if (msGrowMapLayers(map) == NULL)
16✔
1027
      return (-1);
1028
    layerIdx = map->numlayers;
16✔
1029
    map->numlayers++;
16✔
1030
    layerObj *layer = GET_LAYER(map, layerIdx);
16✔
1031
    if (initLayer(layer, map) == -1)
16✔
1032
      return (-1);
1033
    layer->name = msStrdup(EMBED_LEGEND_LAYER_NAME);
16✔
1034
    layer->type = MS_LAYER_POINT;
16✔
1035

1036
    if (msGrowLayerClasses(layer) == NULL)
16✔
1037
      return (-1);
1038
    if (initClass(layer->class[0]) == -1)
16✔
1039
      return (-1);
1040
    layer->numclasses = 1; /* so we make sure to free it */
16✔
1041

1042
    /* update the layer order list with the layer's index. */
1043
    map->layerorder[layerIdx] = layerIdx;
16✔
1044
  }
1045

1046
  layerObj *layer = GET_LAYER(map, layerIdx);
16✔
1047

1048
  layer->status = MS_ON;
16✔
1049

1050
  classObj *klass = layer->class[0];
16✔
1051
  if (map->legend.postlabelcache) { /* add it directly to the image */
16✔
1052
    if (MS_UNLIKELY(msMaybeAllocateClassStyle(klass, 0) == MS_FAILURE))
8✔
1053
      return MS_FAILURE;
1054
    klass->styles[0]->symbol = symbolIdx;
8✔
1055
    if (MS_UNLIKELY(MS_FAILURE == msDrawMarkerSymbol(map, img, &point,
8✔
1056
                                                     klass->styles[0], 1.0)))
1057
      return MS_FAILURE;
1058
  } else {
1059
    if (!klass->labels) {
8✔
1060
      if (msGrowClassLabels(klass) == NULL)
8✔
1061
        return MS_FAILURE;
1062
      labelObj *label = klass->labels[0];
8✔
1063
      initLabel(label);
8✔
1064
      klass->numlabels = 1;
8✔
1065
      label->force = MS_TRUE;
8✔
1066
      label->size =
8✔
1067
          MS_MEDIUM; /* must set a size to have a valid label definition */
1068
      label->priority = MS_MAX_LABEL_PRIORITY;
8✔
1069
    }
1070
    labelObj *label = klass->labels[0];
8✔
1071
    if (label->numstyles == 0) {
8✔
1072
      if (msGrowLabelStyles(label) == NULL)
8✔
1073
        return (MS_FAILURE);
1074
      label->numstyles = 1;
8✔
1075
      initStyle(label->styles[0]);
8✔
1076
      label->styles[0]->_geomtransform.type = MS_GEOMTRANSFORM_LABELPOINT;
8✔
1077
    }
1078
    label->styles[0]->symbol = symbolIdx;
8✔
1079
    if (MS_UNLIKELY(MS_FAILURE == msAddLabel(map, img, label, layerIdx, 0, NULL,
8✔
1080
                                             &point, -1, NULL)))
1081
      return MS_FAILURE;
1082
  }
1083

1084
  /* Mark layer as deleted so that it doesn't interfere with html legends or
1085
   * with saving maps */
1086
  layer->status = MS_DELETE;
16✔
1087

1088
  return MS_SUCCESS;
16✔
1089
}
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