/*-------------------------------------------------------------------- * $Id: pshistogram.c,v 1.4.4.2 2002/02/27 17:41:10 pwessel Exp $ * * Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith * See COPYING file for copying and redistribution conditions. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contact info: gmt.soest.hawaii.edu *--------------------------------------------------------------------*/ /* * pshistogram.c -- a program for plotting histograms * * * Author: Walter H. F. Smith * Date: 15 February, 1988 * * Updated to v2.0 5-May-1991 Paul Wessel * Updated to v3.0 1-Jan-1995 Paul Wessel * Updated to v3.1 13-Jun-1998 Paul Wessel * Updated to v3.3 23-Apr-1999 Paul Wessel * Now uses two passes to get accurate mean and stdev * Version: 3.4.1 */ #include "gmt.h" #define COUNTS 0 #define FREQ_PCT 1 #define LOG_COUNTS 2 #define LOG_FREQ_PCT 3 #define LOG10_COUNTS 4 #define LOG10_FREQ_PCT 5 double yy0, yy1, xmin, xmax; float *x; int *boxh; int n = 0; int n_boxes = 0; int n_counted = 0; double box_width = 0.0; double west = 0.0, east = 0.0, south = 0.0, north = 0.0; float stats[6]; FILE *fp_in = NULL; char buffer[BUFSIZ], format[BUFSIZ]; int center_box = FALSE, cumulative = FALSE, draw_outline = FALSE; int second = FALSE, hist_type = COUNTS; struct GMT_PEN pen; struct GMT_FILL fill; int read_data(void); int fill_boxes(void); int plot_boxes(BOOLEAN stairs, BOOLEAN flip_to_y); int get_loc_scl(float *x, int n, float *stats); main (int argc, char **argv) { int i; BOOLEAN inquire = FALSE, error = FALSE, automatic = FALSE; int fill_bar = FALSE, stairs = FALSE, flip_to_y = FALSE, dump_boxes = FALSE; double tmp; char string[BUFSIZ], *text; argc = GMT_begin (argc, argv); GMT_init_pen (&pen, GMT_PENWIDTH); GMT_init_fill (&fill, -1, 0, 0); /* Do not fill is default */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { /* Common parameters */ case 'B': case 'H': case 'J': case 'K': case 'O': case 'P': case 'U': case 'V': case 'X': case 'x': case 'Y': case 'y': case 'c': case '\0': error += GMT_get_common_args (argv[i], &west, &east, &south, &north); break; /* Supplemental parameters */ case 'b': error += GMT_io_selection (&argv[i][2]); break; case '2': second = TRUE; break; case 'A': flip_to_y = TRUE; break; case 'C': center_box = TRUE; break; case 'E': sscanf (&argv[i][2], "%lf/%lf", &z_project.view_azimuth, &z_project.view_elevation); break; case 'G': if (GMT_getfill (&argv[i][2], &fill)) { GMT_fill_syntax ('G'); error++; } fill_bar = TRUE; break; case 'L': /* Set line attributes */ if (GMT_getpen (&argv[i][2], &pen)) { GMT_pen_syntax ('L'); error++; } draw_outline = TRUE; break; case 'I': inquire = TRUE; if (argv[i][2] == 'o') dump_boxes = TRUE; break; case 'Q': cumulative = TRUE; break; case 'S': stairs = TRUE; break; case 'R': project_info.region_supplied = TRUE; strcpy (string, &argv[i][2]); text = strtok (string, "/"); west = GMT_ddmmss_to_degree (text); text = strtok (CNULL, "/"); east = GMT_ddmmss_to_degree (text); text = strtok (CNULL, "/"); south = GMT_ddmmss_to_degree (text); text = strtok (CNULL, "/"); north = GMT_ddmmss_to_degree (text); break; case 'W': box_width = atof (&argv[i][2]); break; case 'Z': hist_type = atoi (&argv[i][2]); break; default: error = TRUE; GMT_default_error (argv[i][1]); break; } } else { if ((fp_in = GMT_fopen(argv[i], GMT_io.r_mode)) == NULL) { fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, argv[i]); exit (EXIT_FAILURE); } } } if (argc == 1 || GMT_quick) { fprintf (stderr,"pshistogram %s - Calculate and plot histograms\n\n", GMT_VERSION); fprintf (stderr, "usage: pshistogram [file] -Jx|X -W [-2] [-B] [-C] [-Eaz/el] [-G]\n"); fprintf (stderr, "\t[-H[]] [-I[o]] [-K] [-L] [-O] [-P] [-Q] [-Rw/e/s/n] [-S] [-U[text]] [-V]\n"); fprintf (stderr, "\t[-X] [-Y] [-Z[type]] [-c] [-bi[s][]]\n\n"); if (GMT_quick) exit (EXIT_FAILURE); fprintf (stderr, "\t-Jx|X for linear projection. Scale in %s/units (or width in %s)\n", GMT_unit_names[gmtdefs.measure_unit], GMT_unit_names[gmtdefs.measure_unit]); fprintf (stderr, "\t Use / to specify separate x/y scaling.\n"); fprintf (stderr, "\t If -JX is used then give axes lengths in %s rather than scales\n", GMT_unit_names[gmtdefs.measure_unit]); fprintf (stderr, "\t-W sets the bin width\n"); fprintf (stderr, "\n\tOPTIONS:\n"); fprintf (stderr, "\t-2 use second rather than first column\n"); GMT_explain_option ('b'); fprintf (stderr, "\t-A will plot horizontal bars [Default is vertical]\n"); fprintf (stderr, "\t-C will center the bins\n"); fprintf (stderr, "\t-E set azimuth and elevation of viewpoint for 3-D perspective [180/90]\n"); fprintf (stderr, "\t-G Specify color or pattern for columns. fill can be either\n"); fprintf (stderr, "\t 1) (each 0-255) for color or (0-255) for gray-shade [0]\n"); fprintf (stderr, "\t 2) p[or P]/ for predefined patterns (1-90).\n"); GMT_explain_option ('H'); fprintf (stderr, "\t-I will inquire about min/max x and y. No plotting is done\n"); fprintf (stderr, "\t Append o to output the resulting x, y data\n"); GMT_explain_option ('K'); fprintf (stderr, "\t-L is penwidth. Specify r/g/b for colored line.\n"); GMT_explain_option ('O'); GMT_explain_option ('P'); fprintf (stderr, "\t-Q plot a cumulative histogram\n"); GMT_explain_option ('r'); fprintf (stderr, "\t If neither -R nor -I are set, w/e/s/n will be based on input data\n"); fprintf (stderr, "\t-S Draws a stairs-step diagram instead of histogram.\n"); GMT_explain_option ('U'); GMT_explain_option ('V'); GMT_explain_option ('X'); fprintf (stderr, "\t-Z to choose type of vertical axis. Select from\n"); fprintf (stderr, "\t 0 - Counts [Default]\n"); fprintf (stderr, "\t 1 - Frequency percent\n"); fprintf (stderr, "\t 2 - Log (1+counts)\n"); fprintf (stderr, "\t 3 - Log (1+frequency percent)\n"); fprintf (stderr, "\t 4 - Log10 (1+counts)\n"); fprintf (stderr, "\t 5 - Log10 (1+frequency percent)\n"); GMT_explain_option ('c'); GMT_explain_option ('i'); GMT_explain_option ('n'); fprintf (stderr, "\t Default is 2 input columns\n"); GMT_explain_option ('.'); exit (EXIT_FAILURE); } if (!inquire && (project_info.projection < 0 || project_info.projection > 9)) { fprintf (stderr, "%s: GMT SYNTAX ERROR -J option: Only linear projection supported.\n", GMT_program); error++; } if (z_project.view_azimuth > 360.0 || z_project.view_elevation <= 0.0 || z_project.view_elevation > 90.0) { fprintf (stderr, "%s: GMT SYNTAX ERROR -E option: Enter azimuth in 0-360 range, elevation in 0-90 range\n", GMT_program); error++; } if (hist_type < COUNTS || hist_type > LOG10_FREQ_PCT) { fprintf (stderr, "%s: GMT SYNTAX ERROR -Z option: histogram type must be in 0-5 range\n", GMT_program); error++; } if (box_width <= 0.0) { fprintf (stderr, "%s: GMT SYNTAX ERROR -W option: bin width must be nonzero\n", GMT_program); error++; } /* Now must specify either fill color with -G or outline pen with -L; JLL */ if (!(inquire || fill_bar || draw_outline)) { fprintf (stderr, "%s: Must specify either fill color (-G), outline pen attributes (-L), or both.\n", GMT_program); error++; } if (GMT_io.binary[0] && gmtdefs.io_header) { fprintf (stderr, "%s: GMT SYNTAX ERROR. Binary input data cannot have header -H\n", GMT_program); error++; } if (GMT_io.binary[0] && GMT_io.ncol[0] == 0) GMT_io.ncol[0] = 2; if (GMT_io.binary[0] && GMT_io.ncol[0] < 0) { fprintf (stderr, "%s: GMT SYNTAX ERROR. Must specify number of columns in binary input data (-bi)\n", GMT_program); error++; } if (error) exit (EXIT_FAILURE); GMT_put_history (argc, argv); /* Update .gmtcommands */ if (GMT_io.binary[0] && gmtdefs.verbose) { char *type[2] = {"double", "single"}; fprintf (stderr, "%s: Expects %d-column %s-precision binary data\n", GMT_program, GMT_io.ncol[0], type[GMT_io.single_precision[0]]); } if (inquire) gmtdefs.verbose = TRUE; if (!inquire && !project_info.region_supplied) automatic = TRUE; if (fp_in == NULL) { fp_in = GMT_stdin; #ifdef SET_IO_MODE GMT_setmode (0); #endif } if ( read_data() ) { fprintf (stderr, "%s: Fatal error, read only 0 points.\n", GMT_program); exit (EXIT_FAILURE); } if (gmtdefs.verbose) { sprintf (format, "%%s: Extreme values of the data :\t%s\t%s\n", gmtdefs.d_format, gmtdefs.d_format); fprintf (stderr, format, GMT_program, x[0], x[n-1]); sprintf (format, "%%s: Locations: L2, L1, LMS; Scales: L2, L1, LMS\t%s\t%s\t%s\t%s\t%s\t%s\n", gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format); fprintf (stderr, format, GMT_program, stats[0], stats[1], stats[2], stats[3], stats[4], stats[5]); } if (west == east) { /* Set automatic x range [ and tickmarks] */ if (frame_info.anot_int[0] == 0.0) { tmp = pow (10.0, floor (d_log10 (xmax-xmin))); if (((xmax-xmin) / tmp) < 3.0) tmp *= 0.5; } else tmp = frame_info.anot_int[0]; west = floor (xmin / tmp) * tmp; east = ceil (xmax / tmp) * tmp; if (frame_info.anot_int[0] == 0.0) { frame_info.anot_int[0] = frame_info.frame_int[0] = tmp; frame_info.plot = TRUE; } } if ( fill_boxes () ) { fprintf (stderr, "%s: Fatal error during box fill.\n", GMT_program); exit (EXIT_FAILURE); } if (gmtdefs.verbose) { sprintf (format, "\n%%s: min/max values are :\t%s\t%s\t%s\t%s\n", gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format); fprintf (stderr, format, GMT_program, xmin, xmax, yy0, yy1); } if (inquire) { /* Only info requested, quit before plotting */ if (dump_boxes) { int ibox; double xx, yy; sprintf (format, "%s\t%s\n", gmtdefs.d_format, gmtdefs.d_format); for (ibox = 0; ibox < n_boxes; ibox++) { if (boxh[ibox] == 0) continue; xx = west + ibox * box_width; if (center_box) xx -= (0.5 * box_width); if (hist_type == LOG_COUNTS) yy = d_log1p( (double)boxh[ibox]); else if (hist_type == LOG10_COUNTS) yy = d_log101p( (double)boxh[ibox]); else if (hist_type == FREQ_PCT) yy = (100.0 * boxh[ibox]) / n_counted; else if (hist_type == LOG_FREQ_PCT) yy = d_log1p( 100.0 * boxh[ibox] / n_counted ); else if (hist_type == LOG10_FREQ_PCT) yy = d_log101p( 100.0 * boxh[ibox] / n_counted ); else yy = boxh[ibox]; fprintf (GMT_stdout, format, xx, yy); } } GMT_free ((void *) x); GMT_free ((void *) boxh); exit (EXIT_SUCCESS); } if (automatic) { /* Set up s/n based on 'clever' rounding up of the minmax values */ project_info.region = TRUE; south = 0.0; if (frame_info.anot_int[1] == 0.0) { tmp = pow (10.0, floor (d_log10 (yy1))); if ((yy1 / tmp) < 3.0) tmp *= 0.5; } else tmp = frame_info.anot_int[1]; north = ceil (yy1 / tmp) * tmp; if (frame_info.anot_int[1] == 0.0) { /* Tickmarks not set */ frame_info.anot_int[1] = frame_info.frame_int[1] = tmp; frame_info.plot = TRUE; } if (project_info.pars[0] == 0.0) { /* Must give default xscale */ project_info.pars[0] = gmtdefs.x_axis_length / (east - west); project_info.projection = LINEAR; } if (project_info.pars[1] == 0.0) { /* Must give default yscale */ project_info.pars[1] = gmtdefs.y_axis_length / north; project_info.projection = LINEAR; } } if (automatic && gmtdefs.verbose) { sprintf (format, "%%s: Use w/e/s/n = %s/%s/%s/%s and x-tick/y-tick = %s/%s\n", gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format); fprintf (stderr, format, GMT_program, west, east, south, north, frame_info.anot_int[0], frame_info.anot_int[1]); } if (flip_to_y) { char buffer[128]; d_swap (frame_info.anot_int[0], frame_info.anot_int[1]); d_swap (frame_info.frame_int[0], frame_info.frame_int[1]); d_swap (frame_info.grid_int[0], frame_info.grid_int[1]); text = frame_info.label[0]; strcpy (buffer, frame_info.label[0]); strcpy(frame_info.label[0], frame_info.label[1]); strcpy (frame_info.label[1], buffer); GMT_map_setup (south, north, west, east); } else GMT_map_setup (west, east, south, north); ps_plotinit (CNULL, gmtdefs.overlay, gmtdefs.page_orientation, gmtdefs.x_origin, gmtdefs.y_origin, gmtdefs.global_x_scale, gmtdefs.global_y_scale, gmtdefs.n_copies, gmtdefs.dpi, GMT_INCH, gmtdefs.paper_width, gmtdefs.page_rgb, GMT_epsinfo (argv[0])); GMT_echo_command (argc, argv); if (gmtdefs.unix_time) GMT_timestamp (argc, argv); if (project_info.three_D) ps_transrotate (-z_project.xmin, -z_project.ymin, 0.0); GMT_map_clip_on (GMT_no_rgb, 3); if ( plot_boxes (stairs, flip_to_y) ) { fprintf (stderr, "%s: Fatal error during box plotting.\n", GMT_program); exit (EXIT_FAILURE); } GMT_map_clip_off(); if (frame_info.plot) GMT_map_basemap (); if (project_info.three_D) ps_rotatetrans (z_project.xmin, z_project.ymin, 0.0); ps_plotend (gmtdefs.last_page); GMT_free ((void *) x); GMT_free ((void *) boxh); GMT_end (argc, argv); } int read_data (void) { int i, n_alloc = GMT_CHUNK, n_expected_fields, n_fields; double *in; x = (float *) GMT_memory (VNULL, (size_t)n_alloc , sizeof (float), GMT_program); n = 0; xmin = DBL_MAX; xmax = -DBL_MAX; n_expected_fields = (GMT_io.ncol[0]) ? GMT_io.ncol[0] : 1 + second; if (gmtdefs.io_header) for (i = 0; i < gmtdefs.n_header_recs; i++) GMT_fgets (buffer, BUFSIZ, fp_in); while ((n_fields = GMT_input (fp_in, &n_expected_fields, &in)) >= 0 && !(GMT_io.status & GMT_IO_EOF)) { if (GMT_io.status & GMT_IO_MISMATCH) { fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, 2, n); exit (EXIT_FAILURE); } x[n] = (float)((second) ? in[1] : in[0]); if (!GMT_is_fnan (x[n])) n++; if (n == n_alloc) { n_alloc += GMT_CHUNK; x = (float *) GMT_memory ((void *)x, (size_t) n_alloc, sizeof (float), GMT_program); } xmin = MIN (xmin, x[n-1]); xmax = MAX (xmax, x[n-1]); } if (gmtdefs.verbose) { fprintf (stderr,"%s: %d points read\n", GMT_program, n); } x = (float *) GMT_memory ((void *)x, (size_t)n , sizeof (float), GMT_program); if (fp_in != GMT_stdin) GMT_fclose (fp_in); get_loc_scl (x, n, stats); return( !(n) ); } int fill_boxes (void) { double add_half = 0.0; int b0, b1, i, ibox, count_sum; n_boxes = (int)ceil( (east - west) / box_width); if (center_box) { n_boxes++; add_half = 0.5; } if (n_boxes <= 0) return (-1); boxh = (int *) GMT_memory (VNULL, (size_t)n_boxes , sizeof (int), GMT_program); n_counted = 0; /* First fill boxes with counts */ for (i = 0; i < n; i++) { ibox = (int)floor( ( (x[i] - west) / box_width) + add_half); if (ibox < 0 || ibox >= n_boxes) continue; boxh[ibox] ++; n_counted++; } if (cumulative) { count_sum = 0; for (ibox = 0; ibox < n_boxes; ibox++) { count_sum += boxh[ibox]; boxh[ibox] = count_sum; } b0 = 0; b1 = count_sum; } else { b0 = n_counted; b1 = 0; for (ibox = 0; ibox < n_boxes; ibox++) { if (b0 > boxh[ibox]) b0 = boxh[ibox]; if (b1 < boxh[ibox]) b1 = boxh[ibox]; } } /* Now find out what the min max y will be */ if (b0) { if (hist_type == LOG_COUNTS) yy0 = d_log1p((double)b0); else if (hist_type == LOG10_COUNTS) yy0 = d_log101p( (double)b0); else if (hist_type == FREQ_PCT) yy0 = (100.0 * b0) / n_counted; else if (hist_type == LOG_FREQ_PCT) yy0 = d_log1p( 100.0 * b0 / n_counted ); else if (hist_type == LOG10_FREQ_PCT) yy0 = d_log101p( 100.0 * b0 / n_counted ); else yy0 = b0; } else { yy0 = 0; } if (b1) { if (hist_type == LOG_COUNTS) yy1 = d_log1p( (double)b1); else if (hist_type == LOG10_COUNTS) yy1 = d_log101p( (double)b1); else if (hist_type == FREQ_PCT) yy1 = (100.0 * b1) / n_counted; else if (hist_type == LOG_FREQ_PCT) yy1 = d_log1p( 100.0 * b1 / n_counted ); else if (hist_type == LOG10_FREQ_PCT) yy1 = d_log101p( 100.0 * b1 / n_counted ); else yy1 = b1; } else { yy1 = 0; } return (0); } int plot_boxes (BOOLEAN stairs, BOOLEAN flip_to_y) { int i, ibox, first = TRUE; double x[4], y[4], xx, yy, *px, *py; if (draw_outline) GMT_setpen (&pen); if (flip_to_y) { px = y; py = x; } else { px = x; py = y; } for (ibox = 0; ibox < n_boxes; ibox++) { if (stairs || boxh[ibox]) { x[0] = west + ibox * box_width; if (center_box) x[0] -= (0.5 * box_width); x[1] = x[0] + box_width; if (x[0] < west) x[0] = west; if (x[1] > east) x[1] = east; x[2] = x[1]; x[3] = x[0]; y[0] = y[1] = south; if (hist_type == LOG_COUNTS) y[2] = d_log1p( (double)boxh[ibox]); else if (hist_type == LOG10_COUNTS) y[2] = d_log101p( (double)boxh[ibox]); else if (hist_type == FREQ_PCT) y[2] = (100.0 * boxh[ibox]) / n_counted; else if (hist_type == LOG_FREQ_PCT) y[2] = d_log1p( 100.0 * boxh[ibox] / n_counted ); else if (hist_type == LOG10_FREQ_PCT) y[2] = d_log101p( 100.0 * boxh[ibox] / n_counted ); else y[2] = boxh[ibox]; y[3] = y[2]; for (i = 0; i < 4; i++) { GMT_geo_to_xy (px[i], py[i], &xx, &yy); if (project_info.three_D) GMT_xyz_to_xy (xx, yy, project_info.z_level, &xx, &yy); px[i] = xx; py[i] = yy; } if (stairs) { if (first) { first = FALSE; ps_plot (px[0], py[0], 3); } ps_plot (px[3], py[3], 2); ps_plot (px[2], py[2], 2); } else GMT_fill (px, py, 4, &fill, draw_outline); } } if (stairs) ps_plot (px[1], py[1], 2); return(0); } int get_loc_scl (float *x, int n, float *stats) { /* Returns stats[] = L2, L1, LMS location, L2, L1, LMS scale */ int i, j; double xsum, x2sum, dx, median, mode, mad, lmsscl; if (n < 3) return(-1); qsort ((void *)x, (size_t)n, sizeof(float), GMT_comp_float_asc); /* Get median */ j = n/2; median = (n%2) ? x[j] : (0.5 * (x[j] + x[j-1])); stats[1] = (float)median; /* Get mode */ GMT_mode_f (x, n, j, 0, &mode); stats[2] = (float)mode; /* Get MAD for L1 */ GMT_getmad_f (x, n, median, &mad); stats[4] = (float)mad; /* Get LMSscale for mode */ GMT_getmad_f (x, n, mode, &lmsscl); stats[5] = (float)lmsscl; /* Calculate mean and stdev in two passes to minimize risk of overflow */ xsum = x2sum = 0.0; for (i = 0; i < n; i++) xsum += x[i]; xsum /= n; /* xsum is now the mean value */ for (i = 0; i < n; i++) { dx = x[i] - xsum; x2sum += (dx * dx); } stats[0] = (float)xsum; stats[3] = (float)sqrt (x2sum / (n - 1)); return (0); }