gsd_legend.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*!
  2. \file gsd_legend.c
  3. \brief OGSF library - legend creation
  4. GRASS OpenGL gsurf OGSF Library
  5. Converted code from legend.c in SG3d
  6. routines to set viewport, close viewport, and make legend
  7. (C) 1999-2008 by the GRASS Development Team
  8. This program is free software under the
  9. GNU General Public License (>=v2).
  10. Read the file COPYING that comes with GRASS
  11. for details.
  12. \author Bill Brown USACERL
  13. \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
  14. */
  15. #include <stdlib.h>
  16. #include <grass/config.h>
  17. #if defined(OPENGL_X11) || defined(OPENGL_WINDOWS)
  18. #include <GL/gl.h>
  19. #include <GL/glu.h>
  20. #elif defined(OPENGL_AQUA)
  21. #include <OpenGL/gl.h>
  22. #include <OpenGL/glu.h>
  23. #endif
  24. #include <grass/gis.h>
  25. #include <grass/raster.h>
  26. #include <grass/glocale.h>
  27. #include <grass/ogsf.h>
  28. #include "rgbpack.h"
  29. static float *Listcats;
  30. static int Listnum = 0;
  31. /**** TODO
  32. static int bigger(float *f1, float *f2)
  33. {
  34. return (*f1 < *f2 ? -1 : (*f1 > *f2));
  35. }
  36. *****/
  37. #define MAX_LEGEND 256
  38. /*!
  39. \brief ADD
  40. \param wl
  41. \param wb
  42. \param wr
  43. \param wt
  44. */
  45. void gsd_bgn_legend_viewport(GLint wl, GLint wb, GLint wr, GLint wt)
  46. {
  47. /* sets the viewport for the legend and the model matrix */
  48. gsd_colormode(CM_COLOR);
  49. glPushAttrib(GL_VIEWPORT);
  50. glMatrixMode(GL_PROJECTION);
  51. gsd_pushmatrix();
  52. GS_set_draw(GSD_FRONT);
  53. GS_ready_draw();
  54. gsd_linewidth(1);
  55. gsd_popmatrix();
  56. glViewport(wl, wb, (wr - wl), (wt - wb));
  57. glLoadIdentity();
  58. gluOrtho2D(-0.5, (wr - wl) + 0.5, -0.5, (wt - wb) + 0.5);
  59. glMatrixMode(GL_MODELVIEW);
  60. glPushMatrix();
  61. glLoadIdentity();
  62. return;
  63. }
  64. /*!
  65. \brief ADD
  66. */
  67. void gsd_end_legend_viewport(void)
  68. {
  69. /* closes the legend viewport and resets matrix and buffers */
  70. gsd_popmatrix();
  71. glMatrixMode(GL_PROJECTION);
  72. gsd_popmatrix();
  73. glPopAttrib();
  74. glMatrixMode(GL_MODELVIEW);
  75. gsd_popmatrix();
  76. GS_done_draw();
  77. GS_set_draw(GSD_BACK);
  78. return;
  79. }
  80. /*!
  81. \brief ADD
  82. \param lownum
  83. \param highnum
  84. \param numvals
  85. \param vals
  86. \return 0 on failure
  87. \return range value
  88. */
  89. int gsd_get_nice_range(float lownum, float highnum, int numvals, float *vals)
  90. {
  91. /* get a nice range for displaying legend */
  92. int num = 0;
  93. float curnum, step, start;
  94. if (!numvals)
  95. return (0);
  96. step = (highnum - lownum) / (float)numvals;
  97. gsd_make_nice_number(&step);
  98. /* get a starting point */
  99. start = step * (int)(1 + lownum / step);
  100. if (start - lownum < .65 * step)
  101. start += step;
  102. for (curnum = start; curnum < (highnum - .65 * step); curnum += step) {
  103. vals[num++] = curnum;
  104. }
  105. return (num);
  106. }
  107. /*!
  108. \brief ADD
  109. \param num
  110. \return 0 on error
  111. \return 1 on success
  112. */
  113. int gsd_make_nice_number(float *num)
  114. {
  115. float newnum, nextnum;
  116. if (*num < 0)
  117. return (0);
  118. if (*num < 1) {
  119. newnum = 1.;
  120. while (.5 * newnum > *num) {
  121. nextnum = newnum / 10.;
  122. newnum /= 2.;
  123. if (.5 * newnum > *num)
  124. newnum /= 2.;
  125. if (.5 * newnum > *num)
  126. newnum = nextnum;
  127. }
  128. }
  129. else {
  130. newnum = 1.;
  131. while (2 * newnum <= *num) {
  132. nextnum = newnum * 10.;
  133. newnum *= 2.5;
  134. if (2 * newnum <= *num)
  135. newnum *= 2.;
  136. if (2 * newnum <= *num)
  137. newnum = nextnum;
  138. }
  139. if (newnum == 2.5)
  140. newnum = 3;
  141. /* 2.5 isn't nice, but .25, 25, 250 ... are */
  142. }
  143. *num = newnum;
  144. return (1);
  145. }
  146. /*!
  147. \brief Put legend
  148. \param name
  149. \param fontbase font-base
  150. \param size
  151. \param flags
  152. \param rangef
  153. \param pt
  154. \return
  155. */
  156. GLuint gsd_put_legend(const char *name, GLuint fontbase, int size, int *flags,
  157. float *rangef, int *pt)
  158. {
  159. GLint sl, sr, sb, st;
  160. GLuint legend_list;
  161. int cat_labs = 0, cat_vals = 0, do_invert = 0, discrete = 0;
  162. int is_fp, fprec, iprec;
  163. struct Categories cats;
  164. struct Range range;
  165. struct FPRange fp_range;
  166. const char *mapset;
  167. struct Colors colors;
  168. CELL min, max;
  169. DCELL fmin, fmax;
  170. float labvals[12];
  171. legend_list = gsd_makelist();
  172. gsd_bgnlist(legend_list, 1);
  173. /* set coords from pt */
  174. sl = pt[0];
  175. sr = pt[1];
  176. sb = pt[2];
  177. st = pt[3];
  178. /* set legend flags */
  179. if (flags[0])
  180. cat_vals = 1;
  181. if (flags[1])
  182. cat_labs = 1;
  183. if (flags[3])
  184. discrete = 1;
  185. if (flags[2])
  186. do_invert = 1;
  187. mapset = G_find_raster2(name, "");
  188. if (mapset == NULL) {
  189. G_warning(_("Raster map <%s> not found"), name);
  190. return (-1);
  191. }
  192. is_fp = Rast_map_is_fp(name, mapset);
  193. if (Rast_read_colors(name, mapset, &colors) == -1) {
  194. G_warning(_("Unable to read color file of raster map <%s>"), name);
  195. return (-1);
  196. }
  197. if (cat_labs)
  198. if (Rast_read_cats(name, mapset, &cats) == -1) {
  199. G_warning(_("Unable to read category file of raster map <%s>"),
  200. name);
  201. cat_labs = 0;
  202. }
  203. if (flags[4] && rangef[0] != -9999. && rangef[1] != -9999.) {
  204. fmin = rangef[0];
  205. fmax = rangef[1];
  206. if (!is_fp) {
  207. min = (int)fmin;
  208. max = (int)fmax;
  209. }
  210. }
  211. else {
  212. if (is_fp) {
  213. if (Rast_read_fp_range(name, mapset, &fp_range) != 1) {
  214. G_warning(_("Unable to read fp range of raster map <%s>"),
  215. name);
  216. return (-1);
  217. }
  218. Rast_get_fp_range_min_max(&fp_range, &fmin, &fmax);
  219. if (flags[4] && rangef[0] != -9999.)
  220. fmin = rangef[0];
  221. if (flags[4] && rangef[1] != -9999.)
  222. fmax = rangef[1];
  223. }
  224. else {
  225. if (Rast_read_range(name, mapset, &range) == -1) {
  226. G_warning(_("Unable to read range of raster map <%s>"), name);
  227. return (-1);
  228. }
  229. Rast_get_range_min_max(&range, &min, &max);
  230. if (flags[4] && rangef[0] != -9999.)
  231. min = rangef[0];
  232. if (flags[4] && rangef[1] != -9999.)
  233. max = rangef[1];
  234. fmin = min;
  235. fmax = max;
  236. }
  237. }
  238. if (fmin == fmax)
  239. G_warning(_("Range request error for legend"));
  240. /* set a reasonable precision */
  241. if (is_fp) {
  242. float df;
  243. df = fmax - fmin;
  244. if (df < .1)
  245. fprec = 6;
  246. else if (df < 1)
  247. fprec = 4;
  248. else if (df < 10)
  249. fprec = 3;
  250. else if (df < 100)
  251. fprec = 2;
  252. else
  253. fprec = 1;
  254. }
  255. else {
  256. int tmp, p1, p2;
  257. iprec = p1 = p2 = 1;
  258. if (max > 0)
  259. for (tmp = 1; tmp < max; tmp *= 10, p1++) ;
  260. if (min < 0)
  261. for (tmp = -1; tmp > min; tmp *= 10, p2++) ;
  262. iprec = (p1 > p2 ? p1 : p2);
  263. }
  264. /*********
  265. * TODO incorp lists
  266. if(list && (legend_type & LT_LIST)){
  267. Listcats = list;
  268. Listnum = nlist;
  269. qsort(Listcats, Listnum, sizeof(float), bigger);
  270. discrete = 1;
  271. }
  272. else
  273. Listnum = 0;
  274. *********/
  275. /* how many labels? */
  276. /*
  277. numlabs can't be = max - min + 1 any more because of floating point
  278. maybe shouldn't allow discrete legend for floating point maps (unless list)
  279. or else check number of different values in floating point map
  280. and use each if "reasonable"
  281. gs_get_values_in_range(gs, att, low, high, values, &nvals)
  282. the nvals sent has a max number to return, nvals returned is the actual
  283. number set in values, return val is 1 on success, -1 if > max vals found
  284. might need to think about doing histograms first & use same routines here
  285. could also have a LT_MOST that would limit # to some N most frequent
  286. */
  287. /*!
  288. ???
  289. */
  290. {
  291. int i, k, lleg, horiz;
  292. int red, green, blue;
  293. CELL tcell;
  294. DCELL tdcell, pdcell;
  295. float vert1[2], vert2[2], vert3[2], vert4[2];
  296. float *dv1, *dv2; /* changing vertex coord */
  297. float *sv1, *sv2; /* stable vertex coord */
  298. float stab1, stab2;
  299. unsigned long colr;
  300. float *dividers;
  301. int labw, maxlabw, numlabs;
  302. float labpos, labpt[3];
  303. const char *cstr;
  304. char buff[80];
  305. GLint wt, wb, wl, wr; /* Whole legend area, not just box */
  306. int xoff, yoff;
  307. int incr; /* for do_invert */
  308. horiz = (sr - sl > st - sb);
  309. dividers = NULL;
  310. if (discrete) {
  311. numlabs = Listnum ? Listnum : max - min + 1;
  312. /* watch out for trying to display mega cats */
  313. if (is_fp && !Listnum) {
  314. discrete = 0; /* maybe later do stats & allow if few #s */
  315. G_warning(_("Unable to show discrete FP range (use list"));
  316. return (-1);
  317. }
  318. if (numlabs < MAX_LEGEND)
  319. dividers = (float *)G_malloc(numlabs * sizeof(float));
  320. }
  321. else {
  322. numlabs = gsd_get_nice_range(fmin, fmax, 4, labvals + 1);
  323. labvals[0] = fmin;
  324. labvals[numlabs + 1] = fmax;
  325. numlabs += 2;
  326. }
  327. /* find longest string, reset viewport & saveunder */
  328. maxlabw = 0;
  329. if (cat_labs || cat_vals) {
  330. for (k = 0; k < numlabs; k++) {
  331. if (is_fp) {
  332. tdcell = discrete ? Listcats[k] : labvals[k];
  333. if (cat_labs) {
  334. cstr = Rast_get_d_cat(&tdcell, &cats);
  335. }
  336. if (cat_labs && !cat_vals) {
  337. sprintf(buff, "%s", cstr);
  338. }
  339. else {
  340. if (cat_labs && cat_vals) {
  341. if (cstr)
  342. sprintf(buff, "%.*lf) %s",
  343. fprec, tdcell, cstr);
  344. else
  345. sprintf(buff, "%.*lf", fprec, tdcell);
  346. }
  347. else if (cat_vals)
  348. sprintf(buff, "%.*lf", fprec, tdcell);
  349. }
  350. }
  351. else {
  352. tcell = discrete ? Listnum ?
  353. Listcats[k] : min + k : labvals[k];
  354. if (cat_labs && !cat_vals)
  355. sprintf(buff, "%s", Rast_get_c_cat(&tcell, &cats));
  356. else {
  357. if (cat_labs && cat_vals) {
  358. cstr = Rast_get_c_cat(&tcell, &cats);
  359. if (cstr[0])
  360. sprintf(buff, "%*d) %s", iprec, tcell, cstr);
  361. else
  362. sprintf(buff, "%d", tcell);
  363. }
  364. else if (cat_vals)
  365. sprintf(buff, "%d", tcell);
  366. }
  367. }
  368. labw = gsd_get_txtwidth(buff, size);
  369. if (labw > maxlabw) {
  370. maxlabw = labw;
  371. }
  372. }
  373. }
  374. if (horiz) {
  375. xoff = maxlabw / 2 + get_txtxoffset();
  376. wl = sl - xoff;
  377. wr = sr + xoff;
  378. yoff = 0;
  379. wb = sb;
  380. /*
  381. wt = st + gsd_get_txtheight() + get_txtdescender() +3;
  382. */
  383. wt = st + gsd_get_txtheight(size) * 2 + 3;
  384. }
  385. else {
  386. xoff = 0;
  387. wl = sl;
  388. wr = sr + maxlabw + get_txtxoffset() + 3;
  389. /*
  390. yoff = gsd_get_txtheight()/2 + get_txtdescender();
  391. */
  392. yoff = gsd_get_txtheight(size);
  393. wb = sb - yoff;
  394. wt = st + yoff;
  395. }
  396. /* initialize viewport */
  397. gsd_bgn_legend_viewport(wl, wb, wr, wt);
  398. vert1[X] = vert2[X] = xoff;
  399. vert1[Y] = vert2[Y] = yoff;
  400. if (horiz) {
  401. lleg = sr - sl;
  402. dv1 = vert1 + X;
  403. dv2 = vert2 + X;
  404. sv1 = vert1 + Y;
  405. sv2 = vert2 + Y;
  406. stab2 = vert2[Y] = st - sb + yoff;
  407. stab1 = vert1[Y] = yoff;
  408. if (do_invert)
  409. vert1[X] = vert2[X] = sr - sl + xoff;
  410. }
  411. else {
  412. lleg = st - sb;
  413. dv1 = vert1 + Y;
  414. dv2 = vert2 + Y;
  415. sv1 = vert1 + X;
  416. sv2 = vert2 + X;
  417. stab2 = vert2[X] = sr - sl + xoff;
  418. stab1 = vert1[X] = xoff;
  419. if (do_invert)
  420. vert1[Y] = vert2[Y] = st - sb + yoff;
  421. }
  422. if (discrete) {
  423. if (numlabs > lleg / 5)
  424. G_warning(_("Too many categories to show as discrete!"));
  425. else if (numlabs > 1.2 * lleg / gsd_get_txtheight(size))
  426. G_warning(_("Try using smaller font!"));
  427. }
  428. incr = do_invert ? -1 : 1;
  429. for (k = 0, i = 0; k < lleg; k++) {
  430. if (discrete && Listnum)
  431. tdcell = Listcats[(int)((float)k * numlabs / lleg)];
  432. else {
  433. tcell = min + k * (max - min + 1) / lleg;
  434. tdcell = fmin + k * (fmax - fmin) / lleg;
  435. if (!is_fp)
  436. tdcell = tcell;
  437. }
  438. if (k == 0 || tdcell != pdcell) {
  439. if (is_fp)
  440. Rast_get_d_color(&tdcell,
  441. &red, &green, &blue, &colors);
  442. else
  443. Rast_get_c_color((CELL *)&tdcell, &red, &green, &blue, &colors);
  444. RGB_TO_INT(red, green, blue, colr);
  445. if (discrete) { /* draw black-white-black separator */
  446. if (k > 0) {
  447. *dv1 -= 2. * incr;
  448. *dv2 -= 2. * incr;
  449. gsd_color_func(0x0);
  450. gsd_bgnline();
  451. glVertex2fv(vert1);
  452. glVertex2fv(vert2);
  453. gsd_endline();
  454. *dv1 += 1. * incr;
  455. *dv2 += 1. * incr;
  456. if (dividers)
  457. dividers[i++] = *dv1;
  458. *dv1 += 1. * incr;
  459. *dv2 += 1. * incr;
  460. gsd_color_func(0x0);
  461. gsd_bgnline();
  462. glVertex2fv(vert1);
  463. glVertex2fv(vert2);
  464. gsd_endline();
  465. *dv1 += 1. * incr;
  466. *dv2 += 1. * incr;
  467. pdcell = tdcell;
  468. continue;
  469. }
  470. }
  471. }
  472. gsd_color_func(colr);
  473. gsd_bgnline();
  474. glVertex2fv(vert1);
  475. glVertex2fv(vert2);
  476. gsd_endline();
  477. glFlush();
  478. *dv1 += 1. * incr;
  479. *dv2 += 1. * incr;
  480. pdcell = tdcell;
  481. }
  482. /* Black box */
  483. vert1[X] = vert2[X] = 1. + xoff;
  484. vert1[Y] = vert4[Y] = 1. + yoff;
  485. vert3[X] = vert4[X] = sr - sl - 1. + xoff;
  486. vert3[Y] = vert2[Y] = st - sb - 1. + yoff;
  487. gsd_color_func(0x000000);
  488. gsd_bgnline();
  489. glVertex2fv(vert1);
  490. glVertex2fv(vert2);
  491. glVertex2fv(vert3);
  492. glVertex2fv(vert4);
  493. glVertex2fv(vert1);
  494. gsd_endline();
  495. /* White box */
  496. vert1[X] = vert2[X] = xoff;
  497. vert1[Y] = vert4[Y] = yoff;
  498. vert3[X] = vert4[X] = sr - sl + xoff;
  499. vert3[Y] = vert2[Y] = st - sb + yoff;
  500. gsd_color_func(0xFFFFFF);
  501. gsd_bgnline();
  502. glVertex2fv(vert1);
  503. glVertex2fv(vert2);
  504. glVertex2fv(vert3);
  505. glVertex2fv(vert4);
  506. glVertex2fv(vert1);
  507. gsd_endline();
  508. /* draw discrete dividers */
  509. if (dividers) {
  510. gsd_color_func(0xFFFFFFFF);
  511. *sv1 = stab1;
  512. *sv2 = stab2;
  513. for (k = 0; k < i; k++) {
  514. *dv1 = *dv2 = dividers[k];
  515. gsd_bgnline();
  516. glVertex2fv(vert1);
  517. glVertex2fv(vert2);
  518. gsd_endline();
  519. }
  520. }
  521. if (cat_labs || cat_vals) {
  522. labpt[Z] = 0;
  523. for (k = 0; k < numlabs; k++) {
  524. if (is_fp) {
  525. if (discrete && Listnum) {
  526. tdcell = Listcats[k];
  527. labpos = (k + .5) / numlabs;
  528. }
  529. else {
  530. /* show_all not supported unless Listnum */
  531. tdcell = labvals[k];
  532. labpos = (tdcell - fmin) / (fmax - fmin);
  533. }
  534. }
  535. else {
  536. if (discrete && Listnum) {
  537. tcell = Listcats[k];
  538. labpos = (k + .5) / numlabs;
  539. }
  540. else {
  541. tcell = discrete ? min + k : labvals[k];
  542. labpos = (tcell - min + .5) / (max - min + 1);
  543. }
  544. }
  545. if (do_invert)
  546. labpos = 1. - labpos;
  547. if (cat_labs) {
  548. if (!is_fp)
  549. cstr = Rast_get_c_cat(&tcell, &cats);
  550. else
  551. cstr = Rast_get_d_cat(&tdcell, &cats);
  552. }
  553. if (cat_labs && !cat_vals)
  554. sprintf(buff, "%s", cstr);
  555. else {
  556. if (cat_labs && cat_vals) {
  557. if (cstr)
  558. if (is_fp)
  559. sprintf(buff, "%.*lf) %s",
  560. fprec, tdcell, cstr);
  561. else
  562. sprintf(buff, "%*d) %s", iprec, tcell, cstr);
  563. else if (is_fp)
  564. sprintf(buff, "%.*lf", fprec, tdcell);
  565. else
  566. sprintf(buff, "%d", tcell);
  567. }
  568. else if (cat_vals) {
  569. if (is_fp)
  570. sprintf(buff, "%.*lf", fprec, tdcell);
  571. else
  572. sprintf(buff, "%d", tcell);
  573. }
  574. }
  575. if (horiz) {
  576. labpt[X] = labpos * (sr - sl) + xoff -
  577. gsd_get_txtwidth(buff, size) / 2 - get_txtxoffset();
  578. labpt[Y] =
  579. st - sb + yoff + 3 + gsd_get_txtheight(size) / 2;
  580. }
  581. else {
  582. labpt[X] = sr - sl + xoff + get_txtxoffset() + 3;
  583. /*
  584. labpt[Y] = labpos * (st - sb) + yoff -
  585. gsd_get_txtheight()/2 + get_txtdescender();
  586. */
  587. labpt[Y] = labpos * (st - sb) + yoff -
  588. gsd_get_txtheight(size);
  589. }
  590. /* set color for black text -- maybe add option for color
  591. * supplied with font ??
  592. */
  593. gsd_color_func(0x000000);
  594. do_label_display(fontbase, labpt, buff);
  595. }
  596. }
  597. if (discrete)
  598. G_free(dividers);
  599. }
  600. if (cat_labs)
  601. Rast_free_cats(&cats);
  602. Rast_free_colors(&colors);
  603. gsd_end_legend_viewport();
  604. /*
  605. gsd_unset_font(fontbase);
  606. */
  607. gsd_endlist();
  608. return (legend_list);
  609. }