GRASS GIS 8 Programmer's Manual  8.2.2dev(2023)-3d2c704037
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>"),
169  name, 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>"),
285  name, 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 
340  xname = G_fully_qualified_name(name, G_mapset());
341  found = 0;
342  for (;;) {
343  char buf[GNAME_MAX + GMAPSET_MAX];
344  if (!G_getl2(buf, sizeof(buf), fd))
345  break;
346  if (strcmp(xname, buf) == 0) {
347  found = 1;
348  break;
349  }
350  }
351 
352  if (!found)
353  fprintf(fd, "%s\n", xname);
354 
355  G_free(xname);
356  fclose(fd);
357 
358  return 1;
359 }
360 
361 static FILE *fopen_cellhd_new(const char *name)
362 {
363  return G_fopen_new("cellhd", name);
364 }
365 
366 static int get_reclass_table(FILE * fd, struct Reclass *reclass)
367 {
368  char buf[128];
369  int n;
370  int first, null_str_size;
371  CELL cat;
372  long len;
373 
374  /*
375  * allocate the table, expanding as each entry is read
376  * note that G_realloc() will become G_malloc() if ptr in
377  * NULL
378  */
379  reclass->min = 0;
380  reclass->table = NULL;
381  null_str_size = strlen(NULL_STRING);
382  n = 0;
383  first = 1;
384  while (fgets(buf, sizeof buf, fd)) {
385  if (first) {
386  first = 0;
387  if (sscanf(buf, "#%d", &cat) == 1) {
388  reclass->min = cat;
389  continue;
390  }
391  }
392  if (strncmp(buf, NULL_STRING, null_str_size) == 0)
393  Rast_set_c_null_value(&cat, 1);
394  else {
395  if (sscanf(buf, "%d", &cat) != 1)
396  return -1;
397  }
398  n++;
399  len = (long)n *sizeof(CELL);
400 
401  if (len != (int)len) { /* check for int overflow */
402  if (reclass->table != NULL)
403  G_free(reclass->table);
404  return -2;
405  }
406  reclass->table = (CELL *) G_realloc((char *)reclass->table, (int)len);
407  reclass->table[n - 1] = cat;
408  }
409  reclass->max = reclass->min + n - 1;
410  reclass->num = n;
411  return 1;
412 }
int G_getl2(char *, int, FILE *)
Gets a line of text from a file of any pedigree.
Definition: getl.c:64
#define G_malloc(n)
Definition: defs/gis.h:112
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
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
int Rast_get_reclass(const char *name, const char *mapset, struct Reclass *reclass)
Get reclass.
Definition: reclass.c:140
char * name
Definition: raster.h:33
#define GMAPSET_MAX
Definition: gis.h:178
void Rast_set_c_null_value(CELL *, int)
To set a number of CELL raster values to NULL.
Definition: null_val.c:124
CELL min
Definition: raster.h:37
#define min(x, y)
Definition: draw2.c:31
#define RECLASS_TABLE
Definition: raster.h:7
CELL max
Definition: raster.h:38
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:149
void Rast_free_reclass(struct Reclass *reclass)
Free Reclass structure.
Definition: reclass.c:183
int Rast_put_reclass(const char *name, const struct Reclass *reclass)
Put reclass.
Definition: reclass.c:262
#define NULL
Definition: ccmath.h:32
#define max(x, y)
Definition: draw2.c:32
Definition: raster.h:31
double l
Definition: r_raster.c:39
FILE * G_fopen_new(const char *, const char *)
Open a new database file.
Definition: gis/open.c:220
char * G_fully_qualified_name(const char *, const char *)
Get fully qualified element name.
Definition: nme_in_mps.c:101
int Rast_is_reclass(const char *name, const char *mapset, char *rname, char *rmapset)
Check if raster map is reclassified.
Definition: reclass.c:43
FILE * G_fopen_old_misc(const char *, const char *, const char *, const char *)
open a database misc file for reading
Definition: open_misc.c:210
void G_fseek(FILE *, off_t, int)
Change the file position of the stream.
Definition: gis/seek.c:50
#define GPATH_MAX
Definition: gis.h:180
FILE * G_fopen_old(const char *, const char *, const char *)
Open a database file for reading.
Definition: gis/open.c:253
int Rast_is_reclassed_to(const char *name, const char *mapset, int *nrmaps, char ***rmaps)
Get child reclass maps list.
Definition: reclass.c:79
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
#define GNAME_MAX
Definition: gis.h:177
void G_warning(const char *,...) __attribute__((format(printf
int CELL
Definition: gis.h:613
#define G_realloc(p, n)
Definition: defs/gis.h:114
#define _(str)
Definition: glocale.h:10
int num
Definition: raster.h:36
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
const char * name
Definition: named_colr.c:7
char * mapset
Definition: raster.h:34
int type
Definition: raster.h:35
#define Rast_is_c_null_value(cellVal)
Definition: defs/raster.h:410
CELL * table
Definition: raster.h:39