main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /****************************************************************************
  2. *
  3. * MODULE: r3.out.netCDF
  4. *
  5. * AUTHOR(S): Soeren Gebbert
  6. *
  7. * PURPOSE: Export a 3D raster map as netCDF file
  8. *
  9. * COPYRIGHT: (C) 2012 by the GRASS Development Team
  10. *
  11. * This program is free software under the GNU General Public
  12. * License (>=v2). Read the file COPYING that comes with GRASS
  13. * for details.
  14. *
  15. * TODO: Add time zone support to time variable
  16. * TODO: Implement better support for CF coordinate reference system defined here:
  17. * http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.6/cf-conventions.html#coordinate-system
  18. * https://cf-pcmdi.llnl.gov/trac/wiki/Cf2CrsWkt
  19. * http://trac.osgeo.org/gdal/wiki/NetCDF_ProjectionTestingStatus
  20. *
  21. *****************************************************************************/
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <netcdf.h>
  26. #include <grass/datetime.h>
  27. #include <grass/gis.h>
  28. #include <grass/raster3d.h>
  29. #include <grass/glocale.h>
  30. #include <grass/gprojects.h>
  31. #define NDIMS 3
  32. #define LONG_NAME "long_name"
  33. #define STANDARD_NAME "standard_name"
  34. #define LAT_NAME "latitude"
  35. #define LAT_LONG_NAME "Latitude values"
  36. #define LON_NAME "longitude"
  37. #define LON_LONG_NAME "Longitude values"
  38. #define TIME_NAME "time"
  39. #define X_NAME "x"
  40. #define X_STANDARD_NAME "projection_x_coordinate"
  41. #define X_LONG_NAME "x coordinate of projection"
  42. #define Y_NAME "y"
  43. #define Y_LONG_NAME "y coordinate of projection"
  44. #define Y_STANDARD_NAME "projection_y_coordinate"
  45. #define Z_NAME "z"
  46. #define Z_LONG_NAME "z coordinate of projection"
  47. #define Z_STANDARD_NAME "projection_z_coordinate"
  48. #define UNITS "units"
  49. #define DEGREES_EAST "degrees_east"
  50. #define DEGREES_NORTH "degrees_north"
  51. #define HISTORY_TEXT "GRASS GIS 7 netCDF export of r3.out.netcdf"
  52. #define CF_SUPPORT "CF-1.5"
  53. #define ERR(e) {fatalError(nc_strerror(e));}
  54. /* structs */
  55. typedef struct
  56. {
  57. struct Option *input, *output, *null;
  58. struct Flag *mask, *proj;
  59. } paramType;
  60. RASTER3D_Map *map;
  61. paramType param;
  62. /*---------------------------------------------------------------------------*/
  63. /* Simple error handling routine, will eventually replace this with
  64. * RASTER3D_fatalError.
  65. */
  66. static void fatalError(const char *errorMsg)
  67. {
  68. if (map != NULL) {
  69. /* should unopen map here! */
  70. if (!Rast3d_close(map))
  71. G_fatal_error(_
  72. ("Unable to close 3D raster map while catching error: %s"),
  73. errorMsg);
  74. }
  75. G_fatal_error("%s", errorMsg);
  76. }
  77. /*---------------------------------------------------------------------------*/
  78. /* Convenient way to set up the arguments we are expecting
  79. */
  80. static void setParams()
  81. {
  82. param.input = G_define_standard_option(G_OPT_R3_INPUT);
  83. param.output = G_define_standard_option(G_OPT_F_OUTPUT);
  84. param.output->key = "output";
  85. param.output->description = _("Name for netCDF output file");
  86. param.null = G_define_option();
  87. param.null->key = "null";
  88. param.null->type = TYPE_DOUBLE;
  89. param.null->required = NO;
  90. param.null->multiple = NO;
  91. param.null->description =
  92. _
  93. ("The value to be used for null values, default is the netCDF standard");
  94. param.proj = G_define_flag();
  95. param.proj->key = 'p';
  96. param.proj->description =
  97. _("Export projection information as wkt and proj4 parameter");
  98. param.mask = G_define_flag();
  99. param.mask->key = 'm';
  100. param.mask->description =
  101. _("Use 3D raster mask (if exists) with input map");
  102. }
  103. static void write_netcdf_header(int ncid, RASTER3D_Region * region,
  104. int *varid, char write_proj, char *null)
  105. {
  106. int retval, typeIntern, time;
  107. size_t i;
  108. int is_time = 0;
  109. int is_absolute_time = 0;
  110. float c;
  111. int lat_dimid = 0, lon_dimid = 0, time_dimid = 0;
  112. int lat_varid = 0, lon_varid = 0, time_varid = 0;
  113. int dimids[NDIMS];
  114. struct Cell_head window;
  115. double min, max;
  116. /* global attributes */
  117. if ((retval =
  118. nc_put_att_text(ncid, NC_GLOBAL, "Conventions", strlen(CF_SUPPORT),
  119. CF_SUPPORT)))
  120. ERR(retval);
  121. if ((retval =
  122. nc_put_att_text(ncid, NC_GLOBAL, "history", strlen(HISTORY_TEXT),
  123. HISTORY_TEXT)))
  124. ERR(retval);
  125. G_get_window(&window);
  126. /* Projection parameter */
  127. if (window.proj != PROJECTION_XY && write_proj) {
  128. struct Key_Value *pkv, *ukv;
  129. struct pj_info pjinfo;
  130. char *proj4, *proj4mod;
  131. const char *unfact;
  132. int crs_dimid = 0, crs_varid;
  133. if ((retval =
  134. nc_def_var(ncid, "crs", NC_CHAR, 0, &crs_dimid, &crs_varid)))
  135. ERR(retval);
  136. pkv = G_get_projinfo();
  137. ukv = G_get_projunits();
  138. pj_get_kv(&pjinfo, pkv, ukv);
  139. proj4 = pj_get_def(pjinfo.pj, 0);
  140. pj_free(pjinfo.pj);
  141. #ifdef HAVE_OGR
  142. /* We support the CF suggestion crs_wkt and the gdal spatil_ref attribute */
  143. if ((retval =
  144. nc_put_att_text(ncid, crs_varid, "crs_wkt",
  145. strlen(GPJ_grass_to_wkt(pkv, ukv, 0, 0)),
  146. GPJ_grass_to_wkt(pkv, ukv, 0, 0))))
  147. ERR(retval);
  148. if ((retval =
  149. nc_put_att_text(ncid, crs_varid, "spatial_ref",
  150. strlen(GPJ_grass_to_wkt(pkv, ukv, 0, 0)),
  151. GPJ_grass_to_wkt(pkv, ukv, 0, 0))))
  152. ERR(retval);
  153. #endif
  154. /* Code from g.proj:
  155. * GRASS-style PROJ.4 strings don't include a unit factor as this is
  156. * handled separately in GRASS - must include it here though */
  157. unfact = G_find_key_value("meters", ukv);
  158. if (unfact != NULL && (strcmp(pjinfo.proj, "ll") != 0))
  159. G_asprintf(&proj4mod, "%s +to_meter=%s", proj4, unfact);
  160. else
  161. proj4mod = G_store(proj4);
  162. pj_dalloc(proj4);
  163. if ((retval =
  164. nc_put_att_text(ncid, crs_varid, "crs_proj4", strlen(proj4mod),
  165. proj4mod)))
  166. ERR(retval);
  167. if (pkv)
  168. G_free_key_value(pkv);
  169. if (ukv)
  170. G_free_key_value(ukv);
  171. }
  172. typeIntern = Rast3d_tile_type_map(map);
  173. if (window.proj == PROJECTION_LL) {
  174. /* X-Axis */
  175. if ((retval = nc_def_dim(ncid, LON_NAME, region->cols, &lon_dimid)))
  176. ERR(retval);
  177. if ((retval =
  178. nc_def_var(ncid, LON_NAME, NC_FLOAT, 1, &lon_dimid, &lon_varid)))
  179. ERR(retval);
  180. if ((retval =
  181. nc_put_att_text(ncid, lon_varid, UNITS, strlen(DEGREES_EAST),
  182. DEGREES_EAST)))
  183. ERR(retval);
  184. if ((retval = nc_put_att_text(ncid, lon_varid, LONG_NAME,
  185. strlen(LON_LONG_NAME), LON_LONG_NAME)))
  186. ERR(retval);
  187. if ((retval =
  188. nc_put_att_text(ncid, lon_varid, STANDARD_NAME, strlen(LON_NAME),
  189. LON_NAME)))
  190. ERR(retval);
  191. /* Y-Axis */
  192. if ((retval = nc_def_dim(ncid, LAT_NAME, region->rows, &lat_dimid)))
  193. ERR(retval);
  194. if ((retval =
  195. nc_def_var(ncid, LAT_NAME, NC_FLOAT, 1, &lat_dimid, &lat_varid)))
  196. ERR(retval);
  197. if ((retval =
  198. nc_put_att_text(ncid, lat_varid, UNITS, strlen(DEGREES_NORTH),
  199. DEGREES_NORTH)))
  200. ERR(retval);
  201. if ((retval = nc_put_att_text(ncid, lat_varid, LONG_NAME,
  202. strlen(LAT_LONG_NAME), LAT_LONG_NAME)))
  203. ERR(retval);
  204. if ((retval =
  205. nc_put_att_text(ncid, lat_varid, STANDARD_NAME, strlen(LAT_NAME),
  206. LAT_NAME)))
  207. ERR(retval);
  208. }
  209. else {
  210. /* X-Axis */
  211. if ((retval = nc_def_dim(ncid, X_NAME, region->cols, &lon_dimid)))
  212. ERR(retval);
  213. if ((retval =
  214. nc_def_var(ncid, X_NAME, NC_FLOAT, 1, &lon_dimid, &lon_varid)))
  215. ERR(retval);
  216. if ((retval = nc_put_att_text(ncid, lon_varid, UNITS, strlen("meter"),
  217. "meter")))
  218. ERR(retval);
  219. if ((retval =
  220. nc_put_att_text(ncid, lon_varid, LONG_NAME, strlen(X_LONG_NAME),
  221. X_LONG_NAME)))
  222. ERR(retval);
  223. if ((retval = nc_put_att_text(ncid, lon_varid, STANDARD_NAME,
  224. strlen(X_STANDARD_NAME),
  225. X_STANDARD_NAME)))
  226. ERR(retval);
  227. /* Y-Axis */
  228. if ((retval = nc_def_dim(ncid, Y_NAME, region->rows, &lat_dimid)))
  229. ERR(retval);
  230. if ((retval =
  231. nc_def_var(ncid, Y_NAME, NC_FLOAT, 1, &lat_dimid, &lat_varid)))
  232. ERR(retval);
  233. if ((retval = nc_put_att_text(ncid, lat_varid, UNITS, strlen("meter"),
  234. "meter")))
  235. ERR(retval);
  236. if ((retval =
  237. nc_put_att_text(ncid, lat_varid, LONG_NAME, strlen(Y_LONG_NAME),
  238. Y_LONG_NAME)))
  239. ERR(retval);
  240. if ((retval = nc_put_att_text(ncid, lat_varid, STANDARD_NAME,
  241. strlen(Y_STANDARD_NAME),
  242. Y_STANDARD_NAME)))
  243. ERR(retval);
  244. }
  245. /* We set the vertical axis and its unit. Units can be spatial
  246. * or temporal. Temporal can be absolute or relative.
  247. */
  248. is_time = 0;
  249. is_absolute_time = 0;
  250. if (Rast3d_get_vertical_unit(map) &&
  251. G_strncasecmp(Rast3d_get_vertical_unit(map), "units", 5) != 0) {
  252. /* Time handling */
  253. if (G_is_units_type_temporal(Rast3d_get_vertical_unit2(map))) {
  254. is_time = 1;
  255. char long_name[1024];
  256. char time_unit[1024];
  257. G_snprintf(long_name, 1024, "Time in %s",
  258. Rast3d_get_vertical_unit(map));
  259. if ((retval =
  260. nc_def_dim(ncid, TIME_NAME, region->depths, &time_dimid)))
  261. ERR(retval);
  262. if ((retval =
  263. nc_def_var(ncid, TIME_NAME, NC_INT, 1, &time_dimid,
  264. &time_varid)))
  265. ERR(retval);
  266. /* Temporal unit */
  267. if (G_has_raster3d_timestamp(map->fileName, map->mapset)) {
  268. struct TimeStamp ts;
  269. G_read_raster3d_timestamp(map->fileName, map->mapset, &ts);
  270. /* Days since datum in ISO norm */
  271. if (datetime_is_absolute(&ts.dt[0])) {
  272. is_absolute_time = 1;
  273. G_snprintf(time_unit, 1024,
  274. "%s since %d-%02d-%02d %02d:%02d:%02.0f",
  275. Rast3d_get_vertical_unit(map), ts.dt[0].year,
  276. ts.dt[0].month, ts.dt[0].day, ts.dt[0].hour,
  277. ts.dt[0].minute, ts.dt[0].second);
  278. }
  279. else {
  280. G_snprintf(time_unit, 1024, "%s",
  281. Rast3d_get_vertical_unit(map));
  282. }
  283. }
  284. else {
  285. G_snprintf(time_unit, 1024, "%s since %s",
  286. Rast3d_get_vertical_unit(map),
  287. "1900-01-01 00:00:00");
  288. }
  289. if ((retval =
  290. nc_put_att_text(ncid, time_varid, UNITS, strlen(time_unit),
  291. time_unit)))
  292. ERR(retval);
  293. if ((retval =
  294. nc_put_att_text(ncid, time_varid, LONG_NAME,
  295. strlen(long_name), long_name)))
  296. ERR(retval)
  297. if (is_absolute_time) {
  298. if ((retval =
  299. nc_put_att_text(ncid, time_varid, "calendar",
  300. strlen("gregorian"), "gregorian")))
  301. ERR(retval);
  302. }
  303. else {
  304. if ((retval =
  305. nc_put_att_text(ncid, time_varid, "calendar",
  306. strlen("none"), "none")))
  307. ERR(retval);
  308. }
  309. }
  310. else {
  311. if ((retval =
  312. nc_def_dim(ncid, Z_NAME, region->depths, &time_dimid)))
  313. ERR(retval);
  314. ;
  315. if ((retval =
  316. nc_def_var(ncid, Z_NAME, NC_FLOAT, 1, &time_dimid,
  317. &time_varid)))
  318. ERR(retval);
  319. if ((retval =
  320. nc_put_att_text(ncid, time_varid, UNITS,
  321. strlen(Rast3d_get_vertical_unit(map)),
  322. Rast3d_get_vertical_unit(map))))
  323. ERR(retval);
  324. if ((retval =
  325. nc_put_att_text(ncid, time_varid, LONG_NAME,
  326. strlen(Z_LONG_NAME), Z_LONG_NAME)))
  327. ERR(retval);
  328. if ((retval =
  329. nc_put_att_text(ncid, time_varid, STANDARD_NAME,
  330. strlen(Z_STANDARD_NAME), Z_STANDARD_NAME)))
  331. ERR(retval);
  332. }
  333. }
  334. else {
  335. /* Default is z unit meter */
  336. if ((retval = nc_def_dim(ncid, Z_NAME, region->depths, &time_dimid)))
  337. ERR(retval);
  338. if ((retval =
  339. nc_def_var(ncid, Z_NAME, NC_FLOAT, 1, &time_dimid, &time_varid)))
  340. ERR(retval);
  341. if ((retval =
  342. nc_put_att_text(ncid, time_varid, UNITS, strlen("meter"),
  343. "meter")))
  344. ERR(retval);
  345. if ((retval =
  346. nc_put_att_text(ncid, time_varid, LONG_NAME, strlen(Z_LONG_NAME),
  347. Z_LONG_NAME)))
  348. ERR(retval);
  349. if ((retval =
  350. nc_put_att_text(ncid, time_varid, STANDARD_NAME,
  351. strlen(Z_STANDARD_NAME), Z_STANDARD_NAME)))
  352. ERR(retval);
  353. }
  354. /* z - axis orientation */
  355. if ((retval =
  356. nc_put_att_text(ncid, time_varid, "positive", strlen("up"), "up")))
  357. ERR(retval);
  358. /* Axis identifier attributes */
  359. if ((retval = nc_put_att_text(ncid, lon_varid, "axis", 1, "X")))
  360. ERR(retval);
  361. if ((retval = nc_put_att_text(ncid, lat_varid, "axis", 1, "Y")))
  362. ERR(retval);
  363. if (is_time) {
  364. if ((retval = nc_put_att_text(ncid, time_varid, "axis", 1, "T")))
  365. ERR(retval);
  366. }
  367. else {
  368. if ((retval = nc_put_att_text(ncid, time_varid, "axis", 1, "Z")))
  369. ERR(retval);
  370. }
  371. dimids[0] = time_dimid;
  372. dimids[1] = lat_dimid;
  373. dimids[2] = lon_dimid;
  374. Rast3d_range_load(map);
  375. Rast3d_range_min_max(map, &min, &max);
  376. if (typeIntern == FCELL_TYPE) {
  377. if ((retval =
  378. nc_def_var(ncid, param.input->answer, NC_FLOAT, NDIMS, dimids,
  379. varid)))
  380. ERR(retval);
  381. /* Set the range values */
  382. float fmin = min;
  383. float fmax = max;
  384. if ((retval =
  385. nc_put_att_float(ncid, *varid, "valid_min", NC_FLOAT, 1, &fmin)))
  386. ERR(retval);
  387. if ((retval =
  388. nc_put_att_float(ncid, *varid, "valid_max", NC_FLOAT, 1, &fmax)))
  389. ERR(retval);
  390. if (null) {
  391. float null_val = (float)atof(null);
  392. if ((retval =
  393. nc_put_att_float(ncid, *varid, "missing_value", NC_FLOAT, 1,
  394. &null_val)))
  395. ERR(retval);
  396. if ((retval =
  397. nc_put_att_float(ncid, *varid, "_FillValue", NC_FLOAT, 1,
  398. &null_val)))
  399. ERR(retval);
  400. }
  401. }
  402. else {
  403. if ((retval =
  404. nc_def_var(ncid, param.input->answer, NC_DOUBLE, NDIMS, dimids,
  405. varid)))
  406. ERR(retval);
  407. /* Set the range values */
  408. if ((retval =
  409. nc_put_att_double(ncid, *varid, "valid_min", NC_DOUBLE, 1,
  410. &min)))
  411. ERR(retval);
  412. if ((retval =
  413. nc_put_att_double(ncid, *varid, "valid_max", NC_DOUBLE, 1,
  414. &max)))
  415. ERR(retval);
  416. if (null) {
  417. double null_val = (double)atof(null);
  418. if ((retval =
  419. nc_put_att_double(ncid, *varid, "missing_value", NC_DOUBLE,
  420. 1, &null_val)))
  421. ERR(retval);
  422. if ((retval =
  423. nc_put_att_double(ncid, *varid, "_FillValue", NC_DOUBLE, 1,
  424. &null_val)))
  425. ERR(retval);
  426. }
  427. }
  428. if (window.proj != PROJECTION_XY && write_proj) {
  429. if ((retval =
  430. nc_put_att_text(ncid, *varid, "grid_mapping", strlen("crs"),
  431. "crs")))
  432. ERR(retval);
  433. }
  434. /* End define mode. */
  435. if ((retval = nc_enddef(ncid)))
  436. ERR(retval);
  437. /*
  438. * Build coordinates, we need to use the cell center in case of spatial dimensions
  439. * */
  440. for (i = 0; i < region->cols; i++) {
  441. c = region->west + i * region->ew_res + 0.5 * region->ew_res;
  442. nc_put_var1_float(ncid, lon_varid, &i, &c);
  443. }
  444. for (i = 0; i < region->rows; i++) {
  445. /* c = region->south + i * region->ns_res + 0.5 * region->ns_res; */
  446. c = region->north - i * region->ns_res - 0.5 * region->ns_res;
  447. nc_put_var1_float(ncid, lat_varid, &i, &c);
  448. }
  449. for (i = 0; i < region->depths; i++) {
  450. if (is_time) {
  451. c = i * region->tb_res;
  452. time = (int)c;
  453. nc_put_var1_int(ncid, time_varid, &i, &time);
  454. }
  455. else {
  456. c = region->bottom + i * region->tb_res + 0.5 * region->tb_res;
  457. nc_put_var1_float(ncid, time_varid, &i, &c);
  458. }
  459. }
  460. }
  461. /*---------------------------------------------------------------------------*/
  462. static void write_netcdf_data(int ncid, RASTER3D_Region * region, int varid)
  463. {
  464. DCELL dvalue;
  465. FCELL fvalue;
  466. int x, y, z;
  467. size_t coords[3];
  468. int rows, cols, depths, typeIntern;
  469. rows = region->rows;
  470. cols = region->cols;
  471. depths = region->depths;
  472. typeIntern = Rast3d_tile_type_map(map);
  473. for (z = 0; z < depths; z++) {
  474. G_percent(z, depths, 1);
  475. for (y = 0; y < rows; y++) {
  476. for (x = 0; x < cols; x++) {
  477. coords[0] = z;
  478. coords[1] = y;
  479. coords[2] = x;
  480. if (typeIntern == FCELL_TYPE) {
  481. Rast3d_get_value(map, x, y, z, &fvalue, FCELL_TYPE);
  482. if (!Rast3d_is_null_value_num(&fvalue, FCELL_TYPE)) {
  483. nc_put_var1_float(ncid, varid, coords, &fvalue);
  484. }
  485. }
  486. else {
  487. Rast3d_get_value(map, x, y, z, &dvalue, DCELL_TYPE);
  488. if (!Rast3d_is_null_value_num(&dvalue, DCELL_TYPE)) {
  489. nc_put_var1_double(ncid, varid, coords, &dvalue);
  490. }
  491. }
  492. }
  493. }
  494. }
  495. G_percent(1, 1, 1);
  496. G_percent_reset();
  497. }
  498. /*---------------------------------------------------------------------------*/
  499. int main(int argc, char *argv[])
  500. {
  501. RASTER3D_Region region;
  502. int ncid;
  503. int retval;
  504. int varid;
  505. int changemask = 0;
  506. struct GModule *module;
  507. /* Initialize GRASS */
  508. G_gisinit(argv[0]);
  509. module = G_define_module();
  510. G_add_keyword(_("raster3d"));
  511. G_add_keyword(_("export"));
  512. G_add_keyword(_("netCDF"));
  513. module->description = _("Export a 3D raster map as netCDF file.");
  514. /* Get parameters from user */
  515. setParams();
  516. /* Have GRASS get inputs */
  517. if (G_parser(argc, argv))
  518. exit(EXIT_FAILURE);
  519. if (NULL == G_find_raster3d(param.input->answer, ""))
  520. Rast3d_fatal_error(_("3D raster map <%s> not found"),
  521. param.input->answer);
  522. /* Initiate the default settings */
  523. Rast3d_init_defaults();
  524. /* Figure out the current region settings */
  525. Rast3d_get_window(&region);
  526. /* Open the map and use XY cache mode */
  527. map =
  528. Rast3d_open_cell_old(param.input->answer,
  529. G_find_raster3d(param.input->answer, ""),
  530. &region, RASTER3D_TILE_SAME_AS_FILE,
  531. RASTER3D_USE_CACHE_DEFAULT);
  532. if (map == NULL)
  533. G_fatal_error(_("Unable to open 3D raster map <%s>"),
  534. param.input->answer);
  535. /* Create netCDF file */
  536. if ((retval = nc_create(param.output->answer, NC_CLOBBER, &ncid)))
  537. ERR(retval);
  538. write_netcdf_header(ncid, &region, &varid, param.proj->answer,
  539. param.null->answer);
  540. /*if requested set the Mask on */
  541. if (param.mask->answer) {
  542. if (Rast3d_mask_file_exists()) {
  543. changemask = 0;
  544. if (Rast3d_mask_is_off(map)) {
  545. Rast3d_mask_on(map);
  546. changemask = 1;
  547. }
  548. }
  549. }
  550. write_netcdf_data(ncid, &region, varid);
  551. /*We set the Mask off, if it was off before */
  552. if (param.mask->answer) {
  553. if (Rast3d_mask_file_exists())
  554. if (Rast3d_mask_is_on(map) && changemask)
  555. Rast3d_mask_off(map);
  556. }
  557. /* Close files and exit */
  558. if (!Rast3d_close(map))
  559. fatalError(_("Unable to close 3D raster map"));
  560. /* Close the netCDF file */
  561. if ((retval = nc_close(ncid)))
  562. ERR(retval);
  563. return 0;
  564. }