main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /****************************************************************************
  2. *
  3. * MODULE: r.volume
  4. * AUTHOR(S): Dr. James Hinthorne, Central Washington University GIS Laboratory
  5. * December 1988, (revised April 1989) (original contributor)
  6. * Revised Jul 1995 to use new sites API (McCauley)
  7. * GRASS 6 update: Hamish Bowman <hamish_b yahoo.com>
  8. * Glynn Clements <glynn gclements.plus.com>, Soeren Gebbert <soeren.gebbert gmx.de>
  9. * GRASS 7 update (to use Vlib): Martin Landa <landa.martin gmail.com>
  10. * PURPOSE:
  11. * r.volume is a program to compute the total, and average of cell values
  12. * within regions of a map defined by clumps or patches on a second map
  13. * (or MASK). It also computes the "volume" by multiplying the total
  14. * within a clump by the area of each cell. It also outputs the
  15. * "centroid" location of each clump. Output is to standard out.
  16. *
  17. * COPYRIGHT: (C) 1999-2006, 2013 by the GRASS Development Team
  18. *
  19. * This program is free software under the GNU General Public
  20. * License (>=v2). Read the file COPYING that comes with GRASS
  21. * for details.
  22. *
  23. *****************************************************************************/
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <grass/gis.h>
  27. #include <grass/raster.h>
  28. #include <grass/vector.h>
  29. #include <grass/dbmi.h>
  30. #include <grass/glocale.h>
  31. #include "local_proto.h"
  32. int main(int argc, char *argv[])
  33. {
  34. /* variables */
  35. DCELL *data_buf;
  36. CELL *clump_buf;
  37. CELL i, max;
  38. int row, col, rows, cols;
  39. int out_mode, use_MASK, *n, *e;
  40. long int *count;
  41. int fd_data, fd_clump;
  42. const char *datamap, *clumpmap, *centroidsmap;
  43. double avg, vol, total_vol, east, north, *sum;
  44. struct Cell_head window;
  45. struct Map_info *fd_centroids;
  46. struct line_pnts *Points;
  47. struct line_cats *Cats;
  48. struct field_info *Fi;
  49. char buf[DB_SQL_MAX];
  50. dbString sql;
  51. dbDriver *driver;
  52. struct GModule *module;
  53. struct {
  54. struct Option *input, *clump, *centroids, *output;
  55. } opt;
  56. struct {
  57. struct Flag *report;
  58. } flag;
  59. /* define paramaters and flags */
  60. G_gisinit(argv[0]);
  61. module = G_define_module();
  62. G_add_keyword(_("raster"));
  63. G_add_keyword(_("volume"));
  64. G_add_keyword(_("clumps"));
  65. module->label =
  66. _("Calculates the volume of data \"clumps\".");
  67. module->description = _("Optionally produces a GRASS vector points map "
  68. "containing the calculated centroids of these clumps.");
  69. opt.input = G_define_standard_option(G_OPT_R_INPUT);
  70. opt.input->description =
  71. _("Name of input raster map representing data that will be summed within clumps");
  72. opt.clump = G_define_standard_option(G_OPT_R_INPUT);
  73. opt.clump->key = "clump";
  74. opt.clump->required = NO;
  75. opt.clump->label =
  76. _("Name of input clump raster map");
  77. opt.clump->description = _("Preferably the output of r.clump. "
  78. "If no clump map is given than MASK is used.");
  79. opt.centroids = G_define_standard_option(G_OPT_V_OUTPUT);
  80. opt.centroids->key = "centroids";
  81. opt.centroids->required = NO;
  82. opt.centroids->description = _("Name for output vector points map to contain clump centroids");
  83. opt.output = G_define_standard_option(G_OPT_F_OUTPUT);
  84. opt.output->required = NO;
  85. opt.output->label =
  86. _("Name for output file to hold the report");
  87. opt.output->description =
  88. _("If no output file given report is printed to standard output");
  89. flag.report = G_define_flag();
  90. flag.report->key = 'f';
  91. flag.report->description = _("Generate unformatted report (items separated by colon)");
  92. if (G_parser(argc, argv))
  93. exit(EXIT_FAILURE);
  94. /* get arguments */
  95. datamap = opt.input->answer;
  96. clumpmap = NULL;
  97. if (opt.clump->answer)
  98. clumpmap = opt.clump->answer;
  99. centroidsmap = NULL;
  100. fd_centroids = NULL;
  101. Points = NULL;
  102. Cats = NULL;
  103. driver = NULL;
  104. if (opt.centroids->answer) {
  105. centroidsmap = opt.centroids->answer;
  106. fd_centroids = G_malloc(sizeof(struct Map_info));
  107. }
  108. out_mode = (!flag.report->answer);
  109. /*
  110. * see if MASK or a separate "clumpmap" raster map is to be used
  111. * -- it must(!) be one of those two choices.
  112. */
  113. use_MASK = 0;
  114. if (!clumpmap) {
  115. clumpmap = "MASK";
  116. use_MASK = 1;
  117. if (!G_find_raster2(clumpmap, G_mapset()))
  118. G_fatal_error(_("No MASK found. If no clump map is given than the MASK is required. "
  119. "You need to define a clump raster map or create a MASK by r.mask command."));
  120. G_important_message(_("No clump map given, using MASK"));
  121. }
  122. /* open input and clump raster maps */
  123. fd_data = Rast_open_old(datamap, "");
  124. fd_clump = Rast_open_old(clumpmap, use_MASK ? G_mapset() : "");
  125. /* initialize vector map (for centroids) if needed */
  126. if (centroidsmap) {
  127. Vect_open_new(fd_centroids, centroidsmap, WITHOUT_Z);
  128. Points = Vect_new_line_struct();
  129. Cats = Vect_new_cats_struct();
  130. /* initialize data structures */
  131. Vect_append_point(Points, 0., 0., 0.);
  132. Vect_cat_set(Cats, 1, 1);
  133. }
  134. /* initialize output file */
  135. if (opt.output->answer && strcmp(opt.output->answer, "-") != 0) {
  136. if (freopen(opt.output->answer, "w", stdout) == NULL) {
  137. perror(opt.output->answer);
  138. exit(EXIT_FAILURE);
  139. }
  140. }
  141. /* initialize data accumulation arrays */
  142. max = Rast_get_max_c_cat(clumpmap, use_MASK ? G_mapset() : "");
  143. sum = (double *)G_malloc((max + 1) * sizeof(double));
  144. count = (long int *)G_malloc((max + 1) * sizeof(long int));
  145. G_zero(sum, (max + 1) * sizeof(double));
  146. G_zero(count, (max + 1) * sizeof(long int));
  147. data_buf = Rast_allocate_d_buf();
  148. clump_buf = Rast_allocate_c_buf();
  149. /* get window size */
  150. G_get_window(&window);
  151. rows = window.rows;
  152. cols = window.cols;
  153. /* now get the data -- first pass */
  154. for (row = 0; row < rows; row++) {
  155. G_percent(row, rows, 2);
  156. Rast_get_d_row(fd_data, data_buf, row);
  157. Rast_get_c_row(fd_clump, clump_buf, row);
  158. for (col = 0; col < cols; col++) {
  159. i = clump_buf[col];
  160. if (i > max)
  161. G_fatal_error(_("Invalid category value %d (max=%d): row=%d col=%d"),
  162. i, max, row, col);
  163. if (i < 1) {
  164. G_debug(3, "row=%d col=%d: zero or negs ignored", row, col);
  165. continue; /* ignore zeros and negs */
  166. }
  167. if (Rast_is_d_null_value(&data_buf[col])) {
  168. G_debug(3, "row=%d col=%d: NULL ignored", row, col);
  169. continue;
  170. }
  171. sum[i] += data_buf[col];
  172. count[i]++;
  173. }
  174. }
  175. G_percent(1, 1, 1);
  176. /* free some buffer space */
  177. G_free(data_buf);
  178. G_free(clump_buf);
  179. /* data lists for centroids of clumps */
  180. e = (int *)G_malloc((max + 1) * sizeof(int));
  181. n = (int *)G_malloc((max + 1) * sizeof(int));
  182. i = centroids(fd_clump, e, n, 1, max);
  183. /* close raster maps */
  184. Rast_close(fd_data);
  185. Rast_close(fd_clump);
  186. /* got everything, now do output */
  187. if (centroidsmap) {
  188. G_message(_("Creating vector point map <%s>..."), centroidsmap);
  189. /* set comment */
  190. sprintf(buf, _("From '%s' on raster map <%s> using clumps from <%s>"),
  191. argv[0], datamap, clumpmap);
  192. Vect_set_comment(fd_centroids, buf);
  193. /* create attribute table */
  194. Fi = Vect_default_field_info(fd_centroids, 1, NULL, GV_1TABLE);
  195. driver = db_start_driver_open_database(Fi->driver,
  196. Vect_subst_var(Fi->database, fd_centroids));
  197. if (driver == NULL) {
  198. G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
  199. Vect_subst_var(Fi->database, fd_centroids), Fi->driver);
  200. }
  201. db_set_error_handler_driver(driver);
  202. db_begin_transaction(driver);
  203. db_init_string(&sql);
  204. sprintf(buf, "create table %s (cat integer, volume double precision, "
  205. "average double precision, sum double precision, count integer)",
  206. Fi->table);
  207. db_set_string(&sql, buf);
  208. Vect_map_add_dblink(fd_centroids, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database,
  209. Fi->driver);
  210. G_debug(3, "%s", db_get_string(&sql));
  211. if (db_execute_immediate(driver, &sql) != DB_OK) {
  212. G_fatal_error(_("Unable to create table: %s"), db_get_string(&sql));
  213. }
  214. }
  215. /* print header */
  216. if (out_mode) {
  217. fprintf(stdout, _("\nVolume report on data from <%s> using clumps on <%s> raster map"),
  218. datamap, clumpmap);
  219. fprintf(stdout, "\n\n");
  220. fprintf(stdout,
  221. _("Category Average Data # Cells Centroid Total\n"));
  222. fprintf(stdout,
  223. _("Number in clump Total in clump Easting Northing Volume"));
  224. fprintf(stdout, "\n%s\n", SEP);
  225. }
  226. total_vol = 0.0;
  227. /* print output, write centroids */
  228. for (i = 1; i <= max; i++) {
  229. if (count[i]) {
  230. avg = sum[i] / (double)count[i];
  231. vol = sum[i] * window.ew_res * window.ns_res;
  232. total_vol += vol;
  233. east = window.west + (e[i] + 0.5) * window.ew_res;
  234. north = window.north - (n[i] + 0.5) * window.ns_res;
  235. if (fd_centroids) { /* write centroids if requested */
  236. Points->x[0] = east;
  237. Points->y[0] = north;
  238. Cats->cat[0] = i;
  239. Vect_write_line(fd_centroids, GV_POINT, Points, Cats);
  240. sprintf(buf, "insert into %s values (%d, %f, %f, %f, %ld)",
  241. Fi->table, i, vol, avg, sum[i], count[i]);
  242. db_set_string(&sql, buf);
  243. if (db_execute_immediate(driver, &sql) != DB_OK)
  244. G_fatal_error(_("Cannot insert new row: %s"),
  245. db_get_string(&sql));
  246. }
  247. if (out_mode)
  248. fprintf(stdout,
  249. "%8d%10.2f%10.0f %7ld %10.2f %10.2f %16.2f\n", i,
  250. avg, sum[i], count[i], east, north, vol);
  251. else
  252. fprintf(stdout, "%d:%.2f:%.0f:%ld:%.2f:%.2f:%.2f\n",
  253. i, avg, sum[i], count[i], east, north, vol);
  254. }
  255. }
  256. /* write centroid attributes and close the map*/
  257. if (fd_centroids) {
  258. db_commit_transaction(driver);
  259. Vect_close(fd_centroids);
  260. }
  261. /* print total value */
  262. if (total_vol > 0.0 && out_mode) {
  263. fprintf(stdout, "%s\n", SEP);
  264. fprintf(stdout, "%60s = %14.2f", _("Total Volume"), total_vol);
  265. fprintf(stdout, "\n");
  266. }
  267. exit(EXIT_SUCCESS);
  268. }