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