1 
2 /***************************************************************************
3 *
4 * MODULE:       r.info
5 *
6 * AUTHOR(S):    Michael O'Shea
7 *
8 * PURPOSE:      Outputs basic information about a user-specified raster map layer.
9 *
10 * COPYRIGHT:    (C) 2005-2011 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 #include <math.h>
19 #include <sys/types.h>
20 #include <grass/config.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdarg.h>
24 #include <grass/gis.h>
25 #include <grass/raster.h>
26 #include <grass/glocale.h>
27 #include "local_proto.h"
28 
29 #define printline(x) fprintf (out," | %-74.74s |\n",x)
30 #define divider(x) \
31     fprintf (out," %c",x);\
32     for (i = 0; i < 76; i++)\
33         fprintf(out,"-");\
34     fprintf (out,"%c\n",x)
35 
36 /* local prototypes */
37 static void format_double(const double, char *);
38 static void compose_line(FILE *, const char *, ...);
39 
40 
main(int argc,char ** argv)41 int main(int argc, char **argv)
42 {
43     const char *name, *mapset;
44     const char *title;
45     char tmp1[100], tmp2[100], tmp3[100];
46     char timebuff[256];
47     char *units, *vdatum;
48     int i;
49     CELL mincat = 0, maxcat = 0, cat;
50     FILE *out;
51     struct Range crange;
52     struct FPRange range;
53     struct R_stats rstats;
54     struct Cell_head cellhd;
55     struct Categories cats;
56     struct History hist;
57     struct TimeStamp ts;
58     int time_ok = 0, first_time_ok = 0, second_time_ok = 0;
59     int cats_ok, hist_ok;
60     int is_reclass;
61     RASTER_MAP_TYPE data_type;
62     struct Reclass reclass;
63     struct GModule *module;
64     struct Option *opt1;
65     struct Flag *gflag, *rflag, *eflag, *hflag, *sflag;
66 
67     /* Initialize GIS Engine */
68     G_gisinit(argv[0]);
69 
70     module = G_define_module();
71     G_add_keyword(_("raster"));
72     G_add_keyword(_("metadata"));
73     G_add_keyword(_("extent"));
74     G_add_keyword(_("history"));
75     module->description =
76 	_("Outputs basic information about a raster map.");
77 
78     opt1 = G_define_standard_option(G_OPT_R_MAP);
79 
80     gflag = G_define_flag();
81     gflag->key = 'g';
82     gflag->description = _("Print raster array information in shell script style");
83 
84     rflag = G_define_flag();
85     rflag->key = 'r';
86     rflag->description = _("Print range in shell script style");
87 
88     sflag = G_define_flag();
89     sflag->key = 's';
90     sflag->description = _("Print stats in shell script style");
91 
92     eflag = G_define_flag();
93     eflag->key = 'e';
94     eflag->description = _("Print extended metadata information in shell script style");
95 
96     hflag = G_define_flag();
97     hflag->key = 'h';
98     hflag->description = _("Print raster history instead of info");
99 
100     if (G_parser(argc, argv))
101 	exit(EXIT_FAILURE);
102 
103     if (hflag->answer && (gflag->answer || rflag->answer || sflag->answer || eflag->answer))
104         G_fatal_error(_("Flags -%c and -%c/%c/%c are mutually exclusive"),
105                       hflag->key, gflag->key, rflag->key, eflag->key);
106 
107     name = G_store(opt1->answer);
108     if ((mapset = G_find_raster2(name, "")) == NULL)
109 	G_fatal_error(_("Raster map <%s> not found"), name);
110 
111     Rast_get_cellhd(name, "", &cellhd);
112     cats_ok = Rast_read_cats(name, "", &cats) >= 0;
113     title = Rast_get_cats_title(&cats);
114     hist_ok = Rast_read_history(name, "", &hist) >= 0;
115     is_reclass = Rast_get_reclass(name, "", &reclass);
116     data_type = Rast_map_type(name, "");
117 
118     units = Rast_read_units(name, "");
119 
120     vdatum = Rast_read_vdatum(name, "");
121 
122     /*Check the Timestamp */
123     time_ok = G_read_raster_timestamp(name, "", &ts) > 0;
124     /*Check for valid entries, show none if no timestamp available */
125     if (time_ok) {
126 	if (ts.count > 0)
127 	    first_time_ok = 1;
128 	if (ts.count > 1)
129 	    second_time_ok = 1;
130     }
131 
132     out = stdout;
133 
134     if (eflag->answer || (!gflag->answer && !rflag->answer && !sflag->answer && !hflag->answer)) {
135 	title = "";
136 	/* empty title by default */
137 	/* use title from category file as the primary (and only) title */
138 	if (cats_ok)
139 	    title = cats.title;
140 	/* only use hist file title if there is none in the category file */
141 	if ((!title || title[0] == '\0') && hist_ok) {
142 	    title = Rast_get_history(&hist, HIST_TITLE);
143 	    /* if the title is the same as name of the map, don't use it */
144 	    if (strcmp(title, name) == 0)
145 		title = "";
146 	}
147     }
148 
149     if (!gflag->answer && !rflag->answer && !sflag->answer &&
150 	!eflag->answer && !hflag->answer) {
151 	divider('+');
152 
153 	compose_line(out, "Map:      %-29.29s  Date: %s", name,
154 		     hist_ok ? Rast_get_history(&hist, HIST_MAPID) : "??");
155 	compose_line(out, "Mapset:   %-29.29s  Login of Creator: %s",
156 		     mapset, hist_ok ? Rast_get_history(&hist, HIST_CREATOR) : "??");
157 	compose_line(out, "Location: %s", G_location());
158 	compose_line(out, "DataBase: %s", G_gisdbase());
159 	compose_line(out, "Title:    %s", title);
160 
161 	/* This shows the TimeStamp */
162 	if (time_ok && (first_time_ok || second_time_ok)) {
163 	    G_format_timestamp(&ts, timebuff);
164 	    compose_line(out, "Timestamp: %s", timebuff);
165 	}
166 	else {
167 	    compose_line(out, "Timestamp: none");
168 	}
169 
170 	divider('|');
171 	printline("");
172 
173 	if (cats_ok)
174 	    format_double((double)cats.num, tmp1);
175 
176 	compose_line(out,
177 		     "  Type of Map:  %-20.20s Number of Categories: %-9s",
178 		     hist_ok ? Rast_get_history(&hist, HIST_MAPTYPE) : "??", cats_ok ? tmp1 : "??");
179 
180 	compose_line(out, "  Data Type:    %s",
181 		     (data_type == CELL_TYPE ? "CELL" :
182 		      (data_type == DCELL_TYPE ? "DCELL" :
183 		       (data_type == FCELL_TYPE ? "FCELL" : "??"))));
184 
185 	/* For now hide these unless they exist to keep the noise low. In
186 	 *   future when the two are used more widely they can be printed
187 	 *   along with the standard set. */
188 	if (units || vdatum)
189 	    compose_line(out, "  Data Units:   %-20.20s Vertical datum: %s",
190 			 units ? units : "(none)", vdatum ? vdatum : "(none)");
191 
192 	{
193 	    compose_line(out, "  Rows:         %d", cellhd.rows);
194 	    compose_line(out, "  Columns:      %d", cellhd.cols);
195 	    compose_line(out, "  Total Cells:  %jd",
196 			 (grass_int64)cellhd.rows * cellhd.cols);
197 
198 	    /* This is printed as a guide to what the following eastings and
199 	     * northings are printed in. This data is NOT from the values
200 	     * stored in the map's Cell_head */
201 	    if (G_projection() == PROJECTION_UTM) {
202 		compose_line(out, "       Projection: %s (zone %d)",
203 			     G_database_projection_name(), G_zone());
204 	    }
205 	    else {
206 		compose_line(out, "       Projection: %s",
207 			     G_database_projection_name());
208 	    }
209 
210 	    G_format_northing(cellhd.north, tmp1, cellhd.proj);
211 	    G_format_northing(cellhd.south, tmp2, cellhd.proj);
212 	    G_format_resolution(cellhd.ns_res, tmp3, cellhd.proj);
213 	    compose_line(out, "           N: %10s    S: %10s   Res: %5s",
214 			 tmp1, tmp2, tmp3);
215 
216 	    G_format_easting(cellhd.east, tmp1, cellhd.proj);
217 	    G_format_easting(cellhd.west, tmp2, cellhd.proj);
218 	    G_format_resolution(cellhd.ew_res, tmp3, cellhd.proj);
219 	    compose_line(out, "           E: %10s    W: %10s   Res: %5s",
220 			 tmp1, tmp2, tmp3);
221 
222 	    if (data_type == CELL_TYPE) {
223 		int ret;
224 		CELL min, max;
225 
226 		/* print range only if available */
227 		ret = Rast_read_range(name, "", &crange);
228 		if (ret == 2)
229 		    compose_line(out,
230 				 "  Range of data:    min = NULL  max = NULL");
231 		else if (ret > 0) {
232 		    Rast_get_range_min_max(&crange, &min, &max);
233 
234 		    if (Rast_is_c_null_value(&min)) {
235 			compose_line(out,
236 				     "  Range of data:    min = NULL  max = NULL");
237 		    }
238 		    else {
239 			compose_line(out,
240 				     "  Range of data:    min = %i  max = %i",
241 				     min, max);
242 		    }
243 		}
244 	    }
245 	    else {
246 		int ret;
247 		DCELL min, max;
248 
249 		/* print range only if available */
250 		ret = Rast_read_fp_range(name, "", &range);
251 		if (ret == 2) {
252 		    compose_line(out,
253 				 "  Range of data:    min = NULL  max = NULL");
254 		}
255 		else if (ret > 0) {
256 		    Rast_get_fp_range_min_max(&range, &min, &max);
257 
258 		    if (Rast_is_d_null_value(&min)) {
259 			compose_line(out,
260 				     "  Range of data:    min = NULL  max = NULL");
261 		    }
262 		    else {
263 			if (data_type == FCELL_TYPE) {
264 			    compose_line(out, "  Range of data:    min = %.7g  max = %.7g",
265 					 min, max);
266 			}
267 			else {
268 			    compose_line(out, "  Range of data:    min = %.15g  max = %.15g",
269 					 min, max);
270 			}
271 		    }
272 		}
273 	    }
274 	}
275 
276 	printline("");
277 
278 	if (hist_ok) {
279 	    if (Rast_get_history(&hist, HIST_DATSRC_1)[0] != '\0' ||
280 		Rast_get_history(&hist, HIST_DATSRC_2)[0] != '\0') {
281 		printline("  Data Source:");
282 		compose_line(out, "   %s", Rast_get_history(&hist, HIST_DATSRC_1));
283 		compose_line(out, "   %s", Rast_get_history(&hist, HIST_DATSRC_2));
284 		printline("");
285 	    }
286 
287 	    printline("  Data Description:");
288 	    compose_line(out, "   %s", Rast_get_history(&hist, HIST_KEYWRD));
289 
290 	    printline("");
291 	    if (Rast_history_length(&hist)) {
292 		printline("  Comments:  ");
293 
294 		for (i = 0; i < Rast_history_length(&hist); i++)
295 		    compose_line(out, "   %s", Rast_history_line(&hist, i));
296 	    }
297 
298 	    printline("");
299 	}
300 
301 	if (is_reclass > 0) {
302 	    int first = 1;
303 
304 	    divider('|');
305 
306 	    compose_line(out, "  Reclassification of [%s] in mapset [%s]",
307 			 reclass.name, reclass.mapset);
308 
309 	    printline("");
310 	    printline("        Category        Original categories");
311 	    printline("");
312 
313 	    for (i = 0; i < reclass.num; i++) {
314 		CELL x = reclass.table[i];
315 
316 		if (Rast_is_c_null_value(&x))
317 		    continue;
318 		if (first || x < mincat)
319 		    mincat = x;
320 		if (first || x > maxcat)
321 		    maxcat = x;
322 		first = 0;
323 	    }
324 
325 	    if (!first)
326 		for (cat = mincat; cat <= maxcat; cat++) {
327 		    char text[80];
328 		    char *num;
329 		    int next;
330 
331 		    if (cat == 0)
332 			continue;
333 		    if (G_asprintf(&num, "%5ld", (long)cat) < 1)
334 			G_fatal_error(_("Cannot allocate memory for string"));
335 
336 		    next = 0;
337 		    do {
338 			next = reclass_text(text, cat, &reclass, next);
339 			compose_line(out, "     %5s              %s", num,
340 				     text);
341 			*num = 0;
342 		    }
343 		    while (next >= 0);
344 		}
345 	}
346 	divider('+');
347 
348 	fprintf(out, "\n");
349     }
350     else {	/* g, r, s, e, or h flags */
351 	int need_range, have_range, need_stats, have_stats;
352 
353 	need_range = rflag->answer;
354 	need_stats = sflag->answer;
355 	if (need_stats)
356 	    need_range = 1;
357 
358 	have_range = have_stats = 0;
359 	if (need_range) {
360 	    if (data_type == CELL_TYPE) {
361 		if (Rast_read_range(name, "", &crange) > 0)
362 		    have_range = 1;
363 	    }
364 	    else {
365 		if (Rast_read_fp_range(name, "", &range) > 0)
366 		    have_range = 1;
367 	    }
368 	}
369 	if (need_stats) {
370 	    if (Rast_read_rstats(name, mapset, &rstats) > 0)
371 		have_stats = 1;
372 	}
373 
374 	if ((need_stats && !have_stats) || (need_range && !have_range)) {
375 	    DCELL *dbuf, val, min, max;
376 	    int fd, r, c;
377 	    int first = 1;
378 
379 	    Rast_set_input_window(&cellhd);
380 	    dbuf = Rast_allocate_d_input_buf();
381 	    fd = Rast_open_old(name, mapset);
382 	    min = max = 0;
383 
384 	    for (r = 0; r < cellhd.rows; r++) {
385 		Rast_get_d_row_nomask(fd, dbuf, r);
386 		for (c = 0; c < cellhd.cols; c++) {
387 		    val = dbuf[c];
388 		    if (Rast_is_d_null_value(&val))
389 			continue;
390 		    if (first) {
391 			rstats.sum = val;
392 			rstats.sumsq = (DCELL) val * val;
393 			rstats.count = 1;
394 			min = max = val;
395 
396 			first = 0;
397 		    }
398 		    else {
399 			rstats.sum += val;
400 			rstats.sumsq += (DCELL) val * val;
401 			rstats.count += 1;
402 			if (min > val)
403 			    min = val;
404 			if (max < val)
405 			    max = val;
406 		    }
407 		}
408 	    }
409 	    Rast_close(fd);
410 	    G_free(dbuf);
411 
412 	    if (data_type == CELL_TYPE) {
413 		Rast_init_range(&crange);
414 		if (rstats.count > 0) {
415 		    Rast_update_range((CELL) min, &crange);
416 		    Rast_update_range((CELL) max, &crange);
417 		}
418 	    }
419 	    else {
420 		Rast_init_fp_range(&range);
421 		if (rstats.count > 0) {
422 		    Rast_update_fp_range(min, &range);
423 		    Rast_update_fp_range(max, &range);
424 		}
425 	    }
426 	}
427 
428 	if (gflag->answer) {
429 	    G_format_northing(cellhd.north, tmp1, -1);
430 	    G_format_northing(cellhd.south, tmp2, -1);
431 	    fprintf(out, "north=%s\n", tmp1);
432 	    fprintf(out, "south=%s\n", tmp2);
433 
434 	    G_format_easting(cellhd.east, tmp1, -1);
435 	    G_format_easting(cellhd.west, tmp2, -1);
436 	    fprintf(out, "east=%s\n", tmp1);
437 	    fprintf(out, "west=%s\n", tmp2);
438 
439 	    G_format_resolution(cellhd.ns_res, tmp3, -1);
440 	    fprintf(out, "nsres=%s\n", tmp3);
441 
442 	    G_format_resolution(cellhd.ew_res, tmp3, -1);
443 	    fprintf(out, "ewres=%s\n", tmp3);
444 
445             fprintf(out, "rows=%d\n", cellhd.rows);
446             fprintf(out, "cols=%d\n", cellhd.cols);
447 
448             fprintf(out, "cells=%jd\n",
449                     (grass_int64)cellhd.rows * cellhd.cols);
450 
451 	    fprintf(out, "datatype=%s\n",
452 		    (data_type == CELL_TYPE ? "CELL" :
453 		     (data_type == DCELL_TYPE ? "DCELL" :
454 		      (data_type == FCELL_TYPE ? "FCELL" : "??"))));
455             if (cats_ok)
456                 format_double((double)cats.num, tmp1);
457 	    fprintf(out, "ncats=%s\n",
458                     cats_ok ? tmp1 : "??");
459 	}
460 
461 	if (rflag->answer || sflag->answer) {
462 	    if (data_type == CELL_TYPE) {
463 		CELL min, max;
464 
465 		Rast_get_range_min_max(&crange, &min, &max);
466 		if (Rast_is_c_null_value(&min)) {
467 		    fprintf(out, "min=NULL\n");
468 		    fprintf(out, "max=NULL\n");
469 		}
470 		else {
471 		    fprintf(out, "min=%i\n", min);
472 		    fprintf(out, "max=%i\n", max);
473 		}
474 	    }
475 	    else {
476 		DCELL min, max;
477 
478 		Rast_get_fp_range_min_max(&range, &min, &max);
479 		if (Rast_is_d_null_value(&min)) {
480 		    fprintf(out, "min=NULL\n");
481 		    fprintf(out, "max=NULL\n");
482 		}
483 		else {
484 		    if (data_type == FCELL_TYPE) {
485 			fprintf(out, "min=%.7g\n", min);
486 			fprintf(out, "max=%.7g\n", max);
487 		    }
488 		    else {
489 			fprintf(out, "min=%.15g\n", min);
490 			fprintf(out, "max=%.15g\n", max);
491 		    }
492 		}
493 	    }
494 	}
495 
496 	if (sflag->answer) {
497 
498 	    if (!gflag->answer) {
499 		/* always report total number of cells */
500 		fprintf(out, "cells=%jd\n",
501 			(grass_int64)cellhd.rows * cellhd.cols);
502 	    }
503 
504 	    if (rstats.count > 0) {
505 		double mean, sd;
506 
507 		mean = (double)(rstats.sum / rstats.count);
508 		sd = sqrt(rstats.sumsq / rstats.count - (mean * mean));
509 
510 
511 		if (data_type == CELL_TYPE) {
512 		    CELL min, max;
513 
514 		    Rast_get_range_min_max(&crange, &min, &max);
515 		    if (min == max) {
516 			mean = min;
517 			sd = 0;
518 		    }
519 		}
520 		else {
521 		    DCELL min, max;
522 
523 		    Rast_get_fp_range_min_max(&range, &min, &max);
524 		    if (min == max) {
525 			mean = min;
526 			sd = 0;
527 		    }
528 		}
529 
530 		fprintf(out, "n=%jd\n", rstats.count);
531 		fprintf(out, "mean=%.15g\n", mean);
532 		fprintf(out, "stddev=%.15g\n", sd);
533 		fprintf(out, "sum=%.15g\n", rstats.sum);
534 	    }
535 	    else {
536 		fprintf(out, "n=0\n");
537 		fprintf(out, "mean=NULL\n");
538 		fprintf(out, "stddev=NULL\n");
539 		fprintf(out, "sum=NULL\n");
540 	    }
541 	}
542 
543 	if (eflag->answer) {
544             char xname[GNAME_MAX], xmapset[GMAPSET_MAX];
545             G_unqualified_name(name, mapset, xname, xmapset);
546 
547             fprintf(out, "map=%s\n", xname);
548 	    fprintf(out, "maptype=%s\n", hist_ok ? Rast_get_history(&hist, HIST_MAPTYPE) : "??");
549             fprintf(out, "mapset=%s\n", mapset);
550             fprintf(out, "location=%s\n", G_location());
551             fprintf(out, "database=%s\n", G_gisdbase());
552             fprintf(out, "date=\"%s\"\n", hist_ok ? Rast_get_history(&hist, HIST_MAPID) : "??");
553             fprintf(out, "creator=\"%s\"\n", hist_ok ? Rast_get_history(&hist, HIST_CREATOR) : "??");
554 	    fprintf(out, "title=\"%s\"\n", title);
555 	    if (time_ok && (first_time_ok || second_time_ok)) {
556 
557 		G_format_timestamp(&ts, timebuff);
558 
559 		/*Create the r.info timestamp string */
560 		fprintf(out, "timestamp=\"%s\"\n", timebuff);
561 
562 	    }
563 	    else {
564 		fprintf(out, "timestamp=\"none\"\n");
565 	    }
566 	    fprintf(out, "units=%s\n", units ? units : "\"none\"");
567 	    fprintf(out, "vdatum=%s\n", vdatum ? vdatum : "\"none\"");
568 	    fprintf(out, "source1=\"%s\"\n", hist_ok ? Rast_get_history(&hist, HIST_DATSRC_1) : "\"none\"");
569 	    fprintf(out, "source2=\"%s\"\n", hist_ok ? Rast_get_history(&hist, HIST_DATSRC_2) : "\"none\"");
570 	    fprintf(out, "description=\"%s\"\n", hist_ok ? Rast_get_history(&hist, HIST_KEYWRD) : "\"none\"");
571 	    if (Rast_history_length(&hist)) {
572 		fprintf(out, "comments=\"");
573 		for (i = 0; i < Rast_history_length(&hist); i++)
574 		    fprintf(out, "%s", Rast_history_line(&hist, i));
575                 fprintf(out, "\"\n");
576 	    }
577 	}
578 
579 	if (hflag->answer) {
580 	    if (hist_ok) {
581 		fprintf(out, "Data Source:\n");
582 		fprintf(out, "   %s\n", Rast_get_history(&hist, HIST_DATSRC_1));
583 		fprintf(out, "   %s\n", Rast_get_history(&hist, HIST_DATSRC_2));
584 		fprintf(out, "Data Description:\n");
585 		fprintf(out, "   %s\n", Rast_get_history(&hist, HIST_KEYWRD));
586 		if (Rast_history_length(&hist)) {
587 		    fprintf(out, "Comments:\n");
588 		    for (i = 0; i < Rast_history_length(&hist); i++)
589 			fprintf(out, "   %s\n", Rast_history_line(&hist, i));
590 		}
591 	    }
592 	}
593     }				/* else rflag or sflag or tflag or gflag or hflag or mflag */
594 
595     return EXIT_SUCCESS;
596 }
597 
598 
format_double(const double value,char * buf)599 static void format_double(const double value, char *buf)
600 {
601     sprintf(buf, "%.8f", value);
602     G_trim_decimal(buf);
603 }
604 
605 
compose_line(FILE * out,const char * fmt,...)606 static void compose_line(FILE * out, const char *fmt, ...)
607 {
608     char *line = NULL;
609     va_list ap;
610 
611     va_start(ap, fmt);
612 
613     if (G_vasprintf(&line, fmt, ap) <= 0)
614 	G_fatal_error(_("Cannot allocate memory for string"));
615 
616     va_end(ap);
617 
618     printline(line);
619     G_free(line);
620 }
621