main.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /****************************************************************************
  2. *
  3. * MODULE: r.spread
  4. * AUTHOR(S): Jianping Xu, 1995 (original contributor)
  5. * Center for Remote Sensing and Spatial Analysis (CRSSA)
  6. * Rutgers University.
  7. * Andreas.Lange <andreas.lange rhein-main.de>, Eric G. Miller <egm2 jps.net>,
  8. * Markus Neteler <neteler itc.it>, Roberto Flor <flor itc.it>,
  9. * Brad Douglas <rez touchofmadness.com>,
  10. * Glynn Clements <glynn gclements.plus.com>, Jachym Cepicky <jachym les-ejk.cz>
  11. * PURPOSE:
  12. * This is the main program for simulating elliptical spread.
  13. *
  14. * It
  15. * 1) determines the earliest time a phenomenon REACHES to a
  16. * map cell, NOT the time that cell is EXHAUSTED.
  17. * 3) If a cell is spread barrier, a no-data value is assigned
  18. * to it.
  19. * COPYRIGHT: (C) 2000-2006 by the GRASS Development Team
  20. *
  21. * This program is free software under the GNU General Public
  22. * License (>=v2). Read the file COPYING that comes with GRASS
  23. * for details.
  24. *
  25. *****************************************************************************/
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <math.h>
  29. #include <sys/types.h>
  30. #include <unistd.h>
  31. #include <grass/gis.h>
  32. #include <grass/raster.h>
  33. #include <grass/glocale.h>
  34. #include "cmd_line.h"
  35. #include "costHa.h"
  36. #include "cell_ptrHa.h"
  37. #include "local_proto.h"
  38. #define DATA(map, r, c) (map)[(r) * ncols + (c)]
  39. CELL *cell;
  40. CELL *x_cell;
  41. CELL *y_cell;
  42. CELL *map_max;
  43. CELL *map_dir;
  44. CELL *map_base;
  45. CELL *map_spotdist;
  46. CELL *map_velocity;
  47. CELL *map_mois;
  48. float *map_out;
  49. CELL *map_x_out;
  50. CELL *map_y_out;
  51. CELL *map_visit;
  52. char buf[400];
  53. float zero = 0.0;
  54. float neg = -2.0;
  55. int BARRIER = 0;
  56. int max_fd, dir_fd, base_fd, start_fd;
  57. int spotdist_fd, velocity_fd, mois_fd;
  58. int cum_fd, x_fd, y_fd;
  59. int nrows, ncols;
  60. long heap_len;
  61. struct Cell_head window;
  62. struct costHa *heap;
  63. int main(int argc, char *argv[])
  64. {
  65. int col, row;
  66. /* to menage start (source) raster map */
  67. struct Range start_range;
  68. CELL start_range_min, start_range_max;
  69. int start_is_time; /* 0 or 1 */
  70. struct
  71. {
  72. struct Option *max, *dir, *base, *start,
  73. *spotdist, *velocity, *mois,
  74. *least, *comp_dens, *init_time,
  75. *time_lag, *backdrop, *out, *x_out, *y_out;
  76. } parm;
  77. struct
  78. {
  79. /* please, remove display before GRASS 7 released */
  80. struct Flag *display, *spotting, *start_is_time;
  81. } flag;
  82. struct GModule *module;
  83. /* initialize access to database and create temporary files */
  84. G_gisinit(argv[0]);
  85. /* Set description */
  86. module = G_define_module();
  87. G_add_keyword(_("raster"));
  88. G_add_keyword(_("fire"));
  89. G_add_keyword(_("spread"));
  90. G_add_keyword(_("hazard"));
  91. G_add_keyword(_("model"));
  92. module->label =
  93. _("Simulates elliptically anisotropic spread.");
  94. module->description =
  95. _("Generates a raster map of the cumulative time of spread, "
  96. "given raster maps containing the rates of spread (ROS), "
  97. "the ROS directions and the spread origins. "
  98. "It optionally produces raster maps to contain backlink UTM "
  99. "coordinates for tracing spread paths. "
  100. "Usable for fire spread simulations.");
  101. parm.base = G_define_option();
  102. parm.base->key = "base_ros";
  103. parm.base->type = TYPE_STRING;
  104. parm.base->required = YES;
  105. parm.base->gisprompt = "old,cell,raster";
  106. parm.base->guisection = _("Input");
  107. parm.base->label =
  108. _("Raster map containing base ROS (cm/min)");
  109. parm.base->description =
  110. _("Name of an existing raster map layer in the user's "
  111. "current mapset search path containing the ROS values in the directions "
  112. "perpendicular to maximum ROSes' (cm/minute). These ROSes are also the ones "
  113. "without the effect of directional factors.");
  114. parm.max = G_define_option();
  115. parm.max->key = "max_ros";
  116. parm.max->type = TYPE_STRING;
  117. parm.max->required = YES;
  118. parm.max->gisprompt = "old,cell,raster";
  119. parm.max->guisection = _("Input");
  120. parm.max->label =
  121. _("Raster map containing maximal ROS (cm/min)");
  122. parm.max->description =
  123. _("Name of an existing raster map layer in the user's current "
  124. "mapset search path containing the maximum ROS values (cm/minute).");
  125. parm.dir = G_define_option();
  126. parm.dir->key = "direction_ros";
  127. parm.dir->type = TYPE_STRING;
  128. parm.dir->required = YES;
  129. parm.dir->gisprompt = "old,cell,raster";
  130. parm.dir->guisection = _("Input");
  131. parm.dir->label =
  132. _("Raster map containing directions of maximal ROS (degree)");
  133. parm.dir->description =
  134. _("Name of an existing raster map layer in the user's "
  135. "current mapset search path containing directions of the maximum ROSes, "
  136. "clockwise from north (degree)."); /* TODO: clockwise from north? see r.ros */
  137. parm.start = G_define_option();
  138. parm.start->key = "start";
  139. parm.start->type = TYPE_STRING;
  140. parm.start->required = YES;
  141. parm.start->gisprompt = "old,cell,raster";
  142. parm.start->guisection = _("Input");
  143. parm.start->label =
  144. _("Raster map containing starting sources");
  145. parm.start->description =
  146. _("Name of an existing raster map layer in the "
  147. "user's current mapset search path containing starting locations of the "
  148. "spread phenomenon. Any positive integers in this map are recognized as "
  149. "starting sources (seeds).");
  150. parm.spotdist = G_define_option();
  151. parm.spotdist->key = "spotting_distance";
  152. parm.spotdist->type = TYPE_STRING;
  153. parm.spotdist->gisprompt = "old,cell,raster";
  154. parm.spotdist->guisection = _("Input");
  155. parm.spotdist->label =
  156. _("Raster map containing maximal spotting distance (m, required with -s)");
  157. parm.spotdist->description =
  158. _("Name of an existing raster map layer in "
  159. "the user's current mapset search path containing the maximum potential "
  160. "spotting distances (meters).");
  161. parm.velocity = G_define_option();
  162. parm.velocity->key = "wind_speed";
  163. parm.velocity->type = TYPE_STRING;
  164. parm.velocity->gisprompt = "old,cell,raster";
  165. parm.velocity->guisection = _("Input");
  166. parm.velocity->label =
  167. _("Raster map containing midflame wind speed (ft/min, required with -s)");
  168. parm.velocity->description =
  169. _("Name of an existing raster map layer in the "
  170. "user's current mapset search path containing wind velocities at half of "
  171. "the average flame height (feet/minute).");
  172. parm.mois = G_define_option();
  173. parm.mois->key = "fuel_moisture";
  174. parm.mois->type = TYPE_STRING;
  175. parm.mois->gisprompt = "old,cell,raster";
  176. parm.mois->guisection = _("Input");
  177. parm.mois->label =
  178. _("Raster map containing fine fuel moisture of the cell receiving a spotting firebrand (%, required with -s)");
  179. parm.mois->description =
  180. _("Name of an existing raster map layer in the "
  181. "user's current mapset search path containing the 1-hour (<.25\") fuel "
  182. "moisture (percentage content multiplied by 100).");
  183. parm.least = G_define_option();
  184. parm.least->key = "least_size";
  185. parm.least->type = TYPE_STRING;
  186. parm.least->key_desc = "odd int";
  187. parm.least->options = "3,5,7,9,11,13,15";
  188. parm.least->label =
  189. _("Basic sampling window size needed to meet certain accuracy (3)"); /* TODO: what is 3 here? default? */
  190. parm.least->description =
  191. _("An odd integer ranging 3 - 15 indicating "
  192. "the basic sampling window size within which all cells will be considered "
  193. "to see whether they will be reached by the current spread cell. The default "
  194. "number is 3 which means a 3x3 window.");
  195. parm.comp_dens = G_define_option();
  196. parm.comp_dens->key = "comp_dens";
  197. parm.comp_dens->type = TYPE_STRING;
  198. parm.comp_dens->key_desc = "decimal";
  199. parm.comp_dens->label =
  200. _("Sampling density for additional computing (range: 0.0 - 1.0 (0.5))"); /* TODO: again, what is 0.5?, TODO: range not set */
  201. parm.comp_dens->description =
  202. _("A decimal number ranging 0.0 - 1.0 indicating "
  203. "additional sampling cells will be considered to see whether they will be "
  204. "reached by the current spread cell. The closer to 1.0 the decimal number "
  205. "is, the longer the program will run and the higher the simulation accuracy "
  206. "will be. The default number is 0.5.");
  207. parm.init_time = G_define_option();
  208. parm.init_time->key = "init_time";
  209. parm.init_time->type = TYPE_STRING;
  210. parm.init_time->key_desc = "int (>= 0)"; /* TODO: move to ->options */
  211. parm.init_time->answer = "0";
  212. parm.init_time->label =
  213. _("Initial time for current simulation (0) (min)");
  214. parm.init_time->description =
  215. _("A non-negative number specifying the initial "
  216. "time for the current spread simulation (minutes). This is useful when multiple "
  217. "phase simulation is conducted. The default time is 0.");
  218. parm.time_lag = G_define_option();
  219. parm.time_lag->key = "lag";
  220. parm.time_lag->type = TYPE_STRING;
  221. parm.time_lag->key_desc = "int (>= 0)"; /* TODO: move to ->options */
  222. parm.time_lag->label =
  223. _("Simulating time duration LAG (fill the region) (min)"); /* TODO: what does this mean? */
  224. parm.time_lag->description =
  225. _("A non-negative integer specifying the simulating "
  226. "duration time lag (minutes). The default is infinite, but the program will "
  227. "terminate when the current geographic region/mask has been filled. It also "
  228. "controls the computational time, the shorter the time lag, the faster the "
  229. "program will run.");
  230. /* TODO: what's this? probably display, so remove */
  231. parm.backdrop = G_define_option();
  232. parm.backdrop->key = "backdrop";
  233. parm.backdrop->type = TYPE_STRING;
  234. parm.backdrop->gisprompt = "old,cell,raster";
  235. parm.backdrop->label =
  236. _("Name of raster map as a display backdrop");
  237. parm.backdrop->description =
  238. _("Name of an existing raster map layer in the "
  239. "user's current mapset search path to be used as the background on which "
  240. "the \"live\" movement will be shown.");
  241. parm.out = G_define_option();
  242. parm.out->key = "output";
  243. parm.out->type = TYPE_STRING;
  244. parm.out->required = YES;
  245. parm.out->gisprompt = "new,cell,raster";
  246. parm.out->guisection = _("Output");
  247. parm.out->label =
  248. _("Raster map to contain output spread time (min)");
  249. parm.out->description =
  250. _("Name of the new raster map layer to contain "
  251. "the results of the cumulative spread time needed for a phenomenon to reach "
  252. "each cell from the starting sources (minutes).");
  253. parm.x_out = G_define_option();
  254. parm.x_out->key = "x_output";
  255. parm.x_out->type = TYPE_STRING;
  256. parm.x_out->gisprompt = "new,cell,raster";
  257. parm.x_out->guisection = _("Output");
  258. parm.x_out->label =
  259. _("Name of raster map to contain X back coordinates");
  260. parm.x_out->description =
  261. _("Name of the new raster map layer to contain "
  262. "the results of backlink information in UTM easting coordinates for each "
  263. "cell.");
  264. parm.y_out = G_define_option();
  265. parm.y_out->key = "y_output";
  266. parm.y_out->type = TYPE_STRING;
  267. parm.y_out->gisprompt = "new,cell,raster";
  268. parm.y_out->guisection = _("Output");
  269. parm.y_out->label =
  270. _("Name of raster map to contain Y back coordinates");
  271. parm.y_out->description =
  272. _("Name of the new raster map layer to contain "
  273. "the results of backlink information in UTM northing coordinates for each "
  274. "cell.");
  275. #if 0
  276. flag.display = G_define_flag();
  277. flag.display->key = 'd';
  278. flag.display->label = _("DISPLAY 'live' spread process on screen");
  279. flag.display->description =
  280. _("Display the 'live' simulation on screen. A graphics window "
  281. "must be opened and selected before using this option.");
  282. #endif
  283. flag.spotting = G_define_flag();
  284. flag.spotting->key = 's';
  285. flag.spotting->description = _("Consider spotting effect (for wildfires)");
  286. flag.start_is_time = G_define_flag();
  287. flag.start_is_time->key = 'i';
  288. flag.start_is_time->label = _("Use start raster map values in"
  289. " output spread time raster map");
  290. flag.start_is_time->description = _("Designed to be used with output"
  291. " of previous run of r.spread when computing spread iteratively."
  292. " The values in start raster map are considered as time."
  293. " Allowed values in raster map are from zero"
  294. " to the value of init_time option."
  295. " If not enabled, init_time is used in the area of start raster map");
  296. /* Parse command line */
  297. if (G_parser(argc, argv))
  298. exit(EXIT_FAILURE);
  299. /* FIXME - allow seed to be specified for repeatability */
  300. G_srand48_auto();
  301. display = NULL;
  302. spotting = flag.spotting->answer;
  303. max_layer = parm.max->answer;
  304. dir_layer = parm.dir->answer;
  305. base_layer = parm.base->answer;
  306. start_layer = parm.start->answer;
  307. backdrop_layer = parm.backdrop->answer;
  308. out_layer = parm.out->answer;
  309. if (parm.x_out->answer) {
  310. x_out = 1;
  311. x_out_layer = parm.x_out->answer;
  312. }
  313. if (parm.y_out->answer) {
  314. y_out = 1;
  315. y_out_layer = parm.y_out->answer;
  316. }
  317. if (spotting) {
  318. if (!
  319. (parm.spotdist->answer && parm.velocity->answer &&
  320. parm.mois->answer)) {
  321. G_warning
  322. ("SPOTTING DISTANCE, fuel MOISTURE, or wind VELOCITY map not given w/ -s");
  323. G_usage();
  324. exit(EXIT_FAILURE);
  325. }
  326. else {
  327. spotdist_layer = parm.spotdist->answer;
  328. velocity_layer = parm.velocity->answer;
  329. mois_layer = parm.mois->answer;
  330. }
  331. }
  332. /*Check the given the least sampling size, assign the default if needed */
  333. if (parm.least->answer)
  334. least = atoi(parm.least->answer);
  335. else
  336. least = 3;
  337. /*Check the given computing density, assign the default if needed */
  338. if (parm.comp_dens->answer) {
  339. comp_dens = atof(parm.comp_dens->answer);
  340. if (comp_dens < 0.0 || comp_dens > 1.0) {
  341. G_warning("Illegal computing density <%s>",
  342. parm.comp_dens->answer);
  343. G_usage();
  344. exit(EXIT_FAILURE);
  345. }
  346. }
  347. else {
  348. comp_dens = 0.5;
  349. }
  350. /*Check the given initial time and simulation time lag, assign the default if needed */
  351. init_time = atoi(parm.init_time->answer);
  352. if (init_time < 0) {
  353. G_warning("Illegal initial time <%s>", parm.init_time->answer);
  354. G_usage();
  355. exit(EXIT_FAILURE);
  356. }
  357. if (parm.time_lag->answer) {
  358. time_lag = atoi(parm.time_lag->answer);
  359. if (time_lag < 0) {
  360. G_warning("Illegal simulating time lag <%s>",
  361. parm.time_lag->answer);
  362. G_usage();
  363. exit(EXIT_FAILURE);
  364. }
  365. }
  366. else {
  367. time_lag = 99999;
  368. }
  369. /* Get database window parameters */
  370. G_get_window(&window);
  371. /* find number of rows and columns in window */
  372. nrows = Rast_window_rows();
  373. ncols = Rast_window_cols();
  374. /*transfor measurement unit from meters to centimeters due to ROS unit
  375. *if the input ROSs are in m/min units, cancell the following*/
  376. window.ns_res = 100 * window.ns_res;
  377. window.ew_res = 100 * window.ew_res;
  378. /* Initialize display screens */
  379. #if 0
  380. if (display)
  381. display_init();
  382. #endif
  383. /* Check if input layers exists in data base */
  384. if (G_find_raster2(max_layer, "") == NULL)
  385. G_fatal_error("Raster map <%s> not found", max_layer);
  386. if (G_find_raster2(dir_layer, "") == NULL)
  387. G_fatal_error(_("Raster map <%s> not found"), dir_layer);
  388. if (G_find_raster2(base_layer, "") == NULL)
  389. G_fatal_error(_("Raster map <%s> not found"), base_layer);
  390. if (G_find_raster2(start_layer, "") == NULL)
  391. G_fatal_error(_("Raster map <%s> not found"), start_layer);
  392. if (spotting) {
  393. if (G_find_raster2(spotdist_layer, "") == NULL)
  394. G_fatal_error(_("Raster map <%s> not found"), spotdist_layer);
  395. if (G_find_raster2(velocity_layer, "") == NULL)
  396. G_fatal_error(_("Raster map <%s> not found"), velocity_layer);
  397. if (G_find_raster2(mois_layer, "") == NULL)
  398. G_fatal_error(_("Raster map <%s> not found"), mois_layer);
  399. }
  400. /* Open input cell layers for reading */
  401. max_fd = Rast_open_old(max_layer, G_find_raster2(max_layer, ""));
  402. dir_fd = Rast_open_old(dir_layer, G_find_raster2(dir_layer, ""));
  403. base_fd = Rast_open_old(base_layer, G_find_raster2(base_layer, ""));
  404. if (spotting) {
  405. spotdist_fd =
  406. Rast_open_old(spotdist_layer, G_find_raster2(spotdist_layer, ""));
  407. velocity_fd =
  408. Rast_open_old(velocity_layer, G_find_raster2(velocity_layer, ""));
  409. mois_fd = Rast_open_old(mois_layer, G_find_raster2(mois_layer, ""));
  410. }
  411. /* Allocate memories for a row */
  412. cell = Rast_allocate_c_buf();
  413. if (x_out)
  414. x_cell = Rast_allocate_c_buf();
  415. if (y_out)
  416. y_cell = Rast_allocate_c_buf();
  417. /* Allocate memories for a map */
  418. map_max = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  419. map_dir = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  420. map_base = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  421. map_visit = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  422. map_out = (float *)G_calloc(nrows * ncols + 1, sizeof(float));
  423. if (spotting) {
  424. map_spotdist = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  425. map_velocity = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  426. map_mois = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  427. }
  428. if (x_out)
  429. map_x_out = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  430. if (y_out)
  431. map_y_out = (CELL *) G_calloc(nrows * ncols + 1, sizeof(CELL));
  432. /* Write the input layers in the map "arrays" */
  433. G_message(_("Reading inputs..."));
  434. for (row = 0; row < nrows; row++) {
  435. G_percent(row, nrows, 2);
  436. Rast_get_c_row(max_fd, cell, row);
  437. for (col = 0; col < ncols; col++)
  438. DATA(map_max, row, col) = cell[col];
  439. Rast_get_c_row(dir_fd, cell, row);
  440. for (col = 0; col < ncols; col++)
  441. DATA(map_dir, row, col) = cell[col];
  442. Rast_get_c_row(base_fd, cell, row);
  443. for (col = 0; col < ncols; col++)
  444. DATA(map_base, row, col) = cell[col];
  445. if (spotting) {
  446. Rast_get_c_row(spotdist_fd, cell, row);
  447. for (col = 0; col < ncols; col++)
  448. DATA(map_spotdist, row, col) = cell[col];
  449. Rast_get_c_row(velocity_fd, cell, row);
  450. for (col = 0; col < ncols; col++)
  451. DATA(map_velocity, row, col) = cell[col];
  452. Rast_get_c_row(mois_fd, cell, row);
  453. for (col = 0; col < ncols; col++)
  454. DATA(map_mois, row, col) = cell[col];
  455. }
  456. }
  457. G_percent(row, nrows, 2);
  458. /* Scan the START layer searching for starting points.
  459. * Create an array of starting points (min_heap) ordered by costs.
  460. */
  461. start_fd = Rast_open_old(start_layer, G_find_raster2(start_layer, ""));
  462. Rast_read_range(start_layer, G_find_file("cell", start_layer, ""),
  463. &start_range);
  464. Rast_get_range_min_max(&start_range, &start_range_min, &start_range_max);
  465. start_is_time = flag.start_is_time->answer;
  466. /* values higher than init_time are unexpected and may cause segfaults */
  467. if (start_is_time && start_range_max > init_time)
  468. G_fatal_error(_("Maximum of start raster map is grater than init_time"
  469. " (%d > %d)"), start_range_max, init_time);
  470. /* values lower then zero does not make sense for time */
  471. if (start_is_time && start_range_min < 0)
  472. G_fatal_error(_("Minimum of start raster map is less than zero"
  473. " (%d < 0)"), start_range_min);
  474. /* Initialize the heap */
  475. heap =
  476. (struct costHa *)G_calloc(nrows * ncols + 1, sizeof(struct costHa));
  477. heap_len = 0;
  478. G_message(_("Reading %s..."), start_layer);
  479. G_debug(1, "Collecting origins...");
  480. collect_ori(start_fd, start_is_time);
  481. G_debug(1, "Done");
  482. /* Major computation of spread time */
  483. G_debug(1, "Spreading...");
  484. spread();
  485. G_debug(1, "Done");
  486. /* Open cumulative cost layer (and x, y direction layers) for writing */
  487. cum_fd = Rast_open_c_new(out_layer);
  488. if (x_out)
  489. x_fd = Rast_open_c_new(x_out_layer);
  490. if (y_out)
  491. y_fd = Rast_open_c_new(y_out_layer);
  492. /* prepare output -- adjust from cm to m */
  493. window.ew_res = window.ew_res / 100;
  494. window.ns_res = window.ns_res / 100;
  495. /* copy maps in ram to output maps */
  496. ram2out();
  497. G_free(map_max);
  498. G_free(map_dir);
  499. G_free(map_base);
  500. G_free(map_out);
  501. G_free(map_visit);
  502. if (x_out)
  503. G_free(map_x_out);
  504. if (y_out)
  505. G_free(map_y_out);
  506. if (spotting) {
  507. G_free(map_spotdist);
  508. G_free(map_mois);
  509. G_free(map_velocity);
  510. }
  511. Rast_close(max_fd);
  512. Rast_close(dir_fd);
  513. Rast_close(base_fd);
  514. Rast_close(start_fd);
  515. Rast_close(cum_fd);
  516. if (x_out)
  517. Rast_close(x_fd);
  518. if (y_out)
  519. Rast_close(y_fd);
  520. if (spotting) {
  521. Rast_close(spotdist_fd);
  522. Rast_close(velocity_fd);
  523. Rast_close(mois_fd);
  524. }
  525. /* close graphics */
  526. #if 0
  527. if (display)
  528. display_close();
  529. #endif
  530. exit(EXIT_SUCCESS);
  531. }