reclass.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. #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>rname</i> and
  28. * <i>rmapset</i> buffers.
  29. *
  30. * \param name map name
  31. * \param mapset mapset name
  32. * \param[out] rname name of reference map
  33. * \param[out] rmapset 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. fclose(fd);
  112. return i;
  113. }
  114. /*!
  115. \brief Get reclass
  116. \param name map name
  117. \param mapset mapset name
  118. \param[out] reclass pointer to Reclass structure
  119. \return -1 on error
  120. \return type code
  121. */
  122. int Rast_get_reclass(const char *name, const char *mapset,
  123. struct Reclass *reclass)
  124. {
  125. FILE *fd;
  126. int stat;
  127. fd = fopen_cellhd_old(name, mapset);
  128. if (fd == NULL)
  129. return -1;
  130. reclass->name = NULL;
  131. reclass->mapset = NULL;
  132. reclass->type = reclass_type(fd, &reclass->name, &reclass->mapset);
  133. if (reclass->type <= 0) {
  134. fclose(fd);
  135. return reclass->type;
  136. }
  137. switch (reclass->type) {
  138. case RECLASS_TABLE:
  139. stat = get_reclass_table(fd, reclass);
  140. break;
  141. default:
  142. stat = -1;
  143. }
  144. fclose(fd);
  145. if (stat < 0) {
  146. if (stat == -2)
  147. G_warning(_("Too many reclass categories for <%s@%s>"),
  148. name, mapset);
  149. else
  150. G_warning(_("Illegal reclass format in header file for <%s@%s>"),
  151. name, mapset);
  152. stat = -1;
  153. }
  154. return stat;
  155. }
  156. /*!
  157. \brief Free Reclass structure
  158. \param reclass pointer to Reclass structure
  159. */
  160. void Rast_free_reclass(struct Reclass *reclass)
  161. {
  162. switch (reclass->type) {
  163. case RECLASS_TABLE:
  164. if (reclass->num > 0)
  165. G_free(reclass->table);
  166. reclass->num = 0;
  167. if (reclass->name)
  168. G_free(reclass->name);
  169. if (reclass->mapset)
  170. G_free(reclass->mapset);
  171. reclass->name = NULL;
  172. reclass->mapset = NULL;
  173. break;
  174. default:
  175. break;
  176. }
  177. }
  178. static int reclass_type(FILE * fd, char **rname, char **rmapset)
  179. {
  180. char buf[128];
  181. char label[128], arg[128];
  182. int i;
  183. int type;
  184. /* Check to see if this is a reclass file */
  185. if (fgets(buf, sizeof(buf), fd) == NULL)
  186. return 0;
  187. if (strncmp(buf, "reclas", 6))
  188. return 0;
  189. /* later may add other types of reclass */
  190. type = RECLASS_TABLE;
  191. /* Read the mapset and file name of the REAL cell file */
  192. if (*rname)
  193. **rname = '\0';
  194. if (*rmapset)
  195. **rmapset = '\0';
  196. for (i = 0; i < 2; i++) {
  197. if (fgets(buf, sizeof buf, fd) == NULL)
  198. return -1;
  199. if (sscanf(buf, "%[^:]:%s", label, arg) != 2)
  200. return -1;
  201. if (strncmp(label, "maps", 4) == 0) {
  202. if (*rmapset)
  203. strcpy(*rmapset, arg);
  204. else
  205. *rmapset = G_store(arg);
  206. }
  207. else if (strncmp(label, "name", 4) == 0) {
  208. if (*rname)
  209. strcpy(*rname, arg);
  210. else
  211. *rname = G_store(arg);
  212. }
  213. else
  214. return -1;
  215. }
  216. if (**rmapset && **rname)
  217. return type;
  218. else
  219. return -1;
  220. }
  221. static FILE *fopen_cellhd_old(const char *name, const char *mapset)
  222. {
  223. return G_fopen_old("cellhd", name, mapset);
  224. }
  225. /*!
  226. \brief Put reclass
  227. \param name map name
  228. \param reclass pointer to Reclass structure
  229. \return -1 on error
  230. \return 1 on success
  231. */
  232. int Rast_put_reclass(const char *name, const struct Reclass *reclass)
  233. {
  234. FILE *fd;
  235. long min, max;
  236. int found;
  237. char buf1[GPATH_MAX], buf2[GNAME_MAX], *p;
  238. char *xname;
  239. switch (reclass->type) {
  240. case RECLASS_TABLE:
  241. if (reclass->min > reclass->max || reclass->num <= 0) {
  242. G_fatal_error(_("Illegal reclass request"));
  243. return -1;
  244. }
  245. break;
  246. default:
  247. G_fatal_error(_("Illegal reclass type"));
  248. return -1;
  249. }
  250. fd = fopen_cellhd_new(name);
  251. if (fd == NULL) {
  252. G_warning(_("Unable to create header file for <%s@%s>"),
  253. name, G_mapset());
  254. return -1;
  255. }
  256. fprintf(fd, "reclass\n");
  257. fprintf(fd, "name: %s\n", reclass->name);
  258. fprintf(fd, "mapset: %s\n", reclass->mapset);
  259. /* find first non-null entry */
  260. for (min = 0; min < reclass->num; min++)
  261. if (!Rast_is_c_null_value(&reclass->table[min]))
  262. break;
  263. /* find last non-zero entry */
  264. for (max = reclass->num - 1; max >= 0; max--)
  265. if (!Rast_is_c_null_value(&reclass->table[max]))
  266. break;
  267. /*
  268. * if the resultant table is empty, write out a dummy table
  269. * else write out the table
  270. * first entry is #min
  271. * rest are translations for cat min+i
  272. */
  273. if (min > max)
  274. fprintf(fd, "0\n");
  275. else {
  276. fprintf(fd, "#%ld\n", (long)reclass->min + min);
  277. while (min <= max) {
  278. if (Rast_is_c_null_value(&reclass->table[min]))
  279. fprintf(fd, "%s\n", NULL_STRING);
  280. else
  281. fprintf(fd, "%ld\n", (long)reclass->table[min]);
  282. min++;
  283. }
  284. }
  285. fclose(fd);
  286. strcpy(buf2, reclass->name);
  287. if ((p = strchr(buf2, '@')))
  288. *p = 0;
  289. G_file_name_misc(buf1, "cell_misc", "reclassed_to", reclass->name,
  290. reclass->mapset);
  291. fd = fopen(buf1, "a+");
  292. if (fd == NULL) {
  293. #if 0
  294. G_warning(_("Unable to create dependency file in <%s@%s>"),
  295. buf2, reclass->mapset);
  296. #endif
  297. return 1;
  298. }
  299. G_fseek(fd, 0L, SEEK_SET);
  300. xname = G_fully_qualified_name(name, G_mapset());
  301. found = 0;
  302. for (;;) {
  303. char buf[GNAME_MAX + GMAPSET_MAX];
  304. if (!G_getl2(buf, sizeof(buf), fd))
  305. break;
  306. if (strcmp(xname, buf) == 0) {
  307. found = 1;
  308. break;
  309. }
  310. }
  311. if (!found)
  312. fprintf(fd, "%s\n", xname);
  313. G_free(xname);
  314. fclose(fd);
  315. return 1;
  316. }
  317. static FILE *fopen_cellhd_new(const char *name)
  318. {
  319. return G_fopen_new("cellhd", name);
  320. }
  321. static int get_reclass_table(FILE * fd, struct Reclass *reclass)
  322. {
  323. char buf[128];
  324. int n;
  325. int first, null_str_size;
  326. CELL cat;
  327. long len;
  328. /*
  329. * allocate the table, expanding as each entry is read
  330. * note that G_realloc() will become G_malloc() if ptr in
  331. * NULL
  332. */
  333. reclass->min = 0;
  334. reclass->table = NULL;
  335. null_str_size = strlen(NULL_STRING);
  336. n = 0;
  337. first = 1;
  338. while (fgets(buf, sizeof buf, fd)) {
  339. if (first) {
  340. first = 0;
  341. if (sscanf(buf, "#%d", &cat) == 1) {
  342. reclass->min = cat;
  343. continue;
  344. }
  345. }
  346. if (strncmp(buf, NULL_STRING, null_str_size) == 0)
  347. Rast_set_c_null_value(&cat, 1);
  348. else {
  349. if (sscanf(buf, "%d", &cat) != 1)
  350. return -1;
  351. }
  352. n++;
  353. len = (long)n *sizeof(CELL);
  354. if (len != (int)len) { /* check for int overflow */
  355. if (reclass->table != NULL)
  356. G_free(reclass->table);
  357. return -2;
  358. }
  359. reclass->table = (CELL *) G_realloc((char *)reclass->table, (int)len);
  360. reclass->table[n - 1] = cat;
  361. }
  362. reclass->max = reclass->min + n - 1;
  363. reclass->num = n;
  364. return 1;
  365. }