GRASS 8 Programmer's Manual 8.6.0dev(2026)-55de52a352
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, j, k, l;
83 char buf2[256], buf3[256];
84
85 fd = G_fopen_old_misc("cell_misc", "reclassed_to", name, mapset);
86
87 if (fd == NULL) {
88 return -1;
89 }
90
91 if (rmaps)
92 *rmaps = NULL;
93 for (i = 0; !feof(fd) && fgets(buf2, 255, fd);) {
94 l = strlen(buf2);
95 for (j = 0, k = 0; j < l; j++) {
96 if (buf2[j] == '#' ||
97 ((buf2[j] == ' ' || buf2[j] == '\t' || buf2[j] == '\n') && k))
98 break;
99 else if (buf2[j] != ' ' && buf2[j] != '\t')
100 buf3[k++] = buf2[j];
101 }
102
103 if (k) {
104 buf3[k] = 0;
105 i++;
106 if (rmaps) {
107 *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
108 (*rmaps)[i - 1] = (char *)G_malloc(k + 1);
109 strncpy((*rmaps)[i - 1], buf3, k);
110 (*rmaps)[i - 1][k] = 0;
111 }
112 }
113 }
114
115 if (nrmaps)
116 *nrmaps = i;
117
118 if (i && rmaps) {
119 i++;
120 *rmaps = (char **)G_realloc(*rmaps, i * sizeof(char *));
121 (*rmaps)[i - 1] = NULL;
122 }
123
124 fclose(fd);
125
126 return i;
127}
128
129/*!
130 \brief Get reclass
131
132 \param name map name
133 \param mapset mapset name
134 \param[out] reclass pointer to Reclass structure
135
136 \return type code (>=1), 0 if no reclass, -1 on error
137 */
138int Rast_get_reclass(const char *name, const char *mapset,
139 struct Reclass *reclass)
140{
141 FILE *fd;
142 int stat;
143 char rname[GNAME_MAX] = {0}, rmapset[GMAPSET_MAX] = {0};
144 char *tmp_name = rname, *tmp_mapset = rmapset;
145
146 fd = fopen_cellhd_old(name, mapset);
147 if (fd == NULL)
148 return -1;
149 char *error_message = NULL;
150 reclass->type = reclass_type(fd, &tmp_name, &tmp_mapset, &error_message);
151 reclass->name = G_store(tmp_name);
152 reclass->mapset = G_store(tmp_mapset);
153 if (reclass->type == 0) {
154 // no reclass
155 fclose(fd);
156 return reclass->type;
157 }
158 if (reclass->type < 0) {
159 // error
160 fclose(fd);
161 G_warning(_("Error reading beginning of header file for <%s@%s>: %s"),
162 name, mapset, error_message);
163 if (error_message != NULL)
165 return reclass->type;
166 }
167
168 switch (reclass->type) {
169 case RECLASS_TABLE:
170 stat = get_reclass_table(fd, reclass, &error_message);
171 break;
172 default:
173 stat = -1;
174 }
175
176 fclose(fd);
177 if (stat < 0) {
178 if (stat == -2)
179 G_warning(_("Too many reclass categories for <%s@%s>"), name,
180 mapset);
181 else
182 G_warning(
183 _("Illegal reclass format in header file for <%s@%s>: %s"),
184 name, mapset, error_message);
185 stat = -1;
186 }
187 if (error_message != NULL)
189 return stat;
190}
191
192/*!
193 \brief Free Reclass structure
194
195 \param reclass pointer to Reclass structure
196 */
197void Rast_free_reclass(struct Reclass *reclass)
198{
199 switch (reclass->type) {
200 case RECLASS_TABLE:
201 if (reclass->num > 0)
202 G_free(reclass->table);
203 reclass->num = 0;
204 if (reclass->name)
205 G_free(reclass->name);
206 if (reclass->mapset)
207 G_free(reclass->mapset);
208 reclass->name = NULL;
209 reclass->mapset = NULL;
210 break;
211 default:
212 break;
213 }
214}
215
216/**
217 * \brief Get reclass type if it is a reclass file
218 *
219 * \param fd[in] file descriptor
220 * \param rname[out] name of the reclass from raster
221 * \param rmapset[out] name of the mapset of the raster
222 * \param error_message[out] will be assigned a newly error message if not NULL
223 *
224 * \returns RECLASS_TABLE if reclass, 0 if not, -1 on error
225 */
226static int reclass_type(FILE *fd, char **rname, char **rmapset,
227 char **error_message)
228{
229 char
230 buf[GNAME_MAX + 128 + 1]; // name or mapset plus the label and separator
231 char label[128], arg[GNAME_MAX];
232 int i;
233 int type;
234
235 /* Check to see if this is a reclass file */
236 if (fgets(buf, sizeof(buf), fd) == NULL)
237 return 0;
238 if (strncmp(buf, "reclas", 6))
239 return 0;
240 /* later may add other types of reclass */
241 type = RECLASS_TABLE;
242
243 /* Read the mapset and file name of the REAL cell file */
244 if (*rname)
245 **rname = '\0';
246 if (*rmapset)
247 **rmapset = '\0';
248 for (i = 0; i < 2; i++) {
249 if (fgets(buf, sizeof buf, fd) == NULL) {
250 if (error_message != NULL) {
251 G_asprintf(error_message, _("File too short, reading line %d"),
252 i + 1);
253 }
254 return -1;
255 }
256 if (buf[strlen(buf) - 1] != '\n') {
257 if (error_message != NULL) {
258 G_asprintf(error_message, _("Line too long: %s..."), buf);
259 }
260 return -1;
261 }
262 if (sscanf(buf, "%[^:]:%s", label, arg) != 2) {
263 if (error_message != NULL) {
264 G_asprintf(error_message, _("Format is not key:value: %s"),
265 buf);
266 }
267 return -1;
268 }
269 if (strncmp(label, "maps", 4) == 0 && *rmapset) {
271 }
272 else if (strncmp(label, "name", 4) == 0 && *rname) {
274 }
275 else {
276 if (error_message != NULL) {
277 G_asprintf(error_message, _("Unknown key at line: %s"), buf);
278 }
279 return -1;
280 }
281 }
282 if ((*rmapset && **rmapset) || (*rname && **rname))
283 return type;
284 else {
285 // If they do not occur in the two lines we expect them.
286 if (**rname && error_message != NULL) {
288 _("Mapset not read, only raster name: %s"), *rname);
289 }
290 else if (**rmapset && error_message != NULL) {
292 _("Raster name not read, only mapset: %s"), *rmapset);
293 }
294 else if (error_message != NULL) {
295 *error_message = G_store(_("Raster name and mapset not read"));
296 }
297 return -1;
298 }
299}
300
301static FILE *fopen_cellhd_old(const char *name, const char *mapset)
302{
303 return G_fopen_old("cellhd", name, mapset);
304}
305
306/*!
307 \brief Put reclass
308
309 \param name map name
310 \param reclass pointer to Reclass structure
311
312 \return -1 on error
313 \return 1 on success
314 */
315int Rast_put_reclass(const char *name, const struct Reclass *reclass)
316{
317 FILE *fd;
318 long min, max;
319 int found;
320 char buf1[GPATH_MAX], buf2[GNAME_MAX], *p;
321 char *xname;
322
323 switch (reclass->type) {
324 case RECLASS_TABLE:
325 if (reclass->min > reclass->max || reclass->num <= 0) {
326 G_fatal_error(_("Illegal reclass request"));
327 return -1;
328 }
329 break;
330 default:
331 G_fatal_error(_("Illegal reclass type"));
332 return -1;
333 }
334
335 fd = fopen_cellhd_new(name);
336 if (fd == NULL) {
337 G_warning(_("Unable to create header file for <%s@%s>"), name,
338 G_mapset());
339 return -1;
340 }
341
342 fprintf(fd, "reclass\n");
343 fprintf(fd, "name: %s\n", reclass->name);
344 fprintf(fd, "mapset: %s\n", reclass->mapset);
345
346 /* find first non-null entry */
347 for (min = 0; min < reclass->num; min++)
348 if (!Rast_is_c_null_value(&reclass->table[min]))
349 break;
350 /* find last non-zero entry */
351 for (max = reclass->num - 1; max >= 0; max--)
352 if (!Rast_is_c_null_value(&reclass->table[max]))
353 break;
354
355 /*
356 * if the resultant table is empty, write out a dummy table
357 * else write out the table
358 * first entry is #min
359 * rest are translations for cat min+i
360 */
361 if (min > max)
362 fprintf(fd, "0\n");
363 else {
364 fprintf(fd, "#%ld\n", (long)reclass->min + min);
365 while (min <= max) {
366 if (Rast_is_c_null_value(&reclass->table[min]))
367 fprintf(fd, "%s\n", NULL_STRING);
368 else
369 fprintf(fd, "%ld\n", (long)reclass->table[min]);
370 min++;
371 }
372 }
373 fclose(fd);
374
375 strcpy(buf2, reclass->name);
376 if ((p = strchr(buf2, '@')))
377 *p = 0;
378
379 G_file_name_misc(buf1, "cell_misc", "reclassed_to", reclass->name,
380 reclass->mapset);
381
382 fd = fopen(buf1, "a+");
383 if (fd == NULL) {
384#if 0
385 G_warning(_("Unable to create dependency file in <%s@%s>"),
386 buf2, reclass->mapset);
387#endif
388 return 1;
389 }
390
391 G_fseek(fd, 0L, SEEK_SET);
392
394 found = 0;
395 for (;;) {
396 char buf[GNAME_MAX + GMAPSET_MAX];
397
398 if (!G_getl2(buf, sizeof(buf), fd))
399 break;
400 if (strcmp(xname, buf) == 0) {
401 found = 1;
402 break;
403 }
404 }
405
406 if (!found)
407 fprintf(fd, "%s\n", xname);
408
409 G_free(xname);
410 fclose(fd);
411
412 return 1;
413}
414
415static FILE *fopen_cellhd_new(const char *name)
416{
417 return G_fopen_new("cellhd", name);
418}
419
420/**
421 * \brief Get reclass table from header file
422 *
423 * If there is reading error due to the format, -1 is returned and,
424 * if error_message is not NULL, it will be set to a pointer to a newly
425 * allocated string containing an error message with the line where error
426 * was encountered.
427 *
428 * \param fd header file
429 * \param[out] reclass pointer to Reclass structure
430 * \param[out] error_message pointer to error message
431
432 * \return 1 on success, -1 on format error, -2 on too many categories
433 */
434static int get_reclass_table(FILE *fd, struct Reclass *reclass,
435 char **error_message)
436{
437 char buf[128];
438 int n;
439 int first, null_str_size;
440 CELL cat;
441 long len;
442
443 /*
444 * allocate the table, expanding as each entry is read
445 * note that G_realloc() will become G_malloc() if ptr in
446 * NULL
447 */
448 reclass->min = 0;
449 reclass->table = NULL;
450 null_str_size = strlen(NULL_STRING);
451 n = 0;
452 first = 1;
453 bool min_set = false;
454 while (fgets(buf, sizeof buf, fd)) {
455 if (first) {
456 first = 0;
457 if (sscanf(buf, "#%d", &cat) == 1) {
458 reclass->min = cat;
459 min_set = true;
460 continue;
461 }
462 }
463 if (strncmp(buf, NULL_STRING, null_str_size) == 0)
464 Rast_set_c_null_value(&cat, 1);
465 else {
466 if (sscanf(buf, "%d", &cat) != 1) {
467 if (reclass->table != NULL)
468 G_free(reclass->table);
469 if (error_message != NULL) {
470 if (min_set)
472 _("Reading integer failed on line: %s "
473 "(after reading min: %d)"),
474 buf, reclass->min);
475 else
477 _("First entry (min) not read yet and "
478 "reading integer failed on line: %s"),
479 buf);
480 }
481 return -1;
482 }
483 }
484 n++;
485 len = (long)n * sizeof(CELL);
486
487 if (len != (int)len) { /* check for int overflow */
488 if (reclass->table != NULL)
489 G_free(reclass->table);
490 return -2;
491 }
492 reclass->table = (CELL *)G_realloc((char *)reclass->table, (int)len);
493 reclass->table[n - 1] = cat;
494 }
495 reclass->max = reclass->min + n - 1;
496 reclass->num = n;
497 return 1;
498}
#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:210
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:85
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:194
#define GPATH_MAX
Definition gis.h:196
#define GNAME_MAX
Definition gis.h:193
int CELL
Definition gis.h:631
#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:138
int Rast_put_reclass(const char *name, const struct Reclass *reclass)
Put reclass.
Definition reclass.c:315
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:197
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