main.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. /* d.legend a.k.a d.leg.thin
  2. *
  3. * MODULE: d.legend
  4. *
  5. * Based on the old d.leg.thin, which replaced an even older
  6. * module called "d.legend".
  7. *
  8. * PURPOSE: Draw a graphical legend for a raster on the display mon
  9. *
  10. * AUTHORS:
  11. * Original version:
  12. * Bill Brown, U.S. Army Construction Engineering Research Laboratories
  13. * FP Support:
  14. * Radim Blazek
  15. * Merge of original "d.legend" code into d.leg.thin (now this module):
  16. * Markus Neteler
  17. * Late 2002: Rewrite of much of the code:
  18. * Hamish Bowman, Otago University, New Zealand
  19. *
  20. * COPYRIGHT: (c) 2006 The GRASS Development Team
  21. *
  22. * This program is free software under the GNU General Public
  23. * License (>=v2). Read the file COPYING that comes with GRASS
  24. * for details.
  25. *
  26. */
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <math.h>
  30. #include <grass/gis.h>
  31. #include <grass/raster.h>
  32. #include <grass/display.h>
  33. #include <grass/glocale.h>
  34. #include "local_proto.h"
  35. int main(int argc, char **argv)
  36. {
  37. char buff[512];
  38. char *map_name;
  39. int black;
  40. int cats_num;
  41. int color;
  42. int cur_dot_row;
  43. int do_cats;
  44. int dots_per_line;
  45. int thin;
  46. int i, j, k;
  47. int lines, steps;
  48. int fp;
  49. double t, b, l, r;
  50. int hide_catnum, hide_catstr, hide_nodata, do_smooth;
  51. char *cstr;
  52. int white;
  53. double x_box[5];
  54. double y_box[5];
  55. struct Categories cats;
  56. struct Colors colors;
  57. struct GModule *module;
  58. struct Option *opt_input, *opt_color, *opt_lines, *opt_thin, *opt_labelnum, *opt_at, *opt_use, *opt_range, *opt_font, *opt_path, *opt_charset, *opt_fontscale;
  59. struct Flag *hidestr, *hidenum, *hidenodata, *smooth, *flipit, *size;
  60. struct Range range;
  61. struct FPRange fprange;
  62. CELL min_ind, max_ind, null_cell;
  63. DCELL dmin, dmax, val;
  64. CELL min_colr, max_colr;
  65. DCELL min_dcolr, max_dcolr;
  66. int x0, x1, y0, y1, xyTemp;
  67. double X0, X1, Y0, Y1;
  68. int SigDigits;
  69. unsigned int MaxLabelLen;
  70. char DispFormat[5]; /* %.Xf\0 */
  71. int flip, horiz, UserRange;
  72. double UserRangeMin, UserRangeMax, UserRangeTemp;
  73. double *catlist, maxCat;
  74. int catlistCount, use_catlist;
  75. double fontscale;
  76. /* Initialize the GIS calls */
  77. G_gisinit(argv[0]);
  78. module = G_define_module();
  79. G_add_keyword(_("display"));
  80. G_add_keyword(_("cartography"));
  81. module->description =
  82. _("Displays a legend for a raster map in the active frame "
  83. "of the graphics monitor.");
  84. opt_input = G_define_standard_option(G_OPT_R_MAP);
  85. opt_input->description = _("Name of raster map");
  86. opt_color = G_define_standard_option(G_OPT_C_FG);
  87. opt_color->label = _("Text color");
  88. opt_color->guisection = _("Font settings");
  89. opt_lines = G_define_option();
  90. opt_lines->key = "lines";
  91. opt_lines->type = TYPE_INTEGER;
  92. opt_lines->answer = "0";
  93. opt_lines->options = "0-1000";
  94. opt_lines->description =
  95. _("Number of text lines (useful for truncating long legends)");
  96. opt_lines->guisection = _("Advanced");
  97. opt_thin = G_define_option();
  98. opt_thin->key = "thin";
  99. opt_thin->type = TYPE_INTEGER;
  100. opt_thin->required = NO;
  101. opt_thin->answer = "1";
  102. opt_thin->options = "1-1000";
  103. opt_thin->description = _("Thinning factor (thin=10 gives cats 0,10,20...)");
  104. opt_thin->guisection = _("Advanced");
  105. opt_labelnum = G_define_option();
  106. opt_labelnum->key = "labelnum";
  107. opt_labelnum->type = TYPE_INTEGER;
  108. opt_labelnum->answer = "5";
  109. opt_labelnum->options = "2-100";
  110. opt_labelnum->description = _("Number of text labels for smooth gradient legend");
  111. opt_labelnum->guisection = _("Gradient");
  112. opt_at = G_define_option();
  113. opt_at->key = "at";
  114. opt_at->key_desc = "bottom,top,left,right";
  115. opt_at->type = TYPE_DOUBLE; /* needs to be TYPE_DOUBLE to get past options check */
  116. opt_at->required = NO;
  117. opt_at->options = "0-100";
  118. opt_at->label =
  119. _("Size and placement as percentage of screen coordinates (0,0 is lower left)");
  120. opt_at->description = opt_at->key_desc;
  121. opt_at->answer = NULL;
  122. opt_use = G_define_option();
  123. opt_use->key = "use";
  124. opt_use->type = TYPE_DOUBLE; /* string as it is fed through the parser? */
  125. opt_use->required = NO;
  126. opt_use->description =
  127. _("List of discrete category numbers/values for legend");
  128. opt_use->multiple = YES;
  129. opt_use->guisection = _("Subset");
  130. opt_range = G_define_option();
  131. opt_range->key = "range";
  132. opt_range->key_desc = "min,max";
  133. opt_range->type = TYPE_DOUBLE; /* should it be type_double or _string ?? */
  134. opt_range->required = NO;
  135. opt_range->description =
  136. _("Use a subset of the map range for the legend (min,max)");
  137. opt_range->guisection = _("Subset");
  138. opt_font = G_define_option();
  139. opt_font->key = "font";
  140. opt_font->type = TYPE_STRING;
  141. opt_font->required = NO;
  142. opt_font->description = _("Font name");
  143. opt_font->guisection = _("Font settings");
  144. /* HB: this option should become redundant as soon as the bug in trunk's D_text_size() is fixed. */
  145. opt_fontscale = G_define_option();
  146. opt_fontscale->key = "fontscale";
  147. opt_fontscale->type = TYPE_DOUBLE;
  148. opt_fontscale->required = NO;
  149. opt_fontscale->answer = "5";
  150. opt_fontscale->options = "0-100";
  151. opt_fontscale->description = _("Font scaling factor for floating point legends");
  152. opt_fontscale->guisection = _("Font settings");
  153. opt_path = G_define_standard_option(G_OPT_F_INPUT);
  154. opt_path->key = "path";
  155. opt_path->required = NO;
  156. opt_path->description = _("Path to font file");
  157. opt_path->gisprompt = "old_file,font,file";
  158. opt_path->guisection = _("Font settings");
  159. opt_charset = G_define_option();
  160. opt_charset->key = "charset";
  161. opt_charset->type = TYPE_STRING;
  162. opt_charset->required = NO;
  163. opt_charset->description =
  164. _("Text encoding (only applicable to TrueType fonts)");
  165. opt_charset->guisection = _("Font settings");
  166. hidestr = G_define_flag();
  167. hidestr->key = 'v';
  168. hidestr->description = _("Do not show category labels");
  169. hidestr->guisection = _("Advanced");
  170. hidenum = G_define_flag();
  171. hidenum->key = 'c';
  172. hidenum->description = _("Do not show category numbers");
  173. hidenum->guisection = _("Advanced");
  174. hidenodata = G_define_flag();
  175. hidenodata->key = 'n';
  176. hidenodata->description = _("Skip categories with no label");
  177. hidenodata->guisection = _("Advanced");
  178. smooth = G_define_flag();
  179. smooth->key = 's';
  180. smooth->description = _("Draw smooth gradient");
  181. smooth->guisection = _("Gradient");
  182. flipit = G_define_flag();
  183. flipit->key = 'f';
  184. flipit->description = _("Flip legend");
  185. flipit->guisection = _("Advanced");
  186. size = G_define_flag();
  187. size->key = 's';
  188. size->description = _("Font size is height in pixels");
  189. size->guisection = _("Font settings");
  190. /* Check command line */
  191. if (G_parser(argc, argv))
  192. exit(EXIT_FAILURE);
  193. map_name = opt_input->answer;
  194. hide_catstr = hidestr->answer; /* note hide_catstr gets changed and re-read below */
  195. hide_catnum = hidenum->answer;
  196. hide_nodata = hidenodata->answer;
  197. do_smooth = smooth->answer;
  198. flip = flipit->answer;
  199. /* Create the fontscale factor for floating point legends */
  200. fontscale = atof(opt_fontscale->answer);
  201. fontscale = 1.0 / (100.0/fontscale);
  202. color = D_parse_color(opt_color->answer, TRUE);
  203. if (opt_lines->answer != NULL)
  204. sscanf(opt_lines->answer, "%d", &lines);
  205. thin = 1;
  206. if (opt_thin->answer != NULL)
  207. sscanf(opt_thin->answer, "%d", &thin);
  208. if (!thin)
  209. thin = 1;
  210. if (opt_labelnum->answer != NULL)
  211. sscanf(opt_labelnum->answer, "%d", &steps);
  212. catlistCount = 0;
  213. if (opt_use->answer != NULL) { /* should this be answerS ? */
  214. use_catlist = TRUE;
  215. catlist = (double *)G_calloc(100 + 1, sizeof(double));
  216. for (i = 0; i < 100; i++) /* fill with dummy values */
  217. catlist[i] = 1.0 * (i + 1);
  218. catlist[i] = 0;
  219. for (i = 0; (opt_use->answers[i] != NULL) && i < 100; i++)
  220. catlist[i] = atof(opt_use->answers[i]);
  221. catlistCount = i;
  222. }
  223. else
  224. use_catlist = FALSE;
  225. UserRange = FALSE;
  226. if (opt_range->answer != NULL) { /* should this be answerS ? */
  227. sscanf(opt_range->answers[0], "%lf", &UserRangeMin);
  228. sscanf(opt_range->answers[1], "%lf", &UserRangeMax);
  229. UserRange = TRUE;
  230. if (UserRangeMin > UserRangeMax) {
  231. UserRangeTemp = UserRangeMax;
  232. UserRangeMax = UserRangeMin;
  233. UserRangeMin = UserRangeTemp;
  234. flip = !flip;
  235. }
  236. }
  237. if (Rast_read_colors(map_name, "", &colors) == -1)
  238. G_fatal_error(_("Color file for <%s> not available"), map_name);
  239. fp = Rast_map_is_fp(map_name, "");
  240. if (fp && !use_catlist) {
  241. do_smooth = TRUE;
  242. /* fprintf(stderr, "FP map found - switching gradient legend on\n"); */
  243. flip = !flip;
  244. }
  245. if (Rast_read_cats(map_name, "", &cats) == -1)
  246. G_warning(_("Category file for <%s> not available"), map_name);
  247. Rast_set_c_null_value(&null_cell, 1);
  248. if (D_open_driver() != 0)
  249. G_fatal_error(_("No graphics device selected. "
  250. "Use d.mon to select graphics device."));
  251. white = D_translate_color(DEFAULT_FG_COLOR);
  252. black = D_translate_color(DEFAULT_BG_COLOR);
  253. if (opt_font->answer)
  254. D_font(opt_font->answer);
  255. else if (opt_path->answer)
  256. D_font(opt_path->answer);
  257. if (opt_charset->answer)
  258. D_encoding(opt_charset->answer);
  259. /* Figure out where to put text */
  260. D_setup_unity(0);
  261. D_get_src(&t, &b, &l, &r);
  262. if (opt_at->answer != NULL) {
  263. sscanf(opt_at->answers[0], "%lf", &Y1);
  264. sscanf(opt_at->answers[1], "%lf", &Y0);
  265. sscanf(opt_at->answers[2], "%lf", &X0);
  266. sscanf(opt_at->answers[3], "%lf", &X1);
  267. }
  268. else { /* default */
  269. Y1 = 12;
  270. Y0 = 88;
  271. X0 = 3;
  272. X1 = 7;
  273. }
  274. x0 = l + (int)((r - l) * X0 / 100.);
  275. x1 = l + (int)((r - l) * X1 / 100.);
  276. y0 = t + (int)((b - t) * (100. - Y0) / 100.); /* make lower left the origin */
  277. y1 = t + (int)((b - t) * (100. - Y1) / 100.);
  278. if (y0 > y1) { /* allow for variety in order of corner */
  279. flip = !flip; /* selection without broken output */
  280. xyTemp = y0;
  281. y0 = y1;
  282. y1 = xyTemp;
  283. }
  284. if (x0 > x1) {
  285. xyTemp = x0;
  286. x0 = x1;
  287. x1 = xyTemp;
  288. }
  289. if (x0 == x1)
  290. x1++; /* avoid 0 width boxes */
  291. if (y0 == y1)
  292. y1++;
  293. if ((x0 < l) || (x1 > r) || (y0 < t) || (y1 > b)) /* for mouse or at= 0- or 100+; needs to be after order check */
  294. G_warning(_("Legend box lies outside of frame. Text may not display properly."));
  295. horiz = (x1 - x0 > y1 - y0);
  296. if (horiz)
  297. G_message(_("Drawing horizontal legend as box width exceeds height"));
  298. if (!fp && horiz) /* better than nothing */
  299. do_smooth = TRUE;
  300. MaxLabelLen = 0; /* init variable */
  301. /* How many categories to show */
  302. if (!fp) {
  303. if (Rast_read_range(map_name, "", &range) == -1)
  304. G_fatal_error(_("Range information for <%s> not available (run r.support)"),
  305. map_name);
  306. Rast_get_range_min_max(&range, &min_ind, &max_ind);
  307. if (Rast_is_c_null_value(&min_ind))
  308. G_fatal_error(_("Input map contains no data"));
  309. Rast_get_c_color_range(&min_colr, &max_colr, &colors);
  310. if (UserRange) {
  311. if (min_ind < UserRangeMin)
  312. min_ind = (int)ceil(UserRangeMin);
  313. if (max_ind > UserRangeMax)
  314. max_ind = (int)floor(UserRangeMax);
  315. if (min_ind > UserRangeMin) {
  316. min_ind =
  317. UserRangeMin <
  318. min_colr ? min_colr : (int)ceil(UserRangeMin);
  319. G_warning(_("Requested range exceeds lower limit of actual data"));
  320. }
  321. if (max_ind < UserRangeMax) {
  322. max_ind =
  323. UserRangeMax >
  324. max_colr ? max_colr : (int)floor(UserRangeMax);
  325. G_warning(_("Requested range exceeds upper limit of actual data"));
  326. }
  327. }
  328. /* cats_num is total number of categories in raster */
  329. /* do_cats is total number of categories to be displayed */
  330. /* k is number of cats to be displayed after skipping unlabeled cats */
  331. /* lines is number of text lines/legend window */
  332. cats_num = max_ind - min_ind + 1;
  333. if (lines == 0)
  334. lines = cats_num;
  335. do_cats = cats_num > lines ? lines : cats_num;
  336. if (do_cats == cats_num)
  337. lines = (int)ceil((1.0 * lines) / thin);
  338. if (!use_catlist) {
  339. catlist = (double *)G_calloc(lines + 1, sizeof(double));
  340. catlistCount = lines;
  341. }
  342. /* see how many boxes there REALLY will be */
  343. maxCat = 0.0;
  344. for (i = min_ind, j = 1, k = 0; j <= do_cats && i <= max_ind;
  345. j++, i += thin) {
  346. if (!flip)
  347. cstr = Rast_get_c_cat(&i, &cats);
  348. else {
  349. CELL cat = max_ind - (i - min_ind);
  350. cstr = Rast_get_c_cat(&cat, &cats);
  351. }
  352. if (!use_catlist)
  353. catlist[j - 1] = (double)i;
  354. if (!cstr[0]) { /* no cat label found, skip str output */
  355. if (hide_nodata)
  356. continue;
  357. }
  358. else { /* ie has a label */
  359. if (!hide_catstr && (MaxLabelLen < strlen(cstr)))
  360. MaxLabelLen = strlen(cstr);
  361. }
  362. if (!hide_catnum)
  363. if (i > maxCat)
  364. maxCat = (double)i;
  365. k++; /* count of actual boxes drawn (hide_nodata option invaidates using j-1) */
  366. }
  367. lines = k;
  368. /* figure out how long the category + label will be */
  369. if (use_catlist) {
  370. MaxLabelLen = 0;
  371. maxCat = 0; /* reset */
  372. for (i = 0, k = 0; i < catlistCount; i++) {
  373. if ((catlist[i] < min_ind) || (catlist[i] > max_ind)) {
  374. G_fatal_error(_("use=%s out of range [%d,%d] (extend with range= ?)"),
  375. opt_use->answers[i], min_ind, max_ind);
  376. }
  377. cstr = Rast_get_d_cat(&catlist[i], &cats);
  378. if (!cstr[0]) { /* no cat label found, skip str output */
  379. if (hide_nodata)
  380. continue;
  381. }
  382. else { /* ie has a label */
  383. if (!hide_catstr && (MaxLabelLen < strlen(cstr)))
  384. MaxLabelLen = strlen(cstr);
  385. }
  386. if (!hide_catnum)
  387. if (catlist[i] > maxCat)
  388. maxCat = catlist[i];
  389. k++;
  390. }
  391. if (0 == k) /* nothing to draw */
  392. lines = 0;
  393. }
  394. if (MaxLabelLen > 0) { /* ie we've picked up at least one label */
  395. MaxLabelLen++; /* compensate for leading space */
  396. if (!hide_catnum)
  397. MaxLabelLen += 3; /* compensate for "%2d) " */
  398. }
  399. else {
  400. if (!hide_catnum)
  401. MaxLabelLen = 1;
  402. }
  403. /* compensate for categories >100 */
  404. if (!hide_catnum) {
  405. if (maxCat > 99)
  406. MaxLabelLen += (int)(log10(maxCat));
  407. }
  408. /* following covers both the above if(do_cats == cats_num) and k++ loop */
  409. if (lines < 1) {
  410. lines = 1; /* ward off the dpl floating point exception */
  411. G_fatal_error(_("Nothing to draw! (no categories with labels? out of range?)"));
  412. }
  413. /* Figure number of lines, number of pixles per line and text size */
  414. dots_per_line = ((y1 - y0) / lines);
  415. /* switch to a smooth legend for CELL maps with too many cats */
  416. /* an alternate solution is to set dots_per_line=1 */
  417. if ((dots_per_line == 0) && (do_smooth == 0)) {
  418. if (!use_catlist) {
  419. G_message(_("Forcing a smooth legend: too many categories for current window height"));
  420. do_smooth = 1;
  421. }
  422. }
  423. /* center really tiny legends */
  424. if (opt_at->answer == NULL) { /* if defualt scaling */
  425. if (!do_smooth && (dots_per_line < 4)) /* if so small that there's no box */
  426. if ((b - (dots_per_line * lines)) / (b * 1.0) > 0.15) /* if there's more than a 15% blank at the bottom */
  427. y0 = ((b - t) - (dots_per_line * lines)) / 2;
  428. }
  429. /* D_text_size((int)(dots_per_line*4/5), (int)(dots_per_line*4/5)) ; redundant */
  430. /* if(Rast_is_c_null_value(&min_ind) && Rast_is_c_null_value(&max_ind))
  431. {
  432. min_ind = 1;
  433. max_ind = 0;
  434. } */
  435. if (horiz)
  436. sprintf(DispFormat, "%%d");
  437. else {
  438. if (maxCat > 0.0)
  439. sprintf(DispFormat, "%%%dd", (int)(log10(fabs(maxCat))) + 1);
  440. else
  441. sprintf(DispFormat, "%%2d");
  442. }
  443. }
  444. else { /* is fp */
  445. if (Rast_read_fp_range(map_name, "", &fprange) == -1)
  446. G_fatal_error(_("Range information for <%s> not available"),
  447. map_name);
  448. Rast_get_fp_range_min_max(&fprange, &dmin, &dmax);
  449. Rast_get_d_color_range(&min_dcolr, &max_dcolr, &colors);
  450. if (UserRange) {
  451. if (dmin < UserRangeMin)
  452. dmin = UserRangeMin;
  453. if (dmax > UserRangeMax)
  454. dmax = UserRangeMax;
  455. if (dmin > UserRangeMin) {
  456. dmin = UserRangeMin < min_dcolr ? min_dcolr : UserRangeMin;
  457. G_warning(_("Color range exceeds lower limit of actual data"));
  458. }
  459. if (dmax < UserRangeMax) {
  460. dmax = UserRangeMax > max_dcolr ? max_dcolr : UserRangeMax;
  461. G_warning(_("Color range exceeds upper limit of actual data"));
  462. }
  463. }
  464. if (use_catlist) {
  465. for (i = 0; i < catlistCount; i++) {
  466. if ((catlist[i] < dmin) || (catlist[i] > dmax)) {
  467. G_fatal_error(_("use=%s out of range [%.3f, %.3f] (extend with range= ?)"),
  468. opt_use->answers[i], dmin, dmax);
  469. }
  470. if (strlen(opt_use->answers[i]) > MaxLabelLen)
  471. MaxLabelLen = strlen(opt_use->answers[i]);
  472. }
  473. }
  474. do_cats = 0; /* if only to get rid of the compiler warning */
  475. cats_num = 0; /* if only to get rid of the compiler warning */
  476. /* determine how many significant digits to display based on range */
  477. if (0 == (dmax - dmin)) /* trap divide by 0 for single value rasters */
  478. sprintf(DispFormat, "%%f");
  479. else {
  480. SigDigits = (int)ceil(log10(fabs(25 / (dmax - dmin))));
  481. if (SigDigits < 0)
  482. SigDigits = 0;
  483. if (SigDigits < 7)
  484. sprintf(DispFormat, "%%.%df", SigDigits);
  485. else
  486. sprintf(DispFormat, "%%.2g"); /* eg 4.2e-9 */
  487. }
  488. }
  489. if (use_catlist) {
  490. cats_num = catlistCount;
  491. do_cats = catlistCount;
  492. lines = catlistCount;
  493. do_smooth = 0;
  494. }
  495. if (do_smooth) {
  496. int wleg, lleg, dx, dy;
  497. int txsiz;
  498. int ppl;
  499. int tcell;
  500. float ScaleFactor = 1.0;
  501. if (horiz) {
  502. lleg = x1 - x0;
  503. dx = 0;
  504. dy = y1 - y0;
  505. if (fp)
  506. flip = !flip; /* horiz floats look better not flipped by default */
  507. }
  508. else {
  509. lleg = y1 - y0;
  510. dy = 0;
  511. dx = x1 - x0;
  512. }
  513. /* Draw colors */
  514. for (k = 0; k < lleg; k++) {
  515. if (!fp) {
  516. if (!flip)
  517. tcell =
  518. min_ind + k * (double)(1 + max_ind - min_ind) / lleg;
  519. else
  520. tcell =
  521. (max_ind + 1) - k * (double)(1 + max_ind -
  522. min_ind) / lleg;
  523. D_color((CELL) tcell, &colors);
  524. }
  525. else {
  526. if (!flip)
  527. val = dmin + k * (dmax - dmin) / lleg;
  528. else
  529. val = dmax - k * (dmax - dmin) / lleg;
  530. D_d_color(val, &colors);
  531. }
  532. if (dx < dy)
  533. D_box_abs(x0 + k, y0, x0 + k + (dx ? -dx : 1),
  534. y0 - (dy ? -dy : 1));
  535. else
  536. D_box_abs(x0, y0 + k, x0 - (dx ? -dx : 1),
  537. y0 + k + (dy ? -dy : 1));
  538. }
  539. /* Format text */
  540. if (!fp) { /* cut down labelnum so they don't repeat */
  541. if (do_cats < steps)
  542. steps = do_cats;
  543. if (1 == steps)
  544. steps = 2; /* ward off the ppl floating point exception */
  545. }
  546. for (k = 0; k < steps; k++) {
  547. if (!fp) {
  548. if (!flip)
  549. tcell =
  550. min_ind + k * (double)(max_ind - min_ind) / (steps -
  551. 1);
  552. else
  553. tcell =
  554. max_ind - k * (double)(max_ind - min_ind) / (steps -
  555. 1);
  556. cstr = Rast_get_c_cat(&tcell, &cats);
  557. if (!cstr[0]) /* no cats found, disable str output */
  558. hide_catstr = 1;
  559. else
  560. hide_catstr = hidestr->answer;
  561. buff[0] = 0; /* blank string */
  562. if (!hide_catnum) { /* num */
  563. sprintf(buff, DispFormat, tcell);
  564. if (!hide_catstr) /* both */
  565. strcat(buff, ")");
  566. }
  567. if (!hide_catstr) /* str */
  568. sprintf(buff + strlen(buff), " %s", cstr);
  569. }
  570. else { /* ie FP map */
  571. if (hide_catnum)
  572. buff[0] = 0; /* no text */
  573. else {
  574. if (!flip)
  575. val = dmin + k * (dmax - dmin) / (steps - 1);
  576. else
  577. val = dmax - k * (dmax - dmin) / (steps - 1);
  578. sprintf(buff, DispFormat, val);
  579. }
  580. }
  581. /* this probably shouldn't happen mid-loop as text sizes
  582. might not end up being uniform, but it's a start */
  583. if (strlen(buff) > MaxLabelLen)
  584. MaxLabelLen = strlen(buff);
  585. /* Draw text */
  586. if (!horiz)
  587. txsiz = (int)((y1 - y0) * fontscale);
  588. else
  589. txsiz = (int)((x1 - x0) * fontscale);
  590. /* scale text to fit in window if position not manually set */
  591. /* usually not needed, except when frame is really narrow */
  592. if (opt_at->answer == NULL) { /* ie default scaling */
  593. ScaleFactor = ((r - x1) / ((MaxLabelLen + 1) * txsiz * 0.81)); /* ?? txsiz*.81=actual text width. */
  594. if (ScaleFactor < 1.0) {
  595. txsiz = (int)(txsiz * ScaleFactor);
  596. }
  597. }
  598. if (txsiz < 0)
  599. txsiz = 0; /* keep it sane */
  600. D_text_size(txsiz, txsiz);
  601. D_use_color(color);
  602. ppl = (lleg) / (steps - 1);
  603. if (!horiz) {
  604. if (!k) /* first */
  605. D_pos_abs(x1 + 4, y0 + txsiz);
  606. else if (k == steps - 1) /* last */
  607. D_pos_abs(x1 + 4, y1);
  608. else
  609. D_pos_abs(x1 + 4, y0 + ppl * k + txsiz / 2);
  610. }
  611. else {
  612. /* text width is 0.81 of text height? so even though we set width
  613. to txsiz with D_text_size(), we still have to reduce.. hmmm */
  614. if (!k) /* first */
  615. D_pos_abs(x0 - (strlen(buff) * txsiz * .81 / 2),
  616. y1 + 4 + txsiz);
  617. else if (k == steps - 1) /* last */
  618. D_pos_abs(x1 - (strlen(buff) * txsiz * .81 / 2),
  619. y1 + 4 + txsiz);
  620. else
  621. D_pos_abs(x0 + ppl * k -
  622. (strlen(buff) * txsiz * .81 / 2),
  623. y1 + 4 + txsiz);
  624. }
  625. if(color)
  626. D_text(buff);
  627. } /*for */
  628. lleg = y1 - y0;
  629. wleg = x1 - x0;
  630. /* Black box */
  631. D_use_color(black);
  632. D_begin();
  633. D_move_abs(x0 + 1, y0 + 1);
  634. D_cont_rel(0, lleg - 2);
  635. D_cont_rel(wleg - 2, 0);
  636. D_cont_rel(0, 2 - lleg);
  637. D_close();
  638. D_end();
  639. D_stroke();
  640. /* White box */
  641. D_use_color(white);
  642. D_begin();
  643. D_move_abs(x0, y0);
  644. D_cont_rel(0, lleg);
  645. D_cont_rel(wleg, 0);
  646. D_cont_rel(0, -lleg);
  647. D_close();
  648. D_end();
  649. D_stroke();
  650. }
  651. else { /* non FP, no smoothing */
  652. int txsiz, true_l, true_r;
  653. float ScaleFactor = 1.0;
  654. /* set legend box bounds */
  655. true_l = l;
  656. true_r = r; /* preserve window width */
  657. l = x0;
  658. t = y0;
  659. r = x1;
  660. b = y1;
  661. D_pos_abs(x0, y0);
  662. /* figure out box height */
  663. if (do_cats == cats_num)
  664. dots_per_line = (b - t) / (lines + 1); /* +1 line for the two 1/2s at top and bottom */
  665. else
  666. dots_per_line = (b - t) / (lines + 2); /* + another line for 'x of y categories' text */
  667. /* adjust text size */
  668. /* txsiz = (int)((y1-y0)/(1.5*(lines+5))); */
  669. txsiz = (int)((y1 - y0) / (2.0 * lines));
  670. /* scale text to fit in window if position not manually set */
  671. if (opt_at->answer == NULL) { /* ie defualt scaling */
  672. ScaleFactor = ((true_r - true_l) / ((MaxLabelLen + 3) * txsiz * 0.81)); /* ?? txsiz*.81=actual text width. */
  673. if (ScaleFactor < 1.0) {
  674. txsiz = (int)floor(txsiz * ScaleFactor);
  675. dots_per_line = (int)floor(dots_per_line * ScaleFactor);
  676. }
  677. }
  678. if (dots_per_line < txsiz)
  679. txsiz = dots_per_line;
  680. D_text_size(txsiz, txsiz);
  681. /* Set up box arrays */
  682. x_box[0] = 0;
  683. y_box[0] = 0;
  684. x_box[1] = 0;
  685. y_box[1] = (5 - dots_per_line);
  686. x_box[2] = (dots_per_line - 5);
  687. y_box[2] = 0;
  688. x_box[3] = 0;
  689. y_box[3] = (dots_per_line - 5);
  690. x_box[4] = (5 - dots_per_line);
  691. y_box[4] = 0;
  692. /* Draw away */
  693. /* if(ScaleFactor < 1.0) */
  694. /* cur_dot_row = ((b-t) - (dots_per_line*lines))/2; *//* this will center the legend */
  695. /* else */
  696. cur_dot_row = t + dots_per_line / 2;
  697. /* j = (do_cats == cats_num ? 1 : 2 ); */
  698. for (i = 0, k = 0; i < catlistCount; i++)
  699. /* for(i=min_ind, j=1, k=0; j<=do_cats && i<=max_ind; j++, i+=thin) */
  700. {
  701. if (!flip)
  702. cstr = Rast_get_d_cat(&catlist[i], &cats);
  703. else
  704. cstr = Rast_get_d_cat(&catlist[catlistCount - i - 1], &cats);
  705. if (!cstr[0]) { /* no cat label found, skip str output */
  706. hide_catstr = 1;
  707. if (hide_nodata)
  708. continue;
  709. }
  710. else
  711. hide_catstr = hidestr->answer;
  712. k++; /* count of actual boxes drawn (hide_nodata option invaidates using j-1) */
  713. /* White box */
  714. cur_dot_row += dots_per_line;
  715. D_use_color(white);
  716. D_begin();
  717. D_move_abs(l + 2, (cur_dot_row - 1));
  718. D_cont_rel(0, (3 - dots_per_line));
  719. D_cont_rel((dots_per_line - 3), 0);
  720. D_cont_rel(0, (dots_per_line - 3));
  721. D_close();
  722. D_end();
  723. D_stroke();
  724. /* Black box */
  725. D_use_color(black);
  726. D_begin();
  727. D_move_abs(l + 3, (cur_dot_row - 2));
  728. D_cont_rel(0, (5 - dots_per_line));
  729. D_cont_rel((dots_per_line - 5), 0);
  730. D_cont_rel(0, (dots_per_line - 5));
  731. D_close();
  732. D_end();
  733. D_stroke();
  734. /* Color solid box */
  735. if (!fp) {
  736. if (!flip)
  737. D_color((CELL) (int)catlist[i], &colors);
  738. else
  739. D_color((CELL) (int)catlist[catlistCount - i - 1],
  740. &colors);
  741. }
  742. else {
  743. if (!flip)
  744. D_d_color(catlist[i], &colors);
  745. else
  746. D_d_color(catlist[catlistCount - i - 1], &colors);
  747. }
  748. D_pos_abs(l + 3, (cur_dot_row - 2));
  749. D_polygon_rel(x_box, y_box, 5);
  750. /* Draw text */
  751. D_use_color(color);
  752. if (!fp) {
  753. /* nothing, box only */
  754. buff[0] = 0;
  755. if (!hide_catnum) { /* num */
  756. sprintf(buff, DispFormat, (int)catlist[i]);
  757. if (!flip)
  758. sprintf(buff, DispFormat, (int)catlist[i]);
  759. else
  760. sprintf(buff, DispFormat,
  761. (int)catlist[catlistCount - i - 1]);
  762. if (!hide_catstr) /* both */
  763. strcat(buff, ")");
  764. }
  765. if (!hide_catstr) /* str */
  766. sprintf(buff + strlen(buff), " %s", cstr);
  767. }
  768. else { /* is fp */
  769. if (!flip) {
  770. if(use_catlist)
  771. /* pass through format exactly as given by the user in
  772. the use= command line parameter (helps with log scale) */
  773. sprintf(buff, "%s", opt_use->answers[i]);
  774. else
  775. /* automatically generated/tuned decimal precision format */
  776. sprintf(buff, DispFormat, catlist[i]);
  777. }
  778. else {
  779. if(use_catlist)
  780. sprintf(buff, "%s", opt_use->answers[catlistCount - i - 1]);
  781. else
  782. sprintf(buff, DispFormat, catlist[catlistCount - i - 1]);
  783. }
  784. }
  785. D_pos_abs((l + 3 + dots_per_line), (cur_dot_row) - 3);
  786. if(color)
  787. D_text(buff);
  788. }
  789. if (0 == k)
  790. G_fatal_error(_("Nothing to draw! (no categories with labels?)")); /* "(..., out of range?)" */
  791. if (do_cats != cats_num) {
  792. cur_dot_row += dots_per_line;
  793. /* sprintf(buff, "%d of %d categories\n", (j-1), cats_num) ; */
  794. sprintf(buff, "%d of %d categories\n", k, cats_num);
  795. /* shrink text if it will run off the screen */
  796. MaxLabelLen = strlen(buff) + 4;
  797. ScaleFactor = ((true_r - true_l) / (MaxLabelLen * txsiz * 0.81)); /* ?? txsiz*.81=actual text width. */
  798. if (ScaleFactor < 1.0) {
  799. txsiz = (int)floor(txsiz * ScaleFactor);
  800. D_text_size(txsiz, txsiz);
  801. }
  802. D_use_color(white);
  803. D_pos_abs((l + 3 + dots_per_line), (cur_dot_row));
  804. if(color)
  805. D_text(buff);
  806. }
  807. }
  808. D_save_command(G_recreate_command());
  809. D_close_driver();
  810. exit(EXIT_SUCCESS);
  811. }