GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-bb27c0570b
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 
16 #include <grass/gis.h>
17 #include <grass/raster.h>
18 #include <grass/glocale.h>
19 
20 static const char NULL_STRING[] = "null";
21 static int reclass_type(FILE *, char **, char **);
22 static FILE *fopen_cellhd_old(const char *, const char *);
23 static FILE *fopen_cellhd_new(const char *);
24 static int get_reclass_table(FILE *, struct Reclass *);
25 
26 /*!
27  * \brief Check if raster map is reclassified
28  *
29  * This function determines if the raster map <i>name</i> in
30  * <i>mapset</i> is a reclass file. If it is, then the name and mapset
31  * of the referenced raster map are copied into the <i>rname</i> and
32  * <i>rmapset</i> buffers.
33  *
34  * \param name map name
35  * \param mapset mapset name
36  * \param[out] rname name of reference map
37  * \param[out] rmapset mapset where reference map lives
38  *
39  * \returns 1 if it is a reclass file
40  * \return 0 if it is not
41  * \return -1 if there was a problem reading the raster header
42  */
43 int Rast_is_reclass(const char *name, const char *mapset, char *rname,
44  char *rmapset)
45 {
46  FILE *fd;
47  int type;
48 
49  fd = fopen_cellhd_old(name, mapset);
50  if (fd == NULL)
51  return -1;
52 
53  type = reclass_type(fd, &rname, &rmapset);
54  fclose(fd);
55  if (type < 0)
56  return -1;
57  else
58  return type != 0;
59 }
60 
61 /*!
62  * \brief Get child reclass maps list
63  *
64  * This function generates a child reclass maps list from the
65  * cell_misc/reclassed_to file which stores this list. The
66  * cell_misc/reclassed_to file is written by Rast_put_reclass().
67  * Rast_is_reclassed_to() is used by <tt>g.rename</tt>, <tt>g.remove</tt>
68  * and <tt>r.reclass</tt> to prevent accidentally deleting the parent
69  * map of a reclassed raster map.
70  *
71  * \param name map name
72  * \param mapset mapset name
73  * \param[out] nrmaps number of reference maps
74  * \param[out] rmaps array of names of reference maps
75  *
76  * \return number of reference maps
77  * \return -1 on error
78  */
79 int Rast_is_reclassed_to(const char *name, const char *mapset, int *nrmaps,
80  char ***rmaps)
81 {
82  FILE *fd;
83  int i, j, k, l;
84  char buf2[256], buf3[256];
85 
86  fd = G_fopen_old_misc("cell_misc", "reclassed_to", name, mapset);
87 
88  if (fd == NULL) {
89  return -1;
90  }
91 
92  if (rmaps)
93  *rmaps = NULL;
94  for (i = 0; !feof(fd) && fgets(buf2, 255, fd);) {
95  l = strlen(buf2);
96  for (j = 0, k = 0; j < l; j++) {
97  if (buf2[j] == '#' ||
98  ((buf2[j] == ' ' || buf2[j] == '\t' || buf2[j] == '\n') && k))
99  break;
100  else if (buf2[j] != ' ' && buf2[j] != '\t')
101  buf3[k++] = buf2[j];
102  }
103 
104  if (k) {
105  buf3[k] = 0;
106  i++;
107  if (rmaps) {
108  *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
109  (*rmaps)[i - 1] = (char *)G_malloc(k + 1);
110  strncpy((*rmaps)[i - 1], buf3, k);
111  (*rmaps)[i - 1][k] = 0;
112  }
113  }
114  }
115 
116  if (nrmaps)
117  *nrmaps = i;
118 
119  if (i && rmaps) {
120  i++;
121  *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
122  (*rmaps)[i - 1] = NULL;
123  }
124 
125  fclose(fd);
126 
127  return i;
128 }
129 
130 /*!
131  \brief Get reclass
132 
133  \param name map name
134  \param mapset mapset name
135  \param[out] reclass pointer to Reclass structure
136 
137  \return -1 on error
138  \return type code
139  */
140 int Rast_get_reclass(const char *name, const char *mapset,
141  struct Reclass *reclass)
142 {
143  FILE *fd;
144  int stat;
145 
146  fd = fopen_cellhd_old(name, mapset);
147  if (fd == NULL)
148  return -1;
149  reclass->name = NULL;
150  reclass->mapset = NULL;
151  reclass->type = reclass_type(fd, &reclass->name, &reclass->mapset);
152  if (reclass->type <= 0) {
153  fclose(fd);
154  return reclass->type;
155  }
156 
157  switch (reclass->type) {
158  case RECLASS_TABLE:
159  stat = get_reclass_table(fd, reclass);
160  break;
161  default:
162  stat = -1;
163  }
164 
165  fclose(fd);
166  if (stat < 0) {
167  if (stat == -2)
168  G_warning(_("Too many reclass categories for <%s@%s>"), name,
169  mapset);
170  else
171  G_warning(_("Illegal reclass format in header file for <%s@%s>"),
172  name, mapset);
173  stat = -1;
174  }
175  return stat;
176 }
177 
178 /*!
179  \brief Free Reclass structure
180 
181  \param reclass pointer to Reclass structure
182  */
183 void Rast_free_reclass(struct Reclass *reclass)
184 {
185  switch (reclass->type) {
186  case RECLASS_TABLE:
187  if (reclass->num > 0)
188  G_free(reclass->table);
189  reclass->num = 0;
190  if (reclass->name)
191  G_free(reclass->name);
192  if (reclass->mapset)
193  G_free(reclass->mapset);
194  reclass->name = NULL;
195  reclass->mapset = NULL;
196  break;
197  default:
198  break;
199  }
200 }
201 
202 static int reclass_type(FILE *fd, char **rname, char **rmapset)
203 {
204  char buf[128];
205  char label[128], arg[128];
206  int i;
207  int type;
208 
209  /* Check to see if this is a reclass file */
210  if (fgets(buf, sizeof(buf), fd) == NULL)
211  return 0;
212  if (strncmp(buf, "reclas", 6))
213  return 0;
214  /* later may add other types of reclass */
215  type = RECLASS_TABLE;
216 
217  /* Read the mapset and file name of the REAL cell file */
218  if (*rname)
219  **rname = '\0';
220  if (*rmapset)
221  **rmapset = '\0';
222  for (i = 0; i < 2; i++) {
223  if (fgets(buf, sizeof buf, fd) == NULL)
224  return -1;
225  if (sscanf(buf, "%[^:]:%s", label, arg) != 2)
226  return -1;
227  if (strncmp(label, "maps", 4) == 0) {
228  if (*rmapset)
229  strcpy(*rmapset, arg);
230  else
231  *rmapset = G_store(arg);
232  }
233  else if (strncmp(label, "name", 4) == 0) {
234  if (*rname)
235  strcpy(*rname, arg);
236  else
237  *rname = G_store(arg);
238  }
239  else
240  return -1;
241  }
242  if (**rmapset && **rname)
243  return type;
244  else
245  return -1;
246 }
247 
248 static FILE *fopen_cellhd_old(const char *name, const char *mapset)
249 {
250  return G_fopen_old("cellhd", name, mapset);
251 }
252 
253 /*!
254  \brief Put reclass
255 
256  \param name map name
257  \param reclass pointer to Reclass structure
258 
259  \return -1 on error
260  \return 1 on success
261  */
262 int Rast_put_reclass(const char *name, const struct Reclass *reclass)
263 {
264  FILE *fd;
265  long min, max;
266  int found;
267  char buf1[GPATH_MAX], buf2[GNAME_MAX], *p;
268  char *xname;
269 
270  switch (reclass->type) {
271  case RECLASS_TABLE:
272  if (reclass->min > reclass->max || reclass->num <= 0) {
273  G_fatal_error(_("Illegal reclass request"));
274  return -1;
275  }
276  break;
277  default:
278  G_fatal_error(_("Illegal reclass type"));
279  return -1;
280  }
281 
282  fd = fopen_cellhd_new(name);
283  if (fd == NULL) {
284  G_warning(_("Unable to create header file for <%s@%s>"), name,
285  G_mapset());
286  return -1;
287  }
288 
289  fprintf(fd, "reclass\n");
290  fprintf(fd, "name: %s\n", reclass->name);
291  fprintf(fd, "mapset: %s\n", reclass->mapset);
292 
293  /* find first non-null entry */
294  for (min = 0; min < reclass->num; min++)
295  if (!Rast_is_c_null_value(&reclass->table[min]))
296  break;
297  /* find last non-zero entry */
298  for (max = reclass->num - 1; max >= 0; max--)
299  if (!Rast_is_c_null_value(&reclass->table[max]))
300  break;
301 
302  /*
303  * if the resultant table is empty, write out a dummy table
304  * else write out the table
305  * first entry is #min
306  * rest are translations for cat min+i
307  */
308  if (min > max)
309  fprintf(fd, "0\n");
310  else {
311  fprintf(fd, "#%ld\n", (long)reclass->min + min);
312  while (min <= max) {
313  if (Rast_is_c_null_value(&reclass->table[min]))
314  fprintf(fd, "%s\n", NULL_STRING);
315  else
316  fprintf(fd, "%ld\n", (long)reclass->table[min]);
317  min++;
318  }
319  }
320  fclose(fd);
321 
322  strcpy(buf2, reclass->name);
323  if ((p = strchr(buf2, '@')))
324  *p = 0;
325 
326  G_file_name_misc(buf1, "cell_misc", "reclassed_to", reclass->name,
327  reclass->mapset);
328 
329  fd = fopen(buf1, "a+");
330  if (fd == NULL) {
331 #if 0
332  G_warning(_("Unable to create dependency file in <%s@%s>"),
333  buf2, reclass->mapset);
334 #endif
335  return 1;
336  }
337 
338  G_fseek(fd, 0L, SEEK_SET);
339 
341  found = 0;
342  for (;;) {
343  char buf[GNAME_MAX + GMAPSET_MAX];
344 
345  if (!G_getl2(buf, sizeof(buf), fd))
346  break;
347  if (strcmp(xname, buf) == 0) {
348  found = 1;
349  break;
350  }
351  }
352 
353  if (!found)
354  fprintf(fd, "%s\n", xname);
355 
356  G_free(xname);
357  fclose(fd);
358 
359  return 1;
360 }
361 
362 static FILE *fopen_cellhd_new(const char *name)
363 {
364  return G_fopen_new("cellhd", name);
365 }
366 
367 static int get_reclass_table(FILE *fd, struct Reclass *reclass)
368 {
369  char buf[128];
370  int n;
371  int first, null_str_size;
372  CELL cat;
373  long len;
374 
375  /*
376  * allocate the table, expanding as each entry is read
377  * note that G_realloc() will become G_malloc() if ptr in
378  * NULL
379  */
380  reclass->min = 0;
381  reclass->table = NULL;
382  null_str_size = strlen(NULL_STRING);
383  n = 0;
384  first = 1;
385  while (fgets(buf, sizeof buf, fd)) {
386  if (first) {
387  first = 0;
388  if (sscanf(buf, "#%d", &cat) == 1) {
389  reclass->min = cat;
390  continue;
391  }
392  }
393  if (strncmp(buf, NULL_STRING, null_str_size) == 0)
394  Rast_set_c_null_value(&cat, 1);
395  else {
396  if (sscanf(buf, "%d", &cat) != 1)
397  return -1;
398  }
399  n++;
400  len = (long)n * sizeof(CELL);
401 
402  if (len != (int)len) { /* check for int overflow */
403  if (reclass->table != NULL)
404  G_free(reclass->table);
405  return -2;
406  }
407  reclass->table = (CELL *)G_realloc((char *)reclass->table, (int)len);
408  reclass->table[n - 1] = cat;
409  }
410  reclass->max = reclass->min + n - 1;
411  reclass->num = n;
412  return 1;
413 }
#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:251
int G_getl2(char *, int, FILE *)
Gets a line of text from a file of any pedigree.
Definition: getl.c:65
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
#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
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:219
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:401
#define min(x, y)
Definition: draw2.c:29
#define max(x, y)
Definition: draw2.c:30
#define GMAPSET_MAX
Definition: gis.h:192
#define GPATH_MAX
Definition: gis.h:194
#define GNAME_MAX
Definition: gis.h:191
int CELL
Definition: gis.h:625
#define _(str)
Definition: glocale.h:10
const char * name
Definition: named_colr.c:6
#define strcpy
Definition: parson.c:62
double l
Definition: r_raster.c:39
#define RECLASS_TABLE
Definition: raster.h:7
int Rast_is_reclass(const char *name, const char *mapset, char *rname, char *rmapset)
Check if raster map is reclassified.
Definition: reclass.c:43
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:262
int Rast_is_reclassed_to(const char *name, const char *mapset, int *nrmaps, char ***rmaps)
Get child reclass maps list.
Definition: reclass.c:79
void Rast_free_reclass(struct Reclass *reclass)
Free Reclass structure.
Definition: reclass.c:183
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