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