reclass.c 9.0 KB

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