GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-a277d8547c
raster/color_write.c
Go to the documentation of this file.
1 /*!
2  * \file lib/raster/color_write.c
3  *
4  * \brief Raster Library - Write color table of raster map
5  *
6  * (C) 1999-2009 by the GRASS Development Team
7  *
8  * This program is free software under the GNU General Public
9  * License (>=v2). Read the file COPYING that comes with GRASS
10  * for details.
11  *
12  * \author USACERL and many others
13  */
14 
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <grass/gis.h>
19 #include <grass/glocale.h>
20 #include <grass/raster.h>
21 
22 static void write_rules(FILE *, struct _Color_Rule_ *, DCELL, DCELL);
23 static void write_new_colors(FILE *, struct Colors *);
24 static void write_old_colors(FILE *, struct Colors *);
25 static void forced_write_old_colors(FILE *, struct Colors *);
26 static void format_min(char *, double);
27 static void format_max(char *, double);
28 
29 #define FORMAT_STR_SZ 100
30 
31 /*!
32  * \brief Write map layer color table
33  *
34  * The color table is written for the raster map <i>name</i> in the
35  * specified <i>mapset</i> from the <i>colors</i> structure.
36  *
37  * If there is an error, -1 is returned. No diagnostic is
38  * printed. Otherwise, 1 is returned.
39  *
40  * The <i>colors</i> structure must be created properly, i.e.,
41  * Rast_init_colors() to initialize the structure and Rast_add_c_color_rule()
42  * to set the category colors. These routines are called by
43  * higher level routines which read or create entire color tables,
44  * such as Rast_read_colors() or Rast_make_ramp_colors().
45  *
46  * <b>Note:</b> The calling sequence for this function deserves
47  * special attention. The <i>mapset</i> parameter seems to imply that
48  * it is possible to overwrite the color table for a raster map which
49  * is in another mapset. However, this is not what actually
50  * happens. It is very useful for users to create their own color
51  * tables for raster maps in other mapsets, but without overwriting
52  * other users' color tables for the same raster map. If <i>mapset</i>
53  * is the current mapset, then the color file for <i>name</i> will be
54  * overwritten by the new color table. But if <i>mapset</i> is not the
55  * current mapset, then the color table is actually written in the
56  * current mapset under the <tt>colr2</tt> element as:
57  * <tt>colr2/mapset/name</tt>.
58  *
59  * The rules are written out using floating-point format, removing
60  * trailing zeros (possibly producing integers). The flag marking the
61  * colors as floating-point is <b>not</b> written.
62  *
63  * If the environment variable FORCE_GRASS3_COLORS is set (to anything at all)
64  * then the output format is 3.0, even if the structure contains 4.0 rules.
65  * This allows users to create 3.0 color files for export to sites which
66  * don't yet have 4.0
67  *
68  * \param name map name
69  * \param mapset mapset name
70  * \param colors pointer to structure Colors which holds color info
71  *
72  * \return void
73  */
74 void Rast_write_colors(const char *name, const char *mapset,
75  struct Colors *colors)
76 {
77  char element[512];
78  char xname[GNAME_MAX], xmapset[GMAPSET_MAX];
79  FILE *fd;
80 
81  if (G_name_is_fully_qualified(name, xname, xmapset)) {
82  if (strcmp(xmapset, mapset) != 0)
83  G_fatal_error(_("Qualified name <%s> doesn't match mapset <%s>"),
84  name, mapset);
85  name = xname;
86  }
87  /*
88  * if mapset is current mapset, remove colr2 file (created by pre 3.0 grass)
89  * and then write original color table
90  * else write secondary color table
91  */
92  snprintf(element, sizeof(element), "colr2/%s", mapset);
93  if (strcmp(mapset, G_mapset()) == 0) {
94  G_remove(element, name); /* get rid of existing colr2, if any */
95  strcpy(element, "colr");
96  }
97  if (!(fd = G_fopen_new(element, name)))
98  G_fatal_error(_("Unable to create <%s> file for map <%s>"), element,
99  name);
100 
101  Rast__write_colors(fd, colors);
102  fclose(fd);
103 }
104 
105 /*!
106  * \brief Write map layer color table
107  *
108  * \param fd file descriptor
109  * \param colors pointer to Colors structure which holds color info
110  */
111 void Rast__write_colors(FILE *fd, struct Colors *colors)
112 {
113  if (getenv("FORCE_GRASS3_COLORS"))
114  forced_write_old_colors(fd, colors);
115  else if (colors->version < 0)
116  write_old_colors(fd, colors);
117  else
118  write_new_colors(fd, colors);
119 }
120 
121 static void write_new_colors(FILE *fd, struct Colors *colors)
122 {
123  char str1[100], str2[100];
124 
125  format_min(str1, (double)colors->cmin);
126  format_max(str2, (double)colors->cmax);
127  fprintf(fd, "%% %s %s\n", str1, str2);
128 
129  if (colors->shift) {
130  snprintf(str2, sizeof(str2), "%.17g", (double)colors->shift);
131  G_trim_decimal(str2);
132  fprintf(fd, "shift:%s\n", str2);
133  }
134  if (colors->invert)
135  fprintf(fd, "invert\n");
136 
137  if (colors->null_set) {
138  fprintf(fd, "nv:%d", colors->null_red);
139  if (colors->null_red != colors->null_grn ||
140  colors->null_red != colors->null_blu)
141  fprintf(fd, ":%d:%d", colors->null_grn, colors->null_blu);
142  fprintf(fd, "\n");
143  }
144  if (colors->undef_set) {
145  fprintf(fd, "*:%d", colors->undef_red);
146  if (colors->undef_red != colors->undef_grn ||
147  colors->undef_red != colors->undef_blu)
148  fprintf(fd, ":%d:%d", colors->undef_grn, colors->undef_blu);
149  fprintf(fd, "\n");
150  }
151  if (colors->modular.rules) {
152  fprintf(fd, "%s\n", "%%");
153  write_rules(fd, colors->modular.rules, colors->cmin, colors->cmax);
154  fprintf(fd, "%s\n", "%%");
155  }
156  if (colors->fixed.rules)
157  write_rules(fd, colors->fixed.rules, colors->cmin, colors->cmax);
158 }
159 
160 /* overall min and max data values in color table */
161 static void write_rules(FILE *fd, struct _Color_Rule_ *crules, DCELL dmin,
162  DCELL dmax)
163 {
164  struct _Color_Rule_ *rule;
165  char str[FORMAT_STR_SZ];
166 
167  /* find the end of the rules list */
168  rule = crules;
169  while (rule->next)
170  rule = rule->next;
171 
172  /* write out the rules in reverse order */
173  for (; rule; rule = rule->prev) {
174  if (rule->low.value == dmin)
175  format_min(str, (double)rule->low.value);
176  else {
177  snprintf(str, FORMAT_STR_SZ, "%.17g", (double)rule->low.value);
178  G_trim_decimal(str);
179  }
180  fprintf(fd, "%s:%d", str, (int)rule->low.red);
181  if (rule->low.red != rule->low.grn || rule->low.red != rule->low.blu)
182  fprintf(fd, ":%d:%d", rule->low.grn, rule->low.blu);
183  /* even if low==high, write second end when the high is dmax */
184  if (rule->high.value == dmax || rule->low.value != rule->high.value) {
185  if (rule->high.value == dmax)
186  format_max(str, (double)rule->high.value);
187  else {
188  snprintf(str, FORMAT_STR_SZ, "%.17g", (double)rule->high.value);
189  G_trim_decimal(str);
190  }
191  fprintf(fd, " %s:%d", str, (int)rule->high.red);
192  if (rule->high.red != rule->high.grn ||
193  rule->high.red != rule->high.blu)
194  fprintf(fd, ":%d:%d", rule->high.grn, rule->high.blu);
195  }
196  fprintf(fd, "\n");
197  }
198 }
199 
200 static void write_old_colors(FILE *fd, struct Colors *colors)
201 {
202  int i, n;
203 
204  fprintf(fd, "#%ld first color\n", (long)colors->fixed.min);
205  if (colors->null_set) {
206  fprintf(fd, "%d %d %d\n", (int)colors->null_red, (int)colors->null_grn,
207  (int)colors->null_blu);
208  }
209  else
210  fprintf(fd, "255 255 255\n"); /* white */
211 
212  n = colors->fixed.max - colors->fixed.min + 1;
213 
214  for (i = 0; i < n; i++) {
215  fprintf(fd, "%d", (int)colors->fixed.lookup.red[i]);
216  if (colors->fixed.lookup.red[i] != colors->fixed.lookup.grn[i] ||
217  colors->fixed.lookup.red[i] != colors->fixed.lookup.blu[i])
218  fprintf(fd, " %d %d", (int)colors->fixed.lookup.grn[i],
219  (int)colors->fixed.lookup.blu[i]);
220  fprintf(fd, "\n");
221  }
222 }
223 
224 static void forced_write_old_colors(FILE *fd, struct Colors *colors)
225 {
226  int red, grn, blu;
227  CELL cat;
228 
229  fprintf(fd, "#%ld first color\n", (long)colors->cmin);
230  cat = 0;
231  Rast_get_c_color(&cat, &red, &grn, &blu, colors);
232  fprintf(fd, "%d %d %d\n", red, grn, blu);
233 
234  for (cat = colors->cmin; cat <= colors->cmax; cat++) {
235  Rast_get_c_color(&cat, &red, &grn, &blu, colors);
236  fprintf(fd, "%d", red);
237  if (red != grn || red != blu)
238  fprintf(fd, " %d %d", grn, blu);
239  fprintf(fd, "\n");
240  }
241 }
242 
243 static void format_min(char *str, double dval)
244 {
245  double dtmp;
246 
247  snprintf(str, FORMAT_STR_SZ, "%.17g", dval);
248  /* Note that G_trim_decimal() does not trim e.g. 1.0000000e-20 */
249  G_trim_decimal(str);
250  sscanf(str, "%lf", &dtmp);
251  if (dtmp != dval) { /* if no zeros after decimal point were trimmed */
252  /* lower dval by GRASS_EPSILON fraction */
253  if (dval > 0)
254  snprintf(str, FORMAT_STR_SZ, "%.17g", dval * (1 - GRASS_EPSILON));
255  else
256  snprintf(str, FORMAT_STR_SZ, "%.17g", dval * (1 + GRASS_EPSILON));
257  }
258 }
259 
260 static void format_max(char *str, double dval)
261 {
262  double dtmp;
263 
264  snprintf(str, FORMAT_STR_SZ, "%.17g", dval);
265  /* Note that G_trim_decimal() does not trim e.g. 1.0000000e-20 */
266  G_trim_decimal(str);
267  sscanf(str, "%lf", &dtmp);
268  if (dtmp != dval) { /* if no zeros after decimal point were trimmed */
269  /* increase dval by by GRASS_EPSILON fraction */
270  if (dval > 0)
271  snprintf(str, FORMAT_STR_SZ, "%.17g", dval * (1 + GRASS_EPSILON));
272  else
273  snprintf(str, FORMAT_STR_SZ, "%.17g", dval * (1 - GRASS_EPSILON));
274  }
275 }
int G_name_is_fully_qualified(const char *, char *, char *)
Check if map name is fully qualified (map @ mapset)
Definition: nme_in_mps.c:36
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
void G_trim_decimal(char *)
Removes trailing zeros from decimal number.
Definition: trim_dec.c:24
int G_remove(const char *, const char *)
Remove a database file.
Definition: remove.c:44
FILE * G_fopen_new(const char *, const char *)
Open a new database file.
Definition: gis/open.c:219
int Rast_get_c_color(const CELL *, int *, int *, int *, struct Colors *)
Gets color from raster map (CELL)
Definition: color_get.c:67
#define GRASS_EPSILON
Definition: gis.h:172
#define GMAPSET_MAX
Definition: gis.h:191
#define GNAME_MAX
Definition: gis.h:190
double DCELL
Definition: gis.h:629
int CELL
Definition: gis.h:628
#define _(str)
Definition: glocale.h:10
const char * name
Definition: named_colr.c:6
#define strcpy
Definition: parson.c:62
void Rast__write_colors(FILE *fd, struct Colors *colors)
Write map layer color table.
void Rast_write_colors(const char *name, const char *mapset, struct Colors *colors)
Write map layer color table.
#define FORMAT_STR_SZ
Definition: gis.h:686
int null_set
Definition: gis.h:691
unsigned char undef_blu
Definition: gis.h:698
DCELL cmax
Definition: gis.h:702
unsigned char null_blu
Definition: gis.h:694
unsigned char undef_grn
Definition: gis.h:697
unsigned char undef_red
Definition: gis.h:696
struct _Color_Info_ fixed
Definition: gis.h:699
struct _Color_Info_ modular
Definition: gis.h:700
int version
Definition: gis.h:687
int invert
Definition: gis.h:689
int undef_set
Definition: gis.h:695
unsigned char null_grn
Definition: gis.h:693
DCELL shift
Definition: gis.h:688
unsigned char null_red
Definition: gis.h:692
DCELL cmin
Definition: gis.h:701
struct _Color_Info_::@3 lookup
unsigned char * red
Definition: gis.h:667
DCELL max
Definition: gis.h:683
unsigned char * blu
Definition: gis.h:669
struct _Color_Rule_ * rules
Definition: gis.h:663
unsigned char * grn
Definition: gis.h:668
DCELL min
Definition: gis.h:683
struct _Color_Rule_ * prev
Definition: gis.h:659
struct _Color_Rule_ * next
Definition: gis.h:658
struct _Color_Value_ low high
Definition: gis.h:657
unsigned char blu
Definition: gis.h:653
unsigned char red
Definition: gis.h:651
DCELL value
Definition: gis.h:650
unsigned char grn
Definition: gis.h:652
Definition: lidar.h:85