Coverage Report

Created: 2025-06-22 06:59

/src/MapServer/src/mapchart.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Implementation of dynamic charting (MS-RFC-29)
6
 * Author:   Thomas Bonfort ( thomas.bonfort[at]gmail.com )
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2007 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
0
#define MS_CHART_TYPE_PIE 1
33
0
#define MS_CHART_TYPE_BAR 2
34
0
#define MS_CHART_TYPE_VBAR 3
35
36
/*
37
** check if an object of width w and height h placed at point x,y can fit in an
38
*image of width mw and height mh
39
*/
40
#define MS_CHART_FITS(x, y, w, h, mw, mh)                                      \
41
0
  (((x) - (w) / 2. > 0.) && ((x) + (w) / 2. < (mw)) &&                         \
42
0
   ((y) - (h) / 2. > 0.) && ((y) + (h) / 2.) < (mh))
43
44
/*
45
** find a point on a shape. check if it fits in image
46
** returns
47
**  MS_SUCCESS and point coordinates in 'p' if chart fits in image
48
**  MS_FAILURE if no point could be found
49
*/
50
int findChartPoint(mapObj *map, shapeObj *shape, int width, int height,
51
0
                   pointObj *center) {
52
0
  int middle, numpoints;
53
0
  double invcellsize = 1.0 / map->cellsize; /*speed up MAP2IMAGE_X/Y_IC_DBL*/
54
0
  switch (shape->type) {
55
0
  case MS_SHAPE_POINT:
56
0
    center->x = MS_MAP2IMAGE_X_IC_DBL(shape->line[0].point[0].x,
57
0
                                      map->extent.minx, invcellsize);
58
0
    center->y = MS_MAP2IMAGE_Y_IC_DBL(shape->line[0].point[0].y,
59
0
                                      map->extent.maxy, invcellsize);
60
61
0
    if (MS_CHART_FITS(center->x, center->y, width, height, map->width,
62
0
                      map->height))
63
0
      return MS_SUCCESS;
64
0
    else
65
0
      return MS_FAILURE;
66
0
    break;
67
0
  case MS_SHAPE_LINE:
68
    /*loop through line segments starting from middle (alternate between before
69
     *and after middle point) *first segment that fits is chosen
70
     */
71
0
    middle = shape->line[0].numpoints / 2; /*start with middle segment of line*/
72
0
    numpoints = shape->line[0].numpoints;
73
0
    if (1 <= middle) {
74
0
      int idx = middle + 1;
75
0
      if (idx < numpoints) {
76
0
        center->x =
77
0
            (shape->line[0].point[idx - 1].x + shape->line[0].point[idx].x) /
78
0
            2.;
79
0
        center->y =
80
0
            (shape->line[0].point[idx - 1].y + shape->line[0].point[idx].y) /
81
0
            2.;
82
0
        center->x =
83
0
            MS_MAP2IMAGE_X_IC_DBL(center->x, map->extent.minx, invcellsize);
84
0
        center->y =
85
0
            MS_MAP2IMAGE_Y_IC_DBL(center->y, map->extent.maxy, invcellsize);
86
87
0
        if (MS_CHART_FITS(center->x, center->y, width, height, map->width,
88
0
                          map->height))
89
0
          return MS_SUCCESS;
90
91
0
        return MS_FAILURE;
92
0
      }
93
0
      idx = middle - 1;
94
0
      center->x =
95
0
          (shape->line[0].point[idx].x + shape->line[0].point[idx + 1].x) / 2;
96
0
      center->y =
97
0
          (shape->line[0].point[idx].y + shape->line[0].point[idx + 1].y) / 2;
98
0
      center->x =
99
0
          MS_MAP2IMAGE_X_IC_DBL(center->x, map->extent.minx, invcellsize);
100
0
      center->y =
101
0
          MS_MAP2IMAGE_Y_IC_DBL(center->y, map->extent.maxy, invcellsize);
102
103
0
      if (MS_CHART_FITS(center->x, center->y, width, height, map->width,
104
0
                        map->height))
105
0
        return MS_SUCCESS;
106
0
      return MS_FAILURE;
107
0
    }
108
0
    return MS_FAILURE;
109
0
    break;
110
0
  case MS_SHAPE_POLYGON:
111
0
    msPolygonLabelPoint(shape, center, -1);
112
0
    center->x = MS_MAP2IMAGE_X_IC_DBL(center->x, map->extent.minx, invcellsize);
113
0
    center->y = MS_MAP2IMAGE_Y_IC_DBL(center->y, map->extent.maxy, invcellsize);
114
115
0
    if (MS_CHART_FITS(center->x, center->y, width, height, map->width,
116
0
                      map->height))
117
0
      return MS_SUCCESS;
118
0
    else
119
0
      return MS_FAILURE;
120
0
    break;
121
0
  default:
122
0
    return MS_FAILURE;
123
0
  }
124
0
}
125
126
int WARN_UNUSED drawRectangle(mapObj *map, imageObj *image, double mx,
127
                              double my, double Mx, double My,
128
0
                              styleObj *style) {
129
0
  shapeObj shape;
130
0
  lineObj line;
131
0
  pointObj point[5];
132
0
  line.point = point;
133
0
  line.numpoints = 5;
134
0
  shape.line = &line;
135
0
  shape.numlines = 1;
136
137
0
  point[0].x = point[4].x = point[3].x = mx;
138
0
  point[0].y = point[4].y = point[1].y = my;
139
  /* cppcheck-suppress unreadVariable */
140
0
  point[1].x = point[2].x = Mx;
141
  /* cppcheck-suppress unreadVariable */
142
0
  point[2].y = point[3].y = My;
143
144
0
  return msDrawShadeSymbol(map, image, &shape, style, 1.0);
145
0
}
146
147
int WARN_UNUSED msDrawVBarChart(mapObj *map, imageObj *image, pointObj *center,
148
                                double *values, styleObj **styles,
149
0
                                int numvalues, double barWidth) {
150
151
0
  int c;
152
0
  double left, cur; /*shortcut to pixel boundaries of the chart*/
153
0
  double height = 0;
154
155
0
  for (c = 0; c < numvalues; c++) {
156
0
    height += values[c];
157
0
  }
158
159
0
  cur = center->y + height / 2.;
160
0
  left = center->x - barWidth / 2.;
161
162
0
  for (c = 0; c < numvalues; c++) {
163
0
    if (MS_UNLIKELY(MS_FAILURE == drawRectangle(map, image, left, cur,
164
0
                                                left + barWidth,
165
0
                                                cur - values[c], styles[c])))
166
0
      return MS_FAILURE;
167
0
    cur -= values[c];
168
0
  }
169
0
  return MS_SUCCESS;
170
0
}
171
172
int msDrawBarChart(mapObj *map, imageObj *image, pointObj *center,
173
                   double *values, styleObj **styles, int numvalues,
174
                   double width, double height, double *maxVal, double *minVal,
175
0
                   double barWidth) {
176
177
0
  double upperLimit, lowerLimit;
178
0
  double shapeMaxVal, shapeMinVal, pixperval;
179
0
  int c;
180
0
  double vertOrigin, vertOriginClipped, horizStart, y;
181
0
  double left, top, bottom; /*shortcut to pixel boundaries of the chart*/
182
183
0
  top = center->y - height / 2.;
184
0
  bottom = center->y + height / 2.;
185
0
  left = center->x - width / 2.;
186
187
0
  shapeMaxVal = shapeMinVal = values[0];
188
0
  for (c = 1; c < numvalues; c++) {
189
0
    if (maxVal == NULL || minVal == NULL) { /*compute bounds if not specified*/
190
0
      if (values[c] > shapeMaxVal)
191
0
        shapeMaxVal = values[c];
192
0
      if (values[c] < shapeMinVal)
193
0
        shapeMinVal = values[c];
194
0
    }
195
0
  }
196
197
  /*
198
   * use specified bounds if wanted
199
   * if not, always show the origin
200
   */
201
0
  upperLimit = (maxVal != NULL) ? *maxVal : MS_MAX(shapeMaxVal, 0);
202
0
  lowerLimit = (minVal != NULL) ? *minVal : MS_MIN(shapeMinVal, 0);
203
0
  if (upperLimit == lowerLimit) {
204
    /* treat the case where we would have an unspecified behavior */
205
0
    upperLimit += 0.5;
206
0
    lowerLimit -= 0.5;
207
0
  }
208
209
0
  pixperval = height / (upperLimit - lowerLimit);
210
0
  vertOrigin = bottom + lowerLimit * pixperval;
211
0
  vertOriginClipped = (vertOrigin < top)      ? top
212
0
                      : (vertOrigin > bottom) ? bottom
213
0
                                              : vertOrigin;
214
0
  horizStart = left;
215
216
0
  for (c = 0; c < numvalues; c++) {
217
0
    double barHeight = values[c] * pixperval;
218
    /*clip bars*/
219
0
    y = ((vertOrigin - barHeight) < top)    ? top
220
0
        : (vertOrigin - barHeight > bottom) ? bottom
221
0
                                            : vertOrigin - barHeight;
222
0
    if (y != vertOriginClipped) { /*don't draw bars of height == 0 (i.e. either
223
                                     values==0, or clipped)*/
224
0
      if (values[c] > 0) {
225
0
        if (MS_UNLIKELY(MS_FAILURE == drawRectangle(map, image, horizStart, y,
226
0
                                                    horizStart + barWidth - 1,
227
0
                                                    vertOriginClipped,
228
0
                                                    styles[c])))
229
0
          return MS_FAILURE;
230
0
      } else {
231
0
        if (MS_UNLIKELY(MS_FAILURE ==
232
0
                        drawRectangle(map, image, horizStart, vertOriginClipped,
233
0
                                      horizStart + barWidth - 1, y, styles[c])))
234
0
          return MS_FAILURE;
235
0
      }
236
0
    }
237
0
    horizStart += barWidth;
238
0
  }
239
0
  return MS_SUCCESS;
240
0
}
241
242
int WARN_UNUSED msDrawPieChart(mapObj *map, imageObj *image, pointObj *center,
243
                               double diameter, double *values,
244
0
                               styleObj **styles, int numvalues) {
245
0
  int i;
246
0
  double dTotal = 0., start = 0;
247
248
0
  for (i = 0; i < numvalues; i++) {
249
0
    if (values[i] < 0.) {
250
0
      msSetError(MS_MISCERR, "cannot draw pie charts for negative values",
251
0
                 "msDrawPieChart()");
252
0
      return MS_FAILURE;
253
0
    }
254
0
    dTotal += values[i];
255
0
  }
256
257
0
  for (i = 0; i < numvalues; i++) {
258
0
    double angle = values[i];
259
0
    if (angle == 0)
260
0
      continue; /*no need to draw. causes artifacts with outlines*/
261
0
    angle *= 360.0 / dTotal;
262
0
    if (MS_UNLIKELY(MS_FAILURE == msDrawPieSlice(map, image, center, styles[i],
263
0
                                                 diameter / 2., start,
264
0
                                                 start + angle)))
265
0
      return MS_FAILURE;
266
267
0
    start += angle;
268
0
  }
269
0
  return MS_SUCCESS;
270
0
}
271
272
int getNextShape(mapObj *map, layerObj *layer, double *values, int *nvalues,
273
0
                 styleObj **styles, shapeObj *shape) {
274
0
  int status;
275
0
  int c;
276
0
  status = msLayerNextShape(layer, shape);
277
0
  if (status == MS_SUCCESS) {
278
279
0
    if (layer->project) {
280
0
      if (layer->reprojectorLayerToMap == NULL) {
281
0
        layer->reprojectorLayerToMap =
282
0
            msProjectCreateReprojector(&layer->projection, &map->projection);
283
0
        if (layer->reprojectorLayerToMap == NULL) {
284
0
          return MS_FAILURE;
285
0
        }
286
0
      }
287
0
      msProjectShapeEx(layer->reprojectorLayerToMap, shape);
288
0
    }
289
290
0
    if (msBindLayerToShape(layer, shape,
291
0
                           MS_DRAWMODE_FEATURES | MS_DRAWMODE_LABELS) !=
292
0
        MS_SUCCESS)
293
0
      return MS_FAILURE; /* error message is set in msBindLayerToShape() */
294
295
0
    *nvalues = 0;
296
0
    for (c = 0; c < layer->numclasses; c++) {
297
0
      if (msEvalExpression(layer, shape, &(layer->class[c] -> expression),
298
0
                           layer->classitemindex) == MS_TRUE) {
299
0
        values[*nvalues] = (layer->class[c] -> styles[0] -> size);
300
0
        styles[*nvalues] = layer->class[c]->styles[0];
301
0
        (*nvalues)++;
302
0
      }
303
0
    }
304
0
  }
305
0
  return status;
306
0
}
307
308
/* eventually add a class to the layer to get the diameter from an attribute */
309
0
int pieLayerProcessDynamicDiameter(layerObj *layer) {
310
0
  const char *chartRangeProcessingKey = NULL;
311
0
  char *attrib;
312
0
  double mindiameter = -1, maxdiameter, minvalue, maxvalue;
313
0
  classObj *newclass;
314
0
  styleObj *newstyle;
315
0
  const char *chartSizeProcessingKey =
316
0
      msLayerGetProcessingKey(layer, "CHART_SIZE");
317
0
  if (chartSizeProcessingKey != NULL)
318
0
    return MS_FALSE;
319
0
  chartRangeProcessingKey = msLayerGetProcessingKey(layer, "CHART_SIZE_RANGE");
320
0
  if (chartRangeProcessingKey == NULL)
321
0
    return MS_FALSE;
322
0
  attrib = msStrdup(chartRangeProcessingKey);
323
0
  char *space = strchr(attrib, ' ');
324
0
  if (space) {
325
0
    *space = '\0';
326
0
    if (sscanf(space + 1, "%lf %lf %lf %lf", &mindiameter, &maxdiameter,
327
0
               &minvalue, &maxvalue) != 4) {
328
      /*we don't have the attribute and the four range values*/
329
0
      free(attrib);
330
0
      msSetError(MS_MISCERR,
331
0
                 "Chart Layer format error for processing key \"CHART_RANGE\"",
332
0
                 "msDrawChartLayer()");
333
0
      return MS_FAILURE;
334
0
    }
335
0
  }
336
337
  /*create a new class in the layer containing the wanted attribute
338
   * as the SIZE of its first STYLE*/
339
0
  newclass = msGrowLayerClasses(layer);
340
0
  if (newclass == NULL) {
341
0
    free(attrib);
342
0
    return MS_FAILURE;
343
0
  }
344
0
  initClass(newclass);
345
0
  layer->numclasses++;
346
347
  /*create and attach a new styleObj to our temp class
348
   * and bind the wanted attribute to its SIZE
349
   */
350
0
  newstyle = msGrowClassStyles(newclass);
351
0
  if (newstyle == NULL) {
352
0
    free(attrib);
353
0
    return MS_FAILURE;
354
0
  }
355
0
  initStyle(newstyle);
356
0
  newclass->numstyles++;
357
0
  newclass->name = (char *)msStrdup("__MS_SIZE_ATTRIBUTE_");
358
0
  newstyle->bindings[MS_STYLE_BINDING_SIZE].item = msStrdup(attrib);
359
0
  newstyle->numbindings++;
360
0
  free(attrib);
361
362
0
  return MS_TRUE;
363
0
}
364
365
/* clean up the class added temporarily */
366
0
static void pieLayerCleanupDynamicDiameter(layerObj *layer) {
367
0
  if (layer->numclasses > 0 &&
368
0
      EQUALN(layer->class[layer->numclasses - 1] -> name,
369
0
             "__MS_SIZE_ATTRIBUTE_", 20)) {
370
0
    classObj *c = msRemoveClass(layer, layer->numclasses - 1);
371
0
    freeClass(c);
372
0
    msFree(c);
373
0
  }
374
0
}
375
376
0
int msDrawPieChartLayer(mapObj *map, layerObj *layer, imageObj *image) {
377
0
  shapeObj shape;
378
0
  int status = MS_SUCCESS;
379
0
  const char *chartRangeProcessingKey = NULL;
380
0
  const char *chartSizeProcessingKey =
381
0
      msLayerGetProcessingKey(layer, "CHART_SIZE");
382
0
  double diameter = 0, mindiameter = -1, maxdiameter = 0, minvalue = 0,
383
0
         maxvalue = 0, exponent = 0;
384
0
  double *values;
385
0
  styleObj **styles;
386
0
  pointObj center;
387
0
  int numvalues =
388
0
      layer->numclasses; /* the number of classes to represent in the graph */
389
0
  int numvalues_for_shape = 0;
390
391
0
  if (chartSizeProcessingKey == NULL) {
392
0
    chartRangeProcessingKey =
393
0
        msLayerGetProcessingKey(layer, "CHART_SIZE_RANGE");
394
0
    if (chartRangeProcessingKey == NULL)
395
0
      diameter = 20;
396
0
    else {
397
0
      const int nvalues =
398
0
          sscanf(chartRangeProcessingKey, "%*s %lf %lf %lf %lf %lf",
399
0
                 &mindiameter, &maxdiameter, &minvalue, &maxvalue, &exponent);
400
0
      if (nvalues != 4 && nvalues != 5) {
401
0
        msSetError(
402
0
            MS_MISCERR,
403
0
            "msDrawChart format error for processing key \"CHART_SIZE_RANGE\": "
404
0
            "itemname minsize maxsize minval maxval [exponent] is expected",
405
0
            "msDrawPieChartLayer()");
406
0
        return MS_FAILURE;
407
0
      }
408
0
    }
409
0
  } else {
410
0
    if (sscanf(chartSizeProcessingKey, "%lf", &diameter) != 1) {
411
0
      msSetError(MS_MISCERR,
412
0
                 "msDrawChart format error for processing key \"CHART_SIZE\"",
413
0
                 "msDrawPieChartLayer()");
414
0
      return MS_FAILURE;
415
0
    }
416
0
  }
417
418
0
  layer->project =
419
0
      msProjectionsDiffer(&(layer->projection), &(map->projection));
420
421
  /* step through the target shapes */
422
0
  msInitShape(&shape);
423
424
0
  values = (double *)calloc(numvalues, sizeof(double));
425
0
  MS_CHECK_ALLOC(values, numvalues * sizeof(double), MS_FAILURE);
426
0
  styles = (styleObj **)malloc((numvalues) * sizeof(styleObj *));
427
0
  if (styles == NULL) {
428
0
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
429
0
               "msDrawPieChartLayer()", __FILE__, __LINE__,
430
0
               (unsigned int)(numvalues * sizeof(styleObj *)));
431
0
    free(values);
432
0
    return MS_FAILURE;
433
0
  }
434
435
0
  while (MS_SUCCESS == getNextShape(map, layer, values, &numvalues_for_shape,
436
0
                                    styles, &shape)) {
437
0
    if (chartRangeProcessingKey != NULL)
438
0
      numvalues_for_shape--;
439
0
    if (numvalues_for_shape == 0) {
440
0
      msFreeShape(&shape);
441
0
      continue;
442
0
    }
443
0
    msDrawStartShape(map, layer, image, &shape);
444
0
    if (chartRangeProcessingKey != NULL) {
445
0
      diameter = values[numvalues_for_shape];
446
0
      if (mindiameter >= 0) {
447
0
        if (diameter <= minvalue)
448
0
          diameter = mindiameter;
449
0
        else if (diameter >= maxvalue)
450
0
          diameter = maxdiameter;
451
0
        else {
452
0
          if (exponent <= 0)
453
0
            diameter = MS_NINT(mindiameter +
454
0
                               ((diameter - minvalue) / (maxvalue - minvalue)) *
455
0
                                   (maxdiameter - mindiameter));
456
0
          else
457
0
            diameter = MS_NINT(
458
0
                mindiameter + pow((diameter - minvalue) / (maxvalue - minvalue),
459
0
                                  1.0 / exponent) *
460
0
                                  (maxdiameter - mindiameter));
461
0
        }
462
0
      }
463
0
    }
464
0
    if (findChartPoint(map, &shape, diameter, diameter, &center) ==
465
0
        MS_SUCCESS) {
466
0
      status = msDrawPieChart(map, image, &center, diameter, values, styles,
467
0
                              numvalues_for_shape);
468
0
    }
469
0
    msDrawEndShape(map, layer, image, &shape);
470
0
    msFreeShape(&shape);
471
0
  }
472
0
  free(values);
473
0
  free(styles);
474
0
  return status;
475
0
}
476
477
0
int msDrawVBarChartLayer(mapObj *map, layerObj *layer, imageObj *image) {
478
0
  shapeObj shape;
479
0
  int status = MS_SUCCESS;
480
0
  const char *chartSizeProcessingKey =
481
0
      msLayerGetProcessingKey(layer, "CHART_SIZE");
482
0
  const char *chartScaleProcessingKey =
483
0
      msLayerGetProcessingKey(layer, "CHART_SCALE");
484
0
  double barWidth, scale = 1.0;
485
0
  double *values;
486
0
  styleObj **styles;
487
0
  pointObj center;
488
0
  int numvalues = layer->numclasses;
489
0
  int numvalues_for_shape;
490
0
  if (chartSizeProcessingKey == NULL) {
491
0
    barWidth = 20;
492
0
  } else {
493
0
    if (sscanf(chartSizeProcessingKey, "%lf", &barWidth) != 1) {
494
0
      msSetError(MS_MISCERR,
495
0
                 "msDrawChart format error for processing key \"CHART_SIZE\"",
496
0
                 "msDrawVBarChartLayer()");
497
0
      return MS_FAILURE;
498
0
    }
499
0
  }
500
501
0
  if (chartScaleProcessingKey) {
502
0
    if (sscanf(chartScaleProcessingKey, "%lf", &scale) != 1) {
503
0
      msSetError(MS_MISCERR,
504
0
                 "Error reading value for processing key \"CHART_SCALE\"",
505
0
                 "msDrawVBarChartLayer()");
506
0
      return MS_FAILURE;
507
0
    }
508
0
  }
509
0
  msInitShape(&shape);
510
511
0
  values = (double *)calloc(numvalues, sizeof(double));
512
0
  MS_CHECK_ALLOC(values, numvalues * sizeof(double), MS_FAILURE);
513
0
  styles = (styleObj **)malloc(numvalues * sizeof(styleObj *));
514
0
  if (styles == NULL) {
515
0
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
516
0
               "msDrawVBarChartLayer()", __FILE__, __LINE__,
517
0
               (unsigned int)(numvalues * sizeof(styleObj *)));
518
0
    free(values);
519
0
    return MS_FAILURE;
520
0
  }
521
522
0
  while (MS_SUCCESS == getNextShape(map, layer, values, &numvalues_for_shape,
523
0
                                    styles, &shape)) {
524
0
    int i;
525
0
    double h = 0;
526
0
    if (numvalues_for_shape == 0) {
527
0
      continue;
528
0
    }
529
0
    for (i = 0; i < numvalues_for_shape; i++) {
530
0
      values[i] *= scale;
531
0
      h += values[i];
532
0
    }
533
0
    msDrawStartShape(map, layer, image, &shape);
534
0
    if (findChartPoint(map, &shape, barWidth, h, &center) == MS_SUCCESS) {
535
0
      status = msDrawVBarChart(map, image, &center, values, styles,
536
0
                               numvalues_for_shape, barWidth);
537
0
    }
538
0
    msDrawEndShape(map, layer, image, &shape);
539
0
    msFreeShape(&shape);
540
0
  }
541
0
  free(values);
542
0
  free(styles);
543
0
  return status;
544
0
}
545
546
0
int msDrawBarChartLayer(mapObj *map, layerObj *layer, imageObj *image) {
547
0
  shapeObj shape;
548
0
  int status = MS_SUCCESS;
549
0
  const char *chartSizeProcessingKey =
550
0
      msLayerGetProcessingKey(layer, "CHART_SIZE");
551
0
  const char *barMax = msLayerGetProcessingKey(layer, "CHART_BAR_MAXVAL");
552
0
  const char *barMin = msLayerGetProcessingKey(layer, "CHART_BAR_MINVAL");
553
0
  double width, height;
554
0
  double barWidth;
555
0
  double *values;
556
0
  styleObj **styles;
557
0
  pointObj center;
558
0
  double barMaxVal = 0.0, barMinVal = 0.0;
559
0
  int numvalues = layer->numclasses;
560
0
  int numvalues_for_shape;
561
0
  if (chartSizeProcessingKey == NULL) {
562
0
    width = height = 20;
563
0
  } else {
564
0
    const int ret = sscanf(chartSizeProcessingKey, "%lf %lf", &width, &height);
565
0
    if (ret == 1) {
566
0
      height = width;
567
0
    } else if (ret != 2) {
568
0
      msSetError(MS_MISCERR,
569
0
                 "msDrawChart format error for processing key \"CHART_SIZE\"",
570
0
                 "msDrawBarChartLayer()");
571
0
      return MS_FAILURE;
572
0
    }
573
0
  }
574
575
0
  if (barMax) {
576
0
    if (sscanf(barMax, "%lf", &barMaxVal) != 1) {
577
0
      msSetError(MS_MISCERR,
578
0
                 "Error reading value for processing key \"CHART_BAR_MAXVAL\"",
579
0
                 "msDrawBarChartLayer()");
580
0
      return MS_FAILURE;
581
0
    }
582
0
  }
583
0
  if (barMin) {
584
0
    if (sscanf(barMin, "%lf", &barMinVal) != 1) {
585
0
      msSetError(MS_MISCERR,
586
0
                 "Error reading value for processing key \"CHART_BAR_MINVAL\"",
587
0
                 "msDrawBarChartLayer()");
588
0
      return MS_FAILURE;
589
0
    }
590
0
  }
591
0
  if (barMin && barMax && barMinVal >= barMaxVal) {
592
0
    msSetError(MS_MISCERR,
593
0
               "\"CHART_BAR_MINVAL\" must be less than \"CHART_BAR_MAXVAL\"",
594
0
               "msDrawBarChartLayer()");
595
0
    return MS_FAILURE;
596
0
  }
597
0
  barWidth = (double)width / (double)layer->numclasses;
598
0
  if (!barWidth) {
599
0
    msSetError(
600
0
        MS_MISCERR,
601
0
        "Specified width of chart too small to fit given number of classes",
602
0
        "msDrawBarChartLayer()");
603
0
    return MS_FAILURE;
604
0
  }
605
606
0
  msInitShape(&shape);
607
608
0
  values = (double *)calloc(numvalues, sizeof(double));
609
0
  MS_CHECK_ALLOC(values, numvalues * sizeof(double), MS_FAILURE);
610
0
  styles = (styleObj **)malloc(numvalues * sizeof(styleObj *));
611
0
  if (styles == NULL) {
612
0
    msSetError(MS_MEMERR, "%s: %d: Out of memory allocating %u bytes.\n",
613
0
               "msDrawBarChartLayer()", __FILE__, __LINE__,
614
0
               (unsigned int)(numvalues * sizeof(styleObj *)));
615
0
    free(values);
616
0
    return MS_FAILURE;
617
0
  }
618
619
0
  while (MS_SUCCESS == getNextShape(map, layer, values, &numvalues_for_shape,
620
0
                                    styles, &shape)) {
621
0
    if (numvalues_for_shape == 0)
622
0
      continue;
623
0
    msDrawStartShape(map, layer, image, &shape);
624
0
    if (findChartPoint(map, &shape, width, height, &center) == MS_SUCCESS) {
625
0
      status = msDrawBarChart(map, image, &center, values, styles,
626
0
                              numvalues_for_shape, width, height,
627
0
                              (barMax != NULL) ? &barMaxVal : NULL,
628
0
                              (barMin != NULL) ? &barMinVal : NULL, barWidth);
629
0
    }
630
0
    msDrawEndShape(map, layer, image, &shape);
631
0
    msFreeShape(&shape);
632
0
  }
633
0
  free(values);
634
0
  free(styles);
635
0
  return status;
636
0
}
637
638
/**
639
 * Generic function to render chart layers.
640
 */
641
0
int msDrawChartLayer(mapObj *map, layerObj *layer, imageObj *image) {
642
643
0
  rectObj searchrect;
644
0
  const char *chartTypeProcessingKey =
645
0
      msLayerGetProcessingKey(layer, "CHART_TYPE");
646
0
  int chartType = MS_CHART_TYPE_PIE;
647
0
  int status = MS_FAILURE;
648
649
0
  if (image && map) {
650
0
    if (!(MS_RENDERER_PLUGIN(image->format))) {
651
0
      msSetError(MS_MISCERR,
652
0
                 "chart drawing currently only supports GD and AGG renderers",
653
0
                 "msDrawChartLayer()");
654
0
      return MS_FAILURE;
655
0
    }
656
657
0
    if (chartTypeProcessingKey != NULL) {
658
0
      if (strcasecmp(chartTypeProcessingKey, "PIE") == 0) {
659
0
        chartType = MS_CHART_TYPE_PIE;
660
0
      } else if (strcasecmp(chartTypeProcessingKey, "BAR") == 0) {
661
0
        chartType = MS_CHART_TYPE_BAR;
662
0
      } else if (strcasecmp(chartTypeProcessingKey, "VBAR") == 0) {
663
0
        chartType = MS_CHART_TYPE_VBAR;
664
0
      } else {
665
0
        msSetError(MS_MISCERR,
666
0
                   "unknown chart type for processing key \"CHART_TYPE\", must "
667
0
                   "be one of \"PIE\" or \"BAR\"",
668
0
                   "msDrawChartLayer()");
669
0
        return MS_FAILURE;
670
0
      }
671
0
    }
672
0
    if (chartType == MS_CHART_TYPE_PIE) {
673
0
      pieLayerProcessDynamicDiameter(layer);
674
0
    }
675
676
    /* open this layer */
677
0
    status = msLayerOpen(layer);
678
0
    if (status != MS_SUCCESS)
679
0
      return MS_FAILURE;
680
681
0
    status = msLayerWhichItems(layer, MS_FALSE, NULL);
682
0
    if (status != MS_SUCCESS) {
683
0
      msLayerClose(layer);
684
0
      return MS_FAILURE;
685
0
    }
686
    /* identify target shapes */
687
0
    if (layer->transform == MS_TRUE)
688
0
      searchrect = map->extent;
689
0
    else {
690
0
      searchrect.minx = searchrect.miny = 0;
691
0
      searchrect.maxx = map->width - 1;
692
0
      searchrect.maxy = map->height - 1;
693
0
    }
694
695
0
    if ((map->projection.numargs > 0) && (layer->projection.numargs > 0))
696
0
      msProjectRect(&map->projection, &layer->projection,
697
0
                    &searchrect); /* project the searchrect to source coords */
698
699
0
    status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
700
0
    if (status == MS_DONE) { /* no overlap */
701
0
      msLayerClose(layer);
702
0
      if (chartType == MS_CHART_TYPE_PIE)
703
0
        pieLayerCleanupDynamicDiameter(layer);
704
0
      return MS_SUCCESS;
705
0
    } else if (status != MS_SUCCESS) {
706
0
      msLayerClose(layer);
707
0
      if (chartType == MS_CHART_TYPE_PIE)
708
0
        pieLayerCleanupDynamicDiameter(layer);
709
0
      return MS_FAILURE;
710
0
    }
711
0
    switch (chartType) {
712
0
    case MS_CHART_TYPE_PIE:
713
0
      status = msDrawPieChartLayer(map, layer, image);
714
0
      break;
715
0
    case MS_CHART_TYPE_BAR:
716
0
      status = msDrawBarChartLayer(map, layer, image);
717
0
      break;
718
0
    case MS_CHART_TYPE_VBAR:
719
0
      status = msDrawVBarChartLayer(map, layer, image);
720
0
      break;
721
0
    default:
722
0
      return MS_FAILURE; /*shouldn't be here anyways*/
723
0
    }
724
725
0
    msLayerClose(layer);
726
727
0
    if (chartType == MS_CHART_TYPE_PIE)
728
0
      pieLayerCleanupDynamicDiameter(layer);
729
0
  }
730
0
  return status;
731
0
}