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, ¢er) == MS_SUCCESS) {
422 status = msDrawPieChart(map,image, ¢er, 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, ¢er)==MS_SUCCESS) {
484 status = msDrawVBarChart(map,image,
485 ¢er,
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, ¢er)==MS_SUCCESS) {
566 status = msDrawBarChart(map,image,
567 ¢er,
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