GRASS 8 Programmer's Manual 8.6.0dev(2026)-1d1e47ad9d
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 */
62{
63 /* sets the viewport for the legend and the model matrix */
64
67
69
73
75
77
78 glViewport(wl, wb, (wr - wl), (wt - wb));
80 gluOrtho2D(-0.5, (wr - wl) + 0.5, -0.5, (wt - wb) + 0.5);
84
85 return;
86}
87
88/*!
89 \brief ADD
90 */
92{
93 /* closes the legend viewport and resets matrix and buffers */
94
98
102
103 GS_done_draw();
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;
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
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;
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 {
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 snprintf(buff, sizeof(buff), "%s", cstr);
408 }
409 else {
410 if (cat_labs && cat_vals) {
411 if (cstr)
412 snprintf(buff, sizeof(buff), "%.*lf) %s", fprec,
413 tdcell, cstr);
414 else
415 snprintf(buff, sizeof(buff), "%.*lf", fprec,
416 tdcell);
417 }
418 else if (cat_vals)
419 snprintf(buff, sizeof(buff), "%.*lf", fprec,
420 tdcell);
421 }
422 }
423 else {
424 tcell =
425 discrete ? Listnum ? Listcats[k] : min + k : labvals[k];
426 if (cat_labs && !cat_vals)
427 snprintf(buff, sizeof(buff), "%s",
428 Rast_get_c_cat(&tcell, &cats));
429 else {
430 if (cat_labs && cat_vals) {
431 cstr = Rast_get_c_cat(&tcell, &cats);
432 if (cstr[0])
433 snprintf(buff, sizeof(buff), "%*d) %s", iprec,
434 tcell, cstr);
435 else
436 snprintf(buff, sizeof(buff), "%d", tcell);
437 }
438 else if (cat_vals)
439 snprintf(buff, sizeof(buff), "%d", tcell);
440 }
441 }
442 labw = gsd_get_txtwidth(buff, size);
443 if (labw > maxlabw) {
444 maxlabw = labw;
445 }
446 }
447 }
448
449 if (horiz) {
450 xoff = maxlabw / 2 + get_txtxoffset();
451 wl = sl - xoff;
452 wr = sr + xoff;
453 yoff = 0;
454 wb = sb;
455 /*
456 wt = st + gsd_get_txtheight() + get_txtdescender() +3;
457 */
458 wt = st + gsd_get_txtheight(size) * 2 + 3;
459 }
460 else {
461 xoff = 0;
462 wl = sl;
463 wr = sr + maxlabw + get_txtxoffset() + 3;
464 /*
465 yoff = gsd_get_txtheight()/2 + get_txtdescender();
466 */
467 yoff = gsd_get_txtheight(size);
468 wb = sb - yoff;
469 wt = st + yoff;
470 }
471
472 /* initialize viewport */
474
475 vert1[X] = vert2[X] = xoff;
476 vert1[Y] = vert2[Y] = yoff;
477 if (horiz) {
478 lleg = sr - sl;
479 dv1 = vert1 + X;
480 dv2 = vert2 + X;
481 sv1 = vert1 + Y;
482 sv2 = vert2 + Y;
483 stab2 = vert2[Y] = st - sb + yoff;
484 stab1 = vert1[Y] = yoff;
485 if (do_invert)
486 vert1[X] = vert2[X] = sr - sl + xoff;
487 }
488 else {
489 lleg = st - sb;
490 dv1 = vert1 + Y;
491 dv2 = vert2 + Y;
492 sv1 = vert1 + X;
493 sv2 = vert2 + X;
494 stab2 = vert2[X] = sr - sl + xoff;
495 stab1 = vert1[X] = xoff;
496 if (do_invert)
497 vert1[Y] = vert2[Y] = st - sb + yoff;
498 }
499
500 if (discrete) {
501 if (numlabs > lleg / 5)
502 G_warning(_("Too many categories to show as discrete!"));
503 else if (numlabs > 1.2 * lleg / gsd_get_txtheight(size))
504 G_warning(_("Try using smaller font!"));
505 }
506
507 incr = do_invert ? -1 : 1;
508 for (k = 0, i = 0; k < lleg; k++) {
509 if (discrete && Listnum)
510 tdcell = Listcats[(int)((float)k * numlabs / lleg)];
511 else {
512 tcell = min + k * (max - min + 1) / lleg;
513 tdcell = fmin + k * (fmax - fmin) / lleg;
514 if (!is_fp)
515 tdcell = tcell;
516 }
517 if (k == 0 || tdcell != pdcell) {
518 if (is_fp)
519 Rast_get_d_color(&tdcell, &red, &green, &blue, &colors);
520 else
521 Rast_get_c_color((CELL *)&tdcell, &red, &green, &blue,
522 &colors);
523
524 RGB_TO_INT(red, green, blue, colr);
525 if (discrete) { /* draw black-white-black separator */
526 if (k > 0) {
527 *dv1 -= 2. * incr;
528 *dv2 -= 2. * incr;
529 gsd_color_func(0x0);
530 gsd_bgnline();
533 gsd_endline();
534
535 *dv1 += 1. * incr;
536 *dv2 += 1. * incr;
537 if (dividers)
538 dividers[i++] = *dv1;
539
540 *dv1 += 1. * incr;
541 *dv2 += 1. * incr;
542 gsd_color_func(0x0);
543 gsd_bgnline();
546 gsd_endline();
547
548 *dv1 += 1. * incr;
549 *dv2 += 1. * incr;
550 pdcell = tdcell;
551 continue;
552 }
553 }
554 }
555
557 gsd_bgnline();
560 gsd_endline();
561 glFlush();
562 *dv1 += 1. * incr;
563 *dv2 += 1. * incr;
564 pdcell = tdcell;
565 }
566
567 /* Black box */
568 vert1[X] = vert2[X] = 1. + xoff;
569 vert1[Y] = vert4[Y] = 1. + yoff;
570 vert3[X] = vert4[X] = sr - sl - 1. + xoff;
571 vert3[Y] = vert2[Y] = st - sb - 1. + yoff;
572
573 gsd_color_func(0x000000);
574 gsd_bgnline();
580 gsd_endline();
581
582 /* White box */
583 vert1[X] = vert2[X] = xoff;
584 vert1[Y] = vert4[Y] = yoff;
585 vert3[X] = vert4[X] = sr - sl + xoff;
586 vert3[Y] = vert2[Y] = st - sb + yoff;
587
588 gsd_color_func(0xFFFFFF);
589 gsd_bgnline();
595 gsd_endline();
596
597 /* draw discrete dividers */
598 if (dividers) {
599 gsd_color_func(0xFFFFFFFF);
600 *sv1 = stab1;
601 *sv2 = stab2;
602 for (k = 0; k < i; k++) {
603 *dv1 = *dv2 = dividers[k];
604 gsd_bgnline();
607 gsd_endline();
608 }
609 }
610
611 if (cat_labs || cat_vals) {
612 labpt[Z] = 0;
613 for (k = 0; k < numlabs; k++) {
614 if (is_fp) {
615 if (discrete && Listnum) {
616 tdcell = Listcats[k];
617 labpos = (k + .5) / numlabs;
618 }
619 else {
620 /* show_all not supported unless Listnum */
621 tdcell = labvals[k];
622 labpos = (tdcell - fmin) / (fmax - fmin);
623 }
624 }
625 else {
626 if (discrete && Listnum) {
627 tcell = Listcats[k];
628 labpos = (k + .5) / numlabs;
629 }
630 else {
631 tcell = discrete ? min + k : labvals[k];
632 labpos = (tcell - min + .5) / (max - min + 1);
633 }
634 }
635 if (do_invert)
636 labpos = 1. - labpos;
637 if (cat_labs) {
638 if (!is_fp)
639 cstr = Rast_get_c_cat(&tcell, &cats);
640 else
641 cstr = Rast_get_d_cat(&tdcell, &cats);
642 }
643 if (cat_labs && !cat_vals)
644 snprintf(buff, sizeof(buff), "%s", cstr);
645 else {
646 if (cat_labs && cat_vals) {
647 if (cstr)
648 if (is_fp)
649 snprintf(buff, sizeof(buff), "%.*lf) %s", fprec,
650 tdcell, cstr);
651 else
652 snprintf(buff, sizeof(buff), "%*d) %s", iprec,
653 tcell, cstr);
654 else if (is_fp)
655 snprintf(buff, sizeof(buff), "%.*lf", fprec,
656 tdcell);
657 else
658 snprintf(buff, sizeof(buff), "%d", tcell);
659 }
660 else if (cat_vals) {
661 if (is_fp)
662 snprintf(buff, sizeof(buff), "%.*lf", fprec,
663 tdcell);
664 else
665 snprintf(buff, sizeof(buff), "%d", tcell);
666 }
667 }
668 if (horiz) {
669 labpt[X] = labpos * (sr - sl) + xoff -
670 gsd_get_txtwidth(buff, size) / 2 -
672 labpt[Y] = st - sb + yoff + 3 + gsd_get_txtheight(size) / 2;
673 }
674 else {
675 labpt[X] = sr - sl + xoff + get_txtxoffset() + 3;
676 /*
677 labpt[Y] = labpos * (st - sb) + yoff -
678 gsd_get_txtheight()/2 + get_txtdescender();
679 */
680 labpt[Y] =
681 labpos * (st - sb) + yoff - gsd_get_txtheight(size);
682 }
683 /* set color for black text -- maybe add option for color
684 * supplied with font ??
685 */
686 gsd_color_func(0x000000);
688 }
689 }
690
691 if (discrete)
693 }
694
695 if (cat_labs)
696 Rast_free_cats(&cats);
697
698 Rast_free_colors(&colors);
699
701
702 /*
703 gsd_unset_font(fontbase);
704 */
705
706 gsd_endlist();
707
708 return (legend_list);
709}
#define NULL
Definition ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
const char * G_find_raster2(const char *, const char *)
Find a raster map (look but don't touch)
Definition find_rast.c:76
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 GS_set_draw(int)
Sets which buffer to draw to.
Definition gs2.c:2462
void do_label_display(GLuint, float *, const char *)
Display label.
Definition gsd_fonts.c:97
void gsd_bgnlist(int, int)
ADD.
Definition gsd_prim.c:1125
int gsd_makelist(void)
ADD.
Definition gsd_prim.c:1094
int gsd_get_txtwidth(const char *, int)
Get text width.
Definition gsd_fonts.c:36
int gsd_get_txtheight(int size)
Get text height.
Definition gsd_fonts.c:53
void gsd_color_func(unsigned int)
Set current color.
Definition gsd_prim.c:698
void GS_ready_draw(void)
Definition gs2.c:2488
void gsd_popmatrix(void)
Pop the current matrix stack.
Definition gsd_prim.c:501
int get_txtxoffset(void)
Get text offset.
Definition gsd_fonts.c:85
void gsd_linewidth(short)
Set width of rasterized lines.
Definition gsd_prim.c:267
void gsd_endline(void)
End line.
Definition gsd_prim.c:407
void gsd_colormode(int)
Set color mode.
Definition gsd_prim.c:98
void gsd_bgnline(void)
Begin line.
Definition gsd_prim.c:397
void GS_done_draw(void)
Draw done, swap buffers.
Definition gs2.c:2501
void Rast_free_cats(struct Categories *)
Free category structure memory.
int Rast_get_c_color(const CELL *, int *, int *, int *, struct Colors *)
Gets color from raster map (CELL)
Definition color_get.c:67
int Rast_read_colors(const char *, const char *, struct Colors *)
Read color table of raster map.
int Rast_read_cats(const char *, const char *, struct Categories *)
Read raster category file.
int Rast_read_fp_range(const char *, const char *, struct FPRange *)
Read floating-point range.
void Rast_free_colors(struct Colors *)
Free color structure memory.
Definition color_free.c:30
void Rast_get_fp_range_min_max(const struct FPRange *, DCELL *, DCELL *)
Get minimum and maximum value from fp range.
char * Rast_get_c_cat(CELL *, struct Categories *)
Get a raster category label (CELL)
void Rast_get_range_min_max(const struct Range *, CELL *, CELL *)
Get range min and max.
int Rast_read_range(const char *, const char *, struct Range *)
Read raster range (CELL)
int Rast_map_is_fp(const char *, const char *)
Check if raster map is floating-point.
int Rast_get_d_color(const DCELL *, int *, int *, int *, struct Colors *)
Gets color from raster map (DCELL)
Definition color_get.c:109
char * Rast_get_d_cat(DCELL *, struct Categories *)
Get a raster category label (DCELL)
#define min(x, y)
Definition draw2.c:29
#define max(x, y)
Definition draw2.c:30
double DCELL
Definition gis.h:635
int CELL
Definition gis.h:634
#define _(str)
Definition glocale.h:10
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
const char * name
Definition named_colr.c:6
OGSF header file (structures)
#define X
Definition ogsf.h:140
#define Z
Definition ogsf.h:142
#define GSD_FRONT
Definition ogsf.h:104
#define Y
Definition ogsf.h:141
#define CM_COLOR
Definition ogsf.h:148
#define GSD_BACK
Definition ogsf.h:105
struct state * st
Definition parser.c:104
#define RGB_TO_INT(r, g, b, i)
Definition rgbpack.h:12
Definition gis.h:692