viewshed.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /****************************************************************************
  2. *
  3. * MODULE: r.viewshed
  4. *
  5. * AUTHOR(S): Laura Toma, Bowdoin College - ltoma@bowdoin.edu
  6. * Yi Zhuang - yzhuang@bowdoin.edu
  7. *
  8. * Ported to GRASS by William Richard -
  9. * wkrichar@bowdoin.edu or willster3021@gmail.com
  10. * Markus Metz: surface interpolation
  11. *
  12. * Date: april 2011
  13. *
  14. * PURPOSE: To calculate the viewshed (the visible cells in the
  15. * raster) for the given viewpoint (observer) location. The
  16. * visibility model is the following: Two points in the raster are
  17. * considered visible to each other if the cells where they belong are
  18. * visible to each other. Two cells are visible to each other if the
  19. * line-of-sight that connects their centers does not intersect the
  20. * terrain. The terrain is NOT viewed as a tesselation of flat cells,
  21. * i.e. if the line-of-sight does not pass through the cell center,
  22. * elevation is determined using bilinear interpolation.
  23. * The viewshed algorithm is efficient both in
  24. * terms of CPU operations and I/O operations. It has worst-case
  25. * complexity O(n lg n) in the RAM model and O(sort(n)) in the
  26. * I/O-model. For the algorithm and all the other details see the
  27. * paper: "Computing Visibility on * Terrains in External Memory" by
  28. * Herman Haverkort, Laura Toma and Yi Zhuang.
  29. *
  30. * COPYRIGHT: (C) 2008 by the GRASS Development Team
  31. *
  32. * This program is free software under the GNU General Public License
  33. * (>=v2). Read the file COPYING that comes with GRASS for details.
  34. *
  35. ****************************************************************************/
  36. #include <assert.h>
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. extern "C"
  40. {
  41. #include "grass/gis.h"
  42. #include "grass/glocale.h"
  43. }
  44. #include "viewshed.h"
  45. #include "visibility.h"
  46. #include "eventlist.h"
  47. #include "statusstructure.h"
  48. #include "grass.h"
  49. #define VIEWSHEDDEBUG if(0)
  50. #define INMEMORY_DEBUG if(0)
  51. /* ------------------------------------------------------------ */
  52. /* return the memory usage (in bytes) of viewshed */
  53. long long get_viewshed_memory_usage(GridHeader * hd)
  54. {
  55. assert(hd);
  56. /* the output visibility grid */
  57. long long totalcells = (long long)hd->nrows * (long long)hd->ncols;
  58. G_verbose_message(_("rows=%d, cols=%d, total = %lld"), hd->nrows, hd->ncols,
  59. totalcells);
  60. long long gridMemUsage = totalcells * sizeof(float);
  61. G_debug(1, "grid usage=%lld", gridMemUsage);
  62. /* the event array */
  63. long long eventListMemUsage = totalcells * 3 * sizeof(AEvent);
  64. G_debug(1, "memory_usage: eventList=%lld", eventListMemUsage);
  65. /* the double array <data> that stores all the cells in the same row
  66. as the viewpoint */
  67. long long dataMemUsage = (long long)(hd->ncols * sizeof(double));
  68. G_debug(1, "viewshed memory usage: size AEvent=%dB, nevents=%lld, \
  69. total=%lld B (%d MB)", (int)sizeof(AEvent), totalcells * 3,
  70. gridMemUsage + eventListMemUsage + dataMemUsage,
  71. (int)((gridMemUsage + eventListMemUsage + dataMemUsage) >> 20));
  72. return (gridMemUsage + eventListMemUsage + dataMemUsage);
  73. }
  74. /* ------------------------------------------------------------ */
  75. void
  76. print_viewshed_timings(Rtimer initEventTime,
  77. Rtimer sortEventTime, Rtimer sweepTime)
  78. {
  79. char timeused[1000];
  80. G_verbose_message(_("Sweep timings:"));
  81. rt_sprint_safe(timeused, initEventTime);
  82. G_verbose_message("Init events: %s", timeused);
  83. rt_sprint_safe(timeused, sortEventTime);
  84. G_verbose_message("Sort events: %s", timeused);
  85. rt_sprint_safe(timeused, sweepTime);
  86. G_verbose_message("Process events: %s", timeused);
  87. return;
  88. }
  89. /* ------------------------------------------------------------ */
  90. static void print_statusnode(StatusNode sn)
  91. {
  92. G_debug(3, "processing (row=%d, col=%d, dist=%f, grad=%f)",
  93. sn.row, sn.col, sn.dist2vp, sn.gradient[1]);
  94. return;
  95. }
  96. /* ------------------------------------------------------------ */
  97. /* allocates the eventlist array used by kreveled_in_memory; it is
  98. possible that the amount of memory required is more than that
  99. supported by the platform; for e.g. on a 32-bt platform cannot
  100. allocate more than 4GB. Try to detect this situation. */
  101. AEvent *allocate_eventlist(GridHeader * hd)
  102. {
  103. AEvent *eventList;
  104. long long totalsize = hd->ncols * hd->nrows * 3;
  105. totalsize *= sizeof(AEvent);
  106. G_debug(1, "total size of eventlist is %lld B (%d MB); ",
  107. totalsize, (int)(totalsize >> 20));
  108. /* what's the size of size_t on this machine? */
  109. int sizet_size;
  110. sizet_size = (int)sizeof(size_t);
  111. G_debug(1, "size_t is %d B", sizet_size);
  112. if (sizet_size >= 8) {
  113. G_debug(1, "64-bit platform, great.");
  114. }
  115. else {
  116. /* this is the max value of size_t */
  117. long long maxsizet = ((long long)1 << (sizeof(size_t) * 8)) - 1;
  118. G_debug(1, "max size_t is %lld", maxsizet);
  119. /* checking whether allocating totalsize causes an overflow */
  120. if (totalsize > maxsizet) {
  121. G_fatal_error(_("Running the program in-memory mode requires " \
  122. "memory beyond the capability of the platform. " \
  123. "Use external mode, or a 64-bit platform."));
  124. }
  125. }
  126. G_debug(1, "allocating eventList...");
  127. eventList = (AEvent *) G_malloc(totalsize);
  128. assert(eventList);
  129. G_debug(1, "...ok");
  130. return eventList;
  131. }
  132. /*///////////////////////////////////////////////////////////
  133. ------------------------------------------------------------ run
  134. Viewshed's sweep algorithm on the grid stored in the given file, and
  135. with the given viewpoint. Create a visibility grid and return
  136. it. The computation runs in memory, which means the input grid, the
  137. status structure and the output grid are stored in arrays in
  138. memory.
  139. The output: A cell x in the visibility grid is recorded as follows:
  140. if it is NODATA, then x is set to NODATA
  141. if it is invisible, then x is set to INVISIBLE
  142. if it is visible, then x is set to the vertical angle wrt to viewpoint
  143. */
  144. MemoryVisibilityGrid *viewshed_in_memory(char *inputfname, GridHeader * hd,
  145. Viewpoint * vp,
  146. ViewOptions viewOptions)
  147. {
  148. assert(inputfname && hd && vp);
  149. G_verbose_message(_("Start sweeping."));
  150. /* ------------------------------ */
  151. /* create the visibility grid */
  152. MemoryVisibilityGrid *visgrid;
  153. visgrid = create_inmem_visibilitygrid(*hd, *vp);
  154. /* set everything initially invisible */
  155. set_inmem_visibilitygrid(visgrid, INVISIBLE);
  156. assert(visgrid);
  157. G_debug(1, "visibility grid size: %d x %d x %d B (%d MB)",
  158. hd->nrows, hd->ncols, (int)sizeof(float),
  159. (int)(((long long)(hd->nrows * hd->ncols *
  160. sizeof(float))) >> 20));
  161. /* ------------------------------ */
  162. /* construct the event list corresponding to the given input file
  163. and viewpoint; this creates an array of all the cells on the
  164. same row as the viewpoint */
  165. surface_type **data;
  166. size_t nevents;
  167. Rtimer initEventTime;
  168. rt_start(initEventTime);
  169. AEvent *eventList = allocate_eventlist(hd);
  170. nevents = init_event_list_in_memory(eventList, inputfname, vp, hd,
  171. viewOptions, &data, visgrid);
  172. assert(data);
  173. rt_stop(initEventTime);
  174. G_debug(1, "actual nb events is %lu", (long unsigned int)nevents);
  175. /* ------------------------------ */
  176. /*sort the events radially by angle */
  177. Rtimer sortEventTime;
  178. rt_start(sortEventTime);
  179. G_verbose_message(_("Sorting events..."));
  180. fflush(stdout);
  181. /*this is recursive and seg faults for large arrays
  182. //qsort(eventList, nevents, sizeof(AEvent), radial_compare_events);
  183. //this is too slow...
  184. //heapsort(eventList, nevents, sizeof(AEvent), radial_compare_events);
  185. //iostream quicksort */
  186. RadialCompare cmpObj;
  187. quicksort(eventList, nevents, cmpObj);
  188. G_verbose_message(_("Done."));
  189. fflush(stdout);
  190. rt_stop(sortEventTime);
  191. /* ------------------------------ */
  192. /*create the status structure */
  193. StatusList *status_struct = create_status_struct();
  194. /*Put cells that are initially on the sweepline into status structure */
  195. Rtimer sweepTime;
  196. StatusNode sn;
  197. rt_start(sweepTime);
  198. for (dimensionType i = vp->col + 1; i < hd->ncols; i++) {
  199. AEvent e;
  200. double ax, ay;
  201. sn.col = i;
  202. sn.row = vp->row;
  203. e.col = i;
  204. e.row = vp->row;
  205. e.elev[0] = data[0][i];
  206. e.elev[1] = data[1][i];
  207. e.elev[2] = data[2][i];
  208. if (!is_nodata(visgrid->grid->hd, data[1][i]) &&
  209. !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
  210. viewOptions.maxDist)) {
  211. /*calculate Distance to VP and Gradient, store them into sn */
  212. /* need either 3 elevation values or
  213. * 3 gradients calculated from 3 elevation values */
  214. /* need also 3 angles */
  215. e.eventType = ENTERING_EVENT;
  216. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  217. sn.angle[0] = calculate_angle(ax, ay, vp->col, vp->row);
  218. calculate_event_gradient(&sn, 0, ay, ax, e.elev[0], vp, *hd);
  219. e.eventType = CENTER_EVENT;
  220. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  221. sn.angle[1] = calculate_angle(ax, ay, vp->col, vp->row);
  222. calculate_dist_n_gradient(&sn, e.elev[1], vp, *hd);
  223. e.eventType = EXITING_EVENT;
  224. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  225. sn.angle[2] = calculate_angle(ax, ay, vp->col, vp->row);
  226. calculate_event_gradient(&sn, 2, ay, ax, e.elev[2], vp, *hd);
  227. assert(sn.angle[1] == 0);
  228. if (sn.angle[0] > sn.angle[1])
  229. sn.angle[0] -= 2 * M_PI;
  230. G_debug(2, "inserting: ");
  231. print_statusnode(sn);
  232. /*insert sn into the status structure */
  233. insert_into_status_struct(sn, status_struct);
  234. }
  235. }
  236. G_free(data[0]);
  237. G_free(data);
  238. /* ------------------------------ */
  239. /*sweep the event list */
  240. long nvis = 0; /*number of visible cells */
  241. AEvent *e;
  242. G_important_message(_("Computing visibility..."));
  243. G_percent(0, 100, 2);
  244. for (size_t i = 0; i < nevents; i++) {
  245. int perc = (int)(1000000 * i / nevents);
  246. if (perc > 0 && perc < 1000000)
  247. G_percent(perc, 1000000, 1);
  248. /*get out one event at a time and process it according to its type */
  249. e = &(eventList[i]);
  250. sn.col = e->col;
  251. sn.row = e->row;
  252. //sn.elev = e->elev;
  253. /*calculate Distance to VP and Gradient */
  254. calculate_dist_n_gradient(&sn, e->elev[1] + vp->target_offset, vp, *hd);
  255. G_debug(3, "event: ");
  256. print_event(*e, 3);
  257. G_debug(3, "sn.dist=%f, sn.gradient=%f", sn.dist2vp, sn.gradient[1]);
  258. switch (e->eventType) {
  259. case ENTERING_EVENT:
  260. double ax, ay;
  261. /*insert node into structure */
  262. G_debug(3, "..ENTER-EVENT: insert");
  263. /* need either 3 elevation values or
  264. * 3 gradients calculated from 3 elevation values */
  265. /* need also 3 angles */
  266. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  267. //sn.angle[0] = calculate_angle(ax, ay, vp->col, vp->row);
  268. sn.angle[0] = e->angle;
  269. calculate_event_gradient(&sn, 0, ay, ax, e->elev[0], vp, *hd);
  270. e->eventType = CENTER_EVENT;
  271. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  272. sn.angle[1] = calculate_angle(ax, ay, vp->col, vp->row);
  273. calculate_dist_n_gradient(&sn, e->elev[1], vp, *hd);
  274. e->eventType = EXITING_EVENT;
  275. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  276. sn.angle[2] = calculate_angle(ax, ay, vp->col, vp->row);
  277. calculate_event_gradient(&sn, 2, ay, ax, e->elev[2], vp, *hd);
  278. e->eventType = ENTERING_EVENT;
  279. if (e->angle < M_PI) {
  280. if (sn.angle[0] > sn.angle[1])
  281. sn.angle[0] -= 2 * M_PI;
  282. }
  283. else {
  284. if (sn.angle[0] > sn.angle[1]) {
  285. sn.angle[1] += 2 * M_PI;
  286. sn.angle[2] += 2 * M_PI;
  287. }
  288. }
  289. insert_into_status_struct(sn, status_struct);
  290. break;
  291. case EXITING_EVENT:
  292. /*delete node out of status structure */
  293. G_debug(3, "..EXIT-EVENT: delete");
  294. /* need only distance */
  295. delete_from_status_struct(status_struct, sn.dist2vp);
  296. break;
  297. case CENTER_EVENT:
  298. G_debug(3, "..QUERY-EVENT: query");
  299. /*calculate visibility */
  300. double max;
  301. /* consider current angle and gradient */
  302. max =
  303. find_max_gradient_in_status_struct(status_struct, sn.dist2vp,
  304. e->angle, sn.gradient[1]);
  305. /*the point is visible: store its vertical angle */
  306. if (max <= sn.gradient[1]) {
  307. float vert_angle = get_vertical_angle(*vp, sn, e->elev[1] + vp->target_offset,
  308. viewOptions.doCurv);
  309. add_result_to_inmem_visibilitygrid(visgrid, sn.row, sn.col,
  310. vert_angle);
  311. assert(vert_angle >= 0);
  312. /* when you write the visibility grid you assume that
  313. visible values are positive */
  314. nvis++;
  315. }
  316. //else {
  317. /* cell is invisible */
  318. /* the visibility grid is initialized all invisible */
  319. //visgrid->grid->grid_data[sn.row][sn.col] = INVISIBLE;
  320. //}
  321. break;
  322. }
  323. }
  324. rt_stop(sweepTime);
  325. G_percent(1, 1, 1);
  326. G_verbose_message(_("Sweeping done."));
  327. G_verbose_message(_("Total cells %ld, visible cells %ld (%.1f percent)."),
  328. (long)visgrid->grid->hd->nrows * visgrid->grid->hd->ncols,
  329. nvis,
  330. (float)((float)nvis * 100 /
  331. (float)(visgrid->grid->hd->nrows *
  332. visgrid->grid->hd->ncols)));
  333. print_viewshed_timings(initEventTime, sortEventTime, sweepTime);
  334. /*cleanup */
  335. G_free(eventList);
  336. return visgrid;
  337. }
  338. /*///////////////////////////////////////////////////////////
  339. ------------------------------------------------------------
  340. run Viewshed's algorithm on the grid stored in the given file, and
  341. with the given viewpoint. Create a visibility grid and return it. It
  342. runs in external memory, i.e. the input grid and the outpt grid are
  343. stored as streams
  344. */
  345. IOVisibilityGrid *viewshed_external(char *inputfname, GridHeader * hd,
  346. Viewpoint * vp, ViewOptions viewOptions)
  347. {
  348. assert(inputfname && hd && vp);
  349. G_message(_("Start sweeping."));
  350. /* ------------------------------ */
  351. /*initialize the visibility grid */
  352. IOVisibilityGrid *visgrid;
  353. visgrid = init_io_visibilitygrid(*hd, *vp);
  354. /* ------------------------------ */
  355. /* construct the event list corresponding to the give input file and
  356. viewpoint; this creates an array of all the cells on
  357. the same row as the viewpoint */
  358. Rtimer initEventTime, sortEventTime, sweepTime;
  359. AMI_STREAM < AEvent > *eventList;
  360. surface_type **data;
  361. rt_start(initEventTime);
  362. eventList = init_event_list(inputfname, vp, hd, viewOptions,
  363. &data, visgrid);
  364. assert(eventList && data);
  365. eventList->seek(0);
  366. rt_stop(initEventTime);
  367. /*printf("Event stream length: %lu\n", (unsigned long)eventList->stream_len()); */
  368. /* ------------------------------ */
  369. /*sort the events radially by angle */
  370. G_verbose_message(_("Sorting events..."));
  371. rt_start(sortEventTime);
  372. sort_event_list(&eventList);
  373. eventList->seek(0); /*this does not seem to be ensured by sort?? */
  374. rt_stop(sortEventTime);
  375. /* ------------------------------ */
  376. /*create the status structure */
  377. StatusList *status_struct = create_status_struct();
  378. /* Put cells that are initially on the sweepline into status
  379. structure */
  380. StatusNode sn;
  381. G_message(_("Initialize sweepline..."));
  382. rt_start(sweepTime);
  383. for (dimensionType i = vp->col + 1; i < hd->ncols; i++) {
  384. AEvent e;
  385. double ax, ay;
  386. G_percent(i, hd->ncols, 2);
  387. sn.col = i;
  388. sn.row = vp->row;
  389. e.col = i;
  390. e.row = vp->row;
  391. e.elev[0] = data[0][i];
  392. e.elev[1] = data[1][i];
  393. e.elev[2] = data[2][i];
  394. if (!is_nodata(visgrid->hd, data[1][i]) &&
  395. !is_point_outside_max_dist(*vp, *hd, sn.row, sn.col,
  396. viewOptions.maxDist)) {
  397. /*calculate Distance to VP and Gradient, store them into sn */
  398. /* need either 3 elevation values or
  399. * 3 gradients calculated from 3 elevation values */
  400. /* need also 3 angles */
  401. e.eventType = ENTERING_EVENT;
  402. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  403. sn.angle[0] = calculate_angle(ax, ay, vp->col, vp->row);
  404. calculate_event_gradient(&sn, 0, ay, ax, e.elev[0], vp, *hd);
  405. e.eventType = CENTER_EVENT;
  406. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  407. sn.angle[1] = calculate_angle(ax, ay, vp->col, vp->row);
  408. calculate_dist_n_gradient(&sn, e.elev[1], vp, *hd);
  409. e.eventType = EXITING_EVENT;
  410. calculate_event_position(e, vp->row, vp->col, &ay, &ax);
  411. sn.angle[2] = calculate_angle(ax, ay, vp->col, vp->row);
  412. calculate_event_gradient(&sn, 2, ay, ax, e.elev[2], vp, *hd);
  413. assert(sn.angle[1] == 0);
  414. if (sn.angle[0] > sn.angle[1])
  415. sn.angle[0] -= 2 * M_PI;
  416. G_debug(3, "inserting: ");
  417. print_statusnode(sn);
  418. /*insert sn into the status structure */
  419. insert_into_status_struct(sn, status_struct);
  420. }
  421. }
  422. G_percent(hd->ncols, hd->ncols, 2);
  423. G_free(data[0]);
  424. G_free(data);
  425. /* ------------------------------ */
  426. /*sweep the event list */
  427. long nvis = 0; /*number of visible cells */
  428. VisCell viscell;
  429. AEvent *e;
  430. AMI_err ae;
  431. off_t nbEvents = eventList->stream_len();
  432. /*printf("nbEvents = %ld\n", (long) nbEvents); */
  433. G_message(_("Determine visibility..."));
  434. G_percent(0, 100, 2);
  435. for (off_t i = 0; i < nbEvents; i++) {
  436. int perc = (int)(1000000 * i / nbEvents);
  437. if (perc > 0)
  438. G_percent(perc, 1000000, 1);
  439. /*get out one event at a time and process it according to its type */
  440. ae = eventList->read_item(&e);
  441. assert(ae == AMI_ERROR_NO_ERROR);
  442. sn.col = e->col;
  443. sn.row = e->row;
  444. //sn.elev = e->elev;
  445. /*calculate Distance to VP and Gradient */
  446. calculate_dist_n_gradient(&sn, e->elev[1] + vp->target_offset, vp, *hd);
  447. G_debug(3, "next event: ");
  448. print_statusnode(sn);
  449. switch (e->eventType) {
  450. case ENTERING_EVENT:
  451. double ax, ay;
  452. /*insert node into structure */
  453. /* need either 3 elevation values or
  454. * 3 gradients calculated from 3 elevation values */
  455. /* need also 3 angles */
  456. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  457. //sn.angle[0] = calculate_angle(ax, ay, vp->col, vp->row);
  458. sn.angle[0] = e->angle;
  459. calculate_event_gradient(&sn, 0, ay, ax, e->elev[0], vp, *hd);
  460. e->eventType = CENTER_EVENT;
  461. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  462. sn.angle[1] = calculate_angle(ax, ay, vp->col, vp->row);
  463. calculate_dist_n_gradient(&sn, e->elev[1], vp, *hd);
  464. e->eventType = EXITING_EVENT;
  465. calculate_event_position(*e, vp->row, vp->col, &ay, &ax);
  466. sn.angle[2] = calculate_angle(ax, ay, vp->col, vp->row);
  467. calculate_event_gradient(&sn, 2, ay, ax, e->elev[2], vp, *hd);
  468. e->eventType = ENTERING_EVENT;
  469. if (e->angle < M_PI) {
  470. if (sn.angle[0] > sn.angle[1])
  471. sn.angle[0] -= 2 * M_PI;
  472. }
  473. else {
  474. if (sn.angle[0] > sn.angle[1]) {
  475. sn.angle[1] += 2 * M_PI;
  476. sn.angle[2] += 2 * M_PI;
  477. }
  478. }
  479. G_debug(3, "..ENTER-EVENT: insert");
  480. insert_into_status_struct(sn, status_struct);
  481. break;
  482. case EXITING_EVENT:
  483. /*delete node out of status structure */
  484. G_debug(3, "..EXIT-EVENT: delete");
  485. delete_from_status_struct(status_struct, sn.dist2vp);
  486. break;
  487. case CENTER_EVENT:
  488. G_debug(3, "..QUERY-EVENT: query");
  489. /*calculate visibility */
  490. viscell.row = sn.row;
  491. viscell.col = sn.col;
  492. double max;
  493. max =
  494. find_max_gradient_in_status_struct(status_struct, sn.dist2vp,
  495. e->angle, sn.gradient[1]);
  496. /*the point is visible */
  497. if (max <= sn.gradient[1]) {
  498. viscell.angle =
  499. get_vertical_angle(*vp, sn, e->elev[1] + vp->target_offset, viewOptions.doCurv);
  500. assert(viscell.angle >= 0);
  501. /* viscell.vis = VISIBLE; */
  502. add_result_to_io_visibilitygrid(visgrid, &viscell);
  503. nvis++;
  504. }
  505. else {
  506. /* else the cell is invisible; we do not write it to the
  507. visibility stream because we only record visible cells, and
  508. nodata cells; */
  509. /* viscell.vis = INVISIBLE; */
  510. /* add_result_to_io_visibilitygrid(visgrid, &viscell); */
  511. }
  512. break;
  513. }
  514. } /* for each event */
  515. rt_stop(sweepTime);
  516. G_percent(1, 1, 1);
  517. G_message(_("Sweeping done."));
  518. G_verbose_message(_("Total cells %ld, visible cells %ld (%.1f percent)."),
  519. (long)visgrid->hd->nrows * visgrid->hd->ncols,
  520. nvis,
  521. (float)((float)nvis * 100 /
  522. (float)(visgrid->hd->nrows * visgrid->hd->ncols)));
  523. print_viewshed_timings(initEventTime, sortEventTime, sweepTime);
  524. /*cleanup */
  525. delete eventList;
  526. return visgrid;
  527. }