1 
2 /***************************************************************************
3  *
4  * MODULE:       r3.in.ascii
5  *
6  * AUTHOR(S):    Roman Waupotitsch, Michael Shapiro, Helena Mitasova, Bill Brown,
7  * 		Lubos Mitas, Jaro Hofierka, Soeren Gebbert
8  *
9  * PURPOSE:      Convert a 3D ASCII raster text file into a (binary) 3D raster map layer
10  *
11  * COPYRIGHT:    (C) 2005 by the GRASS Development Team
12  *
13  *               This program is free software under the GNU General Public
14  *               License (>=v2). Read the file COPYING that comes with GRASS
15  *               for details.
16  *
17  *****************************************************************************/
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <grass/gis.h>
23 #include <grass/raster3d.h>
24 #include <grass/glocale.h>
25 
26 #define ROW_ORDER_NORTH_TO_SOUTH 1
27 #define ROW_ORDER_SOUTH_TO_NORTH 2
28 #define DEPTH_ORDER_BOTTOM_TO_TOP 3
29 #define DEPTH_ORDER_TOP_TO_BOTTOM 4
30 
31 /*- prototypes --------------------------------------------------------------*/
32 
33 static void fatalError(char *errorMsg); /*Simple Error message */
34 
35 static void setParams(); /*Fill the paramType structure */
36 
37 /*Puts the userdefined parameters into easier handable variables */
38 static void getParams(char **input, char **output, int *convertNull,
39                       char *nullValue);
40 
41 /* copy content of one file into another (taken form r.in.ascii) */
42 static int file_cpy(FILE *, FILE *);
43 
44 /*reads a g3d ascii-file headerfile-string */
45 static void readHeaderString(FILE * fp, char *valueString, double *value);
46 
47 static FILE *openAscii(char *asciiFile, RASTER3D_Region * region); /*open the g3d ascii file */
48 
49 /*This function does all the work, it reads the values from the g3d ascii-file and put
50    it into an g3d-map */
51 static void asciiToG3d(FILE * fp, RASTER3D_Region * region, int convertNull,
52                        char *nullValue);
53 
54 /*---------------------------------------------------------------------------*/
55 
56 /* global variables */
57 void *map = NULL;
58 int rowOrder;
59 int depthOrder;
60 
61 extern void *Rast3d_open_new_param();
62 
63 /*---------------------------------------------------------------------------*/
fatalError(char * errorMsg)64 void fatalError(char *errorMsg)
65 {
66     if (map != NULL) {
67         /* should unopen map here! */
68         Rast3d_close(map);
69     }
70 
71     Rast3d_fatal_error("%s", errorMsg);
72 }
73 
74 /*---------------------------------------------------------------------------*/
75 
76 typedef struct {
77     struct Option *input, *output, *nv;
78 } paramType;
79 
80 static paramType param;
81 
setParams()82 static void setParams()
83 {
84     param.input = G_define_standard_option(G_OPT_F_INPUT);
85     param.input->required = YES;
86     param.input->label = _("Name of input file to be imported");
87     param.input->description = _("'-' for standard input");
88 
89     param.output = G_define_standard_option(G_OPT_R3_OUTPUT);
90 
91     param.nv = G_define_standard_option(G_OPT_M_NULL_VALUE);
92     param.nv->answer = "*";
93     param.nv->description =  /* TODO: '*' or 'none' in the msg ?? */
94 	_("String representing NULL value data cell (use 'none' if no such value)");
95 }
96 
97 /*---------------------------------------------------------------------------*/
98 
99 void
getParams(char ** input,char ** output,int * convertNull,char * nullValue)100 getParams(char **input, char **output, int *convertNull, char *nullValue)
101 {
102     *input = param.input->answer;
103     *output = param.output->answer;
104     *convertNull = (strcmp(param.nv->answer, "none") != 0);
105     if (*convertNull)
106         if (sscanf(param.nv->answer, "%s", nullValue) != 1)
107             fatalError("getParams: NULL-value value invalid");
108 
109     G_debug(3, "getParams: input: %s, output: %s", *input, *output);
110 }
111 
112 /*---------------------------------------------------------------------------*/
113 
file_cpy(FILE * from,FILE * to)114 static int file_cpy(FILE * from, FILE * to)
115 {
116     char buf[BUFSIZ];
117     size_t size;
118     int written = 0;
119 
120     while (1) {
121         size = fread(buf, 1, BUFSIZ, from);
122         if (!size) {
123             if (written) {
124                 fflush(to);
125                 G_fseek(to, 0L, SEEK_SET);
126             }
127             return 0;
128         }
129         if (!fwrite(buf, 1, size, to)) {
130             G_warning(_("Unable to write to file"));
131             return -1;
132         }
133         written = 1;
134     }
135     /* NOTREACHED */
136     return -1;
137 }
138 
readHeaderString(FILE * fp,char * valueString,double * value)139 void readHeaderString(FILE * fp, char *valueString, double *value)
140 {
141     static char format[100];
142     char line_buff[1024];
143 
144     /* to avoid buffer overflows we use G_snprintf */
145     G_snprintf(format, 100, "%s %%lf", valueString);
146     G_getl2(line_buff, 1024, fp);
147     if (sscanf(line_buff, format, value) != 1) {
148         /* this would be ideal to merge if Rast3d_close could be solved */
149         G_warning("Bad value for '%s': %s", valueString, line_buff);
150         fatalError("readHeaderString: header value invalid");
151     }
152 }
153 
154 /*---------------------------------------------------------------------------*/
155 
openAscii(char * asciiFile,RASTER3D_Region * region)156 FILE *openAscii(char *asciiFile, RASTER3D_Region * region)
157 {
158     FILE *fp;
159     char *tmp_file;
160     double tmp;
161     char buff[1024];
162     char line_buff[1024];
163 
164     G_debug(3, "openAscii: opens the ascii file and reads the header");
165 
166     if (strcmp(asciiFile, "-") == 0) {
167         tmp_file = G_tempfile();
168         if (NULL == (fp = fopen(tmp_file, "w+")))
169             G_fatal_error(_("Unable to open temporary file <%s>"), tmp_file);
170         unlink(tmp_file);
171         if (0 > file_cpy(stdin, fp))
172             G_fatal_error(_("Unable to read input from stdin"));
173     }
174     else {
175         fp = fopen(asciiFile, "r");
176     }
177     if (fp == NULL) {
178         perror(asciiFile);
179         G_usage();
180         exit(EXIT_FAILURE);
181     }
182 
183     /* Initialize the default order */
184     rowOrder = ROW_ORDER_NORTH_TO_SOUTH;
185     depthOrder = DEPTH_ORDER_BOTTOM_TO_TOP;
186 
187     /* Read the first line and check for grass version */
188     G_getl2(line_buff, 1024, fp);
189 
190     /* First check for new ascii format*/
191     if (sscanf(line_buff, "version: %s", buff) == 1) {
192         G_message("Found version information: %s\n", buff);
193         if (G_strcasecmp(buff, "grass7") == 0) {
194 
195             /* Parse the row and depth order */
196             G_getl2(line_buff, 1024, fp);
197             if (sscanf(line_buff, "order: %s", buff) != 1)
198                 fatalError("Unable to parse the row and depth order");
199 
200             if (G_strcasecmp(buff, "nsbt") == 0) {
201                 rowOrder = ROW_ORDER_NORTH_TO_SOUTH;
202                 depthOrder = DEPTH_ORDER_BOTTOM_TO_TOP;
203                 G_message("Found north -> south, bottom -> top order (nsbt)");
204             }
205             if (G_strcasecmp(buff, "snbt") == 0) {
206                 rowOrder = ROW_ORDER_SOUTH_TO_NORTH;
207                 depthOrder = DEPTH_ORDER_BOTTOM_TO_TOP;
208                 G_message("Found south -> north, bottom -> top order (snbt)");
209             }
210             if (G_strcasecmp(buff, "nstb") == 0) {
211                 rowOrder = ROW_ORDER_NORTH_TO_SOUTH;
212                 depthOrder = DEPTH_ORDER_TOP_TO_BOTTOM;
213                 G_message("Found north -> south, top -> bottom order (nstb)");
214             }
215             if (G_strcasecmp(buff, "sntb") == 0) {
216                 rowOrder = ROW_ORDER_SOUTH_TO_NORTH;
217                 depthOrder = DEPTH_ORDER_TOP_TO_BOTTOM;
218                 G_message("Found south -> north, top -> bottom order (sntb)");
219             }
220         } else {
221             G_fatal_error(_("Unsupported GRASS version %s"), buff);
222         }
223     } else {
224         /* Rewind the stream if no grass version info found */
225         rewind(fp);
226     }
227 
228     Rast3d_get_window(region);
229 
230     readHeaderString(fp, "north:", &(region->north));
231     readHeaderString(fp, "south:", &(region->south));
232     readHeaderString(fp, "east:", &(region->east));
233     readHeaderString(fp, "west:", &(region->west));
234     readHeaderString(fp, "top:", &(region->top));
235     readHeaderString(fp, "bottom:", &(region->bottom));
236     readHeaderString(fp, "rows:", &tmp);
237     region->rows = (int) tmp;
238     readHeaderString(fp, "cols:", &tmp);
239     region->cols = (int) tmp;
240     readHeaderString(fp, "levels:", &tmp);
241     region->depths = (int) tmp;
242 
243     return fp;
244 }
245 
246 /*---------------------------------------------------------------------------*/
247 
248 #undef MAX
249 #define MAX(a,b) (a > b ? a : b)
250 
251 void
asciiToG3d(FILE * fp,RASTER3D_Region * region,int convertNull,char * nullValue)252 asciiToG3d(FILE * fp, RASTER3D_Region * region, int convertNull, char *nullValue)
253 {
254     int x, y, z;
255     int col, row, depth;
256     double value;
257     char buff[256];
258     int tileX, tileY, tileZ;
259 
260     Rast3d_get_tile_dimensions_map(map, &tileX, &tileY, &tileZ);
261     Rast3d_min_unlocked(map, RASTER3D_USE_CACHE_X);
262 
263     Rast3d_autolock_on(map);
264     Rast3d_unlock_all(map);
265     G_message(_("Loading data ...  (%dx%dx%d)"), region->cols, region->rows,
266               region->depths);
267 
268     G_debug(3,
269             "asciiToG3d: writing the 3D raster map, with rows %i cols %i depths %i",
270             region->rows, region->cols, region->depths);
271 
272     for (z = 0; z < region->depths; z++) {
273         G_percent(z, region->depths, 1);
274 
275         if ((z % tileZ) == 0)
276             Rast3d_unlock_all(map);
277 
278         for (y = 0; y < region->rows; y++) /* go south to north */
279             for (x = 0; x < region->cols; x++) {
280 
281                 /* From west to east */
282                 col = x;
283                 /* The default is to read rows from north to south */
284                 row = y;
285                 /* From bottom to the top */
286                 depth = z;
287 
288                 /* Read rows as from south to north */
289                 if (rowOrder == ROW_ORDER_SOUTH_TO_NORTH)
290                     row = region->rows - y - 1;
291 
292                 /* Read XY layer from top to bottom */
293                 if (depthOrder == DEPTH_ORDER_TOP_TO_BOTTOM)
294                     depth = region->depths - z - 1;
295 
296                 if (fscanf(fp, "%s", buff) != 1) {
297                     if (feof(fp))
298                         G_warning(_("End of file reached while still loading data."));
299                     G_debug(3,
300                             "missing data at col=%d row=%d depth=%d last_value=[%.4f]",
301                             x + 1, y + 1, z + 1, value);
302                     fatalError("asciiToG3d: read failed");
303                 }
304 
305                 /* Check for null value */
306                 if (convertNull && strncmp(buff, nullValue, strlen(nullValue)) == 0) {
307                     Rast3d_set_null_value(&value, 1, DCELL_TYPE);
308                 } else {
309                     if (sscanf(buff, "%lf", &value) != 1) {
310                         G_warning(_("Invalid value detected"
311                                     " (at col=%d row=%d depth=%d): %s"),
312                                   x + 1, y + 1, z + 1, buff);
313                         fatalError("asciiToG3d: read failed");
314                     }
315                 }
316                 /* Write the data */
317                 Rast3d_put_double(map, col, row, depth, value);
318             }
319     }
320 
321     if (fscanf(fp, "%lf", &value) == 1) {
322         G_warning(_("Data exists in input file after fully importing "
323                     "expected data.  [%.4f ...]"), value);
324     }
325 
326     if (!Rast3d_flush_all_tiles(map))
327         fatalError("asciiTog3d: error flushing tiles");
328 
329     Rast3d_autolock_off(map);
330     Rast3d_unlock_all(map);
331 
332     G_percent(1, 1, 1);
333 }
334 
335 /*---------------------------------------------------------------------------*/
336 
main(int argc,char * argv[])337 int main(int argc, char *argv[])
338 {
339     char *input, *output;
340     int convertNull;
341     char nullValue[256];
342     int useTypeDefault, type, useCompressionDefault, doCompression;
343     int usePrecisionDefault, precision, useDimensionDefault, tileX, tileY,
344         tileZ;
345     RASTER3D_Region region;
346     FILE *fp;
347     struct GModule *module;
348     struct History history;
349 
350     map = NULL;
351 
352     G_gisinit(argv[0]);
353     module = G_define_module();
354     G_add_keyword(_("raster3d"));
355     G_add_keyword(_("import"));
356     G_add_keyword(_("voxel"));
357     G_add_keyword(_("conversion"));
358     G_add_keyword("ASCII");
359     module->description =
360         _("Converts a 3D ASCII raster text file into a (binary) 3D raster map.");
361 
362     setParams();
363     Rast3d_set_standard3d_input_params();
364 
365     if (G_parser(argc, argv))
366         exit(EXIT_FAILURE);
367 
368     getParams(&input, &output, &convertNull, nullValue);
369     if (!Rast3d_get_standard3d_params(&useTypeDefault, &type,
370                                  &useCompressionDefault, &doCompression,
371                                  &usePrecisionDefault, &precision,
372                                  &useDimensionDefault, &tileX, &tileY,
373                                  &tileZ))
374         fatalError("Error getting standard parameters");
375 
376     Rast3d_init_defaults();
377 
378     fp = openAscii(input, &region);
379 
380     /*Open the new RASTER3D map */
381     map = Rast3d_open_new_param(output, RASTER3D_TILE_SAME_AS_FILE, RASTER3D_USE_CACHE_XY,
382                            &region,
383                            type, doCompression, precision, tileX, tileY,
384                            tileZ);
385 
386     if (map == NULL)
387         fatalError(_("Unable to open 3D raster map"));
388 
389     /*Create the new RASTER3D Map */
390     asciiToG3d(fp, &region, convertNull, nullValue);
391 
392     if (!Rast3d_close(map))
393         fatalError(_("Unable to close 3D raster map"));
394 
395     /* write input name to map history */
396     Rast3d_read_history(output, G_mapset(), &history);
397     Rast_command_history(&history);
398     Rast_set_history(&history, HIST_DATSRC_1, input);
399     Rast3d_write_history(output, &history);
400 
401     map = NULL;
402     if (fclose(fp))
403         fatalError(_("Unable to close ASCII file"));
404 
405     return EXIT_SUCCESS;
406 }
407