GRASS 8 Programmer's Manual 8.6.0dev(2026)-fc1694ea64
Loading...
Searching...
No Matches
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
21static const char NULL_STRING[] = "null";
22static int reclass_type(FILE *, char **, char **, char **);
23static FILE *fopen_cellhd_old(const char *, const char *);
24static FILE *fopen_cellhd_new(const char *);
25static 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 */
44int 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 */
80int 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 */
140int 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)
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)
191 return stat;
192}
193
194/*!
195 \brief Free Reclass structure
196
197 \param reclass pointer to Reclass structure
198 */
199void 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 */
228static 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) {
273 }
274 else if (strncmp(label, "name", 4) == 0 && *rname) {
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) {
290 _("Mapset not read, only raster name: %s"), *rname);
291 }
292 else if (**rmapset && error_message != NULL) {
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
303static 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 */
317int 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
417static 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 */
436static 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)
474 _("Reading integer failed on line: %s "
475 "(after reading min: %d)"),
476 buf, reclass->min);
477 else
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
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:141
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
FILE * G_fopen_new(const char *, const char *)
Open a new database file.
Definition gis/open.c:221
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
#define G_malloc(n)
Definition defs/gis.h:139
void G_fseek(FILE *, off_t, int)
Change the file position of the stream.
Definition gis/seek.c:50
FILE * G_fopen_old(const char *, const char *, const char *)
Open a database file for reading.
Definition gis/open.c:253
char * G_fully_qualified_name(const char *, const char *)
Get fully qualified element name.
Definition nme_in_mps.c:101
int G_asprintf(char **, const char *,...) __attribute__((format(printf
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
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:87
const char * G_mapset(void)
Get current mapset name.
Definition gis/mapset.c:33
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)
#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
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