main.c 28 KB

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