/*-------------------------------------------------------------------- * $Id: grd2cpt.c,v 1.3.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 *--------------------------------------------------------------------*/ /* * grd2cpt reads a 2d binary gridded grdfile and creates a continous-color- * pallette cpt file, with a non-linear histogram-equalized mapping between * hue and data value. (The linear mapping can be made with makecpt.) * * Creates a cumulative distribution function f(z) describing the data * in the grdfile. f(z) is sampled at z values supplied by the user * [with -S option] or guessed from the sample mean and standard deviation. * f(z) is then found by looping over the grd array for each z and counting * data values <= z. Once f(z) is found then a master cpt table is resampled * based on a normalized f(z). * * Author: Walter H. F. Smith * Date: 12-JAN-1994 * Revised: PW: 12-MAY-1998, for GMT 3.1 * PW: 08-MAR-1998, for GMT 3.2 to allow use of master cptfiles * PW: 08-JUL-2000, for GMT 3.3.5 * Version: 3.4.1 * */ #include "gmt.h" struct CDF_CPT { double z; /* Data value */ double f; /* Cumulative distribution function f(z) */ } *cdf_cpt = NULL; main (int argc, char **argv) { int i, j, nxy, nfound, ngood, ncdf; BOOLEAN error = FALSE, set_limits = FALSE, set_z_vals = FALSE, reverse = FALSE, ok = FALSE, continuous = FALSE; double *z, min_limit, max_limit, z_start, z_stop, z_inc, mean, sd; char *table = CNULL, *grdfile = CNULL, CPT_lis[BUFSIZ], CPT_file[BUFSIZ], format[BUFSIZ]; struct GRD_HEADER grd; float *zdata; FILE *fpc = NULL; argc = GMT_begin (argc, argv); /* Get list of available color tables in GMT /share */ sprintf (CPT_lis, "%s%cshare%cGMT_CPT.lis", GMTHOME, DIR_DELIM, DIR_DELIM); if ((fpc = fopen (CPT_lis, "r")) == NULL) { fprintf (stderr, "%s: ERROR: Cannot open file %s\n", GMT_program, CPT_lis); exit (EXIT_FAILURE); } for (i = 1; !error && i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { /* Common parameters */ case 'V': case '\0': error += GMT_get_common_args (argv[i], 0, 0, 0, 0); break; /* Supplemental parameters */ case 'C': /* Get cpt table */ table = &argv[i][2]; break; case 'I': reverse = TRUE; break; case 'L': if (sscanf(&argv[i][2], "%lf/%lf", &min_limit, &max_limit) != 2) { fprintf(stderr,"%s: GMT SYNTAX ERROR -L option: Cannot decode limits\n", GMT_program); error++; } else { if (min_limit >= max_limit) { fprintf(stderr,"%s: GMT SYNTAX ERROR -L option: min_limit must be less than max_limit.\n", GMT_program); error++; } } if (!error) set_limits = TRUE; break; case 'S': if (sscanf(&argv[i][2], "%lf/%lf/%lf", &z_start, &z_stop, &z_inc) != 3) { fprintf(stderr,"%s: GMT SYNTAX ERROR -S option: Cannot decode values\n", GMT_program); error++; } else { if (z_stop <= z_start || z_inc <= 0.0) { fprintf(stderr,"%s: GMT SYNTAX ERROR -S option: Bad arguments\n", GMT_program); error++; } } if (!error) set_z_vals = TRUE; break; case 'Z': continuous = TRUE; break; default: error = TRUE; GMT_default_error (argv[i][1]); break; } } else { grdfile = argv[i]; if (GMT_read_grd_info (grdfile, &grd)) { fprintf (stderr, "%s: Error opening file %s\n", GMT_program, grdfile); error++; } } } if (argc == 1 || GMT_quick) { fprintf (stderr,"grd2cpt %s - Make a histogram-equalized color palette table from a grdfile\n\n", GMT_VERSION); fprintf (stderr, "usage: grd2cpt [-C] [-I] [-L/]\n"); fprintf (stderr, "\t[-S//] [-V] [-Z]\n"); if (GMT_quick) exit (EXIT_FAILURE); fprintf (stderr, "\t is the name of the 2-D binary data set\n"); fprintf (stderr, "\n\tOPTIONS:\n"); fprintf(stderr,"\t-C Specify a colortable [Default is rainbow]:\n"); fprintf(stderr,"\t [Original z-range is given in brackets]\n"); fprintf(stderr,"\t ---------------------------------\n"); while (fgets (format, BUFSIZ, fpc)) if (!(format[0] == '#' || format[0] == 0)) fprintf (stderr, "\t %s", format); fclose (fpc); fprintf(stderr,"\t ---------------------------------\n"); fprintf(stderr,"\t-I reverses the sense of the color table\n"); fprintf (stderr, "\t-L Limit the range of the data [Default uses actual min,max of data].\n"); fprintf (stderr, "\t-S Sample points should Step from z_start to z_stop by z_inc [Default guesses some values].\n"); GMT_explain_option ('V'); fprintf(stderr,"\t-Z will create a continuous color palette.\n"); fprintf(stderr,"\t [Default is discontinuous, i.e., constant color intervals]\n"); exit (EXIT_FAILURE); } fclose (fpc); /* Open the specified master color table */ /* First try current directory */ if (table) { if (strstr (table, ".cpt")) strcpy (CPT_file, table); else sprintf (CPT_file, "%s.cpt", table); ok = !access (CPT_file, R_OK); if (ok && gmtdefs.verbose) fprintf (stderr, "%s: Reading %s in current directory\n", GMT_program, CPT_file); } if (!ok) { /* No table in current dir, try /share */ if (table) sprintf (CPT_file, "%s%cshare%cGMT_%s.cpt", GMTHOME, DIR_DELIM, DIR_DELIM, table); else /* Default to rainbow colors */ sprintf (CPT_file, "%s%cshare%cGMT_rainbow.cpt", GMTHOME, DIR_DELIM, DIR_DELIM); if (access (CPT_file, R_OK)) { fprintf (stderr, "%s: ERROR: Cannot find colortable %s\n", GMT_program, CPT_file); error++; } else if (gmtdefs.verbose) fprintf (stderr, "%s: Reading %s\n", GMT_program, CPT_file); } if (error) exit (EXIT_FAILURE); GMT_put_history (argc, argv); /* Update .gmtcommands */ GMT_read_cpt (CPT_file); nxy = grd.nx * grd.ny; zdata = (float *) GMT_memory (VNULL, (size_t)nxy, sizeof (float), GMT_program); if (GMT_read_grd (grdfile, &grd, zdata, 0.0, 0.0, 0.0, 0.0, GMT_pad, FALSE) ) { fprintf(stderr,"%s: Error reading grdfile.\n", GMT_program); GMT_free ((void *)zdata); exit (EXIT_FAILURE); } /* Loop over the file and find NaNs. If set limits, may create more NaNs */ nfound = 0; mean = 0.0; sd = 0.0; if (set_limits) { /* Loop over the grdfile, and set anything outside the limiting values to NaN. */ grd.z_min = min_limit; grd.z_max = max_limit; for (i = 0; i < nxy; i++) { if (GMT_is_fnan (zdata[i])) nfound++; else { if (zdata[i] < min_limit || zdata[i] > max_limit) { nfound++; zdata[i] = GMT_f_NaN; } else { mean += zdata[i]; sd += zdata[i] * zdata[i]; } } } } else { min_limit = grd.z_max; /* This is just to double check grd.z_min, grd.z_max */ max_limit = grd.z_min; for (i = 0; i < nxy; i++) { if (GMT_is_fnan (zdata[i])) nfound++; else { if (zdata[i] < min_limit) min_limit = zdata[i]; if (zdata[i] > max_limit) max_limit = zdata[i]; mean += zdata[i]; sd += zdata[i] * zdata[i]; } } grd.z_min = min_limit; grd.z_max = max_limit; } ngood = nxy - nfound; /* This is the number of non-NaN points for the cdf function */ mean /= ngood; sd /= ngood; sd = sqrt(sd - mean*mean); if (gmtdefs.verbose) { sprintf(format,"%%s: Mean and S.D. of data are %s %s\n", gmtdefs.d_format, gmtdefs.d_format); fprintf(stderr, format, GMT_program, mean, sd); } /* Now the zdata are ready. Decide how to make steps in z. */ if (set_z_vals) { ncdf = (grd.z_min < z_start) ? 1 : 0; ncdf += (int)floor((z_stop - z_start)/z_inc) + 1; if (grd.z_max > z_stop) ncdf++; cdf_cpt = (struct CDF_CPT *)GMT_memory (VNULL, (size_t)ncdf, sizeof(struct CDF_CPT), GMT_program); if (grd.z_min < z_start) { cdf_cpt[0].z = grd.z_min; cdf_cpt[1].z = z_start; i = 2; } else { cdf_cpt[0].z = z_start; i = 1; } j = (grd.z_max > z_stop) ? ncdf - 1 : ncdf; while (i < j) { cdf_cpt[i].z = cdf_cpt[i-1].z + z_inc; i++; } if (j == ncdf-1) cdf_cpt[j].z = grd.z_max; } else { /* This is completely ad-hoc. It chooses z based on steps of 0.1 for a Gaussian CDF: */ ncdf = 11; cdf_cpt = (struct CDF_CPT *)GMT_memory (VNULL, (size_t)ncdf, sizeof(struct CDF_CPT), GMT_program); /* Stupid bug fix here: If (mean-1.28155*sd <= grd.z_min || mean+1.28155*sd >= grd.z_max) then reset mean and sd so they fit inside available range: */ if ((mean - 1.28155*sd) <= grd.z_min || (mean + 1.28155*sd) >= grd.z_max) { mean = 0.5 * (grd.z_min + grd.z_max); sd = (grd.z_max - mean) / 1.5; if (sd <= 0.0) { fprintf (stderr, "%s: ERROR. Min and Max data values are equal.\n", GMT_program); exit (EXIT_FAILURE); } } /* End of stupid bug fix */ cdf_cpt[0].z = grd.z_min; cdf_cpt[1].z = mean - 1.28155 * sd; cdf_cpt[2].z = mean - 0.84162 * sd; cdf_cpt[3].z = mean - 0.52440 * sd; cdf_cpt[4].z = mean - 0.25335 * sd; cdf_cpt[5].z = mean; cdf_cpt[6].z = mean + 0.25335 * sd; cdf_cpt[7].z = mean + 0.52440 * sd; cdf_cpt[8].z = mean + 0.84162 * sd; cdf_cpt[9].z = mean + 1.28155 * sd; cdf_cpt[10].z = grd.z_max; } /* Get here when we are ready to go. cdf_cpt[].z contains the sample points. */ if (gmtdefs.verbose) sprintf (format, "%%s: z = %s and CDF(z) = %s\n", gmtdefs.d_format, gmtdefs.d_format); for (j = 0; j < ncdf; j++) { if (cdf_cpt[j].z <= grd.z_min) cdf_cpt[j].f = 0.0; else if (cdf_cpt[j].z >= grd.z_max) cdf_cpt[j].f = 1.0; else { nfound = 0; for (i = 0; i < nxy; i++) { if (!GMT_is_fnan (zdata[i]) && zdata[i] <= cdf_cpt[j].z) nfound++; } cdf_cpt[j].f = (double)(nfound-1)/(double)(ngood-1); } if (gmtdefs.verbose) fprintf (stderr, format, GMT_program, cdf_cpt[j].z, cdf_cpt[j].f); } /* Now the cdf function has been found. We now resample the chosen cptfile */ /* Write to GMT_stdout. */ fprintf (GMT_stdout, "#\tcpt file created by: %s", GMT_program); for (i = 1; i < argc; i++) fprintf (GMT_stdout, " %s", argv[i]); fprintf (GMT_stdout, "\n"); z = (double *) GMT_memory (VNULL, (size_t)ncdf, sizeof (double), GMT_program); for (i = 0; i < ncdf; i++) z[i] = cdf_cpt[i].z; GMT_sample_cpt (z, -ncdf, continuous, reverse); /* -ve to keep original colors */ GMT_free ((void *)cdf_cpt); GMT_free ((void *)zdata); GMT_free ((void *)z); GMT_end (argc, argv); }