main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /****************************************************************************
  2. *
  3. * MODULE: r.relief
  4. * AUTHOR(S): CERL
  5. * parameters standardized: Markus Neteler, 2008
  6. * updates: Michael Barton, 2004
  7. * updates: Gordon Keith, 2003
  8. * updates: Andreas Lange, 2001
  9. * updates: David Finlayson, 2001
  10. * updates: Markus Neteler, 2001, 1999
  11. * Converted to Python by Glynn Clements
  12. * Converted to C by Markus Metz
  13. * PURPOSE: Creates shaded relief map from raster elevation map (DEM)
  14. * COPYRIGHT: (C) 1999 - 2008, 2010, 2012 by the GRASS Development Team
  15. *
  16. * This program is free software under the GNU General Public
  17. * License (>=v2). Read the file COPYING that comes with GRASS
  18. * for details.
  19. *
  20. *****************************************************************************/
  21. /*
  22. * July 2007 - allow input from other mapsets (Brad Douglas)
  23. *
  24. * May 2005 - fixed wrong units parameter (Markus Neteler)
  25. *
  26. * September 2004 - Added z exaggeration control (Michael Barton)
  27. * April 2004 - updated for GRASS 5.7 by Michael Barton
  28. *
  29. * 9/2004 Adds scale factor input (as per documentation); units set scale only if specified for lat/long regions
  30. * Also, adds option of controlling z-exaggeration.
  31. *
  32. * 6/2003 fixes for Lat/Long Gordon Keith <gordon.keith@csiro.au>
  33. * If n is a number then the ewres and nsres are mulitplied by that scale
  34. * to calculate the shading.
  35. * If n is the letter M (either case) the number of metres is degree of
  36. * latitude is used as the scale.
  37. * If n is the letter f then the number of feet in a degree is used.
  38. * It scales latitude and longitude equally, so it's only approximately
  39. * right, but for shading its close enough. It makes the difference
  40. * between an unusable and usable shade.
  41. *
  42. * 10/2001 fix for testing for dashes in raster file name
  43. * by Andreas Lange <andreas.lange@rhein-main.de>
  44. * 10/2001 added parser support - Markus Neteler
  45. * 9/2001 fix to keep NULLs as is (was value 22 before) - Markus Neteler
  46. * 1/2001 fix for NULL by David Finlayson <david_finlayson@yahoo.com>
  47. * 11/99 updated $ewres to ewres() and $nsres to nsres()
  48. * updated number to FP in r.mapcalc statement Markus Neteler
  49. */
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <math.h>
  53. #include <grass/gis.h>
  54. #include <grass/raster.h>
  55. #include <grass/glocale.h>
  56. int main(int argc, char *argv[])
  57. {
  58. int in_fd;
  59. int out_fd;
  60. DCELL *elev_cell[3], *temp;
  61. DCELL *out_rast, *out_ptr = NULL;
  62. DCELL *c1, *c2, *c3, *c4, *c5, *c6, *c7, *c8, *c9;
  63. int Wrap; /* global wraparound */
  64. struct Cell_head window;
  65. struct History hist;
  66. struct Colors colors;
  67. const char *elev_name;
  68. const char *sr_name;
  69. int out_type = CELL_TYPE;
  70. size_t out_size;
  71. const char *units;
  72. char buf[GNAME_MAX];
  73. int nrows, row;
  74. int ncols, col;
  75. int overwrite=0;
  76. double zmult, scale, altitude, azimuth;
  77. double north, east, south, west, ns_med;
  78. double degrees_to_radians, radians_to_degrees;
  79. double H, V;
  80. double dx; /* partial derivative in ew direction */
  81. double dy; /* partial derivative in ns direction */
  82. double key;
  83. double slp_in_rad, aspect, cang;
  84. struct FPRange range;
  85. DCELL min, max;
  86. struct GModule *module;
  87. struct
  88. {
  89. struct Option *elevation, *relief, *altitude, *azimuth, *zmult,
  90. *scale, *units;
  91. } parm;
  92. char *desc;
  93. G_gisinit(argv[0]);
  94. module = G_define_module();
  95. G_add_keyword(_("raster"));
  96. G_add_keyword(_("elevation"));
  97. G_add_keyword(_("relief"));
  98. G_add_keyword(_("terrain"));
  99. G_add_keyword(_("hillshade"));
  100. module->label = _("Creates shaded relief map from an elevation map (DEM).");
  101. parm.elevation = G_define_standard_option(G_OPT_R_INPUT);
  102. parm.relief = G_define_standard_option(G_OPT_R_OUTPUT);
  103. parm.relief->label = _("Name for output shaded relief map");
  104. parm.altitude = G_define_option();
  105. parm.altitude->key = "altitude";
  106. parm.altitude->type = TYPE_DOUBLE;
  107. parm.altitude->required = NO;
  108. parm.altitude->answer = "30";
  109. parm.altitude->options = "0-90";
  110. parm.altitude->description = _("Altitude of the sun in degrees above the horizon");
  111. parm.altitude->guisection = _("Sun position");
  112. parm.azimuth = G_define_option();
  113. parm.azimuth->key = "azimuth";
  114. parm.azimuth->type = TYPE_DOUBLE;
  115. parm.azimuth->required = NO;
  116. parm.azimuth->answer = "270";
  117. parm.azimuth->options = "0-360";
  118. parm.azimuth->description =
  119. _("Azimuth of the sun in degrees to the east of north");
  120. parm.azimuth->guisection = _("Sun position");
  121. parm.zmult = G_define_option();
  122. parm.zmult->key = "zscale";
  123. parm.zmult->type = TYPE_DOUBLE;
  124. parm.zmult->required = NO;
  125. parm.zmult->answer = "1";
  126. parm.zmult->description = _("Factor for exaggerating relief");
  127. parm.scale = G_define_option();
  128. parm.scale->key = "scale";
  129. parm.scale->type = TYPE_DOUBLE;
  130. parm.scale->required = NO;
  131. parm.scale->answer = "1";
  132. parm.scale->description =
  133. _("Scale factor for converting meters to elevation units");
  134. parm.units = G_define_option();
  135. parm.units->key = "units";
  136. parm.units->type = TYPE_STRING;
  137. parm.units->required = NO;
  138. parm.units->options = "intl,survey";
  139. parm.units->description = _("Elevation units (overrides scale factor)");
  140. desc = NULL;
  141. G_asprintf(&desc,
  142. "intl;%s;"
  143. "survey;%s",
  144. _("international feet"),
  145. _("survey feet"));
  146. parm.units->descriptions = desc;
  147. degrees_to_radians = M_PI / 180.0;
  148. radians_to_degrees = 180. / M_PI;
  149. /* Check due to default output map = input.shade map */
  150. overwrite = G_check_overwrite(argc, argv);
  151. if (G_parser(argc, argv))
  152. exit(EXIT_FAILURE);
  153. elev_name = parm.elevation->answer;
  154. if (parm.relief->answer) {
  155. sr_name = parm.relief->answer;
  156. }
  157. else {
  158. char xname[GNAME_MAX], xmapset[GNAME_MAX];
  159. if (G_name_is_fully_qualified(elev_name, xname, xmapset))
  160. sprintf(buf, "%s.shade", xname);
  161. else
  162. sprintf(buf, "%s.shade", elev_name);
  163. sr_name = G_store(buf);
  164. }
  165. G_check_input_output_name(elev_name, sr_name, G_FATAL_EXIT);
  166. if (G_find_raster2(sr_name, G_mapset())) {
  167. if (overwrite)
  168. G_warning(_("Raster map <%s> already exists and will be overwritten"),
  169. sr_name);
  170. else
  171. G_fatal_error(_("Raster map <%s> already exists"), sr_name);
  172. }
  173. if (sscanf(parm.altitude->answer, "%lf", &altitude) != 1 || altitude < 0.0) {
  174. G_fatal_error(_("%s=%s - must be a non-negative number"),
  175. parm.altitude->key, parm.altitude->answer);
  176. }
  177. altitude *= degrees_to_radians;
  178. if (sscanf(parm.azimuth->answer, "%lf", &azimuth) != 1 || azimuth < 0.0) {
  179. G_fatal_error(_("%s=%s - must be a non-negative number"),
  180. parm.azimuth->key, parm.azimuth->answer);
  181. }
  182. /* correct azimuth to East (GRASS convention):
  183. * this seems to be backwards, but in fact it works so leave it. */
  184. azimuth = (azimuth - 90.) * degrees_to_radians;
  185. if (sscanf(parm.zmult->answer, "%lf", &zmult) != 1 || zmult == 0.0) {
  186. G_fatal_error(_("%s=%s - must not be zero"),
  187. parm.zmult->key, parm.zmult->answer);
  188. }
  189. if (sscanf(parm.scale->answer, "%lf", &scale) != 1 || scale <= 0.0) {
  190. G_fatal_error(_("%s=%s - must be a positive number"),
  191. parm.scale->key,
  192. parm.scale->answer);
  193. }
  194. G_get_set_window(&window);
  195. nrows = Rast_window_rows();
  196. ncols = Rast_window_cols();
  197. /* horizontal distances are calculated in meters by G_distance() */
  198. if (parm.units->answer) {
  199. units = parm.units->answer;
  200. if (strcmp(units, "intl") == 0) {
  201. /* 1 international foot = 0.3048 meters */
  202. scale = 1. / 0.3048;
  203. }
  204. else if (strcmp(units, "survey") == 0) {
  205. /* 1 survey foot = 1200 / 3937 meters */
  206. scale = 3937. / 1200.;
  207. }
  208. }
  209. Wrap = 0;
  210. if (G_projection() == PROJECTION_LL) {
  211. if ((window.west == (window.east - 360.))
  212. || (window.east == (window.west - 360.))) {
  213. Wrap = 1;
  214. ncols += 2;
  215. }
  216. }
  217. /* H = window.ew_res * 4 * 2/ zmult; *//* horizontal (east-west) run
  218. times 4 for weighted difference */
  219. /* V = window.ns_res * 4 * 2/ zmult; *//* vertical (north-south) run
  220. times 4 for weighted difference */
  221. G_begin_distance_calculations();
  222. north = Rast_row_to_northing(0.5, &window);
  223. ns_med = Rast_row_to_northing(1.5, &window);
  224. south = Rast_row_to_northing(2.5, &window);
  225. east = Rast_col_to_easting(2.5, &window);
  226. west = Rast_col_to_easting(0.5, &window);
  227. V = G_distance(east, north, east, south) * 4 * scale / zmult;
  228. H = G_distance(east, ns_med, west, ns_med) * 4 * scale / zmult;
  229. /* ____________________________
  230. |c1 |c2 |c3 |
  231. | | | |
  232. | | north | |
  233. | | | |
  234. |________|________|________|
  235. |c4 |c5 |c6 |
  236. | | | |
  237. | east | ns_med | west |
  238. | | | |
  239. |________|________|________|
  240. |c7 |c8 |c9 |
  241. | | | |
  242. | | south | |
  243. | | | |
  244. |________|________|________|
  245. */
  246. /* open the elevation file for reading */
  247. in_fd = Rast_open_old(elev_name, "");
  248. elev_cell[0] = (DCELL *) G_calloc(ncols + 1, sizeof(DCELL));
  249. Rast_set_d_null_value(elev_cell[0], ncols);
  250. elev_cell[1] = (DCELL *) G_calloc(ncols, sizeof(DCELL));
  251. Rast_set_d_null_value(elev_cell[1], ncols);
  252. elev_cell[2] = (DCELL *) G_calloc(ncols, sizeof(DCELL));
  253. Rast_set_d_null_value(elev_cell[2], ncols);
  254. out_fd = Rast_open_new(sr_name, out_type);
  255. out_rast = Rast_allocate_buf(out_type);
  256. Rast_set_null_value(out_rast, Rast_window_cols(), out_type);
  257. Rast_put_row(out_fd, out_rast, out_type);
  258. out_size = Rast_cell_size(out_type);
  259. if (Wrap) {
  260. Rast_get_d_row_nomask(in_fd, elev_cell[1] + 1, 0);
  261. elev_cell[1][0] = elev_cell[1][Rast_window_cols() - 1];
  262. elev_cell[1][Rast_window_cols() + 1] = elev_cell[1][2];
  263. }
  264. else
  265. Rast_get_d_row_nomask(in_fd, elev_cell[1], 0);
  266. if (Wrap) {
  267. Rast_get_d_row_nomask(in_fd, elev_cell[2] + 1, 1);
  268. elev_cell[2][0] = elev_cell[2][Rast_window_cols() - 1];
  269. elev_cell[2][Rast_window_cols() + 1] = elev_cell[2][2];
  270. }
  271. else
  272. Rast_get_d_row_nomask(in_fd, elev_cell[2], 1);
  273. G_verbose_message(_("Percent complete..."));
  274. for (row = 2; row < nrows; row++) {
  275. /* if projection is Lat/Lon, recalculate V and H */
  276. if (G_projection() == PROJECTION_LL) {
  277. north = Rast_row_to_northing((row - 2 + 0.5), &window);
  278. ns_med = Rast_row_to_northing((row - 1 + 0.5), &window);
  279. south = Rast_row_to_northing((row + 0.5), &window);
  280. east = Rast_col_to_easting(2.5, &window);
  281. west = Rast_col_to_easting(0.5, &window);
  282. V = G_distance(east, north, east, south) * 4 * scale / zmult;
  283. H = G_distance(east, ns_med, west, ns_med) * 4 * scale / zmult;
  284. /* ____________________________
  285. |c1 |c2 |c3 |
  286. | | | |
  287. | | north | |
  288. | | | |
  289. |________|________|________|
  290. |c4 |c5 |c6 |
  291. | | | |
  292. | east | ns_med | west |
  293. | | | |
  294. |________|________|________|
  295. |c7 |c8 |c9 |
  296. | | | |
  297. | | south | |
  298. | | | |
  299. |________|________|________|
  300. */
  301. }
  302. G_percent(row, nrows, 2);
  303. temp = elev_cell[0];
  304. elev_cell[0] = elev_cell[1];
  305. elev_cell[1] = elev_cell[2];
  306. elev_cell[2] = temp;
  307. if (Wrap) {
  308. Rast_get_d_row_nomask(in_fd, elev_cell[2] + 1, row);
  309. elev_cell[2][0] = elev_cell[2][Rast_window_cols() - 1];
  310. elev_cell[2][Rast_window_cols() + 1] = elev_cell[2][2];
  311. }
  312. else
  313. Rast_get_d_row_nomask(in_fd, elev_cell[2], row);
  314. c1 = elev_cell[0];
  315. c2 = c1 + 1;
  316. c3 = c1 + 2;
  317. c4 = elev_cell[1];
  318. c5 = c4 + 1;
  319. c6 = c4 + 2;
  320. c7 = elev_cell[2];
  321. c8 = c7 + 1;
  322. c9 = c7 + 2;
  323. if (Wrap)
  324. out_ptr = out_rast;
  325. else
  326. out_ptr = G_incr_void_ptr(out_rast, out_size);
  327. /*skip first cell of the row */
  328. for (col = ncols - 2; col-- > 0;
  329. c1++, c2++, c3++, c4++, c5++, c6++, c7++, c8++, c9++) {
  330. /* DEBUG:
  331. fprintf(stdout, "\n%.0f %.0f %.0f\n%.0f %.0f %.0f\n%.0f %.0f %.0f\n",
  332. *c1, *c2, *c3, *c4, *c5, *c6, *c7, *c8, *c9);
  333. */
  334. if (Rast_is_d_null_value(c1) || Rast_is_d_null_value(c2) ||
  335. Rast_is_d_null_value(c3) || Rast_is_d_null_value(c4) ||
  336. Rast_is_d_null_value(c5) || Rast_is_d_null_value(c6) ||
  337. Rast_is_d_null_value(c7) || Rast_is_d_null_value(c8) ||
  338. Rast_is_d_null_value(c9)) {
  339. Rast_set_null_value(out_ptr, 1, out_type);
  340. out_ptr = G_incr_void_ptr(out_ptr, out_size);
  341. continue;
  342. } /* no data */
  343. /* shaded relief */
  344. /* slope */
  345. dx = (*c1 + 2 * *c4 + *c7 - *c3 - 2 * *c6 - *c9) / H;
  346. dy = (*c1 + 2 * *c2 + *c3 - *c7 - 2 * *c8 - *c9) / V;
  347. key = dx * dx + dy * dy;
  348. slp_in_rad = M_PI / 2. - atan(sqrt(key));
  349. /* aspect */
  350. aspect = atan2(dy, dx);
  351. if (aspect != aspect)
  352. aspect = degrees_to_radians;
  353. if (dx != 0 || dy != 0) {
  354. if (aspect == 0)
  355. aspect = 2 * M_PI;
  356. }
  357. #if 0
  358. /* the original script was rounding aspect. Why? */
  359. aspect *= radians_to_degrees;
  360. if (aspect < 0)
  361. aspect = (int)(aspect - 0.5);
  362. else
  363. aspect = (int)(aspect + 0.5);
  364. aspect *= degrees_to_radians;
  365. #endif
  366. /* shaded relief */
  367. cang = sin(altitude) * sin(slp_in_rad) +
  368. cos(altitude) * cos(slp_in_rad) * cos(azimuth - aspect);
  369. Rast_set_d_value(out_ptr, (DCELL) 255 * cang, out_type);
  370. out_ptr = G_incr_void_ptr(out_ptr, out_size);
  371. } /* column for loop */
  372. Rast_put_row(out_fd, out_rast, out_type);
  373. } /* row loop */
  374. G_percent(row, nrows, 2);
  375. Rast_close(in_fd);
  376. Rast_set_null_value(out_rast, Rast_window_cols(), out_type);
  377. Rast_put_row(out_fd, out_rast, out_type);
  378. Rast_close(out_fd);
  379. G_debug(1, "Creating support files...");
  380. /* write colors for shaded relief */
  381. Rast_init_colors(&colors);
  382. Rast_read_fp_range(sr_name, G_mapset(), &range);
  383. Rast_get_fp_range_min_max(&range, &min, &max);
  384. min -= 0.01;
  385. max += 0.01;
  386. Rast_make_grey_scale_fp_colors(&colors, min, max);
  387. Rast_write_colors(sr_name, G_mapset(), &colors);
  388. sprintf(buf, "Shaded relief of \"%s\"", elev_name);
  389. Rast_put_cell_title(sr_name, buf);
  390. /* writing history file */
  391. Rast_short_history(sr_name, "raster", &hist);
  392. Rast_append_format_history(&hist, "r.relief settings:");
  393. Rast_append_format_history(&hist,
  394. "altitude=%f azimuth=%f zmult=%f scale=%f",
  395. altitude * radians_to_degrees,
  396. azimuth * radians_to_degrees,
  397. zmult, scale);
  398. Rast_format_history(&hist, HIST_DATSRC_1, "raster elevation file %s", elev_name);
  399. Rast_command_history(&hist);
  400. Rast_write_history(sr_name, &hist);
  401. G_message(_("Shaded relief raster map <%s> complete"), sr_name);
  402. exit(EXIT_SUCCESS);
  403. }