main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /****************************************************************************
  2. *
  3. * MODULE: r.lake
  4. *
  5. * AUTHOR(S): Maris Nartiss - maris.nartiss gmail.com
  6. * with hints from friendly GRASS dev team
  7. *
  8. * PURPOSE: Fills lake with water at given height above DEM.
  9. * As seed You can use already existing map or
  10. * X,Y coordinates.
  11. *
  12. * COPYRIGHT: (C) 2005-2008, 2010 by the GRASS Development Team
  13. *
  14. * This program is free software under the GNU General Public
  15. * License (>=v2). Read the file COPYING that comes with GRASS
  16. * for details.
  17. *
  18. * TODO: - Option to create 3D output;
  19. * - Test with lat/lon location, feets and other crap;
  20. * - In progress: Add different debug level messages;
  21. * - In progress: Option to output resulting lake area and volume.
  22. *
  23. * BUGS: - Lake (seed) map cannot be negative!
  24. * - Negative output (-n) maps cannot be used as input.
  25. * - Code is not large file safe (i.e. array with whole map).
  26. *
  27. *****************************************************************************/
  28. /* You are not allowed to remove this comment block. /M. Nartiss/
  29. *
  30. * Kaarliit, shii programma ir veltiita Tev.
  31. *
  32. */
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <grass/gis.h>
  36. #include <grass/raster.h>
  37. #include <grass/glocale.h>
  38. /* Saves map file from 2d array. NULL must be 0. Also meanwhile calculates area and volume. */
  39. void save_map(FCELL ** out, int out_fd, int rows, int cols, int flag,
  40. FCELL * min_depth, FCELL * max_depth, double *area,
  41. double *volume)
  42. {
  43. int row, col;
  44. double cellsize = -1;
  45. G_debug(1, "Saving new map");
  46. if (G_begin_cell_area_calculations() == 0 || G_begin_cell_area_calculations() == 1) { /* All cells have constant size... */
  47. cellsize = G_area_of_cell_at_row(0);
  48. }
  49. G_debug(1, "Cell area: %f", cellsize);
  50. for (row = 0; row < rows; row++) {
  51. if (cellsize == -1) /* Get LatLon current rows cell size */
  52. cellsize = G_area_of_cell_at_row(row);
  53. for (col = 0; col < cols; col++) {
  54. if (flag == 1) /* Create negative map */
  55. out[row][col] = 0 - out[row][col];
  56. if (out[row][col] == 0) {
  57. Rast_set_f_null_value(&out[row][col], 1);
  58. }
  59. if (out[row][col] > 0 || out[row][col] < 0) {
  60. G_debug(5, "volume %f += cellsize %f * value %f [%d,%d]",
  61. *volume, cellsize, out[row][col], row, col);
  62. *area += cellsize;
  63. *volume += cellsize * out[row][col];
  64. }
  65. /* Get min/max depth. Can be usefull ;) */
  66. if (out[row][col] > *max_depth)
  67. *max_depth = out[row][col];
  68. if (out[row][col] < *min_depth)
  69. *min_depth = out[row][col];
  70. }
  71. Rast_put_f_row(out_fd, out[row]);
  72. G_percent(row + 1, rows, 5);
  73. }
  74. }
  75. /* Check water presence in sliding window */
  76. short is_near_water(FCELL window[][3])
  77. {
  78. int i, j;
  79. /* If center is under water */
  80. if (window[1][1] > 0)
  81. return 1;
  82. for (i = 0; i < 3; i++) {
  83. for (j = 0; j < 3; j++) {
  84. if (window[i][j] > 0)
  85. return 1;
  86. }
  87. }
  88. return 0;
  89. }
  90. /* Loads values into window around central cell */
  91. void load_window_values(FCELL ** in_rows, FCELL window[][3],
  92. int rows, int cols, int row, int col)
  93. {
  94. int i, j;
  95. rows -= 1; /* Row'n'Col count starts from 0! */
  96. cols -= 1;
  97. for (i = -1; i < 2; i++) {
  98. if (row + i < 0 || row + i > rows) { /* First or last line... */
  99. window[i + 1][0] = 0;
  100. window[i + 1][1] = 0;
  101. window[i + 1][2] = 0;
  102. continue;
  103. }
  104. else {
  105. for (j = -1; j < 2; j++) {
  106. if (col + j < 0 || col + j > cols - 1) { /* First or last column... */
  107. window[i + 1][j + 1] = 0;
  108. continue;
  109. }
  110. else { /* All normal cases... */
  111. window[i + 1][j + 1] = in_rows[row + i][col + j];
  112. }
  113. }
  114. }
  115. }
  116. }
  117. int main(int argc, char *argv[])
  118. {
  119. char *terrainmap, *seedmap, *lakemap;
  120. int rows, cols, in_terran_fd, out_fd, lake_fd, row, col, pases, pass;
  121. int lastcount, curcount, start_col = 0, start_row = 0;
  122. double east, north, area = 0, volume = 0;
  123. FCELL **in_terran, **out_water, water_level, max_depth = 0, min_depth = 0;
  124. FCELL water_window[3][3];
  125. struct Option *tmap_opt, *smap_opt, *wlvl_opt, *lake_opt, *sdxy_opt;
  126. struct Flag *negative_flag, *overwrite_flag;
  127. struct GModule *module;
  128. struct Colors colr;
  129. struct Cell_head window;
  130. struct History history;
  131. G_gisinit(argv[0]);
  132. module = G_define_module();
  133. G_add_keyword(_("raster"));
  134. G_add_keyword(_("hydrology"));
  135. module->description = _("Fills lake at given point to given level.");
  136. tmap_opt = G_define_standard_option(G_OPT_R_ELEV);
  137. wlvl_opt = G_define_option();
  138. wlvl_opt->key = "wl";
  139. wlvl_opt->description = _("Water level");
  140. wlvl_opt->type = TYPE_DOUBLE;
  141. wlvl_opt->required = YES;
  142. lake_opt = G_define_standard_option(G_OPT_R_OUTPUT);
  143. lake_opt->key = "lake";
  144. lake_opt->required = NO;
  145. sdxy_opt = G_define_option();
  146. sdxy_opt->key = "xy";
  147. sdxy_opt->description = _("Seed point coordinates");
  148. sdxy_opt->type = TYPE_DOUBLE;
  149. sdxy_opt->key_desc = "east,north";
  150. sdxy_opt->required = NO;
  151. sdxy_opt->multiple = NO;
  152. smap_opt = G_define_standard_option(G_OPT_R_MAP);
  153. smap_opt->key = "seed";
  154. smap_opt->description =
  155. _("Name of input raster map with given starting point(s) (at least 1 cell > 0)");
  156. smap_opt->required = NO;
  157. negative_flag = G_define_flag();
  158. negative_flag->key = 'n';
  159. negative_flag->description =
  160. _("Use negative depth values for lake raster map");
  161. overwrite_flag = G_define_flag();
  162. overwrite_flag->key = 'o';
  163. overwrite_flag->description =
  164. _("Overwrite seed map with result (lake) map");
  165. if (G_parser(argc, argv)) /* Returns 0 if successful, non-zero otherwise */
  166. exit(EXIT_FAILURE);
  167. if (smap_opt->answer && sdxy_opt->answer)
  168. G_fatal_error(_("Both seed map and coordinates cannot be specified"));
  169. if (!smap_opt->answer && !sdxy_opt->answer)
  170. G_fatal_error(_("Seed map or seed coordinates must be set!"));
  171. if (sdxy_opt->answer && !lake_opt->answer)
  172. G_fatal_error(_("Seed coordinates and output map lake= must be set!"));
  173. if (lake_opt->answer && overwrite_flag->answer)
  174. G_fatal_error(_("Both lake and overwrite cannot be specified"));
  175. if (!lake_opt->answer && !overwrite_flag->answer)
  176. G_fatal_error(_("Output lake map or overwrite flag must be set!"));
  177. terrainmap = tmap_opt->answer;
  178. seedmap = smap_opt->answer;
  179. sscanf(wlvl_opt->answer, "%f", &water_level);
  180. lakemap = lake_opt->answer;
  181. /* If lakemap is set, write to it, else is set overwrite flag and we should write to seedmap. */
  182. if (lakemap)
  183. lake_fd = Rast_open_new(lakemap, 1);
  184. rows = Rast_window_rows();
  185. cols = Rast_window_cols();
  186. /* If we use x,y as seed... */
  187. if (sdxy_opt->answer) {
  188. G_get_window(&window);
  189. east = window.east;
  190. north = window.north;
  191. G_scan_easting(sdxy_opt->answers[0], &east, G_projection());
  192. G_scan_northing(sdxy_opt->answers[1], &north, G_projection());
  193. start_col = (int)Rast_easting_to_col(east, &window);
  194. start_row = (int)Rast_northing_to_row(north, &window);
  195. if (start_row < 0 || start_row > rows ||
  196. start_col < 0 || start_col > cols)
  197. G_fatal_error(_("Seed point outside the current region"));
  198. }
  199. /* Open terran map */
  200. in_terran_fd = Rast_open_old(terrainmap, "");
  201. /* Open seed map */
  202. if (smap_opt->answer)
  203. out_fd = Rast_open_old(seedmap, "");
  204. /* Pointers to rows. Row = ptr to 'col' size array. */
  205. in_terran = (FCELL **) G_malloc(rows * sizeof(FCELL *));
  206. out_water = (FCELL **) G_malloc(rows * sizeof(FCELL *));
  207. if (in_terran == NULL || out_water == NULL)
  208. G_fatal_error(_("G_malloc: out of memory"));
  209. G_debug(1, "Loading maps...");
  210. /* foo_rows[row] == array with data (2d array). */
  211. for (row = 0; row < rows; row++) {
  212. in_terran[row] = (FCELL *) G_malloc(cols * sizeof(FCELL));
  213. out_water[row] = (FCELL *) G_calloc(cols, sizeof(FCELL));
  214. /* In newly created space load data from file. */
  215. Rast_get_f_row(in_terran_fd, in_terran[row], row);
  216. if (smap_opt->answer)
  217. Rast_get_f_row(out_fd, out_water[row], row);
  218. G_percent(row + 1, rows, 5);
  219. }
  220. /* Set seed point */
  221. if (sdxy_opt->answer)
  222. /* Check is water level higher than seed point */
  223. if (in_terran[start_row][start_col] >= water_level)
  224. G_fatal_error(_("Given water level at seed point is below earth surface. "
  225. "Increase water level or move seed point."));
  226. out_water[start_row][start_col] = 1;
  227. /* Close seed map for reading. */
  228. if (smap_opt->answer)
  229. Rast_close(out_fd);
  230. /* Open output map for writing. */
  231. if (lakemap)
  232. out_fd = lake_fd;
  233. else
  234. out_fd = Rast_open_new(seedmap, 1);
  235. /* More pases are renudant. Real pases count is controled by altered cell count. */
  236. pases = (int)(rows * cols) / 2;
  237. G_debug(1,
  238. "Starting lake filling at level of %8.4f in %d passes. Percent done:",
  239. water_level, pases);
  240. lastcount = 0;
  241. for (pass = 0; pass < pases; pass++) {
  242. G_debug(3, "Pass: %d", pass);
  243. curcount = 0;
  244. /* Move from left upper corner to right lower corner. */
  245. for (row = 0; row < rows; row++) {
  246. for (col = 0; col < cols; col++) {
  247. /* Loading water data into window. */
  248. load_window_values(out_water, water_window, rows, cols, row,
  249. col);
  250. /* Cheking presence of water. */
  251. if (is_near_water(water_window) == 1) {
  252. if (in_terran[row][col] < water_level) {
  253. out_water[row][col] =
  254. water_level - in_terran[row][col];
  255. curcount++;
  256. }
  257. else {
  258. out_water[row][col] = 0; /* Cell is higher than water level -> NULL. */
  259. }
  260. }
  261. }
  262. }
  263. if (curcount == lastcount)
  264. break; /* We done. */
  265. lastcount = curcount;
  266. curcount = 0;
  267. /* Move backwards - from lower right corner to upper left corner. */
  268. for (row = rows - 1; row >= 0; row--) {
  269. for (col = cols - 1; col >= 0; col--) {
  270. load_window_values(out_water, water_window, rows, cols, row,
  271. col);
  272. if (is_near_water(water_window) == 1) {
  273. if (in_terran[row][col] < water_level) {
  274. out_water[row][col] =
  275. water_level - in_terran[row][col];
  276. curcount++;
  277. }
  278. else {
  279. out_water[row][col] = 0;
  280. }
  281. }
  282. }
  283. }
  284. G_percent(pass + 1, pases, 10);
  285. if (curcount == lastcount)
  286. break; /* We done. */
  287. lastcount = curcount;
  288. } /*pases */
  289. G_percent(pases, pases, 10); /* Show 100%. */
  290. save_map(out_water, out_fd, rows, cols, negative_flag->answer, &min_depth,
  291. &max_depth, &area, &volume);
  292. G_message(_("Lake depth from %f to %f (specified water level is taken as zero)"), min_depth, max_depth);
  293. G_message(_("Lake area %f square meters"), area);
  294. G_message(_("Lake volume %f cubic meters"), volume);
  295. G_warning(_("Volume is correct only if lake depth (terrain raster map) is in meters"));
  296. /* Close all files. Lake map gets written only now. */
  297. Rast_close(in_terran_fd);
  298. Rast_close(out_fd);
  299. /* Add blue color gradient from light bank to dark depth */
  300. Rast_init_colors(&colr);
  301. if (negative_flag->answer == 1) {
  302. Rast_add_f_color_rule(&max_depth, 0, 240, 255,
  303. &min_depth, 0, 50, 170, &colr);
  304. }
  305. else {
  306. Rast_add_f_color_rule(&min_depth, 0, 240, 255,
  307. &max_depth, 0, 50, 170, &colr);
  308. }
  309. Rast_write_colors(lakemap, G_mapset(), &colr);
  310. Rast_short_history(lakemap, "raster", &history);
  311. Rast_command_history(&history);
  312. Rast_write_history(lakemap, &history);
  313. return EXIT_SUCCESS;
  314. }