GRASS GIS 8 Programmer's Manual 8.2.1(2023)-exported
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/*!
147 \brief ADD
148
149 \param num
150
151 \return 0 on error
152 \return 1 on success
153 */
154int gsd_make_nice_number(float *num)
155{
156 float newnum, nextnum;
157
158 if (*num < 0)
159 return (0);
160
161 if (*num < 1) {
162 newnum = 1.;
163 while (.5 * newnum > *num) {
164 nextnum = newnum / 10.;
165 newnum /= 2.;
166 if (.5 * newnum > *num)
167 newnum /= 2.;
168 if (.5 * newnum > *num)
169 newnum = nextnum;
170 }
171 }
172 else {
173 newnum = 1.;
174 while (2 * newnum <= *num) {
175 nextnum = newnum * 10.;
176 newnum *= 2.5;
177 if (2 * newnum <= *num)
178 newnum *= 2.;
179 if (2 * newnum <= *num)
180 newnum = nextnum;
181 }
182 if (newnum == 2.5)
183 newnum = 3;
184 /* 2.5 isn't nice, but .25, 25, 250 ... are */
185 }
186 *num = newnum;
187 return (1);
188}
189
190/*!
191 \brief Put legend
192
193 \param name
194 \param fontbase font-base
195 \param size
196 \param flags
197 \param rangef
198 \param pt
199
200 \return
201 */
202GLuint gsd_put_legend(const char *name, GLuint fontbase, int size, int *flags,
203 float *rangef, int *pt)
204{
205 GLint sl, sr, sb, st;
206 GLuint legend_list;
207 int cat_labs = 0, cat_vals = 0, do_invert = 0, discrete = 0;
208 int is_fp, fprec, iprec;
209 struct Categories cats;
210 struct Range range;
211 struct FPRange fp_range;
212 const char *mapset;
213 struct Colors colors;
214 CELL min, max;
215 DCELL fmin, fmax;
216 float labvals[12];
217
218 legend_list = gsd_makelist();
219 gsd_bgnlist(legend_list, 1);
220
221 /* set coords from pt */
222 sl = pt[0];
223 sr = pt[1];
224 sb = pt[2];
225 st = pt[3];
226
227 /* set legend flags */
228 if (flags[0])
229 cat_vals = 1;
230 if (flags[1])
231 cat_labs = 1;
232 if (flags[3])
233 discrete = 1;
234 if (flags[2])
235 do_invert = 1;
236
237 mapset = G_find_raster2(name, "");
238 if (mapset == NULL) {
239 G_warning(_("Raster map <%s> not found"), name);
240 return (-1);
241 }
242
243 is_fp = Rast_map_is_fp(name, mapset);
244
245 if (Rast_read_colors(name, mapset, &colors) == -1) {
246 G_warning(_("Unable to read color file of raster map <%s>"), name);
247 return (-1);
248 }
249
250 if (cat_labs)
251 if (Rast_read_cats(name, mapset, &cats) == -1) {
252 G_warning(_("Unable to read category file of raster map <%s>"),
253 name);
254 cat_labs = 0;
255 }
256
257
258 if (flags[4] && rangef[0] != -9999. && rangef[1] != -9999.) {
259 fmin = rangef[0];
260 fmax = rangef[1];
261 if (!is_fp) {
262 min = (int)fmin;
263 max = (int)fmax;
264 }
265 }
266 else {
267 if (is_fp) {
268 if (Rast_read_fp_range(name, mapset, &fp_range) != 1) {
269 G_warning(_("Unable to read fp range of raster map <%s>"),
270 name);
271 return (-1);
272 }
273 Rast_get_fp_range_min_max(&fp_range, &fmin, &fmax);
274 if (flags[4] && rangef[0] != -9999.)
275 fmin = rangef[0];
276 if (flags[4] && rangef[1] != -9999.)
277 fmax = rangef[1];
278 }
279 else {
280 if (Rast_read_range(name, mapset, &range) == -1) {
281 G_warning(_("Unable to read range of raster map <%s>"), name);
282 return (-1);
283 }
284 Rast_get_range_min_max(&range, &min, &max);
285 if (flags[4] && rangef[0] != -9999.)
286 min = rangef[0];
287 if (flags[4] && rangef[1] != -9999.)
288 max = rangef[1];
289 fmin = min;
290 fmax = max;
291 }
292 }
293
294 if (fmin == fmax)
295 G_warning(_("Range request error for legend"));
296
297 /* set a reasonable precision */
298 if (is_fp) {
299 float df;
300
301 df = fmax - fmin;
302 if (df < .1)
303 fprec = 6;
304 else if (df < 1)
305 fprec = 4;
306 else if (df < 10)
307 fprec = 3;
308 else if (df < 100)
309 fprec = 2;
310 else
311 fprec = 1;
312
313 }
314 else {
315 int tmp, p1, p2;
316
317 iprec = p1 = p2 = 1;
318 if (max > 0)
319 for (tmp = 1; tmp < max; tmp *= 10, p1++) ;
320 if (min < 0)
321 for (tmp = -1; tmp > min; tmp *= 10, p2++) ;
322
323 iprec = (p1 > p2 ? p1 : p2);
324 }
325
326/*********
327 * TODO incorp lists
328
329 if(list && (legend_type & LT_LIST)){
330 Listcats = list;
331 Listnum = nlist;
332 qsort(Listcats, Listnum, sizeof(float), bigger);
333 discrete = 1;
334 }
335 else
336 Listnum = 0;
337
338*********/
339
340
341 /* how many labels? */
342 /*
343 numlabs can't be = max - min + 1 any more because of floating point
344 maybe shouldn't allow discrete legend for floating point maps (unless list)
345 or else check number of different values in floating point map
346 and use each if "reasonable"
347 gs_get_values_in_range(gs, att, low, high, values, &nvals)
348 the nvals sent has a max number to return, nvals returned is the actual
349 number set in values, return val is 1 on success, -1 if > max vals found
350
351 might need to think about doing histograms first & use same routines here
352 could also have a LT_MOST that would limit # to some N most frequent
353 */
354
355 /*!
356 ???
357 */
358 {
359 int i, k, lleg, horiz;
360 int red, green, blue;
361 CELL tcell;
362 DCELL tdcell, pdcell;
363 float vert1[2], vert2[2], vert3[2], vert4[2];
364 float *dv1, *dv2; /* changing vertex coord */
365 float *sv1, *sv2; /* stable vertex coord */
366 float stab1, stab2;
367 unsigned long colr;
368 float *dividers;
369 int labw, maxlabw, numlabs;
370 float labpos, labpt[3];
371 const char *cstr;
372 char buff[80];
373 GLint wt, wb, wl, wr; /* Whole legend area, not just box */
374 int xoff, yoff;
375 int incr; /* for do_invert */
376
377 horiz = (sr - sl > st - sb);
378 dividers = NULL;
379
380 if (discrete) {
381 numlabs = Listnum ? Listnum : max - min + 1;
382 /* watch out for trying to display mega cats */
383 if (is_fp && !Listnum) {
384 discrete = 0; /* maybe later do stats & allow if few #s */
385 G_warning(_("Unable to show discrete FP range (use list)"));
386 return (-1);
387 }
388 if (numlabs < MAX_LEGEND)
389 dividers = (float *)G_malloc(numlabs * sizeof(float));
390 }
391 else {
392 numlabs = gsd_get_nice_range(fmin, fmax, 4, labvals + 1);
393 labvals[0] = fmin;
394 labvals[numlabs + 1] = fmax;
395 numlabs += 2;
396 }
397
398 /* find longest string, reset viewport & saveunder */
399 maxlabw = 0;
400
401 if (cat_labs || cat_vals) {
402 for (k = 0; k < numlabs; k++) {
403 if (is_fp) {
404 tdcell = discrete ? Listcats[k] : labvals[k];
405 if (cat_labs) {
406 cstr = Rast_get_d_cat(&tdcell, &cats);
407 }
408 if (cat_labs && !cat_vals) {
409 sprintf(buff, "%s", cstr);
410 }
411 else {
412 if (cat_labs && cat_vals) {
413 if (cstr)
414 sprintf(buff, "%.*lf) %s",
415 fprec, tdcell, cstr);
416 else
417 sprintf(buff, "%.*lf", fprec, tdcell);
418 }
419 else if (cat_vals)
420 sprintf(buff, "%.*lf", fprec, tdcell);
421 }
422 }
423 else {
424 tcell = discrete ? Listnum ?
425 Listcats[k] : min + k : labvals[k];
426 if (cat_labs && !cat_vals)
427 sprintf(buff, "%s", Rast_get_c_cat(&tcell, &cats));
428 else {
429 if (cat_labs && cat_vals) {
430 cstr = Rast_get_c_cat(&tcell, &cats);
431 if (cstr[0])
432 sprintf(buff, "%*d) %s", iprec, tcell, cstr);
433 else
434 sprintf(buff, "%d", tcell);
435 }
436 else if (cat_vals)
437 sprintf(buff, "%d", tcell);
438 }
439 }
440 labw = gsd_get_txtwidth(buff, size);
441 if (labw > maxlabw) {
442 maxlabw = labw;
443 }
444 }
445 }
446
447 if (horiz) {
448 xoff = maxlabw / 2 + get_txtxoffset();
449 wl = sl - xoff;
450 wr = sr + xoff;
451 yoff = 0;
452 wb = sb;
453 /*
454 wt = st + gsd_get_txtheight() + get_txtdescender() +3;
455 */
456 wt = st + gsd_get_txtheight(size) * 2 + 3;
457 }
458 else {
459 xoff = 0;
460 wl = sl;
461 wr = sr + maxlabw + get_txtxoffset() + 3;
462 /*
463 yoff = gsd_get_txtheight()/2 + get_txtdescender();
464 */
465 yoff = gsd_get_txtheight(size);
466 wb = sb - yoff;
467 wt = st + yoff;
468 }
469
470 /* initialize viewport */
471 gsd_bgn_legend_viewport(wl, wb, wr, wt);
472
473
474 vert1[X] = vert2[X] = xoff;
475 vert1[Y] = vert2[Y] = yoff;
476 if (horiz) {
477 lleg = sr - sl;
478 dv1 = vert1 + X;
479 dv2 = vert2 + X;
480 sv1 = vert1 + Y;
481 sv2 = vert2 + Y;
482 stab2 = vert2[Y] = st - sb + yoff;
483 stab1 = vert1[Y] = yoff;
484 if (do_invert)
485 vert1[X] = vert2[X] = sr - sl + xoff;
486 }
487 else {
488 lleg = st - sb;
489 dv1 = vert1 + Y;
490 dv2 = vert2 + Y;
491 sv1 = vert1 + X;
492 sv2 = vert2 + X;
493 stab2 = vert2[X] = sr - sl + xoff;
494 stab1 = vert1[X] = xoff;
495 if (do_invert)
496 vert1[Y] = vert2[Y] = st - sb + yoff;
497 }
498
499 if (discrete) {
500 if (numlabs > lleg / 5)
501 G_warning(_("Too many categories to show as discrete!"));
502 else if (numlabs > 1.2 * lleg / gsd_get_txtheight(size))
503 G_warning(_("Try using smaller font!"));
504 }
505
506 incr = do_invert ? -1 : 1;
507 for (k = 0, i = 0; k < lleg; k++) {
508 if (discrete && Listnum)
509 tdcell = Listcats[(int)((float)k * numlabs / lleg)];
510 else {
511 tcell = min + k * (max - min + 1) / lleg;
512 tdcell = fmin + k * (fmax - fmin) / lleg;
513 if (!is_fp)
514 tdcell = tcell;
515 }
516 if (k == 0 || tdcell != pdcell) {
517 if (is_fp)
518 Rast_get_d_color(&tdcell,
519 &red, &green, &blue, &colors);
520 else
521 Rast_get_c_color((CELL *)&tdcell, &red, &green, &blue, &colors);
522
523 RGB_TO_INT(red, green, blue, colr);
524 if (discrete) { /* draw black-white-black separator */
525 if (k > 0) {
526 *dv1 -= 2. * incr;
527 *dv2 -= 2. * incr;
528 gsd_color_func(0x0);
529 gsd_bgnline();
530 glVertex2fv(vert1);
531 glVertex2fv(vert2);
532 gsd_endline();
533
534 *dv1 += 1. * incr;
535 *dv2 += 1. * incr;
536 if (dividers)
537 dividers[i++] = *dv1;
538
539 *dv1 += 1. * incr;
540 *dv2 += 1. * incr;
541 gsd_color_func(0x0);
542 gsd_bgnline();
543 glVertex2fv(vert1);
544 glVertex2fv(vert2);
545 gsd_endline();
546
547 *dv1 += 1. * incr;
548 *dv2 += 1. * incr;
549 pdcell = tdcell;
550 continue;
551 }
552 }
553 }
554
555 gsd_color_func(colr);
556 gsd_bgnline();
557 glVertex2fv(vert1);
558 glVertex2fv(vert2);
559 gsd_endline();
560 glFlush();
561 *dv1 += 1. * incr;
562 *dv2 += 1. * incr;
563 pdcell = tdcell;
564 }
565
566 /* Black box */
567 vert1[X] = vert2[X] = 1. + xoff;
568 vert1[Y] = vert4[Y] = 1. + yoff;
569 vert3[X] = vert4[X] = sr - sl - 1. + xoff;
570 vert3[Y] = vert2[Y] = st - sb - 1. + yoff;
571
572 gsd_color_func(0x000000);
573 gsd_bgnline();
574 glVertex2fv(vert1);
575 glVertex2fv(vert2);
576 glVertex2fv(vert3);
577 glVertex2fv(vert4);
578 glVertex2fv(vert1);
579 gsd_endline();
580
581 /* White box */
582 vert1[X] = vert2[X] = xoff;
583 vert1[Y] = vert4[Y] = yoff;
584 vert3[X] = vert4[X] = sr - sl + xoff;
585 vert3[Y] = vert2[Y] = st - sb + yoff;
586
587 gsd_color_func(0xFFFFFF);
588 gsd_bgnline();
589 glVertex2fv(vert1);
590 glVertex2fv(vert2);
591 glVertex2fv(vert3);
592 glVertex2fv(vert4);
593 glVertex2fv(vert1);
594 gsd_endline();
595
596 /* draw discrete dividers */
597 if (dividers) {
598 gsd_color_func(0xFFFFFFFF);
599 *sv1 = stab1;
600 *sv2 = stab2;
601 for (k = 0; k < i; k++) {
602 *dv1 = *dv2 = dividers[k];
603 gsd_bgnline();
604 glVertex2fv(vert1);
605 glVertex2fv(vert2);
606 gsd_endline();
607 }
608 }
609
610 if (cat_labs || cat_vals) {
611 labpt[Z] = 0;
612 for (k = 0; k < numlabs; k++) {
613 if (is_fp) {
614 if (discrete && Listnum) {
615 tdcell = Listcats[k];
616 labpos = (k + .5) / numlabs;
617 }
618 else {
619 /* show_all not supported unless Listnum */
620 tdcell = labvals[k];
621 labpos = (tdcell - fmin) / (fmax - fmin);
622 }
623 }
624 else {
625 if (discrete && Listnum) {
626 tcell = Listcats[k];
627 labpos = (k + .5) / numlabs;
628 }
629 else {
630 tcell = discrete ? min + k : labvals[k];
631 labpos = (tcell - min + .5) / (max - min + 1);
632 }
633 }
634 if (do_invert)
635 labpos = 1. - labpos;
636 if (cat_labs) {
637 if (!is_fp)
638 cstr = Rast_get_c_cat(&tcell, &cats);
639 else
640 cstr = Rast_get_d_cat(&tdcell, &cats);
641 }
642 if (cat_labs && !cat_vals)
643 sprintf(buff, "%s", cstr);
644 else {
645 if (cat_labs && cat_vals) {
646 if (cstr)
647 if (is_fp)
648 sprintf(buff, "%.*lf) %s",
649 fprec, tdcell, cstr);
650 else
651 sprintf(buff, "%*d) %s", iprec, tcell, cstr);
652 else if (is_fp)
653 sprintf(buff, "%.*lf", fprec, tdcell);
654 else
655 sprintf(buff, "%d", tcell);
656 }
657 else if (cat_vals) {
658 if (is_fp)
659 sprintf(buff, "%.*lf", fprec, tdcell);
660 else
661 sprintf(buff, "%d", tcell);
662 }
663 }
664 if (horiz) {
665 labpt[X] = labpos * (sr - sl) + xoff -
666 gsd_get_txtwidth(buff, size) / 2 - get_txtxoffset();
667 labpt[Y] =
668 st - sb + yoff + 3 + gsd_get_txtheight(size) / 2;
669 }
670 else {
671 labpt[X] = sr - sl + xoff + get_txtxoffset() + 3;
672 /*
673 labpt[Y] = labpos * (st - sb) + yoff -
674 gsd_get_txtheight()/2 + get_txtdescender();
675 */
676 labpt[Y] = labpos * (st - sb) + yoff -
677 gsd_get_txtheight(size);
678 }
679 /* set color for black text -- maybe add option for color
680 * supplied with font ??
681 */
682 gsd_color_func(0x000000);
683 do_label_display(fontbase, labpt, buff);
684 }
685 }
686
687 if (discrete)
688 G_free(dividers);
689 }
690
691 if (cat_labs)
692 Rast_free_cats(&cats);
693
694 Rast_free_colors(&colors);
695
697
698 /*
699 gsd_unset_font(fontbase);
700 */
701
702 gsd_endlist();
703
704 return (legend_list);
705}
void G_free(void *buf)
Free allocated memory.
Definition: alloc.c:149
#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:204
void GS_ready_draw(void)
Definition: gs2.c:2488
void GS_set_draw(int where)
Sets which buffer to draw to.
Definition: gs2.c:2462
void GS_done_draw(void)
Draw done, swap buffers.
Definition: gs2.c:2501
void do_label_display(GLuint fontbase, float *lab_pos, const char *txt)
Display label.
Definition: gsd_fonts.c:98
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:86
int gsd_make_nice_number(float *num)
ADD.
Definition: gsd_legend.c:154
GLuint gsd_put_legend(const char *name, GLuint fontbase, int size, int *flags, float *rangef, int *pt)
Put legend.
Definition: gsd_legend.c:202
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:1143
void gsd_pushmatrix(void)
Push the current matrix stack.
Definition: gsd_prim.c:510
void gsd_colormode(int cm)
Set color mode.
Definition: gsd_prim.c:97
void gsd_bgnlist(int listno, int do_draw)
ADD.
Definition: gsd_prim.c:1128
int gsd_makelist(void)
ADD.
Definition: gsd_prim.c:1096
void gsd_popmatrix(void)
Pop the current matrix stack.
Definition: gsd_prim.c:500
void gsd_endline(void)
End line.
Definition: gsd_prim.c:406
void gsd_bgnline(void)
Begin line.
Definition: gsd_prim.c:396
void gsd_color_func(unsigned int col)
Set current color.
Definition: gsd_prim.c:701
void gsd_linewidth(short n)
Set width of rasterized lines.
Definition: gsd_prim.c:266
const char * name
Definition: named_colr.c:7
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)