pie.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* pie.c
  2. *
  3. * function defined:
  4. *
  5. * pie(dist_stats,colors)
  6. *
  7. * struct stat_list *dist_stats - linked list of statistics
  8. * struct Colors *colors - map colors
  9. *
  10. *
  11. * PURPOSE: To draw a pie-chart representing the histogram
  12. * statistics in the linked list dist_stats.
  13. *
  14. * NOTES:
  15. *
  16. * 1) see dhist.h for a decalaration of the structure stat_list.
  17. * 2) see pie.h for normalized coordinates of the different parts
  18. * of the pie-chart, like the origin of the pie, the label
  19. * positions, etc.
  20. * 3) pie slices are given percent labels (eg. 20%, 70%) if they
  21. * represent over 15% of the pie.
  22. *
  23. *
  24. * Dave Johnson
  25. * DBA Systems, Inc.
  26. * 10560 Arrowhead Drive
  27. * Fairfax, Virginia 22030
  28. *
  29. */
  30. #include <string.h>
  31. #include <grass/raster.h>
  32. #include <grass/display.h>
  33. #include "pie.h"
  34. /*#define DEBUG */
  35. #define YES 1
  36. #define NO 0
  37. int pie(struct stat_list *dist_stats, /* list of distribution statistics */
  38. struct Colors *colors)
  39. {
  40. struct stat_node *ptr;
  41. double arc, arc_counter;
  42. int draw = YES;
  43. long int bar_height; /* height, in pixels, of a histogram bar */
  44. long int bar_color; /* color/category number of a histogram bar */
  45. long int max_tics; /* maximum tics allowed on an axis */
  46. long int xoffset; /* offset for x-axis */
  47. long int yoffset; /* offset for y-axis */
  48. int text_height;
  49. int text_width;
  50. long int i, j;
  51. long int num_cats;
  52. long int tic_every; /* spacing, in units of category value, of tics */
  53. long int tic_unit;
  54. double t, b, l, r;
  55. double tt, tb, tl, tr;
  56. double x_line[5]; /* for border of histogram */
  57. double y_line[5];
  58. double x_box[6]; /* for histogram bar coordinates */
  59. double y_box[6];
  60. double height, width;
  61. double xscale; /* scaling factors */
  62. double yscale;
  63. char xlabel[1024];
  64. char txt[1024];
  65. char tic_name[80];
  66. DCELL dmin, dmax, range_dmin, range_dmax, dval;
  67. /* get coordinates of current screen window */
  68. D_get_src(&t, &b, &l, &r);
  69. /* create legend box border, to be drawn later */
  70. height = b - t;
  71. width = r - l;
  72. x_line[4] = x_line[0] = x_line[1] = l + (BAR_X1 * width);
  73. y_line[4] = y_line[0] = y_line[3] = b - (BAR_Y1 * height);
  74. x_line[2] = x_line[3] = l + (BAR_X2 * width);
  75. bar_height = y_line[1] = y_line[2] = b - (BAR_Y2 * height);
  76. /* figure scaling factors and offsets */
  77. num_cats = dist_stats->maxcat - dist_stats->mincat + 1;
  78. if (nodata) {
  79. num_cats++;
  80. dist_stats->mincat--;
  81. }
  82. xscale = ((double)(x_line[2] - x_line[1]) / ((double)num_cats));
  83. yscale = ((double)(y_line[0] - y_line[1])) / dist_stats->maxstat;
  84. yoffset = (long)(y_line[0]);
  85. if (num_cats >= x_line[2] - x_line[1])
  86. xoffset = (double)x_line[1];
  87. else
  88. xoffset = (double)x_line[0] + 0.5 * xscale; /* boxes need extra space */
  89. #ifdef DEBUG
  90. fprintf(stdout, "num_cats=%ld x1=%d x2=%d xscale=%lf\n",
  91. num_cats, x_line[1], x_line[2], xscale);
  92. #endif
  93. /* figure tic_every and tic_units for the x-axis of the bar-chart.
  94. * tic_every tells how often to place a tic-number. tic_unit tells
  95. * the unit to use in expressing tic-numbers.
  96. */
  97. if (xscale < XTIC_DIST) {
  98. max_tics = (x_line[2] - x_line[1]) / XTIC_DIST;
  99. i = 0;
  100. if (is_fp) {
  101. Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
  102. if (Rast_is_d_null_value(&range_dmin) ||
  103. Rast_is_d_null_value(&range_dmax))
  104. G_fatal_error("Floating point data range is empty");
  105. while ((range_dmax - range_dmin) / tics[i].every > max_tics)
  106. i++;
  107. }
  108. while ((num_cats / tics[i].every) > max_tics)
  109. i++;
  110. tic_every = tics[i].every;
  111. tic_unit = tics[i].unit;
  112. strcpy(tic_name, tics[i].name);
  113. }
  114. else {
  115. if (is_fp && !cat_ranges) {
  116. Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
  117. if (Rast_is_d_null_value(&range_dmin) ||
  118. Rast_is_d_null_value(&range_dmax))
  119. G_fatal_error("Floating point data range is empty");
  120. }
  121. tic_every = 1;
  122. tic_unit = 1;
  123. }
  124. /* PIE & LEGEND LOOP
  125. *
  126. * loop through category range, drawing a pie-slice and a
  127. * legend bar on each iteration evenly divisible, a tic-mark
  128. * on those evenly divisible by tic_unit, and a tic_mark
  129. * number on those evenly divisible by tic_every
  130. *
  131. */
  132. ptr = dist_stats->ptr;
  133. arc_counter = 0;
  134. for (i = dist_stats->mincat; i <= dist_stats->maxcat; i++) {
  135. text_height = height * 0.7 * TEXT_HEIGHT;
  136. text_width = width * 0.7 * TEXT_WIDTH;
  137. D_text_size(text_width, text_height);
  138. draw = NO;
  139. /* figure color and height of the slice of pie
  140. *
  141. * the cat number determines the color, the corresponding stat,
  142. * determines the bar height. if a stat cannot be found for the
  143. * cat, then we don't draw anything, but before in this case we
  144. * used to draw a black box of size 0. Later on when the option
  145. * to specify the backgrown colors will be added, we might still
  146. * draw a box in that color.
  147. */
  148. if (nodata && i == dist_stats->mincat)
  149. /* null */
  150. {
  151. if (dist_stats->null_stat == 0 && xscale > 1)
  152. draw = NO;
  153. else {
  154. draw = YES;
  155. Rast_set_d_null_value(&dval, 1);
  156. arc = 360.0 *((double)dist_stats->null_stat
  157. / (double)dist_stats->sumstat);
  158. draw_slice_filled(colors, dval, color, ORIGIN_X,
  159. ORIGIN_Y, RADIUS,
  160. arc_counter, arc);
  161. /*OUTLINE THE SLICE
  162. draw_slice_unfilled(colors, color,ORIGIN_X,ORIGIN_Y,
  163. RADIUS,arc_counter,arc); */
  164. arc_counter += arc;
  165. D_d_color(dval, colors);
  166. }
  167. }
  168. else if (ptr->cat == i) { /* AH-HA!! found the stat */
  169. if (ptr->stat == 0 && xscale > 1)
  170. draw = NO;
  171. else {
  172. draw = YES;
  173. if (is_fp) {
  174. if (cat_ranges)
  175. Rast_get_ith_d_cat(&cats, (CELL) i, &dmin, &dmax);
  176. else {
  177. dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
  178. dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
  179. }
  180. arc = 360.0 * ptr->stat / dist_stats->sumstat;
  181. draw_slice(colors, 1, dmin, dmax, color, ORIGIN_X, ORIGIN_Y,
  182. RADIUS, arc_counter, arc);
  183. arc_counter += arc;
  184. D_d_color(dmin, colors);
  185. /*OUTLINE THE SLICE */
  186. draw_slice_unfilled(colors, color, ORIGIN_X,
  187. ORIGIN_Y, RADIUS,
  188. arc_counter, arc);
  189. }
  190. else {
  191. bar_color = ptr->cat;
  192. arc = 360.0 * ptr->stat / dist_stats->sumstat;
  193. draw_slice_filled(colors, (DCELL) bar_color, color,
  194. ORIGIN_X, ORIGIN_Y,
  195. RADIUS, arc_counter, arc);
  196. D_color((CELL) bar_color, colors);
  197. arc_counter += arc;
  198. }
  199. }
  200. if (ptr->next != NULL)
  201. ptr = ptr->next;
  202. }
  203. else { /* we have to look for the stat */
  204. /* loop until we find it, or pass where it should be */
  205. while (ptr->cat < i && ptr->next != NULL)
  206. ptr = ptr->next;
  207. if (ptr->cat == i) { /* AH-HA!! found the stat */
  208. if (ptr->stat == 0 && xscale > 1)
  209. draw = NO;
  210. else {
  211. draw = YES;
  212. if (is_fp) {
  213. if (cat_ranges)
  214. Rast_get_ith_d_cat(&cats, (CELL) i, &dmin, &dmax);
  215. else {
  216. dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
  217. dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
  218. }
  219. arc = 360.0 * ptr->stat / dist_stats->sumstat;
  220. draw_slice(colors, 1, dmin, dmax, color,
  221. ORIGIN_X, ORIGIN_Y,
  222. RADIUS, arc_counter, arc);
  223. arc_counter += arc;
  224. /*OUTLINE THE SLICE */
  225. draw_slice_unfilled(colors, color,
  226. ORIGIN_X,
  227. ORIGIN_Y, RADIUS,
  228. arc_counter, arc);
  229. D_d_color(dmin, colors);
  230. }
  231. else {
  232. bar_color = ptr->cat;
  233. arc = 360.0 * ptr->stat / dist_stats->sumstat;
  234. draw_slice_filled(colors, (DCELL) bar_color,
  235. color, ORIGIN_X,
  236. ORIGIN_Y, RADIUS,
  237. arc_counter, arc);
  238. D_color((CELL) bar_color, colors);
  239. arc_counter += arc;
  240. }
  241. }
  242. }
  243. else { /* stat cannot be found */
  244. if (xscale > 1) {
  245. /*
  246. draw=YES;
  247. */
  248. draw = NO;
  249. bar_color = D_translate_color("black");
  250. D_use_color(bar_color);
  251. }
  252. else
  253. draw = NO;
  254. }
  255. }
  256. /* draw the bar */
  257. if (draw == YES) {
  258. if (xscale != 1) {
  259. /* draw the bar as a box */
  260. /* if fp map and not null and range is not empty, draw smooth
  261. color range */
  262. if ((is_fp && !(i == dist_stats->mincat && nodata) &&
  263. dmin != dmax)) {
  264. for (j = 0; j < xscale; j++) {
  265. dval = dmin + j * (dmax - dmin) / xscale;
  266. D_d_color(dval, colors);
  267. x_box[0] = x_box[1] =
  268. xoffset + ((i - dist_stats->mincat) * xscale -
  269. 0.5 * xscale + j);
  270. x_box[2] = x_box[3] =
  271. xoffset + ((i - dist_stats->mincat) * xscale -
  272. 0.5 * xscale + j + 1);
  273. y_box[0] = y_box[3] = y_box[4] = y_line[0];
  274. y_box[1] = y_box[2] = bar_height;
  275. D_polygon_abs(x_box, y_box, 4);
  276. }
  277. }
  278. else { /* draw 1-color bar, color is already set */
  279. x_box[0] = x_box[1] =
  280. xoffset + ((i - dist_stats->mincat) * xscale -
  281. 0.5 * xscale);
  282. x_box[2] = x_box[3] =
  283. xoffset + ((i - dist_stats->mincat) * xscale +
  284. 0.5 * xscale);
  285. y_box[0] = y_box[3] = y_box[4] = y_line[0];
  286. y_box[1] = y_box[2] = bar_height;
  287. D_polygon_abs(x_box, y_box, 4);
  288. }
  289. }
  290. else { /* color is already set for 1-color bar */
  291. /* draw the bar as a line */
  292. x_box[0] = x_box[1] =
  293. xoffset + (i - dist_stats->mincat) * xscale;
  294. y_box[0] = yoffset;
  295. y_box[1] = bar_height;
  296. D_line_abs(x_box[0], y_box[0], x_box[1], y_box[1]);
  297. }
  298. }
  299. /* draw x-axis tic-marks and numbers */
  300. /* draw tick for null and for numbers at every tic step
  301. except when there is null, don't draw tic for mincat+1 */
  302. if ((rem((long int)i, tic_every) == 0L ||
  303. ((i == dist_stats->mincat) && nodata))
  304. && !(nodata && i == dist_stats->mincat + 1)) {
  305. /* draw a numbered tic-mark */
  306. D_use_color(color);
  307. D_begin();
  308. D_move_abs(xoffset
  309. + (i - dist_stats->mincat) * xscale
  310. - 0.5 * xscale,
  311. b - BAR_Y1 * height);
  312. D_cont_rel(0, BIG_TIC * height);
  313. D_end();
  314. D_stroke();
  315. if (nodata && i == dist_stats->mincat)
  316. sprintf(txt, "null");
  317. else if (is_fp)
  318. sprintf(txt, "%d", (int)(dmin / (double)tic_unit));
  319. else
  320. sprintf(txt, "%d", (int)(i / tic_unit));
  321. text_height = height * TEXT_HEIGHT;
  322. text_width = width * TEXT_WIDTH;
  323. D_text_size(text_width, text_height);
  324. D_get_text_box(txt, &tt, &tb, &tl, &tr);
  325. while ((tr - tl) > XTIC_DIST) {
  326. text_width *= 0.95;
  327. text_height *= 0.95;
  328. D_text_size(text_width, text_height);
  329. D_get_text_box(txt, &tt, &tb, &tl, &tr);
  330. }
  331. D_pos_abs(xoffset
  332. + (i - dist_stats->mincat) * xscale
  333. - 0.5 * xscale
  334. - (tr - tl) / 2,
  335. b - XNUMS_Y * height);
  336. D_text(txt);
  337. }
  338. else if (rem(i, tic_unit) == 0.0) {
  339. /* draw a tic-mark */
  340. D_use_color(color);
  341. D_begin();
  342. D_move_abs(xoffset
  343. + (i - dist_stats->mincat) * xscale
  344. - 0.5 * xscale,
  345. b - BAR_Y1 * height);
  346. D_cont_rel(0, SMALL_TIC * height);
  347. D_end();
  348. D_stroke();
  349. }
  350. }
  351. /* draw border around pie */
  352. D_use_color(color);
  353. draw_slice_unfilled(colors, color, ORIGIN_X, ORIGIN_Y, RADIUS, 0.0, 360.0);
  354. /* draw border around legend bar */
  355. D_use_color(color);
  356. D_polyline_abs(x_line, y_line, 5);
  357. /* draw the x-axis label */
  358. if (tic_unit != 1)
  359. sprintf(xlabel, "Cell Values %s", tic_name);
  360. else
  361. sprintf(xlabel, "Cell Values");
  362. text_height = height * TEXT_HEIGHT;
  363. text_width = width * TEXT_WIDTH;
  364. D_text_size(text_width, text_height);
  365. D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
  366. D_pos_abs(l + width / 2 - (tr - tl) / 2,
  367. b - LABEL * height);
  368. D_use_color(color);
  369. D_text(xlabel);
  370. return 0;
  371. }