GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gsd_legend.c
Go to the documentation of this file.
1 
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/glocale.h>
36 #include <grass/gstypes.h>
37 
38 #include "rgbpack.h"
39 
40 static float *Listcats;
41 static int Listnum = 0;
42 
43 /**** TODO
44 static int bigger(float *f1, float *f2)
45 {
46  return (*f1 < *f2 ? -1 : (*f1 > *f2));
47 }
48 *****/
49 
50 #define MAX_LEGEND 256
51 
60 void gsd_bgn_legend_viewport(GLint wl, GLint wb, GLint wr, GLint wt)
61 {
62  /* sets the viewport for the legend and the model matrix */
63 
64  gsd_colormode(CM_COLOR);
65  glPushAttrib(GL_VIEWPORT);
66 
67  glMatrixMode(GL_PROJECTION);
68 
70  GS_set_draw(GSD_FRONT);
71  GS_ready_draw();
72 
73  gsd_linewidth(1);
74 
75  gsd_popmatrix();
76 
77  glViewport(wl, wb, (wr - wl), (wt - wb));
78  glLoadIdentity();
79  gluOrtho2D(-0.5, (wr - wl) + 0.5, -0.5, (wt - wb) + 0.5);
80  glMatrixMode(GL_MODELVIEW);
81  glPushMatrix();
82  glLoadIdentity();
83 
84  return;
85 }
86 
91 {
92  /* closes the legend viewport and resets matrix and buffers */
93 
94  gsd_popmatrix();
95  glMatrixMode(GL_PROJECTION);
96  gsd_popmatrix();
97 
98  glPopAttrib();
99  glMatrixMode(GL_MODELVIEW);
100  gsd_popmatrix();
101 
102  GS_done_draw();
103  GS_set_draw(GSD_BACK);
104 
105  return;
106 }
107 
119 int gsd_get_nice_range(float lownum, float highnum, int numvals, float *vals)
120 {
121  /* get a nice range for displaying legend */
122 
123  int num = 0;
124  float curnum, step, start;
125 
126  if (!numvals)
127  return (0);
128 
129  step = (highnum - lownum) / (float)numvals;
130  gsd_make_nice_number(&step);
131 
132  /* get a starting point */
133  start = step * (int)(1 + lownum / step);
134  if (start - lownum < .65 * step)
135  start += step;
136 
137  for (curnum = start; curnum < (highnum - .65 * step); curnum += step) {
138  vals[num++] = curnum;
139  }
140 
141  return (num);
142 
143 }
144 
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 
201 GLuint 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_cell2(name, "");
237  if (mapset == NULL) {
238  G_warning(_("Raster map <%s> not found"), name);
239  return (-1);
240  }
241 
242  is_fp = G_raster_map_is_fp(name, mapset);
243 
244  if (G_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 (G_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 
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 (G_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  G_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 (G_read_range(name, mapset, &range) == -1) {
280  G_warning(_("Unable to read range of raster map <%s>"), name);
281  return (-1);
282  }
283  G_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  }
313  else {
314  int tmp, p1, p2;
315 
316  iprec = p1 = p2 = 1;
317  if (max > 0)
318  for (tmp = 1; tmp < max; tmp *= 10, p1++) ;
319  if (min < 0)
320  for (tmp = -1; tmp > min; tmp *= 10, p2++) ;
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 
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 list)
344  or else check number of different values in floating point map
345  and use each if "reasonable"
346  gs_get_values_in_range(gs, att, low, high, values, &nvals)
347  the nvals sent has a max number to return, nvals returned is the actual
348  number set in values, return val is 1 on success, -1 if > 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 
357  {
358  int i, k, lleg, horiz;
359  int red, green, blue;
360  CELL tcell;
361  DCELL tdcell, pdcell;
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 {
391  numlabs = gsd_get_nice_range(fmin, fmax, 4, labvals + 1);
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 = G_get_d_raster_cat(&tdcell, &cats);
406  }
407  if (cat_labs && !cat_vals) {
408  sprintf(buff, "%s", cstr);
409  }
410  else {
411  if (cat_labs && cat_vals) {
412  if (cstr)
413  sprintf(buff, "%.*lf) %s",
414  fprec, tdcell, cstr);
415  else
416  sprintf(buff, "%.*lf", fprec, tdcell);
417  }
418  else if (cat_vals)
419  sprintf(buff, "%.*lf", fprec, tdcell);
420  }
421  }
422  else {
423  tcell = discrete ? Listnum ?
424  Listcats[k] : min + k : labvals[k];
425  if (cat_labs && !cat_vals)
426  sprintf(buff, "%s", G_get_cat(tcell, &cats));
427  else {
428  if (cat_labs && cat_vals) {
429  cstr = G_get_cat(tcell, &cats);
430  if (cstr[0])
431  sprintf(buff, "%*d) %s", iprec, tcell, cstr);
432  else
433  sprintf(buff, "%d", tcell);
434  }
435  else if (cat_vals)
436  sprintf(buff, "%d", tcell);
437  }
438  }
439  labw = gsd_get_txtwidth(buff, size);
440  if (labw > maxlabw) {
441  maxlabw = labw;
442  }
443  }
444  }
445 
446  if (horiz) {
447  xoff = maxlabw / 2 + get_txtxoffset();
448  wl = sl - xoff;
449  wr = sr + xoff;
450  yoff = 0;
451  wb = sb;
452  /*
453  wt = st + gsd_get_txtheight() + get_txtdescender() +3;
454  */
455  wt = st + gsd_get_txtheight(size) * 2 + 3;
456  }
457  else {
458  xoff = 0;
459  wl = sl;
460  wr = sr + maxlabw + get_txtxoffset() + 3;
461  /*
462  yoff = gsd_get_txtheight()/2 + get_txtdescender();
463  */
464  yoff = gsd_get_txtheight(size);
465  wb = sb - yoff;
466  wt = st + yoff;
467  }
468 
469  /* initialize viewport */
470  gsd_bgn_legend_viewport(wl, wb, wr, wt);
471 
472 
473  vert1[X] = vert2[X] = xoff;
474  vert1[Y] = vert2[Y] = yoff;
475  if (horiz) {
476  lleg = sr - sl;
477  dv1 = vert1 + X;
478  dv2 = vert2 + X;
479  sv1 = vert1 + Y;
480  sv2 = vert2 + Y;
481  stab2 = vert2[Y] = st - sb + yoff;
482  stab1 = vert1[Y] = yoff;
483  if (do_invert)
484  vert1[X] = vert2[X] = sr - sl + xoff;
485  }
486  else {
487  lleg = st - sb;
488  dv1 = vert1 + Y;
489  dv2 = vert2 + Y;
490  sv1 = vert1 + X;
491  sv2 = vert2 + X;
492  stab2 = vert2[X] = sr - sl + xoff;
493  stab1 = vert1[X] = xoff;
494  if (do_invert)
495  vert1[Y] = vert2[Y] = st - sb + yoff;
496  }
497 
498  if (discrete) {
499  if (numlabs > lleg / 5)
500  G_warning(_("Too many categories to show as discrete!"));
501  else if (numlabs > 1.2 * lleg / gsd_get_txtheight(size))
502  G_warning(_("Try using smaller font!"));
503  }
504 
505  incr = do_invert ? -1 : 1;
506  for (k = 0, i = 0; k < lleg; k++) {
507  if (discrete && Listnum)
508  tdcell = Listcats[(int)((float)k * numlabs / lleg)];
509  else {
510  tcell = min + k * (max - min + 1) / lleg;
511  tdcell = fmin + k * (fmax - fmin) / lleg;
512  if (!is_fp)
513  tdcell = tcell;
514  }
515  if (k == 0 || tdcell != pdcell) {
516  if (is_fp)
517  G_get_d_raster_color(&tdcell,
518  &red, &green, &blue, &colors);
519  else
520  G_get_color((CELL) tdcell, &red, &green, &blue, &colors);
521 
522  RGB_TO_INT(red, green, blue, colr);
523  if (discrete) { /* draw black-white-black separator */
524  if (k > 0) {
525  *dv1 -= 2. * incr;
526  *dv2 -= 2. * incr;
527  gsd_color_func(0x0);
528  gsd_bgnline();
529  glVertex2fv(vert1);
530  glVertex2fv(vert2);
531  gsd_endline();
532 
533  *dv1 += 1. * incr;
534  *dv2 += 1. * incr;
535  if (dividers)
536  dividers[i++] = *dv1;
537 
538  *dv1 += 1. * incr;
539  *dv2 += 1. * incr;
540  gsd_color_func(0x0);
541  gsd_bgnline();
542  glVertex2fv(vert1);
543  glVertex2fv(vert2);
544  gsd_endline();
545 
546  *dv1 += 1. * incr;
547  *dv2 += 1. * incr;
548  pdcell = tdcell;
549  continue;
550  }
551  }
552  }
553 
554  gsd_color_func(colr);
555  gsd_bgnline();
556  glVertex2fv(vert1);
557  glVertex2fv(vert2);
558  gsd_endline();
559  glFlush();
560  *dv1 += 1. * incr;
561  *dv2 += 1. * incr;
562  pdcell = tdcell;
563  }
564 
565  /* Black box */
566  vert1[X] = vert2[X] = 1. + xoff;
567  vert1[Y] = vert4[Y] = 1. + yoff;
568  vert3[X] = vert4[X] = sr - sl - 1. + xoff;
569  vert3[Y] = vert2[Y] = st - sb - 1. + yoff;
570 
571  gsd_color_func(0x000000);
572  gsd_bgnline();
573  glVertex2fv(vert1);
574  glVertex2fv(vert2);
575  glVertex2fv(vert3);
576  glVertex2fv(vert4);
577  glVertex2fv(vert1);
578  gsd_endline();
579 
580  /* White box */
581  vert1[X] = vert2[X] = xoff;
582  vert1[Y] = vert4[Y] = yoff;
583  vert3[X] = vert4[X] = sr - sl + xoff;
584  vert3[Y] = vert2[Y] = st - sb + yoff;
585 
586  gsd_color_func(0xFFFFFF);
587  gsd_bgnline();
588  glVertex2fv(vert1);
589  glVertex2fv(vert2);
590  glVertex2fv(vert3);
591  glVertex2fv(vert4);
592  glVertex2fv(vert1);
593  gsd_endline();
594 
595  /* draw discrete dividers */
596  if (dividers) {
597  gsd_color_func(0xFFFFFFFF);
598  *sv1 = stab1;
599  *sv2 = stab2;
600  for (k = 0; k < i; k++) {
601  *dv1 = *dv2 = dividers[k];
602  gsd_bgnline();
603  glVertex2fv(vert1);
604  glVertex2fv(vert2);
605  gsd_endline();
606  }
607  }
608 
609  if (cat_labs || cat_vals) {
610  labpt[Z] = 0;
611  for (k = 0; k < numlabs; k++) {
612  if (is_fp) {
613  if (discrete && Listnum) {
614  tdcell = Listcats[k];
615  labpos = (k + .5) / numlabs;
616  }
617  else {
618  /* show_all not supported unless Listnum */
619  tdcell = labvals[k];
620  labpos = (tdcell - fmin) / (fmax - fmin);
621  }
622  }
623  else {
624  if (discrete && Listnum) {
625  tcell = Listcats[k];
626  labpos = (k + .5) / numlabs;
627  }
628  else {
629  tcell = discrete ? min + k : labvals[k];
630  labpos = (tcell - min + .5) / (max - min + 1);
631  }
632  }
633  if (do_invert)
634  labpos = 1. - labpos;
635  if (cat_labs) {
636  if (!is_fp)
637  cstr = G_get_cat(tcell, &cats);
638  else
639  cstr = G_get_d_raster_cat(&tdcell, &cats);
640  }
641  if (cat_labs && !cat_vals)
642  sprintf(buff, "%s", cstr);
643  else {
644  if (cat_labs && cat_vals) {
645  if (cstr)
646  if (is_fp)
647  sprintf(buff, "%.*lf) %s",
648  fprec, tdcell, cstr);
649  else
650  sprintf(buff, "%*d) %s", iprec, tcell, cstr);
651  else if (is_fp)
652  sprintf(buff, "%.*lf", fprec, tdcell);
653  else
654  sprintf(buff, "%d", tcell);
655  }
656  else if (cat_vals) {
657  if (is_fp)
658  sprintf(buff, "%.*lf", fprec, tdcell);
659  else
660  sprintf(buff, "%d", tcell);
661  }
662  }
663  if (horiz) {
664  labpt[X] = labpos * (sr - sl) + xoff -
665  gsd_get_txtwidth(buff, size) / 2 - get_txtxoffset();
666  labpt[Y] =
667  st - sb + yoff + 3 + gsd_get_txtheight(size) / 2;
668  }
669  else {
670  labpt[X] = sr - sl + xoff + get_txtxoffset() + 3;
671  /*
672  labpt[Y] = labpos * (st - sb) + yoff -
673  gsd_get_txtheight()/2 + get_txtdescender();
674  */
675  labpt[Y] = labpos * (st - sb) + yoff -
676  gsd_get_txtheight(size);
677  }
678  /* set color for black text -- maybe add option for color
679  * supplied with font ??
680  */
681  gsd_color_func(0x000000);
682  do_label_display(fontbase, labpt, buff);
683  }
684  }
685 
686  if (discrete)
687  G_free(dividers);
688  }
689 
690  if (cat_labs)
691  G_free_cats(&cats);
692 
693  G_free_colors(&colors);
694 
696 
697  /*
698  gsd_unset_font(fontbase);
699  */
700 
701  gsd_endlist();
702 
703  return (legend_list);
704 }
void gsd_end_legend_viewport(void)
ADD.
Definition: gsd_legend.c:90
sprintf(buf2,"%s", G3D_CATS_ELEMENT)
void G_free(void *buf)
Free allocated memory.
Definition: gis/alloc.c:142
void gsd_endlist(void)
End list.
Definition: gsd_prim.c:1121
#define RGB_TO_INT(r, g, b, i)
Definition: rgbpack.h:12
void do_label_display(GLuint fontbase, float *lab_pos, const char *txt)
Display label.
Definition: gsd_fonts.c:99
string name
Definition: render.py:1314
#define min(x, y)
Definition: draw2.c:68
int G_free_colors(struct Colors *colors)
free color structure memory
Definition: color_free.c:17
int gsd_makelist(void)
ADD.
Definition: gsd_prim.c:1074
void GS_done_draw(void)
Draw done, swap buffers.
Definition: GS2.c:2499
long num
Definition: g3dcats.c:93
#define Y(x)
Definition: display/draw.c:246
void gsd_colormode(int cm)
Set color mode.
Definition: gsd_prim.c:88
#define X(y)
Definition: display/draw.c:248
void gsd_color_func(unsigned int col)
Set current color.
Definition: gsd_prim.c:689
int G_read_colors(const char *name, const char *mapset, struct Colors *colors)
read map layer color table
Definition: color_read.c:62
#define max(x, y)
Definition: draw2.c:69
int G_read_range(const char *name, const char *mapset, struct Range *range)
read raster range
Definition: range.c:226
int G_read_cats(const char *name, const char *mapset, struct Categories *pcats)
read raster category file
Definition: gis/cats.c:347
tuple size
value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
Definition: tools.py:2334
int G_raster_map_is_fp(const char *name, const char *mapset)
Check if raster map is floating-point.
Definition: opencell.c:969
int G_get_d_raster_color(const DCELL *rast, int *red, int *grn, int *blu, struct Colors *colors)
Gets color for a DCELL raster.
Definition: color_get.c:139
char buff[1024]
Definition: g3dcats.c:89
void gsd_pushmatrix(void)
Push the current matrix stack.
Definition: gsd_prim.c:498
int G_free_cats(struct Categories *pcats)
free category structure memory
Definition: gis/cats.c:1540
int gsd_get_txtheight(int size)
Get text height.
Definition: gsd_fonts.c:54
int G_get_range_min_max(const struct Range *range, CELL *min, CELL *max)
get range min and max
Definition: range.c:608
void gsd_bgnlist(int listno, int do_draw)
ADD.
Definition: gsd_prim.c:1106
char * G_get_cat(CELL num, struct Categories *pcats)
get a category label
Definition: gis/cats.c:583
char * G_find_cell2(const char *name, const char *mapset)
find a raster map (look but don&#39;t touch)
Definition: find_cell.c:83
void gsd_endline(void)
End line.
Definition: gsd_prim.c:397
int G_get_color(CELL n, int *red, int *grn, int *blu, struct Colors *colors)
Get a category color.
Definition: color_get.c:36
#define MAX_LEGEND
Definition: gsd_legend.c:50
int
Definition: g3dcolor.c:48
int get_txtxoffset(void)
Get text offset.
Definition: gsd_fonts.c:87
char * G_get_d_raster_cat(DCELL *rast, struct Categories *pcats)
given a DCELL value val Returns pointer to a string describing category.
Definition: gis/cats.c:634
GLuint gsd_put_legend(const char *name, GLuint fontbase, int size, int *flags, float *rangef, int *pt)
Put legend.
Definition: gsd_legend.c:201
return NULL
Definition: dbfopen.c:1394
G_warning("category support for [%s] in mapset [%s] %s", name, mapset, type)
int G_get_fp_range_min_max(const struct FPRange *range, DCELL *min, DCELL *max)
Extract the min/max from the range structure r. If the range structure has no defined min/max (first!...
Definition: range.c:667
int gsd_get_nice_range(float lownum, float highnum, int numvals, float *vals)
ADD.
Definition: gsd_legend.c:119
void gsd_bgn_legend_viewport(GLint wl, GLint wb, GLint wr, GLint wt)
ADD.
Definition: gsd_legend.c:60
void gsd_linewidth(short n)
Set width of rasterized lines.
Definition: gsd_prim.c:257
int G_read_fp_range(const char *name, const char *mapset, struct FPRange *drange)
Read the floating point range file f_range. This file is written in binary using XDR format...
Definition: range.c:139
void gsd_popmatrix(void)
Pop the current matrix stack.
Definition: gsd_prim.c:488
void GS_ready_draw(void)
Definition: GS2.c:2486
void gsd_bgnline(void)
Begin line.
Definition: gsd_prim.c:387
int gsd_get_txtwidth(const char *s, int size)
Get text width.
Definition: gsd_fonts.c:37
void GS_set_draw(int where)
Sets which buffer to draw to.
Definition: GS2.c:2457
int gsd_make_nice_number(float *num)
ADD.
Definition: gsd_legend.c:153