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