PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/grass-6.4.2/raster/r.lake/main.c

#
C | 415 lines | 297 code | 63 blank | 55 comment | 92 complexity | 6b9b81748beca63475fe3cc6406c4364 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, 0BSD, BSD-3-Clause
  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 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/glocale.h>
  37. /* Saves map file from 2d array. NULL must be 0. Also meanwhile calculates area and volume. */
  38. void save_map(FCELL ** out, int out_fd, int rows, int cols, int flag,
  39. FCELL * min_depth, FCELL * max_depth, double *area,
  40. double *volume)
  41. {
  42. int row, col;
  43. double cellsize = -1;
  44. G_debug(1, "Saving new map");
  45. if (G_begin_cell_area_calculations() == 0 || G_begin_cell_area_calculations() == 1) { /* All cells have constant size... */
  46. cellsize = G_area_of_cell_at_row(0);
  47. }
  48. G_debug(1, "Cell area: %f", cellsize);
  49. for (row = 0; row < rows; row++) {
  50. if (cellsize == -1) /* Get LatLon current rows cell size */
  51. cellsize = G_area_of_cell_at_row(row);
  52. for (col = 0; col < cols; col++) {
  53. if (flag == 1) /* Create negative map */
  54. out[row][col] = 0 - out[row][col];
  55. if (out[row][col] == 0) {
  56. G_set_f_null_value(&out[row][col], 1);
  57. }
  58. if (out[row][col] > 0 || out[row][col] < 0) {
  59. G_debug(5, "volume %f += cellsize %f * value %f [%d,%d]",
  60. *volume, cellsize, out[row][col], row, col);
  61. *area += cellsize;
  62. *volume += cellsize * out[row][col];
  63. }
  64. /* Get min/max depth. Can be usefull ;) */
  65. if (out[row][col] > *max_depth)
  66. *max_depth = out[row][col];
  67. if (out[row][col] < *min_depth)
  68. *min_depth = out[row][col];
  69. }
  70. if (G_put_f_raster_row(out_fd, out[row]) == -1)
  71. G_fatal_error(_("Failed writing output raster map row %d"), 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, *mapset;
  120. int rows, cols, in_terran_fd, out_fd, lake_fd, row, col, pases, pass;
  121. int lastcount, curcount, start_col, start_row;
  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. module = G_define_module();
  132. module->keywords = _("raster, hydrology");
  133. module->description = _("Fills lake from seed at given level.");
  134. tmap_opt = G_define_option();
  135. tmap_opt->key = "dem";
  136. tmap_opt->key_desc = "name";
  137. tmap_opt->description = _("Name of terrain raster map (DEM)");
  138. tmap_opt->type = TYPE_STRING;
  139. tmap_opt->gisprompt = "old,cell,raster";
  140. tmap_opt->required = YES;
  141. wlvl_opt = G_define_option();
  142. wlvl_opt->key = "wl";
  143. wlvl_opt->description = _("Water level");
  144. wlvl_opt->type = TYPE_DOUBLE;
  145. wlvl_opt->required = YES;
  146. lake_opt = G_define_option();
  147. lake_opt->key = "lake";
  148. lake_opt->key_desc = "name";
  149. lake_opt->description = _("Name for output raster map with lake");
  150. lake_opt->type = TYPE_STRING;
  151. lake_opt->gisprompt = "new,cell,raster";
  152. lake_opt->required = NO;
  153. sdxy_opt = G_define_option();
  154. sdxy_opt->key = "xy";
  155. sdxy_opt->description = _("Seed point coordinates");
  156. sdxy_opt->type = TYPE_DOUBLE;
  157. sdxy_opt->key_desc = "east,north";
  158. sdxy_opt->required = NO;
  159. sdxy_opt->multiple = NO;
  160. smap_opt = G_define_option();
  161. smap_opt->key = "seed";
  162. smap_opt->key_desc = "name";
  163. smap_opt->description =
  164. _("Name of raster map with seed (at least 1 cell > 0)");
  165. smap_opt->type = TYPE_STRING;
  166. smap_opt->gisprompt = "old,cell,raster";
  167. smap_opt->required = NO;
  168. negative_flag = G_define_flag();
  169. negative_flag->key = 'n';
  170. negative_flag->description =
  171. _("Use negative depth values for lake raster map");
  172. overwrite_flag = G_define_flag();
  173. overwrite_flag->key = 'o';
  174. overwrite_flag->description =
  175. _("Overwrite seed map with result (lake) map");
  176. if (G_parser(argc, argv)) /* Returns 0 if successful, non-zero otherwise */
  177. exit(EXIT_FAILURE);
  178. G_gisinit(argv[0]);
  179. if (smap_opt->answer && sdxy_opt->answer)
  180. G_fatal_error(_("Both seed map and coordinates cannot be specified"));
  181. if (!smap_opt->answer && !sdxy_opt->answer)
  182. G_fatal_error(_("Seed map or seed coordinates must be set!"));
  183. if (sdxy_opt->answer && !lake_opt->answer)
  184. G_fatal_error(_("Seed coordinates and output map lake= must be set!"));
  185. if (lake_opt->answer && overwrite_flag->answer)
  186. G_fatal_error(_("Both lake and overwrite cannot be specified"));
  187. if (!lake_opt->answer && !overwrite_flag->answer)
  188. G_fatal_error(_("Output lake map or overwrite flag must be set!"));
  189. terrainmap = tmap_opt->answer;
  190. seedmap = smap_opt->answer;
  191. sscanf(wlvl_opt->answer, "%f", &water_level);
  192. lakemap = lake_opt->answer;
  193. /* If lakemap is set, write to it, else is set overwrite flag and we should write to seedmap. */
  194. if (lakemap) {
  195. lake_fd = G_open_raster_new(lakemap, 1);
  196. if (lake_fd < 0)
  197. G_fatal_error(_("Unable to create raster map <%s>"), lakemap);
  198. }
  199. rows = G_window_rows();
  200. cols = G_window_cols();
  201. /* If we use x,y as seed... */
  202. if (sdxy_opt->answer) {
  203. G_get_window(&window);
  204. east = window.east;
  205. north = window.north;
  206. G_scan_easting(sdxy_opt->answers[0], &east, G_projection());
  207. G_scan_northing(sdxy_opt->answers[1], &north, G_projection());
  208. start_col = (int)G_easting_to_col(east, &window);
  209. start_row = (int)G_northing_to_row(north, &window);
  210. if (start_row < 0 || start_row > rows ||
  211. start_col < 0 || start_col > cols)
  212. G_fatal_error(_("Seed point outside the current region"));
  213. }
  214. /* Open terran map */
  215. mapset = G_find_cell2(terrainmap, "");
  216. if (mapset == NULL)
  217. G_fatal_error(_("Raster map <%s> not found"), terrainmap);
  218. in_terran_fd = G_open_cell_old(terrainmap, mapset);
  219. if (in_terran_fd < 0)
  220. G_fatal_error(_("Unable to open raster map <%s>"),
  221. G_fully_qualified_name(terrainmap, mapset));
  222. /* Open seed map */
  223. if (smap_opt->answer) {
  224. mapset = G_find_cell2(seedmap, "");
  225. if (mapset == NULL)
  226. G_fatal_error(_("Raster map <%s> not found"), seedmap);
  227. out_fd = G_open_cell_old(seedmap, mapset);
  228. if (out_fd < 0)
  229. G_fatal_error(_("Unable to open raster map <%s>"),
  230. G_fully_qualified_name(seedmap, mapset));
  231. }
  232. /* Pointers to rows. Row = ptr to 'col' size array. */
  233. in_terran = (FCELL **) G_malloc(rows * sizeof(FCELL *));
  234. out_water = (FCELL **) G_malloc(rows * sizeof(FCELL *));
  235. if (in_terran == NULL || out_water == NULL)
  236. G_fatal_error(_("G_malloc: out of memory"));
  237. G_debug(1, "Loading maps...");
  238. /* foo_rows[row] == array with data (2d array). */
  239. for (row = 0; row < rows; row++) {
  240. in_terran[row] = (FCELL *) G_malloc(cols * sizeof(FCELL));
  241. out_water[row] = (FCELL *) G_calloc(cols, sizeof(FCELL));
  242. /* In newly created space load data from file. */
  243. if (G_get_f_raster_row(in_terran_fd, in_terran[row], row) != 1)
  244. G_fatal_error(_("Unable to read raster map <%s> row %d"),
  245. terrainmap, row);
  246. if (smap_opt->answer)
  247. if (G_get_f_raster_row(out_fd, out_water[row], row) != 1)
  248. G_fatal_error(_("Unable to read raster map <%s> row %d"),
  249. seedmap, row);
  250. G_percent(row + 1, rows, 5);
  251. }
  252. /* Set seed point */
  253. if (sdxy_opt->answer)
  254. /* Check is water level higher than seed point */
  255. if (in_terran[start_row][start_col] >= water_level)
  256. G_fatal_error(_("Given water level at seed point is below earth surface. "
  257. "Increase water level or move seed point."));
  258. out_water[start_row][start_col] = 1;
  259. /* Close seed map for reading. */
  260. if (smap_opt->answer)
  261. G_close_cell(out_fd);
  262. /* Open output map for writing. */
  263. if (lakemap) {
  264. out_fd = lake_fd;
  265. }
  266. else {
  267. out_fd = G_open_raster_new(seedmap, 1);
  268. if (out_fd < 0)
  269. G_fatal_error(_("Unable to create raster map <%s>"), seedmap);
  270. }
  271. /* More pases are renudant. Real pases count is controled by altered cell count. */
  272. pases = (int)(rows * cols) / 2;
  273. G_debug(1,
  274. "Starting lake filling at level of %8.4f in %d passes. Percent done:",
  275. water_level, pases);
  276. lastcount = 0;
  277. for (pass = 0; pass < pases; pass++) {
  278. G_debug(3, "Pass: %d", pass);
  279. curcount = 0;
  280. /* Move from left upper corner to right lower corner. */
  281. for (row = 0; row < rows; row++) {
  282. for (col = 0; col < cols; col++) {
  283. /* Loading water data into window. */
  284. load_window_values(out_water, water_window, rows, cols, row,
  285. col);
  286. /* Cheking presence of water. */
  287. if (is_near_water(water_window) == 1) {
  288. if (in_terran[row][col] < water_level) {
  289. out_water[row][col] =
  290. water_level - in_terran[row][col];
  291. curcount++;
  292. }
  293. else {
  294. out_water[row][col] = 0; /* Cell is higher than water level -> NULL. */
  295. }
  296. }
  297. }
  298. }
  299. if (curcount == lastcount)
  300. break; /* We done. */
  301. lastcount = curcount;
  302. curcount = 0;
  303. /* Move backwards - from lower right corner to upper left corner. */
  304. for (row = rows - 1; row >= 0; row--) {
  305. for (col = cols - 1; col >= 0; col--) {
  306. load_window_values(out_water, water_window, rows, cols, row,
  307. col);
  308. if (is_near_water(water_window) == 1) {
  309. if (in_terran[row][col] < water_level) {
  310. out_water[row][col] =
  311. water_level - in_terran[row][col];
  312. curcount++;
  313. }
  314. else {
  315. out_water[row][col] = 0;
  316. }
  317. }
  318. }
  319. }
  320. G_percent(pass + 1, pases, 10);
  321. if (curcount == lastcount)
  322. break; /* We done. */
  323. lastcount = curcount;
  324. } /*pases */
  325. G_percent(pases, pases, 10); /* Show 100%. */
  326. save_map(out_water, out_fd, rows, cols, negative_flag->answer, &min_depth,
  327. &max_depth, &area, &volume);
  328. G_message(_("Lake depth from %f to %f"), min_depth, max_depth);
  329. G_message(_("Lake area %f square meters"), area);
  330. G_message(_("Lake volume %f cubic meters"), volume);
  331. G_warning(_("Volume is correct only if lake depth (terrain raster map) is in meters"));
  332. /* Close all files. Lake map gets written only now. */
  333. G_close_cell(in_terran_fd);
  334. G_close_cell(out_fd);
  335. /* Add blue color gradient from light bank to dark depth */
  336. G_init_colors(&colr);
  337. if (negative_flag->answer == 1) {
  338. G_add_f_raster_color_rule(&max_depth, 0, 240, 255,
  339. &min_depth, 0, 50, 170, &colr);
  340. }
  341. else {
  342. G_add_f_raster_color_rule(&min_depth, 0, 240, 255,
  343. &max_depth, 0, 50, 170, &colr);
  344. }
  345. if (G_write_colors(lakemap, G_mapset(), &colr) != 1)
  346. G_fatal_error(_("Unable to read color file of raster map <%s>"),
  347. lakemap);
  348. G_short_history(lakemap, "raster", &history);
  349. G_command_history(&history);
  350. G_write_history(lakemap, &history);
  351. return EXIT_SUCCESS;
  352. }