main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. /****************************************************************************
  2. *
  3. * MODULE: r.sunmask
  4. * AUTHOR(S): Janne Soimasuo, Finland 1994 (original contributor)
  5. * update to FP by Huidae Cho <grass4u gmail.com> 2001
  6. * added solpos algorithm feature by Markus Neteler 2001
  7. * Brad Douglas <rez touchofmadness.com>, Glynn Clements <glynn gclements.plus.com>,
  8. * Hamish Bowman <hamish_b yahoo.com>, Paul Kelly <paul-grass stjohnspoint.co.uk>
  9. * PURPOSE:
  10. * COPYRIGHT: (C) 1999-2013 by the GRASS Development Team
  11. *
  12. * This program is free software under the GNU General Public
  13. * License (>=v2). Read the file COPYING that comes with GRASS
  14. * for details.
  15. *
  16. *****************************************************************************/
  17. /*
  18. * r.sunmask:
  19. * Calculates the cast shadow areas from a DEM
  20. *
  21. * input: DEM (int, float, double)
  22. * output: binary shadow map
  23. * no shadow: null()
  24. * shadow: 1
  25. *
  26. * Algorithm source: unknown (Janne Soimasuo?)
  27. * Original module author: Janne Soimasuo, Finland 1994
  28. *
  29. * GPL >= 2
  30. *
  31. **********************
  32. * Added solpol sun position calculation:
  33. * Markus Neteler 4/2001
  34. *
  35. **********************
  36. * MN 2/2001: attempt to update to FP
  37. * Huidae Cho 3/2001: FP update done
  38. * but it's somewhat slow with non-CELL maps
  39. *********************************************************************/
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <math.h>
  44. #include <grass/gis.h>
  45. #include <grass/raster.h>
  46. #include <grass/glocale.h>
  47. #include "global.h"
  48. #include "solpos00.h"
  49. float asol, phi0, sun_zenith, sun_azimuth; /* from nadir, from north */
  50. int sunset;
  51. /* to be displayed in r.sunmask */
  52. static char *SOLPOSVERSION = "11 April 2001";
  53. extern struct posdata pd, *pdat; /* declare a posdata struct and a pointer for
  54. it (if desired, the structure could be
  55. allocated dynamically with G_malloc) */
  56. struct Cell_head window;
  57. union RASTER_PTR
  58. {
  59. void *v;
  60. CELL *c;
  61. FCELL *f;
  62. DCELL *d;
  63. };
  64. #ifdef RASTER_VALUE_FUNC
  65. double raster_value(union RASTER_PTR buf, int data_type, int col);
  66. #else
  67. #define raster_value(buf, data_type, col) ((double)(data_type == CELL_TYPE ? buf.c[col] : (data_type == FCELL_TYPE ? buf.f[col] : buf.d[col])))
  68. #endif
  69. int main(int argc, char *argv[])
  70. {
  71. extern struct Cell_head window;
  72. union RASTER_PTR elevbuf, tmpbuf, outbuf;
  73. CELL min, max;
  74. DCELL dvalue, dvalue2, dmin, dmax;
  75. struct History hist;
  76. RASTER_MAP_TYPE data_type;
  77. struct Range range;
  78. struct FPRange fprange;
  79. double drow, dcol;
  80. int elev_fd, output_fd, zeros;
  81. struct
  82. {
  83. struct Option *opt1, *opt2, *opt3, *opt4, *north, *east, *year,
  84. *month, *day, *hour, *minutes, *seconds, *timezone;
  85. } parm;
  86. struct Flag *flag1, *flag3, *flag4;
  87. struct GModule *module;
  88. char *name, *outname;
  89. double dazi, dalti;
  90. double azi, alti;
  91. double nstep, estep;
  92. double maxh;
  93. double east, east1, north, north1;
  94. int row1, col1;
  95. char OK;
  96. double timezone;
  97. int year, month, day, hour, minutes, seconds;
  98. long retval;
  99. int solparms, locparms, use_solpos;
  100. double sunrise, sunset, current_time;
  101. int sretr = 0, ssetr = 0, sretr_sec = 0, ssetr_sec = 0;
  102. double dsretr, dssetr;
  103. G_gisinit(argv[0]);
  104. module = G_define_module();
  105. G_add_keyword(_("raster"));
  106. G_add_keyword(_("solar"));
  107. G_add_keyword(_("sun position"));
  108. G_add_keyword(_("shadow"));
  109. module->label = _("Calculates cast shadow areas from sun position and elevation raster map.");
  110. module->description = _("Either exact sun position (A) is specified, or date/time to calculate "
  111. "the sun position (B) by r.sunmask itself.");
  112. parm.opt1 = G_define_standard_option(G_OPT_R_ELEV);
  113. parm.opt2 = G_define_standard_option(G_OPT_R_OUTPUT);
  114. parm.opt2->required = NO;
  115. parm.opt3 = G_define_option();
  116. parm.opt3->key = "altitude";
  117. parm.opt3->type = TYPE_DOUBLE;
  118. parm.opt3->required = NO;
  119. parm.opt3->options = "0-89.999";
  120. parm.opt3->description =
  121. _("Altitude of the sun in degrees above the horizon (A)");
  122. parm.opt3->guisection = _("Position");
  123. parm.opt4 = G_define_option();
  124. parm.opt4->key = "azimuth";
  125. parm.opt4->type = TYPE_DOUBLE;
  126. parm.opt4->required = NO;
  127. parm.opt4->options = "0-360";
  128. parm.opt4->description =
  129. _("Azimuth of the sun in degrees from north (A)");
  130. parm.opt4->guisection = _("Position");
  131. parm.year = G_define_option();
  132. parm.year->key = "year";
  133. parm.year->type = TYPE_INTEGER;
  134. parm.year->required = NO;
  135. parm.year->description = _("Year (B)");
  136. parm.year->options = "1950-2050";
  137. parm.year->guisection = _("Time");
  138. parm.month = G_define_option();
  139. parm.month->key = "month";
  140. parm.month->type = TYPE_INTEGER;
  141. parm.month->required = NO;
  142. parm.month->description = _("Month (B)");
  143. parm.month->options = "0-12";
  144. parm.month->guisection = _("Time");
  145. parm.day = G_define_option();
  146. parm.day->key = "day";
  147. parm.day->type = TYPE_INTEGER;
  148. parm.day->required = NO;
  149. parm.day->description = _("Day (B)");
  150. parm.day->options = "0-31";
  151. parm.day->guisection = _("Time");
  152. parm.hour = G_define_option();
  153. parm.hour->key = "hour";
  154. parm.hour->type = TYPE_INTEGER;
  155. parm.hour->required = NO;
  156. parm.hour->description = _("Hour (B)");
  157. parm.hour->options = "0-24";
  158. parm.hour->guisection = _("Time");
  159. parm.minutes = G_define_option();
  160. parm.minutes->key = "minute";
  161. parm.minutes->type = TYPE_INTEGER;
  162. parm.minutes->required = NO;
  163. parm.minutes->description = _("Minutes (B)");
  164. parm.minutes->options = "0-60";
  165. parm.minutes->guisection = _("Time");
  166. parm.seconds = G_define_option();
  167. parm.seconds->key = "second";
  168. parm.seconds->type = TYPE_INTEGER;
  169. parm.seconds->required = NO;
  170. parm.seconds->description = _("Seconds (B)");
  171. parm.seconds->options = "0-60";
  172. parm.seconds->answer = "0";
  173. parm.seconds->guisection = _("Time");
  174. parm.timezone = G_define_option();
  175. parm.timezone->key = "timezone";
  176. parm.timezone->type = TYPE_INTEGER;
  177. parm.timezone->required = NO;
  178. parm.timezone->label =
  179. _("Timezone");
  180. parm.timezone->description = _("East positive, offset from GMT, also use to adjust daylight savings");
  181. parm.timezone->guisection = _("Time");
  182. parm.east = G_define_option();
  183. parm.east->key = "east";
  184. parm.east->key_desc = "value";
  185. parm.east->type = TYPE_STRING;
  186. parm.east->required = NO;
  187. parm.east->label =
  188. _("Easting coordinate (point of interest)");
  189. parm.east->description = _("Default: map center");
  190. parm.east->guisection = _("Position");
  191. parm.north = G_define_option();
  192. parm.north->key = "north";
  193. parm.north->key_desc = "value";
  194. parm.north->type = TYPE_STRING;
  195. parm.north->required = NO;
  196. parm.north->label =
  197. _("Northing coordinate (point of interest)");
  198. parm.north->description = _("Default: map center");
  199. parm.north->guisection = _("Position");
  200. flag1 = G_define_flag();
  201. flag1->key = 'z';
  202. flag1->description = _("Do not ignore zero elevation");
  203. flag3 = G_define_flag();
  204. flag3->key = 's';
  205. flag3->description = _("Calculate sun position only and exit");
  206. flag3->guisection = _("Print");
  207. flag4 = G_define_flag();
  208. flag4->key = 'g';
  209. flag4->description =
  210. _("Print the sun position output in shell script style");
  211. flag4->guisection = _("Print");
  212. if (G_parser(argc, argv))
  213. exit(EXIT_FAILURE);
  214. zeros = flag1->answer;
  215. G_get_window(&window);
  216. /* if not given, get east and north: XX */
  217. if (!parm.north->answer || !parm.east->answer) {
  218. north = (window.north - window.south) / 2. + window.south;
  219. east = (window.west - window.east) / 2. + window.east;
  220. G_message(_("Using map center coordinates: %f %f"), east, north);
  221. }
  222. else { /* user defined east, north: */
  223. sscanf(parm.north->answer, "%lf", &north);
  224. sscanf(parm.east->answer, "%lf", &east);
  225. if (strlen(parm.east->answer) == 0)
  226. G_fatal_error(_("Empty east coordinate specified"));
  227. if (strlen(parm.north->answer) == 0)
  228. G_fatal_error(_("Empty north coordinate specified"));
  229. }
  230. /* check which method to use for sun position:
  231. either user defines directly sun position or it is calculated */
  232. if (parm.opt3->answer && parm.opt4->answer)
  233. solparms = 1; /* opt3 & opt4 complete */
  234. else
  235. solparms = 0; /* calculate sun position */
  236. if (parm.year->answer && parm.month->answer && parm.day->answer &&
  237. parm.hour->answer && parm.minutes->answer && parm.seconds->answer &&
  238. parm.timezone->answer)
  239. locparms = 1; /* complete */
  240. else
  241. locparms = 0;
  242. if (solparms && locparms) /* both defined */
  243. G_fatal_error(_("Either define sun position or location/date/time parameters"));
  244. if (!solparms && !locparms) /* nothing defined */
  245. G_fatal_error(_("Neither sun position nor east/north, date/time/timezone definition are complete"));
  246. /* if here, one definition was complete */
  247. if (locparms) {
  248. G_message(_("Calculating sun position... (using solpos (V. %s) from NREL)"),
  249. SOLPOSVERSION);
  250. use_solpos = 1;
  251. }
  252. else {
  253. G_message(_("Using user defined sun azimuth, altitude settings (ignoring eventual other values)"));
  254. use_solpos = 0;
  255. }
  256. name = parm.opt1->answer;
  257. outname = parm.opt2->answer;
  258. if (!use_solpos) {
  259. sscanf(parm.opt3->answer, "%lf", &dalti);
  260. sscanf(parm.opt4->answer, "%lf", &dazi);
  261. }
  262. else {
  263. sscanf(parm.year->answer, "%d", &year);
  264. sscanf(parm.month->answer, "%d", &month);
  265. sscanf(parm.day->answer, "%d", &day);
  266. sscanf(parm.hour->answer, "%d", &hour);
  267. sscanf(parm.minutes->answer, "%d", &minutes);
  268. sscanf(parm.seconds->answer, "%d", &seconds);
  269. sscanf(parm.timezone->answer, "%lf", &timezone);
  270. }
  271. /* NOTES: G_calc_solar_position ()
  272. - the algorithm will compensate for leap year.
  273. - longitude, latitude: decimal degree
  274. - timezone: DO NOT ADJUST FOR DAYLIGHT SAVINGS TIME.
  275. - timezone: negative for zones west of Greenwich
  276. - lat/long: east and north positive
  277. - the atmospheric refraction is calculated for 1013hPa, 15 degC
  278. - time: local time from your watch
  279. Order of parameters:
  280. long, lat, timezone, year, month, day, hour, minutes, seconds
  281. */
  282. if (use_solpos) {
  283. G_debug(3, "\nlat:%f long:%f", north, east);
  284. retval =
  285. calc_solar_position(east, north, timezone, year, month, day,
  286. hour, minutes, seconds);
  287. /* Remove +0.5 above if you want round-down instead of round-to-nearest */
  288. sretr = (int)floor(pdat->sretr); /* sunrise */
  289. dsretr = pdat->sretr;
  290. sretr_sec =
  291. (int)
  292. floor(((dsretr - floor(dsretr)) * 60 -
  293. floor((dsretr - floor(dsretr)) * 60)) * 60);
  294. ssetr = (int)floor(pdat->ssetr); /* sunset */
  295. dssetr = pdat->ssetr;
  296. ssetr_sec =
  297. (int)
  298. floor(((dssetr - floor(dssetr)) * 60 -
  299. floor((dssetr - floor(dssetr)) * 60)) * 60);
  300. /* print the results */
  301. if (retval == 0) { /* error check */
  302. if (flag3->answer) {
  303. if (flag4->answer) {
  304. fprintf(stdout, "date=%d/%02d/%02d\n", pdat->year,
  305. pdat->month, pdat->day);
  306. fprintf(stdout, "daynum=%d\n", pdat->daynum);
  307. fprintf(stdout, "time=%02i:%02i:%02i\n", pdat->hour,
  308. pdat->minute, pdat->second);
  309. fprintf(stdout, "decimaltime=%f\n",
  310. pdat->hour + (pdat->minute * 100.0 / 60.0 +
  311. pdat->second * 100.0 / 3600.0) /
  312. 100.);
  313. fprintf(stdout, "longitudine=%f\n", pdat->longitude);
  314. fprintf(stdout, "latitude=%f\n", pdat->latitude);
  315. fprintf(stdout, "timezone=%f\n", pdat->timezone);
  316. fprintf(stdout, "sunazimuth=%f\n", pdat->azim);
  317. fprintf(stdout, "sunangleabovehorizon=%f\n",
  318. pdat->elevref);
  319. if (sretr / 60 <= 24) {
  320. fprintf(stdout, "sunrise=%02d:%02d:%02d\n",
  321. sretr / 60, sretr % 60, sretr_sec);
  322. fprintf(stdout, "sunset=%02d:%02d:%02d\n", ssetr / 60,
  323. ssetr % 60, ssetr_sec);
  324. }
  325. }
  326. else {
  327. fprintf(stdout, "%d/%02d/%02d, daynum: %d, time: %02i:%02i:%02i (decimal time: %f)\n",
  328. pdat->year, pdat->month, pdat->day,
  329. pdat->daynum, pdat->hour, pdat->minute,
  330. pdat->second,
  331. pdat->hour + (pdat->minute * 100.0 / 60.0 +
  332. pdat->second * 100.0 / 3600.0) /
  333. 100.);
  334. fprintf(stdout, "long: %f, lat: %f, timezone: %f\n",
  335. pdat->longitude, pdat->latitude,
  336. pdat->timezone);
  337. fprintf(stdout, "Solar position: sun azimuth: %f, sun angle above horz. (refraction corrected): %f\n",
  338. pdat->azim, pdat->elevref);
  339. if (sretr / 60 <= 24) {
  340. fprintf(stdout, "Sunrise time (without refraction): %02d:%02d:%02d\n",
  341. sretr / 60, sretr % 60, sretr_sec);
  342. fprintf(stdout, "Sunset time (without refraction): %02d:%02d:%02d\n",
  343. ssetr / 60, ssetr % 60, ssetr_sec);
  344. }
  345. }
  346. }
  347. sunrise = pdat->sretr / 60.; /* decimal minutes */
  348. sunset = pdat->ssetr / 60.;
  349. current_time =
  350. pdat->hour + (pdat->minute / 60.) + (pdat->second / 3600.);
  351. }
  352. else /* fatal error in G_calc_solar_position() */
  353. G_fatal_error(_("Please correct settings"));
  354. }
  355. if (use_solpos) {
  356. dalti = pdat->elevref;
  357. dazi = pdat->azim;
  358. } /* otherwise already defined */
  359. /* check sunrise */
  360. if (use_solpos) {
  361. G_debug(3, "current_time:%f sunrise:%f", current_time, sunrise);
  362. if ((current_time < sunrise)) {
  363. if (sretr / 60 <= 24)
  364. G_message(_("Time (%02i:%02i:%02i) is before sunrise (%02d:%02d:%02d)"),
  365. pdat->hour, pdat->minute, pdat->second, sretr / 60,
  366. sretr % 60, sretr_sec);
  367. else
  368. G_message(_("Time (%02i:%02i:%02i) is before sunrise"),
  369. pdat->hour, pdat->minute, pdat->second);
  370. G_warning(_("Nothing to calculate. Please verify settings."));
  371. }
  372. if ((current_time > sunset)) {
  373. if (sretr / 60 <= 24)
  374. G_message(_("Time (%02i:%02i:%02i) is after sunset (%02d:%02d:%02d)"),
  375. pdat->hour, pdat->minute, pdat->second, ssetr / 60,
  376. ssetr % 60, ssetr_sec);
  377. else
  378. G_message(_("Time (%02i:%02i:%02i) is after sunset"),
  379. pdat->hour, pdat->minute, pdat->second);
  380. G_warning(_("Nothing to calculate. Please verify settings."));
  381. }
  382. }
  383. if (flag3->answer && (use_solpos == 1)) { /* we only want the sun position */
  384. exit(EXIT_SUCCESS);
  385. }
  386. else if (flag3->answer && (use_solpos == 0)) {
  387. /* are you joking ? */
  388. G_message(_("You already know the sun position"));
  389. exit(EXIT_SUCCESS);
  390. }
  391. if (!outname)
  392. G_fatal_error(_("Option <%s> required"), parm.opt2->key);
  393. elev_fd = Rast_open_old(name, "");
  394. output_fd = Rast_open_c_new(outname);
  395. data_type = Rast_get_map_type(elev_fd);
  396. elevbuf.v = Rast_allocate_buf(data_type);
  397. tmpbuf.v = Rast_allocate_buf(data_type);
  398. outbuf.v = Rast_allocate_buf(CELL_TYPE); /* binary map */
  399. if (data_type == CELL_TYPE) {
  400. if ((Rast_read_range(name, "", &range)) < 0)
  401. G_fatal_error(_("Unable to open range file for raster map <%s>"), name);
  402. Rast_get_range_min_max(&range, &min, &max);
  403. dmin = (double)min;
  404. dmax = (double)max;
  405. }
  406. else {
  407. Rast_read_fp_range(name, "", &fprange);
  408. Rast_get_fp_range_min_max(&fprange, &dmin, &dmax);
  409. }
  410. azi = 2 * M_PI * dazi / 360;
  411. alti = 2 * M_PI * dalti / 360;
  412. nstep = cos(azi) * window.ns_res;
  413. estep = sin(azi) * window.ew_res;
  414. row1 = 0;
  415. G_message(_("Calculating shadows from DEM..."));
  416. while (row1 < window.rows) {
  417. G_percent(row1, window.rows, 2);
  418. col1 = 0;
  419. drow = -1;
  420. Rast_get_row(elev_fd, elevbuf.v, row1, data_type);
  421. while (col1 < window.cols) {
  422. dvalue = raster_value(elevbuf, data_type, col1);
  423. /* outbuf.c[col1]=1; */
  424. Rast_set_null_value(&outbuf.c[col1], 1, CELL_TYPE);
  425. OK = 1;
  426. east = Rast_col_to_easting(col1 + 0.5, &window);
  427. north = Rast_row_to_northing(row1 + 0.5, &window);
  428. east1 = east;
  429. north1 = north;
  430. if (dvalue == 0.0 && !zeros)
  431. OK = 0;
  432. while (OK == 1)
  433. {
  434. east += estep;
  435. north += nstep;
  436. if (north > window.north || north < window.south
  437. || east > window.east || east < window.west)
  438. OK = 0;
  439. else {
  440. maxh = tan(alti) *
  441. sqrt((north1 - north) * (north1 - north) +
  442. (east1 - east) * (east1 - east));
  443. if ((maxh) > (dmax - dvalue))
  444. OK = 0;
  445. else {
  446. dcol = Rast_easting_to_col(east, &window);
  447. if (drow != Rast_northing_to_row(north, &window)) {
  448. drow = Rast_northing_to_row(north, &window);
  449. Rast_get_row(elev_fd, tmpbuf.v, (int)drow,
  450. data_type);
  451. }
  452. dvalue2 = raster_value(tmpbuf, data_type, (int)dcol);
  453. if ((dvalue2 - dvalue) > (maxh)) {
  454. OK = 0;
  455. outbuf.c[col1] = 1;
  456. }
  457. }
  458. }
  459. }
  460. G_debug(3, "Analysing col %i", col1);
  461. col1 += 1;
  462. }
  463. G_debug(3, "Writing result row %i of %i", row1, window.rows);
  464. Rast_put_row(output_fd, outbuf.c, CELL_TYPE);
  465. row1 += 1;
  466. }
  467. G_percent(1, 1, 1);
  468. Rast_close(output_fd);
  469. Rast_close(elev_fd);
  470. /* writing history file */
  471. Rast_short_history(outname, "raster", &hist);
  472. Rast_format_history(&hist, HIST_DATSRC_1, "raster elevation map %s", name);
  473. Rast_command_history(&hist);
  474. Rast_write_history(outname, &hist);
  475. exit(EXIT_SUCCESS);
  476. }
  477. #ifdef RASTER_VALUE_FUNC
  478. double raster_value(union RASTER_PTR buf, int data_type, int col)
  479. {
  480. double retval;
  481. switch (data_type) {
  482. case CELL_TYPE:
  483. retval = (double)buf.c[col];
  484. break;
  485. case FCELL_TYPE:
  486. retval = (double)buf.f[col];
  487. break;
  488. case DCELL_TYPE:
  489. retval = (double)buf.d[col];
  490. break;
  491. }
  492. return retval;
  493. }
  494. #endif