bar.c 13 KB

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