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