GRASS 8 Programmer's Manual  8.5.0dev(2025)-9d806b45d8
reclass.c
Go to the documentation of this file.
1 /*!
2  * \file lib/raster/reclass.c
3  *
4  * \brief Raster Library - Check if raster map is reclassified
5  *
6  * (C) 2001-2009 by the GRASS Development Team
7  *
8  * This program is free software under the GNU General Public License
9  * (>=v2). Read the file COPYING that comes with GRASS for details.
10  *
11  * \author Original author CERL
12  */
13 
14 #include <string.h>
15 #include <stdbool.h>
16 
17 #include <grass/gis.h>
18 #include <grass/raster.h>
19 #include <grass/glocale.h>
20 
21 static const char NULL_STRING[] = "null";
22 static int reclass_type(FILE *, char **, char **, char **);
23 static FILE *fopen_cellhd_old(const char *, const char *);
24 static FILE *fopen_cellhd_new(const char *);
25 static int get_reclass_table(FILE *, struct Reclass *, char **);
26 
27 /*!
28  * \brief Check if raster map is reclassified
29  *
30  * This function determines if the raster map <i>name</i> in
31  * <i>mapset</i> is a reclass file. If it is, then the name and mapset
32  * of the referenced raster map are copied into the <i>rname</i> and
33  * <i>rmapset</i> buffers.
34  *
35  * \param name map name
36  * \param mapset mapset name
37  * \param[out] rname name of reference map
38  * \param[out] rmapset mapset where reference map lives
39  *
40  * \returns 1 if it is a reclass file
41  * \return 0 if it is not
42  * \return -1 if there was a problem reading the raster header
43  */
44 int Rast_is_reclass(const char *name, const char *mapset, char rname[GNAME_MAX],
45  char rmapset[GMAPSET_MAX])
46 {
47  FILE *fd;
48  int type;
49 
50  fd = fopen_cellhd_old(name, mapset);
51  if (fd == NULL)
52  return -1;
53 
54  type = reclass_type(fd, &rname, &rmapset, NULL);
55  fclose(fd);
56  if (type < 0)
57  return -1;
58  else
59  return type != 0;
60 }
61 
62 /*!
63  * \brief Get child reclass maps list
64  *
65  * This function generates a child reclass maps list from the
66  * cell_misc/reclassed_to file which stores this list. The
67  * cell_misc/reclassed_to file is written by Rast_put_reclass().
68  * Rast_is_reclassed_to() is used by <tt>g.rename</tt>, <tt>g.remove</tt>
69  * and <tt>r.reclass</tt> to prevent accidentally deleting the parent
70  * map of a reclassed raster map.
71  *
72  * \param name map name
73  * \param mapset mapset name
74  * \param[out] nrmaps number of reference maps
75  * \param[out] rmaps array of names of reference maps
76  *
77  * \return number of reference maps
78  * \return -1 on error
79  */
80 int Rast_is_reclassed_to(const char *name, const char *mapset, int *nrmaps,
81  char ***rmaps)
82 {
83  FILE *fd;
84  int i, j, k, l;
85  char buf2[256], buf3[256];
86 
87  fd = G_fopen_old_misc("cell_misc", "reclassed_to", name, mapset);
88 
89  if (fd == NULL) {
90  return -1;
91  }
92 
93  if (rmaps)
94  *rmaps = NULL;
95  for (i = 0; !feof(fd) && fgets(buf2, 255, fd);) {
96  l = strlen(buf2);
97  for (j = 0, k = 0; j < l; j++) {
98  if (buf2[j] == '#' ||
99  ((buf2[j] == ' ' || buf2[j] == '\t' || buf2[j] == '\n') && k))
100  break;
101  else if (buf2[j] != ' ' && buf2[j] != '\t')
102  buf3[k++] = buf2[j];
103  }
104 
105  if (k) {
106  buf3[k] = 0;
107  i++;
108  if (rmaps) {
109  *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
110  (*rmaps)[i - 1] = (char *)G_malloc(k + 1);
111  strncpy((*rmaps)[i - 1], buf3, k);
112  (*rmaps)[i - 1][k] = 0;
113  }
114  }
115  }
116 
117  if (nrmaps)
118  *nrmaps = i;
119 
120  if (i && rmaps) {
121  i++;
122  *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
123  (*rmaps)[i - 1] = NULL;
124  }
125 
126  fclose(fd);
127 
128  return i;
129 }
130 
131 /*!
132  \brief Get reclass
133 
134  \param name map name
135  \param mapset mapset name
136  \param[out] reclass pointer to Reclass structure
137 
138  \return type code (>=1), 0 if no reclass, -1 on error
139  */
140 int Rast_get_reclass(const char *name, const char *mapset,
141  struct Reclass *reclass)
142 {
143  FILE *fd;
144  int stat;
145  char rname[GNAME_MAX] = {0}, rmapset[GMAPSET_MAX] = {0};
146  char *tmp_name = rname, *tmp_mapset = rmapset;
147 
148  fd = fopen_cellhd_old(name, mapset);
149  if (fd == NULL)
150  return -1;
151  char *error_message = NULL;
152  reclass->type = reclass_type(fd, &tmp_name, &tmp_mapset, &error_message);
153  reclass->name = G_store(tmp_name);
154  reclass->mapset = G_store(tmp_mapset);
155  if (reclass->type == 0) {
156  // no reclass
157  fclose(fd);
158  return reclass->type;
159  }
160  if (reclass->type < 0) {
161  // error
162  fclose(fd);
163  G_warning(_("Error reading beginning of header file for <%s@%s>: %s"),
164  name, mapset, error_message);
165  if (error_message != NULL)
166  G_free(error_message);
167  return reclass->type;
168  }
169 
170  switch (reclass->type) {
171  case RECLASS_TABLE:
172  stat = get_reclass_table(fd, reclass, &error_message);
173  break;
174  default:
175  stat = -1;
176  }
177 
178  fclose(fd);
179  if (stat < 0) {
180  if (stat == -2)
181  G_warning(_("Too many reclass categories for <%s@%s>"), name,
182  mapset);
183  else
184  G_warning(
185  _("Illegal reclass format in header file for <%s@%s>: %s"),
186  name, mapset, error_message);
187  stat = -1;
188  }
189  if (error_message != NULL)
190  G_free(error_message);
191  return stat;
192 }
193 
194 /*!
195  \brief Free Reclass structure
196 
197  \param reclass pointer to Reclass structure
198  */
199 void Rast_free_reclass(struct Reclass *reclass)
200 {
201  switch (reclass->type) {
202  case RECLASS_TABLE:
203  if (reclass->num > 0)
204  G_free(reclass->table);
205  reclass->num = 0;
206  if (reclass->name)
207  G_free(reclass->name);
208  if (reclass->mapset)
209  G_free(reclass->mapset);
210  reclass->name = NULL;
211  reclass->mapset = NULL;
212  break;
213  default:
214  break;
215  }
216 }
217 
218 /**
219  * \brief Get reclass type if it is a reclass file
220  *
221  * \param fd[in] file descriptor
222  * \param rname[out] name of the reclass from raster
223  * \param rmapset[out] name of the mapset of the raster
224  * \param error_message[out] will be assigned a newly error message if not NULL
225  *
226  * \returns RECLASS_TABLE if reclass, 0 if not, -1 on error
227  */
228 static int reclass_type(FILE *fd, char **rname, char **rmapset,
229  char **error_message)
230 {
231  char
232  buf[GNAME_MAX + 128 + 1]; // name or mapset plus the label and separator
233  char label[128], arg[GNAME_MAX];
234  int i;
235  int type;
236 
237  /* Check to see if this is a reclass file */
238  if (fgets(buf, sizeof(buf), fd) == NULL)
239  return 0;
240  if (strncmp(buf, "reclas", 6))
241  return 0;
242  /* later may add other types of reclass */
243  type = RECLASS_TABLE;
244 
245  /* Read the mapset and file name of the REAL cell file */
246  if (*rname)
247  **rname = '\0';
248  if (*rmapset)
249  **rmapset = '\0';
250  for (i = 0; i < 2; i++) {
251  if (fgets(buf, sizeof buf, fd) == NULL) {
252  if (error_message != NULL) {
253  G_asprintf(error_message, _("File too short, reading line %d"),
254  i + 1);
255  }
256  return -1;
257  }
258  if (buf[strlen(buf) - 1] != '\n') {
259  if (error_message != NULL) {
260  G_asprintf(error_message, _("Line too long: %s..."), buf);
261  }
262  return -1;
263  }
264  if (sscanf(buf, "%[^:]:%s", label, arg) != 2) {
265  if (error_message != NULL) {
266  G_asprintf(error_message, _("Format is not key:value: %s"),
267  buf);
268  }
269  return -1;
270  }
271  if (strncmp(label, "maps", 4) == 0 && *rmapset) {
272  G_strlcpy(*rmapset, arg, GMAPSET_MAX);
273  }
274  else if (strncmp(label, "name", 4) == 0 && *rname) {
275  G_strlcpy(*rname, arg, GNAME_MAX);
276  }
277  else {
278  if (error_message != NULL) {
279  G_asprintf(error_message, _("Unknown key at line: %s"), buf);
280  }
281  return -1;
282  }
283  }
284  if ((*rmapset && **rmapset) || (*rname && **rname))
285  return type;
286  else {
287  // If they do not occur in the two lines we expect them.
288  if (**rname && error_message != NULL) {
289  G_asprintf(error_message,
290  _("Mapset not read, only raster name: %s"), *rname);
291  }
292  else if (**rmapset && error_message != NULL) {
293  G_asprintf(error_message,
294  _("Raster name not read, only mapset: %s"), *rmapset);
295  }
296  else if (error_message != NULL) {
297  *error_message = G_store(_("Raster name and mapset not read"));
298  }
299  return -1;
300  }
301 }
302 
303 static FILE *fopen_cellhd_old(const char *name, const char *mapset)
304 {
305  return G_fopen_old("cellhd", name, mapset);
306 }
307 
308 /*!
309  \brief Put reclass
310 
311  \param name map name
312  \param reclass pointer to Reclass structure
313 
314  \return -1 on error
315  \return 1 on success
316  */
317 int Rast_put_reclass(const char *name, const struct Reclass *reclass)
318 {
319  FILE *fd;
320  long min, max;
321  int found;
322  char buf1[GPATH_MAX], buf2[GNAME_MAX], *p;
323  char *xname;
324 
325  switch (reclass->type) {
326  case RECLASS_TABLE:
327  if (reclass->min > reclass->max || reclass->num <= 0) {
328  G_fatal_error(_("Illegal reclass request"));
329  return -1;
330  }
331  break;
332  default:
333  G_fatal_error(_("Illegal reclass type"));
334  return -1;
335  }
336 
337  fd = fopen_cellhd_new(name);
338  if (fd == NULL) {
339  G_warning(_("Unable to create header file for <%s@%s>"), name,
340  G_mapset());
341  return -1;
342  }
343 
344  fprintf(fd, "reclass\n");
345  fprintf(fd, "name: %s\n", reclass->name);
346  fprintf(fd, "mapset: %s\n", reclass->mapset);
347 
348  /* find first non-null entry */
349  for (min = 0; min < reclass->num; min++)
350  if (!Rast_is_c_null_value(&reclass->table[min]))
351  break;
352  /* find last non-zero entry */
353  for (max = reclass->num - 1; max >= 0; max--)
354  if (!Rast_is_c_null_value(&reclass->table[max]))
355  break;
356 
357  /*
358  * if the resultant table is empty, write out a dummy table
359  * else write out the table
360  * first entry is #min
361  * rest are translations for cat min+i
362  */
363  if (min > max)
364  fprintf(fd, "0\n");
365  else {
366  fprintf(fd, "#%ld\n", (long)reclass->min + min);
367  while (min <= max) {
368  if (Rast_is_c_null_value(&reclass->table[min]))
369  fprintf(fd, "%s\n", NULL_STRING);
370  else
371  fprintf(fd, "%ld\n", (long)reclass->table[min]);
372  min++;
373  }
374  }
375  fclose(fd);
376 
377  strcpy(buf2, reclass->name);
378  if ((p = strchr(buf2, '@')))
379  *p = 0;
380 
381  G_file_name_misc(buf1, "cell_misc", "reclassed_to", reclass->name,
382  reclass->mapset);
383 
384  fd = fopen(buf1, "a+");
385  if (fd == NULL) {
386 #if 0
387  G_warning(_("Unable to create dependency file in <%s@%s>"),
388  buf2, reclass->mapset);
389 #endif
390  return 1;
391  }
392 
393  G_fseek(fd, 0L, SEEK_SET);
394 
396  found = 0;
397  for (;;) {
398  char buf[GNAME_MAX + GMAPSET_MAX];
399 
400  if (!G_getl2(buf, sizeof(buf), fd))
401  break;
402  if (strcmp(xname, buf) == 0) {
403  found = 1;
404  break;
405  }
406  }
407 
408  if (!found)
409  fprintf(fd, "%s\n", xname);
410 
411  G_free(xname);
412  fclose(fd);
413 
414  return 1;
415 }
416 
417 static FILE *fopen_cellhd_new(const char *name)
418 {
419  return G_fopen_new("cellhd", name);
420 }
421 
422 /**
423  * \brief Get reclass table from header file
424  *
425  * If there is reading error due to the format, -1 is returned and,
426  * if error_message is not NULL, it will be set to a pointer to a newly
427  * allocated string containing an error message with the line where error
428  * was encountered.
429  *
430  * \param fd header file
431  * \param[out] reclass pointer to Reclass structure
432  * \param[out] error_message pointer to error message
433 
434  * \return 1 on success, -1 on format error, -2 on too many categories
435  */
436 static int get_reclass_table(FILE *fd, struct Reclass *reclass,
437  char **error_message)
438 {
439  char buf[128];
440  int n;
441  int first, null_str_size;
442  CELL cat;
443  long len;
444 
445  /*
446  * allocate the table, expanding as each entry is read
447  * note that G_realloc() will become G_malloc() if ptr in
448  * NULL
449  */
450  reclass->min = 0;
451  reclass->table = NULL;
452  null_str_size = strlen(NULL_STRING);
453  n = 0;
454  first = 1;
455  bool min_set = false;
456  while (fgets(buf, sizeof buf, fd)) {
457  if (first) {
458  first = 0;
459  if (sscanf(buf, "#%d", &cat) == 1) {
460  reclass->min = cat;
461  min_set = true;
462  continue;
463  }
464  }
465  if (strncmp(buf, NULL_STRING, null_str_size) == 0)
466  Rast_set_c_null_value(&cat, 1);
467  else {
468  if (sscanf(buf, "%d", &cat) != 1) {
469  if (reclass->table != NULL)
470  G_free(reclass->table);
471  if (error_message != NULL) {
472  if (min_set)
473  G_asprintf(error_message,
474  _("Reading integer failed on line: %s "
475  "(after reading min: %d)"),
476  buf, reclass->min);
477  else
478  G_asprintf(error_message,
479  _("First entry (min) not read yet and "
480  "reading integer failed on line: %s"),
481  buf);
482  }
483  return -1;
484  }
485  }
486  n++;
487  len = (long)n * sizeof(CELL);
488 
489  if (len != (int)len) { /* check for int overflow */
490  if (reclass->table != NULL)
491  G_free(reclass->table);
492  return -2;
493  }
494  reclass->table = (CELL *)G_realloc((char *)reclass->table, (int)len);
495  reclass->table[n - 1] = cat;
496  }
497  reclass->max = reclass->min + n - 1;
498  reclass->num = n;
499  return 1;
500 }
#define NULL
Definition: ccmath.h:32
char * G_file_name_misc(char *, const char *, const char *, const char *, const char *)
Builds full path names to GIS misc data files.
Definition: file_name.c:101
FILE * G_fopen_old(const char *, const char *, const char *)
Open a database file for reading.
Definition: gis/open.c:248
int G_getl2(char *, int, FILE *)
Gets a line of text from a file of any pedigree.
Definition: getl.c:60
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:147
#define G_realloc(p, n)
Definition: defs/gis.h:96
FILE * G_fopen_old_misc(const char *, const char *, const char *, const char *)
open a database misc file for reading
Definition: open_misc.c:205
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition: defs/gis.h:94
void G_fseek(FILE *, off_t, int)
Change the file position of the stream.
Definition: gis/seek.c:50
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
int G_asprintf(char **, const char *,...) __attribute__((format(printf
char * G_fully_qualified_name(const char *, const char *)
Get fully qualified element name.
Definition: nme_in_mps.c:101
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
FILE * G_fopen_new(const char *, const char *)
Open a new database file.
Definition: gis/open.c:216
size_t G_strlcpy(char *, const char *, size_t)
Safe string copy function.
Definition: strlcpy.c:52
void Rast_set_c_null_value(CELL *, int)
To set a number of CELL raster values to NULL.
Definition: null_val.c:124
#define Rast_is_c_null_value(cellVal)
Definition: defs/raster.h:413
#define min(x, y)
Definition: draw2.c:29
#define max(x, y)
Definition: draw2.c:30
#define GMAPSET_MAX
Definition: gis.h:197
#define GPATH_MAX
Definition: gis.h:199
#define GNAME_MAX
Definition: gis.h:196
int CELL
Definition: gis.h:634
#define _(str)
Definition: glocale.h:10
const char * name
Definition: named_colr.c:6
#define strcpy
Definition: parson.c:66
double l
Definition: r_raster.c:39
#define RECLASS_TABLE
Definition: raster.h:7
int Rast_get_reclass(const char *name, const char *mapset, struct Reclass *reclass)
Get reclass.
Definition: reclass.c:140
int Rast_put_reclass(const char *name, const struct Reclass *reclass)
Put reclass.
Definition: reclass.c:317
int Rast_is_reclassed_to(const char *name, const char *mapset, int *nrmaps, char ***rmaps)
Get child reclass maps list.
Definition: reclass.c:80
void Rast_free_reclass(struct Reclass *reclass)
Free Reclass structure.
Definition: reclass.c:199
int Rast_is_reclass(const char *name, const char *mapset, char rname[256], char rmapset[256])
Check if raster map is reclassified.
Definition: reclass.c:44
Definition: raster.h:31
int num
Definition: raster.h:35
CELL * table
Definition: raster.h:38
char * mapset
Definition: raster.h:33
CELL max
Definition: raster.h:37
char * name
Definition: raster.h:32
int type
Definition: raster.h:34
CELL min
Definition: raster.h:36