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