main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /****************************************************************************
  2. *
  3. * MODULE: r.neighbors
  4. * AUTHOR(S): Michael Shapiro, CERL (original contributor)
  5. * Markus Neteler <neteler itc.it>, Bob Covill <bcovill tekmap.ns.ca>,
  6. * Brad Douglas <rez touchofmadness.com>, Glynn Clements <glynn gclements.plus.com>,
  7. * Jachym Cepicky <jachym les-ejk.cz>, Jan-Oliver Wagner <jan intevation.de>,
  8. * Radim Blazek <radim.blazek gmail.com>
  9. *
  10. * PURPOSE: Makes each cell category value a function of the category values
  11. * assigned to the cells around it, and stores new cell values in an
  12. * output raster map layer
  13. * COPYRIGHT: (C) 1999-2006 by the GRASS Development Team
  14. *
  15. * This program is free software under the GNU General Public
  16. * License (>=v2). Read the file COPYING that comes with GRASS
  17. * for details.
  18. *
  19. *****************************************************************************/
  20. #include <string.h>
  21. #include <stdlib.h>
  22. #include <unistd.h>
  23. #include <grass/gis.h>
  24. #include <grass/raster.h>
  25. #include <grass/glocale.h>
  26. #include <grass/stats.h>
  27. #include "ncb.h"
  28. #include "local_proto.h"
  29. typedef int (*ifunc) (void);
  30. struct menu
  31. {
  32. stat_func *method; /* routine to compute new value */
  33. stat_func_w *method_w; /* routine to compute new value (weighted) */
  34. ifunc cat_names; /* routine to make category names */
  35. int copycolr; /* flag if color table can be copied */
  36. int half; /* whether to add 0.5 to result (redundant) */
  37. int otype; /* output type */
  38. char *name; /* method name */
  39. char *text; /* menu display - full description */
  40. };
  41. struct weight_functions
  42. {
  43. char *name; /* name of the weight type */
  44. char *text; /* weight types display - full description */
  45. };
  46. enum out_type {
  47. T_FLOAT = 1,
  48. T_INT = 2,
  49. T_COUNT = 3,
  50. T_COPY = 4,
  51. T_SUM = 5
  52. };
  53. #define NO_CATS 0
  54. /* modify this table to add new methods */
  55. static struct menu menu[] = {
  56. {c_ave, w_ave, NO_CATS, 1, 1, T_FLOAT, "average", "average value"},
  57. {c_median, w_median, NO_CATS, 1, 0, T_FLOAT, "median", "median value"},
  58. {c_mode, w_mode, NO_CATS, 1, 0, T_COPY, "mode", "most frequently occurring value"},
  59. {c_min, NULL, NO_CATS, 1, 0, T_COPY, "minimum", "lowest value"},
  60. {c_max, NULL, NO_CATS, 1, 0, T_COPY, "maximum", "highest value"},
  61. {c_range, NULL, NO_CATS, 1, 0, T_COPY, "range", "range value"},
  62. {c_stddev, w_stddev, NO_CATS, 0, 1, T_FLOAT, "stddev", "standard deviation"},
  63. {c_sum, w_sum, NO_CATS, 1, 0, T_SUM, "sum", "sum of values"},
  64. {c_count, w_count, NO_CATS, 0, 0, T_COUNT, "count", "count of non-NULL values"},
  65. {c_var, w_var, NO_CATS, 0, 1, T_FLOAT, "variance", "statistical variance"},
  66. {c_divr, NULL, divr_cats, 0, 0, T_INT, "diversity",
  67. "number of different values"},
  68. {c_intr, NULL, intr_cats, 0, 0, T_INT, "interspersion",
  69. "number of values different than center value"},
  70. {c_quart1, w_quart1, NO_CATS, 1, 0, T_FLOAT, "quart1", "first quartile"},
  71. {c_quart3, w_quart3, NO_CATS, 1, 0, T_FLOAT, "quart3", "third quartile"},
  72. {c_perc90, w_perc90, NO_CATS, 1, 0, T_FLOAT, "perc90", "ninetieth percentile"},
  73. {c_quant, w_quant, NO_CATS, 1, 0, T_FLOAT, "quantile", "arbitrary quantile"},
  74. {0, 0, 0, 0, 0, 0, 0, 0}
  75. };
  76. struct ncb ncb;
  77. struct output
  78. {
  79. const char *name;
  80. char title[1024];
  81. int fd;
  82. DCELL *buf;
  83. stat_func *method_fn;
  84. stat_func_w *method_fn_w;
  85. int copycolr;
  86. ifunc cat_names;
  87. int map_type;
  88. double quantile;
  89. };
  90. static int find_method(const char *method_name)
  91. {
  92. int i;
  93. for (i = 0; menu[i].name; i++)
  94. if (strcmp(menu[i].name, method_name) == 0)
  95. return i;
  96. G_fatal_error(_("Unknown method <%s>"), method_name);
  97. return -1;
  98. }
  99. static RASTER_MAP_TYPE output_type(RASTER_MAP_TYPE input_type, int weighted, int mode)
  100. {
  101. switch (mode) {
  102. case T_FLOAT:
  103. return DCELL_TYPE;
  104. case T_INT:
  105. return CELL_TYPE;
  106. case T_COUNT:
  107. return weighted ? DCELL_TYPE : CELL_TYPE;
  108. case T_COPY:
  109. return input_type;
  110. case T_SUM:
  111. return weighted ? DCELL_TYPE : input_type;
  112. default:
  113. G_fatal_error(_("Invalid out_type enumeration: %d"), mode);
  114. return -1;
  115. }
  116. }
  117. int main(int argc, char *argv[])
  118. {
  119. char *p;
  120. int in_fd;
  121. int selection_fd;
  122. int num_outputs;
  123. struct output *outputs = NULL;
  124. int copycolr, weights, have_weights_mask;
  125. char *selection;
  126. RASTER_MAP_TYPE map_type;
  127. int row, col;
  128. int readrow;
  129. int nrows, ncols;
  130. int i, n;
  131. struct Colors colr;
  132. struct Cell_head cellhd;
  133. struct Cell_head window;
  134. struct History history;
  135. struct GModule *module;
  136. struct
  137. {
  138. struct Option *input, *output, *selection;
  139. struct Option *method, *size;
  140. struct Option *title;
  141. struct Option *weight;
  142. struct Option *weighting_function;
  143. struct Option *weighting_factor;
  144. struct Option *quantile;
  145. } parm;
  146. struct
  147. {
  148. struct Flag *align, *circle;
  149. } flag;
  150. DCELL *values; /* list of neighborhood values */
  151. DCELL *values_tmp; /* list of neighborhood values */
  152. DCELL(*values_w)[2]; /* list of neighborhood values and weights */
  153. DCELL(*values_w_tmp)[2]; /* list of neighborhood values and weights */
  154. G_gisinit(argv[0]);
  155. module = G_define_module();
  156. G_add_keyword(_("raster"));
  157. G_add_keyword(_("algebra"));
  158. G_add_keyword(_("statistics"));
  159. G_add_keyword(_("aggregation"));
  160. G_add_keyword(_("neighbor"));
  161. G_add_keyword(_("focal statistics"));
  162. G_add_keyword(_("filter"));
  163. module->description =
  164. _("Makes each cell category value a "
  165. "function of the category values assigned to the cells "
  166. "around it, and stores new cell values in an output raster "
  167. "map layer.");
  168. parm.input = G_define_standard_option(G_OPT_R_INPUT);
  169. parm.selection = G_define_standard_option(G_OPT_R_INPUT);
  170. parm.selection->key = "selection";
  171. parm.selection->required = NO;
  172. parm.selection->description = _("Name of an input raster map to select the cells which should be processed");
  173. parm.output = G_define_standard_option(G_OPT_R_OUTPUT);
  174. parm.output->multiple = YES;
  175. parm.size = G_define_option();
  176. parm.size->key = "size";
  177. parm.size->type = TYPE_INTEGER;
  178. parm.size->required = NO;
  179. parm.size->description = _("Neighborhood size");
  180. parm.size->answer = "3";
  181. parm.size->guisection = _("Neighborhood");
  182. parm.method = G_define_option();
  183. parm.method->key = "method";
  184. parm.method->type = TYPE_STRING;
  185. parm.method->required = NO;
  186. parm.method->answer = "average";
  187. p = G_malloc(1024);
  188. for (n = 0; menu[n].name; n++) {
  189. if (n)
  190. strcat(p, ",");
  191. else
  192. *p = 0;
  193. strcat(p, menu[n].name);
  194. }
  195. parm.method->options = p;
  196. parm.method->description = _("Neighborhood operation");
  197. parm.method->multiple = YES;
  198. parm.method->guisection = _("Neighborhood");
  199. parm.weighting_function = G_define_option();
  200. parm.weighting_function->key = "weighting_function";
  201. parm.weighting_function->type = TYPE_STRING;
  202. parm.weighting_function->required = NO;
  203. parm.weighting_function->answer = "none";
  204. parm.weighting_function->options = "none,gaussian,exponential,file";
  205. G_asprintf((char **)&(parm.weighting_function->descriptions),
  206. "none;%s;"
  207. "gaussian;%s;"
  208. "exponential;%s;"
  209. "file;%s;",
  210. _("No weighting"),
  211. _("Gaussian weighting function"),
  212. _("Exponential weighting function"),
  213. _("File with a custom weighting matrix"));
  214. parm.weighting_function->description = _("Weighting function");
  215. parm.weighting_function->multiple = NO;
  216. parm.weighting_factor = G_define_option();
  217. parm.weighting_factor->key = "weighting_factor";
  218. parm.weighting_factor->type = TYPE_DOUBLE;
  219. parm.weighting_factor->required = NO;
  220. parm.weighting_factor->multiple = NO;
  221. parm.weighting_factor->description = _("Factor used in the selected weighting function (ignored for none and file)");
  222. parm.weight = G_define_standard_option(G_OPT_F_INPUT);
  223. parm.weight->key = "weight";
  224. parm.weight->required = NO;
  225. parm.weight->description = _("Text file containing weights");
  226. parm.quantile = G_define_option();
  227. parm.quantile->key = "quantile";
  228. parm.quantile->type = TYPE_DOUBLE;
  229. parm.quantile->required = NO;
  230. parm.quantile->multiple = YES;
  231. parm.quantile->description = _("Quantile to calculate for method=quantile");
  232. parm.quantile->options = "0.0-1.0";
  233. parm.quantile->guisection = _("Neighborhood");
  234. parm.title = G_define_option();
  235. parm.title->key = "title";
  236. parm.title->key_desc = "phrase";
  237. parm.title->type = TYPE_STRING;
  238. parm.title->required = NO;
  239. parm.title->description = _("Title for output raster map");
  240. flag.align = G_define_flag();
  241. flag.align->key = 'a';
  242. flag.align->description = _("Do not align output with the input");
  243. flag.circle = G_define_flag();
  244. flag.circle->key = 'c';
  245. flag.circle->description = _("Use circular neighborhood");
  246. flag.circle->guisection = _("Neighborhood");
  247. if (G_parser(argc, argv))
  248. exit(EXIT_FAILURE);
  249. sscanf(parm.size->answer, "%d", &ncb.nsize);
  250. if (ncb.nsize <= 0)
  251. G_fatal_error(_("Neighborhood size must be positive"));
  252. if (ncb.nsize % 2 == 0)
  253. G_fatal_error(_("Neighborhood size must be odd"));
  254. ncb.dist = ncb.nsize / 2;
  255. if (strcmp(parm.weighting_function->answer, "none") && flag.circle->answer)
  256. G_fatal_error(_("-%c and %s= are mutually exclusive"),
  257. flag.circle->key, parm.weighting_function->answer);
  258. if (strcmp(parm.weighting_function->answer, "file") == 0 && !parm.weight->answer)
  259. G_fatal_error(_("File with weighting matrix is missing."));
  260. /* Check if weighting factor is given for all other weighting functions*/
  261. if (strcmp(parm.weighting_function->answer, "none") &&
  262. strcmp(parm.weighting_function->answer, "file") &&
  263. !parm.weighting_factor->answer)
  264. G_fatal_error(_("Weighting function '%s' requires a %s."),
  265. parm.weighting_function->answer, parm.weighting_factor->key);
  266. ncb.oldcell = parm.input->answer;
  267. if (!flag.align->answer) {
  268. Rast_get_cellhd(ncb.oldcell, "", &cellhd);
  269. G_get_window(&window);
  270. Rast_align_window(&window, &cellhd);
  271. Rast_set_window(&window);
  272. }
  273. nrows = Rast_window_rows();
  274. ncols = Rast_window_cols();
  275. /* open raster maps */
  276. in_fd = Rast_open_old(ncb.oldcell, "");
  277. map_type = Rast_get_map_type(in_fd);
  278. /* process the output maps */
  279. for (i = 0; parm.output->answers[i]; i++)
  280. ;
  281. num_outputs = i;
  282. for (i = 0; parm.method->answers[i]; i++)
  283. ;
  284. if (num_outputs != i)
  285. G_fatal_error(_("%s= and %s= must have the same number of values"),
  286. parm.output->key, parm.method->key);
  287. outputs = G_calloc(num_outputs, sizeof(struct output));
  288. /* read the weights */
  289. weights = 0;
  290. ncb.weights = NULL;
  291. ncb.mask = NULL;
  292. if (strcmp(parm.weighting_function->answer, "file") == 0) {
  293. read_weights(parm.weight->answer);
  294. weights = 1;
  295. }
  296. else if (strcmp(parm.weighting_function->answer, "none")) {
  297. G_verbose_message(_("Computing %s weights..."),
  298. parm.weighting_function->answer);
  299. compute_weights(parm.weighting_function->answer,
  300. atof(parm.weighting_factor->answer));
  301. weights = 1;
  302. }
  303. copycolr = 0;
  304. have_weights_mask = 0;
  305. for (i = 0; i < num_outputs; i++) {
  306. struct output *out = &outputs[i];
  307. const char *output_name = parm.output->answers[i];
  308. const char *method_name = parm.method->answers[i];
  309. int method = find_method(method_name);
  310. RASTER_MAP_TYPE otype = output_type(map_type, weights, menu[method].otype);
  311. out->name = output_name;
  312. if (weights) {
  313. if (menu[method].method_w) {
  314. out->method_fn = NULL;
  315. out->method_fn_w = menu[method].method_w;
  316. }
  317. else {
  318. if (strcmp(parm.weighting_function->answer,"none")) {
  319. G_warning(_("Method %s not compatible with weighing window, using weight mask instead"),
  320. method_name);
  321. if (!have_weights_mask) {
  322. weights_mask();
  323. have_weights_mask = 1;
  324. }
  325. }
  326. out->method_fn = menu[method].method;
  327. out->method_fn_w = NULL;
  328. }
  329. }
  330. else {
  331. out->method_fn = menu[method].method;
  332. out->method_fn_w = NULL;
  333. }
  334. out->copycolr = menu[method].copycolr;
  335. out->cat_names = menu[method].cat_names;
  336. if (out->copycolr)
  337. copycolr = 1;
  338. out->quantile = (parm.quantile->answer && parm.quantile->answers[i])
  339. ? atof(parm.quantile->answers[i])
  340. : 0;
  341. out->buf = Rast_allocate_d_buf();
  342. out->fd = Rast_open_new(output_name, otype);
  343. /* TODO: method=mode should propagate its type */
  344. /* get title, initialize the category and stat info */
  345. if (parm.title->answer)
  346. strcpy(out->title, parm.title->answer);
  347. else
  348. sprintf(out->title, "%dx%d neighborhood: %s of %s",
  349. ncb.nsize, ncb.nsize, menu[method].name, ncb.oldcell);
  350. }
  351. /* copy color table? */
  352. if (copycolr) {
  353. G_suppress_warnings(1);
  354. copycolr =
  355. (Rast_read_colors(ncb.oldcell, "", &colr) > 0);
  356. G_suppress_warnings(0);
  357. }
  358. /* allocate the cell buffers */
  359. allocate_bufs();
  360. /* initialize the cell bufs with 'dist' rows of the old cellfile */
  361. readrow = 0;
  362. for (row = 0; row < ncb.dist; row++)
  363. readcell(in_fd, readrow++, nrows, ncols);
  364. /* open the selection raster map */
  365. if (parm.selection->answer) {
  366. G_message(_("Opening selection map <%s>"), parm.selection->answer);
  367. selection_fd = Rast_open_old(parm.selection->answer, "");
  368. selection = Rast_allocate_null_buf();
  369. } else {
  370. selection_fd = -1;
  371. selection = NULL;
  372. }
  373. if (flag.circle->answer)
  374. circle_mask();
  375. values_w = NULL;
  376. values_w_tmp = NULL;
  377. if (weights) {
  378. values_w =
  379. (DCELL(*)[2]) G_malloc(ncb.nsize * ncb.nsize * 2 * sizeof(DCELL));
  380. values_w_tmp =
  381. (DCELL(*)[2]) G_malloc(ncb.nsize * ncb.nsize * 2 * sizeof(DCELL));
  382. }
  383. values = (DCELL *) G_malloc(ncb.nsize * ncb.nsize * sizeof(DCELL));
  384. values_tmp = (DCELL *) G_malloc(ncb.nsize * ncb.nsize * sizeof(DCELL));
  385. for (row = 0; row < nrows; row++) {
  386. G_percent(row, nrows, 2);
  387. readcell(in_fd, readrow++, nrows, ncols);
  388. if (selection)
  389. Rast_get_null_value_row(selection_fd, selection, row);
  390. for (col = 0; col < ncols; col++) {
  391. if (selection && selection[col]) {
  392. /* ncb.buf length is region row length + 2 * ncb.dist (eq. floor(neighborhood/2))
  393. * Thus original data start is shifted by ncb.dist! */
  394. for (i = 0; i < num_outputs; i++)
  395. outputs[i].buf[col] = ncb.buf[ncb.dist][col + ncb.dist];
  396. continue;
  397. }
  398. if (weights)
  399. n = gather_w(values, values_w, col);
  400. else
  401. n = gather(values, col);
  402. for (i = 0; i < num_outputs; i++) {
  403. struct output *out = &outputs[i];
  404. DCELL *rp = &out->buf[col];
  405. if (n == 0) {
  406. Rast_set_d_null_value(rp, 1);
  407. }
  408. else {
  409. if (out->method_fn_w) {
  410. memcpy(values_w_tmp, values_w, n * 2 * sizeof(DCELL));
  411. (*out->method_fn_w)(rp, values_w_tmp, n, &out->quantile);
  412. }
  413. else {
  414. memcpy(values_tmp, values, n * sizeof(DCELL));
  415. (*out->method_fn)(rp, values_tmp, n, &out->quantile);
  416. }
  417. }
  418. }
  419. }
  420. for (i = 0; i < num_outputs; i++) {
  421. struct output *out = &outputs[i];
  422. Rast_put_d_row(out->fd, out->buf);
  423. }
  424. }
  425. G_percent(row, nrows, 2);
  426. Rast_close(in_fd);
  427. if (selection)
  428. Rast_close(selection_fd);
  429. for (i = 0; i < num_outputs; i++) {
  430. Rast_close(outputs[i].fd);
  431. /* put out category info */
  432. null_cats(outputs[i].title);
  433. if (outputs[i].cat_names)
  434. outputs[i].cat_names();
  435. Rast_write_cats(outputs[i].name, &ncb.cats);
  436. if (copycolr && outputs[i].copycolr)
  437. Rast_write_colors(outputs[i].name, G_mapset(), &colr);
  438. Rast_short_history(outputs[i].name, "raster", &history);
  439. Rast_command_history(&history);
  440. Rast_write_history(outputs[i].name, &history);
  441. }
  442. exit(EXIT_SUCCESS);
  443. }