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