GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-fbabf32052
json_color_out.c
Go to the documentation of this file.
1 /*!
2  * \file lib/raster/json_color_out.c
3  *
4  * \brief Raster Library - Print color table in json format
5  *
6  * (C) 2010-2024 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 Nishant Bansal
13  */
14 
15 #include <math.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include <grass/gis.h>
21 #include <grass/gjson.h>
22 #include <grass/glocale.h>
23 #include <grass/raster.h>
24 
25 #define COLOR_STRING_LENGTH 30
26 
27 /*!
28  \brief Closes the file if it is not stdout.
29 
30  \param fp file where to print color table rules
31  */
32 static void close_file(FILE *fp)
33 {
34  if (fp != stdout)
35  fclose(fp);
36 }
37 
38 /*!
39  \brief Converts RGB color values to HSV format.
40 
41  \note This implementation is experimental and may be subject to change.
42 
43  \param r red component of the RGB color
44  \param g green component of the RGB color
45  \param b blue component of the RGB color
46  \param[out] h pointer to store the calculated hue
47  \param[out] s pointer to store the calculated saturation
48  \param[out] v pointer to store the calculated value
49  */
50 static void rgb_to_hsv(int r, int g, int b, float *h, float *s, float *v)
51 {
52  float r_norm = (float)r / 255.0f;
53  float g_norm = (float)g / 255.0f;
54  float b_norm = (float)b / 255.0f;
55 
56  float cmax = MAX(r_norm, MAX(g_norm, b_norm));
57  float cmin = MIN(r_norm, MIN(g_norm, b_norm));
58  float diff = cmax - cmin;
59 
60  if (cmax == cmin) {
61  *h = 0;
62  }
63  else if (cmax == r_norm) {
64  *h = fmodf((60.0f * ((g_norm - b_norm) / diff) + 360.0f), 360.0f);
65  }
66  else if (cmax == g_norm) {
67  *h = fmodf((60.0f * ((b_norm - r_norm) / diff) + 120.0f), 360.0f);
68  }
69  else {
70  *h = fmodf((60.0f * ((r_norm - g_norm) / diff) + 240.0f), 360.0f);
71  }
72 
73  if (cmax == 0) {
74  *s = 0;
75  }
76  else {
77  *s = (diff / cmax) * 100.0f;
78  }
79 
80  *v = cmax * 100.0f;
81 }
82 
83 /*!
84  \brief Writes color entry in JSON in specified clr_frmt.
85 
86  \param r red component of RGB color
87  \param g green component of RGB color
88  \param b blue component of RGB color
89  \param clr_frmt color format to be used (RGB, HEX, HSV, TRIPLET).
90  \param color_object pointer to the JSON object
91  */
92 static void set_color(int r, int g, int b, ColorFormat clr_frmt,
93  JSON_Object *color_object)
94 {
95  char color_string[COLOR_STRING_LENGTH];
96  float h, s, v;
97 
98  switch (clr_frmt) {
99  case RGB:
100  snprintf(color_string, sizeof(color_string), "rgb(%d, %d, %d)", r, g,
101  b);
102  G_json_object_set_string(color_object, "color", color_string);
103  break;
104 
105  case HEX:
106  snprintf(color_string, sizeof(color_string), "#%02X%02X%02X", r, g, b);
107  G_json_object_set_string(color_object, "color", color_string);
108  break;
109 
110  case HSV:
111  rgb_to_hsv(r, g, b, &h, &s, &v);
112  snprintf(color_string, sizeof(color_string), "hsv(%d, %d, %d)", (int)h,
113  (int)s, (int)v);
114  G_json_object_set_string(color_object, "color", color_string);
115  break;
116 
117  case TRIPLET:
118  snprintf(color_string, sizeof(color_string), "%d:%d:%d", r, g, b);
119  G_json_object_set_string(color_object, "color", color_string);
120  break;
121  }
122 }
123 
124 /*!
125  \brief Writes a JSON rule for a specific color entry.
126 
127  \param val pointer to the DCELL value
128  \param min,max minimum and maximum value for percentage output (used only
129  when \p perc is non-zero)
130  \param r red component of RGB color
131  \param g green component of RGB color
132  \param b blue component of RGB color
133  \param root_array pointer to the JSON array
134  \param perc TRUE for percentage output
135  \param clr_frmt color format to be used (RBG, HEX, HSV, TRIPLET).
136  \param fp file where to print color table rules
137  \param root_value pointer to json value
138  */
139 static void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g,
140  int b, JSON_Array *root_array, int perc,
141  ColorFormat clr_frmt, FILE *fp,
142  JSON_Value *root_value)
143 {
144  static DCELL v0;
145  static int r0 = -1, g0 = -1, b0 = -1;
146 
147  // Skip writing if the current color is the same as the last one
148  if (v0 == *val && r0 == r && g0 == g && b0 == b)
149  return;
150  // Update last processed values
151  v0 = *val, r0 = r, g0 = g, b0 = b;
152 
153  JSON_Value *color_value = G_json_value_init_object();
154  if (color_value == NULL) {
155  G_json_value_free(root_value);
156  close_file(fp);
157  G_fatal_error(_("Failed to initialize JSON object. Out of memory?"));
158  }
159  JSON_Object *color_object = G_json_object(color_value);
160 
161  // Set the value as a percentage if requested, otherwise set it as-is
162  if (perc)
163  G_json_object_set_number(color_object, "value",
164  100 * (*val - *min) / (*max - *min));
165  else
166  G_json_object_set_number(color_object, "value", *val);
167 
168  set_color(r, g, b, clr_frmt, color_object);
169 
170  G_json_array_append_value(root_array, color_value);
171 }
172 
173 /*!
174  \brief Print color table in JSON format
175 
176  \param colors pointer to Colors structure
177  \param min,max minimum and maximum value for percentage output (used only
178  when \p perc is non-zero)
179  \param fp file where to print color table rules
180  \param perc TRUE for percentage output
181  \param clr_frmt color format to be used (RBG, HEX, HSV, TRIPLET).
182  */
184  FILE *fp, int perc, ColorFormat clr_frmt)
185 {
186  JSON_Value *root_value = G_json_value_init_array();
187  if (root_value == NULL) {
188  close_file(fp);
189  G_fatal_error(_("Failed to initialize JSON array. Out of memory?"));
190  }
191  JSON_Array *root_array = G_json_array(root_value);
192 
193  if (colors->version < 0) {
194  /* 3.0 format */
195  CELL lo, hi;
196 
197  // Retrieve the integer color range
198  Rast_get_c_color_range(&lo, &hi, colors);
199 
200  for (int i = lo; i <= hi; i++) {
201  unsigned char r, g, b, set;
202  DCELL val = (DCELL)i;
203 
204  // Look up the color for the current value and write JSON rule
205  Rast_lookup_c_colors(&i, &r, &g, &b, &set, 1, colors);
206  write_json_rule(&val, &min, &max, r, g, b, root_array, perc,
207  clr_frmt, fp, root_value);
208  }
209  }
210  else {
211  // Get the count of floating-point color rules
212  int count = Rast_colors_count(colors);
213 
214  for (int i = 0; i < count; i++) {
215  DCELL val1, val2;
216  unsigned char r1, g1, b1, r2, g2, b2;
217 
218  // Retrieve the color rule values and their respective RGB colors
219  Rast_get_fp_color_rule(&val1, &r1, &g1, &b1, &val2, &r2, &g2, &b2,
220  colors, count - 1 - i);
221 
222  // write JSON rule
223  write_json_rule(&val1, &min, &max, r1, g1, b1, root_array, perc,
224  clr_frmt, fp, root_value);
225  write_json_rule(&val2, &min, &max, r2, g2, b2, root_array, perc,
226  clr_frmt, fp, root_value);
227  }
228  }
229 
230  // Add special color entries for "null" and "default" values
231  {
232  int r, g, b;
233 
234  // Get RGB color for null values and create JSON entry
235  Rast_get_null_value_color(&r, &g, &b, colors);
236  JSON_Value *nv_value = G_json_value_init_object();
237  if (nv_value == NULL) {
238  G_json_value_free(root_value);
239  close_file(fp);
241  _("Failed to initialize JSON object. Out of memory?"));
242  }
243  JSON_Object *nv_object = G_json_object(nv_value);
244  G_json_object_set_string(nv_object, "value", "nv");
245  set_color(r, g, b, clr_frmt, nv_object);
246  G_json_array_append_value(root_array, nv_value);
247 
248  // Get RGB color for default values and create JSON entry
249  Rast_get_default_color(&r, &g, &b, colors);
250  JSON_Value *default_value = G_json_value_init_object();
251  if (default_value == NULL) {
252  G_json_value_free(root_value);
253  close_file(fp);
255  _("Failed to initialize JSON object. Out of memory?"));
256  }
257  JSON_Object *default_object = G_json_object(default_value);
258  G_json_object_set_string(default_object, "value", "default");
259  set_color(r, g, b, clr_frmt, default_object);
260  G_json_array_append_value(root_array, default_value);
261  }
262 
263  // Serialize JSON array to a string and print to the file
265  if (!json_string) {
266  G_json_value_free(root_value);
267  close_file(fp);
268  G_fatal_error(_("Failed to serialize JSON to pretty format."));
269  }
270 
271  fputs(json_string, fp);
272 
274  G_json_value_free(root_value);
275 
276  close_file(fp);
277 }
#define NULL
Definition: ccmath.h:32
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
int Rast_colors_count(const struct Colors *)
Get both modular and fixed rules count.
void Rast_get_c_color_range(CELL *, CELL *, const struct Colors *)
Get color range values (CELL)
Definition: color_range.c:64
int Rast_get_fp_color_rule(DCELL *, unsigned char *, unsigned char *, unsigned char *, DCELL *, unsigned char *, unsigned char *, unsigned char *, const struct Colors *, int)
Get color rule from both modular and fixed rules.
void Rast_get_null_value_color(int *, int *, int *, const struct Colors *)
Gets color for null value.
Definition: color_get.c:126
void Rast_lookup_c_colors(const CELL *, unsigned char *, unsigned char *, unsigned char *, unsigned char *, int, struct Colors *)
Lookup an array of colors.
Definition: color_look.c:45
void Rast_get_default_color(int *, int *, int *, const struct Colors *)
Gets default color.
Definition: color_get.c:154
#define min(x, y)
Definition: draw2.c:29
#define max(x, y)
Definition: draw2.c:30
#define MIN(a, b)
Definition: gis.h:154
double DCELL
Definition: gis.h:629
int CELL
Definition: gis.h:628
#define MAX(a, b)
Definition: gis.h:149
char * G_json_serialize_to_string_pretty(const JSON_Value *value)
Definition: gjson.c:143
void G_json_value_free(JSON_Value *value)
Definition: gjson.c:152
void G_json_free_serialized_string(char *string)
Definition: gjson.c:148
JSON_Status G_json_object_set_number(JSON_Object *object, const char *name, double number)
Definition: gjson.c:83
JSON_Array * G_json_array(const JSON_Value *value)
Definition: gjson.c:97
JSON_Value * G_json_value_init_object(void)
Definition: gjson.c:24
JSON_Value * G_json_value_init_array(void)
Definition: gjson.c:29
JSON_Status G_json_object_set_string(JSON_Object *object, const char *name, const char *string)
Definition: gjson.c:78
JSON_Status G_json_array_append_value(JSON_Array *array, JSON_Value *value)
Definition: gjson.c:118
JSON_Object * G_json_object(const JSON_Value *value)
Definition: gjson.c:39
#define _(str)
Definition: glocale.h:10
int count
void Rast_print_json_colors(struct Colors *colors, DCELL min, DCELL max, FILE *fp, int perc, ColorFormat clr_frmt)
Print color table in JSON format.
#define COLOR_STRING_LENGTH
float g
Definition: named_colr.c:7
const char * json_string(const JSON_Value *value)
Definition: parson.c:2805
struct json_array_t JSON_Array
Definition: parson.h:46
struct json_value_t JSON_Value
Definition: parson.h:47
struct json_object_t JSON_Object
Definition: parson.h:45
double b
Definition: r_raster.c:39
double r
Definition: r_raster.c:39
ColorFormat
Color format identifiers (enum)
Definition: raster.h:178
@ HSV
Definition: raster.h:178
@ RGB
Definition: raster.h:178
@ TRIPLET
Definition: raster.h:178
@ HEX
Definition: raster.h:178
Definition: gis.h:686
int version
Definition: gis.h:687