GRASS 8 Programmer's Manual 8.6.0dev(2026)-51551cd856
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_WINDOWS)
23#define WIN32_LEAN_AND_MEAN
24#include <windows.h>
25#undef WIN32_LEAN_AND_MEAN
26#endif
27#if defined(OPENGL_X11) || defined(OPENGL_WINDOWS)
28#include <GL/gl.h>
29#include <GL/glu.h>
30#elif defined(OPENGL_AQUA)
31#include <OpenGL/gl.h>
32#include <OpenGL/glu.h>
33#endif
34
35#include <grass/gis.h>
36#include <grass/raster.h>
37#include <grass/glocale.h>
38#include <grass/ogsf.h>
39
40#include "rgbpack.h"
41
42static float *Listcats;
43static int Listnum = 0;
44
45/**** TODO
46static int bigger(float *f1, float *f2)
47{
48 return (*f1 < *f2 ? -1 : (*f1 > *f2));
49}
50*****/
51
52#define MAX_LEGEND 256
53
54/*!
55 \brief ADD
56
57 \param wl
58 \param wb
59 \param wr
60 \param wt
61 */
63{
64 /* sets the viewport for the legend and the model matrix */
65
68
70
74
76
78
79 glViewport(wl, wb, (wr - wl), (wt - wb));
81 gluOrtho2D(-0.5, (wr - wl) + 0.5, -0.5, (wt - wb) + 0.5);
85
86 return;
87}
88
89/*!
90 \brief ADD
91 */
93{
94 /* closes the legend viewport and resets matrix and buffers */
95
99
100 glPopAttrib();
103
104 GS_done_draw();
106
107 return;
108}
109
110/*!
111 \brief ADD
112
113 \param lownum
114 \param highnum
115 \param numvals
116 \param vals
117
118 \return 0 on failure
119 \return range value
120 */
121int gsd_get_nice_range(float lownum, float highnum, int numvals, float *vals)
122{
123 /* get a nice range for displaying legend */
124
125 int num = 0;
126 float curnum, step, start;
127
128 if (!numvals)
129 return (0);
130
131 step = (highnum - lownum) / (float)numvals;
133
134 /* get a starting point */
135 start = step * (int)(1 + lownum / step);
136 if (start - lownum < .65 * step)
137 start += step;
138
139 for (curnum = start; curnum < (highnum - .65 * step); curnum += step) {
140 vals[num++] = curnum;
141 }
142
143 return (num);
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;
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
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 if (flags[4] && rangef[0] != -9999. && rangef[1] != -9999.) {
258 fmin = rangef[0];
259 fmax = rangef[1];
260 if (!is_fp) {
261 min = (int)fmin;
262 max = (int)fmax;
263 }
264 }
265 else {
266 if (is_fp) {
267 if (Rast_read_fp_range(name, mapset, &fp_range) != 1) {
268 G_warning(_("Unable to read fp range of raster map <%s>"),
269 name);
270 return (-1);
271 }
272 Rast_get_fp_range_min_max(&fp_range, &fmin, &fmax);
273 if (flags[4] && rangef[0] != -9999.)
274 fmin = rangef[0];
275 if (flags[4] && rangef[1] != -9999.)
276 fmax = rangef[1];
277 }
278 else {
279 if (Rast_read_range(name, mapset, &range) == -1) {
280 G_warning(_("Unable to read range of raster map <%s>"), name);
281 return (-1);
282 }
283 Rast_get_range_min_max(&range, &min, &max);
284 if (flags[4] && rangef[0] != -9999.)
285 min = rangef[0];
286 if (flags[4] && rangef[1] != -9999.)
287 max = rangef[1];
288 fmin = min;
289 fmax = max;
290 }
291 }
292
293 if (fmin == fmax)
294 G_warning(_("Range request error for legend"));
295
296 /* set a reasonable precision */
297 if (is_fp) {
298 float df;
299
300 df = fmax - fmin;
301 if (df < .1)
302 fprec = 6;
303 else if (df < 1)
304 fprec = 4;
305 else if (df < 10)
306 fprec = 3;
307 else if (df < 100)
308 fprec = 2;
309 else
310 fprec = 1;
311 }
312 else {
313 int tmp, p1, p2;
314
315 iprec = p1 = p2 = 1;
316 if (max > 0)
317 for (tmp = 1; tmp < max; tmp *= 10, p1++)
318 ;
319 if (min < 0)
320 for (tmp = -1; tmp > min; tmp *= 10, p2++)
321 ;
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 /* how many labels? */
341 /*
342 numlabs can't be = max - min + 1 any more because of floating point
343 maybe shouldn't allow discrete legend for floating point maps (unless
344 list) or else check number of different values in floating point map and
345 use each if "reasonable" gs_get_values_in_range(gs, att, low, high,
346 values, &nvals) the nvals sent has a max number to return, nvals returned
347 is the actual number set in values, return val is 1 on success, -1 if >
348 max vals found
349
350 might need to think about doing histograms first & use same routines here
351 could also have a LT_MOST that would limit # to some N most frequent
352 */
353
354 /*!
355 ???
356 */
357 {
358 int i, k, lleg, horiz;
359 int red, green, blue;
360 CELL tcell;
362 float vert1[2], vert2[2], vert3[2], vert4[2];
363 float *dv1, *dv2; /* changing vertex coord */
364 float *sv1, *sv2; /* stable vertex coord */
365 float stab1, stab2;
366 unsigned long colr;
367 float *dividers;
368 int labw, maxlabw, numlabs;
369 float labpos, labpt[3];
370 const char *cstr;
371 char buff[80];
372 GLint wt, wb, wl, wr; /* Whole legend area, not just box */
373 int xoff, yoff;
374 int incr; /* for do_invert */
375
376 horiz = (sr - sl > st - sb);
377 dividers = NULL;
378
379 if (discrete) {
380 numlabs = Listnum ? Listnum : max - min + 1;
381 /* watch out for trying to display mega cats */
382 if (is_fp && !Listnum) {
383 discrete = 0; /* maybe later do stats & allow if few #s */
384 G_warning(_("Unable to show discrete FP range (use list)"));
385 return (-1);
386 }
387 if (numlabs < MAX_LEGEND)
388 dividers = (float *)G_malloc(numlabs * sizeof(float));
389 }
390 else {
392 labvals[0] = fmin;
393 labvals[numlabs + 1] = fmax;
394 numlabs += 2;
395 }
396
397 /* find longest string, reset viewport & saveunder */
398 maxlabw = 0;
399
400 if (cat_labs || cat_vals) {
401 for (k = 0; k < numlabs; k++) {
402 if (is_fp) {
403 tdcell = discrete ? Listcats[k] : labvals[k];
404 if (cat_labs) {
405 cstr = Rast_get_d_cat(&tdcell, &cats);
406 }
407 if (cat_labs && !cat_vals) {
408 snprintf(buff, sizeof(buff), "%s", cstr);
409 }
410 else {
411 if (cat_labs && cat_vals) {
412 if (cstr)
413 snprintf(buff, sizeof(buff), "%.*lf) %s", fprec,
414 tdcell, cstr);
415 else
416 snprintf(buff, sizeof(buff), "%.*lf", fprec,
417 tdcell);
418 }
419 else if (cat_vals)
420 snprintf(buff, sizeof(buff), "%.*lf", fprec,
421 tdcell);
422 }
423 }
424 else {
425 tcell =
426 discrete ? Listnum ? Listcats[k] : min + k : labvals[k];
427 if (cat_labs && !cat_vals)
428 snprintf(buff, sizeof(buff), "%s",
429 Rast_get_c_cat(&tcell, &cats));
430 else {
431 if (cat_labs && cat_vals) {
432 cstr = Rast_get_c_cat(&tcell, &cats);
433 if (cstr[0])
434 snprintf(buff, sizeof(buff), "%*d) %s", iprec,
435 tcell, cstr);
436 else
437 snprintf(buff, sizeof(buff), "%d", tcell);
438 }
439 else if (cat_vals)
440 snprintf(buff, sizeof(buff), "%d", tcell);
441 }
442 }
443 labw = gsd_get_txtwidth(buff, size);
444 if (labw > maxlabw) {
445 maxlabw = labw;
446 }
447 }
448 }
449
450 if (horiz) {
451 xoff = maxlabw / 2 + get_txtxoffset();
452 wl = sl - xoff;
453 wr = sr + xoff;
454 yoff = 0;
455 wb = sb;
456 /*
457 wt = st + gsd_get_txtheight() + get_txtdescender() +3;
458 */
459 wt = st + gsd_get_txtheight(size) * 2 + 3;
460 }
461 else {
462 xoff = 0;
463 wl = sl;
464 wr = sr + maxlabw + get_txtxoffset() + 3;
465 /*
466 yoff = gsd_get_txtheight()/2 + get_txtdescender();
467 */
468 yoff = gsd_get_txtheight(size);
469 wb = sb - yoff;
470 wt = st + yoff;
471 }
472
473 /* initialize viewport */
475
476 vert1[X] = vert2[X] = xoff;
477 vert1[Y] = vert2[Y] = yoff;
478 if (horiz) {
479 lleg = sr - sl;
480 dv1 = vert1 + X;
481 dv2 = vert2 + X;
482 sv1 = vert1 + Y;
483 sv2 = vert2 + Y;
484 stab2 = vert2[Y] = st - sb + yoff;
485 stab1 = vert1[Y] = yoff;
486 if (do_invert)
487 vert1[X] = vert2[X] = sr - sl + xoff;
488 }
489 else {
490 lleg = st - sb;
491 dv1 = vert1 + Y;
492 dv2 = vert2 + Y;
493 sv1 = vert1 + X;
494 sv2 = vert2 + X;
495 stab2 = vert2[X] = sr - sl + xoff;
496 stab1 = vert1[X] = xoff;
497 if (do_invert)
498 vert1[Y] = vert2[Y] = st - sb + yoff;
499 }
500
501 if (discrete) {
502 if (numlabs > lleg / 5)
503 G_warning(_("Too many categories to show as discrete!"));
504 else if (numlabs > 1.2 * lleg / gsd_get_txtheight(size))
505 G_warning(_("Try using smaller font!"));
506 }
507
508 incr = do_invert ? -1 : 1;
509 for (k = 0, i = 0; k < lleg; k++) {
510 if (discrete && Listnum)
511 tdcell = Listcats[(int)((float)k * numlabs / lleg)];
512 else {
513 tcell = min + k * (max - min + 1) / lleg;
514 tdcell = fmin + k * (fmax - fmin) / lleg;
515 if (!is_fp)
516 tdcell = tcell;
517 }
518 if (k == 0 || tdcell != pdcell) {
519 if (is_fp)
520 Rast_get_d_color(&tdcell, &red, &green, &blue, &colors);
521 else
522 Rast_get_c_color((CELL *)&tdcell, &red, &green, &blue,
523 &colors);
524
525 RGB_TO_INT(red, green, blue, colr);
526 if (discrete) { /* draw black-white-black separator */
527 if (k > 0) {
528 *dv1 -= 2. * incr;
529 *dv2 -= 2. * incr;
530 gsd_color_func(0x0);
531 gsd_bgnline();
534 gsd_endline();
535
536 *dv1 += 1. * incr;
537 *dv2 += 1. * incr;
538 if (dividers)
539 dividers[i++] = *dv1;
540
541 *dv1 += 1. * incr;
542 *dv2 += 1. * incr;
543 gsd_color_func(0x0);
544 gsd_bgnline();
547 gsd_endline();
548
549 *dv1 += 1. * incr;
550 *dv2 += 1. * incr;
551 pdcell = tdcell;
552 continue;
553 }
554 }
555 }
556
558 gsd_bgnline();
561 gsd_endline();
562 glFlush();
563 *dv1 += 1. * incr;
564 *dv2 += 1. * incr;
565 pdcell = tdcell;
566 }
567
568 /* Black box */
569 vert1[X] = vert2[X] = 1. + xoff;
570 vert1[Y] = vert4[Y] = 1. + yoff;
571 vert3[X] = vert4[X] = sr - sl - 1. + xoff;
572 vert3[Y] = vert2[Y] = st - sb - 1. + yoff;
573
574 gsd_color_func(0x000000);
575 gsd_bgnline();
581 gsd_endline();
582
583 /* White box */
584 vert1[X] = vert2[X] = xoff;
585 vert1[Y] = vert4[Y] = yoff;
586 vert3[X] = vert4[X] = sr - sl + xoff;
587 vert3[Y] = vert2[Y] = st - sb + yoff;
588
589 gsd_color_func(0xFFFFFF);
590 gsd_bgnline();
596 gsd_endline();
597
598 /* draw discrete dividers */
599 if (dividers) {
600 gsd_color_func(0xFFFFFFFF);
601 *sv1 = stab1;
602 *sv2 = stab2;
603 for (k = 0; k < i; k++) {
604 *dv1 = *dv2 = dividers[k];
605 gsd_bgnline();
608 gsd_endline();
609 }
610 }
611
612 if (cat_labs || cat_vals) {
613 labpt[Z] = 0;
614 for (k = 0; k < numlabs; k++) {
615 if (is_fp) {
616 if (discrete && Listnum) {
617 tdcell = Listcats[k];
618 labpos = (k + .5) / numlabs;
619 }
620 else {
621 /* show_all not supported unless Listnum */
622 tdcell = labvals[k];
623 labpos = (tdcell - fmin) / (fmax - fmin);
624 }
625 }
626 else {
627 if (discrete && Listnum) {
628 tcell = Listcats[k];
629 labpos = (k + .5) / numlabs;
630 }
631 else {
632 tcell = discrete ? min + k : labvals[k];
633 labpos = (tcell - min + .5) / (max - min + 1);
634 }
635 }
636 if (do_invert)
637 labpos = 1. - labpos;
638 if (cat_labs) {
639 if (!is_fp)
640 cstr = Rast_get_c_cat(&tcell, &cats);
641 else
642 cstr = Rast_get_d_cat(&tdcell, &cats);
643 }
644 if (cat_labs && !cat_vals)
645 snprintf(buff, sizeof(buff), "%s", cstr);
646 else {
647 if (cat_labs && cat_vals) {
648 if (cstr)
649 if (is_fp)
650 snprintf(buff, sizeof(buff), "%.*lf) %s", fprec,
651 tdcell, cstr);
652 else
653 snprintf(buff, sizeof(buff), "%*d) %s", iprec,
654 tcell, cstr);
655 else if (is_fp)
656 snprintf(buff, sizeof(buff), "%.*lf", fprec,
657 tdcell);
658 else
659 snprintf(buff, sizeof(buff), "%d", tcell);
660 }
661 else if (cat_vals) {
662 if (is_fp)
663 snprintf(buff, sizeof(buff), "%.*lf", fprec,
664 tdcell);
665 else
666 snprintf(buff, sizeof(buff), "%d", tcell);
667 }
668 }
669 if (horiz) {
670 labpt[X] = labpos * (sr - sl) + xoff -
671 gsd_get_txtwidth(buff, size) / 2 -
673 labpt[Y] = st - sb + yoff + 3 + gsd_get_txtheight(size) / 2;
674 }
675 else {
676 labpt[X] = sr - sl + xoff + get_txtxoffset() + 3;
677 /*
678 labpt[Y] = labpos * (st - sb) + yoff -
679 gsd_get_txtheight()/2 + get_txtdescender();
680 */
681 labpt[Y] =
682 labpos * (st - sb) + yoff - gsd_get_txtheight(size);
683 }
684 /* set color for black text -- maybe add option for color
685 * supplied with font ??
686 */
687 gsd_color_func(0x000000);
689 }
690 }
691
692 if (discrete)
694 }
695
696 if (cat_labs)
697 Rast_free_cats(&cats);
698
699 Rast_free_colors(&colors);
700
702
703 /*
704 gsd_unset_font(fontbase);
705 */
706
707 gsd_endlist();
708
709 return (legend_list);
710}
#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:1139
void gsd_pushmatrix(void)
Push the current matrix stack.
Definition gsd_prim.c:510
void GS_set_draw(int)
Sets which buffer to draw to.
Definition gs2.c:2463
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:1124
int gsd_makelist(void)
ADD.
Definition gsd_prim.c:1093
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:697
void GS_ready_draw(void)
Definition gs2.c:2489
void gsd_popmatrix(void)
Pop the current matrix stack.
Definition gsd_prim.c:500
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:266
void gsd_endline(void)
End line.
Definition gsd_prim.c:406
void gsd_colormode(int)
Set color mode.
Definition gsd_prim.c:97
void gsd_bgnline(void)
Begin line.
Definition gsd_prim.c:396
void GS_done_draw(void)
Draw done, swap buffers.
Definition gs2.c:2502
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:654
int CELL
Definition gis.h:653
#define _(str)
Definition glocale.h:10
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:121
void gsd_end_legend_viewport(void)
ADD.
Definition gsd_legend.c:92
void gsd_bgn_legend_viewport(GLint wl, GLint wb, GLint wr, GLint wt)
ADD.
Definition gsd_legend.c:62
#define MAX_LEGEND
Definition gsd_legend.c:52
const char * name
Definition named_colr.c:6
OGSF header file (structures)
#define X
Definition ogsf.h:146
#define Z
Definition ogsf.h:148
#define GSD_FRONT
Definition ogsf.h:110
#define Y
Definition ogsf.h:147
#define CM_COLOR
Definition ogsf.h:154
#define GSD_BACK
Definition ogsf.h:111
struct state * st
Definition parser.c:102
#define RGB_TO_INT(r, g, b, i)
Definition rgbpack.h:12
Definition gis.h:711