Skip to content

Commit

Permalink
Upgrade grd2cpt like makecpt and improve docs (#8138)
Browse files Browse the repository at this point in the history
* Allow -Fg in makecpt/grd2cpt for gray-scale output

No reason not to have -Fg for gray output, via YIQ if needed.
ALso updated the docs.

* Add helper include files since both modules now need thesame

* Update explain_cpt_output.rst_

* Minor text edits in explain_cpt_output.rst_

---------

Co-authored-by: Remko Scharroo <[email protected]>
  • Loading branch information
PaulWessel and remkos authored Dec 5, 2023
1 parent a0a8f41 commit e67d46b
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 134 deletions.
30 changes: 30 additions & 0 deletions doc/rst/source/explain_cpt_output.rst_
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
**-F**\ [**R**\|\ **c**\|\ **g**\|\ **h**\|\ **r**\|\ **x**][**+c**\ [*label*]][**+k**\ *keys*]
Force output CPT to be written in the desired format set by the directives, thus
selecting among color names, single gray-scale values, color names, *r*/*g*/*b* only,
*h*-*s*-*v*, *c*/*m*/*y*/*k*, or *#rrggbb* hex codes. Choose from:

- **R**: Output color names if possible (and use *r*/*g*/*b* otherwise) [Default].
- **c**: Output color using *c*/*m*/*y*/*k* values.
- **g**: Output *gray* singles (will transform any colors to gray via YIQ transformation).
- **h**: Output *h*-*s*-*v* triplets.
- **r**: Output *r*/*g*/*b* codes only (even if a color name applies).
- **x**: Output *#rrggbb* hex codes.

Optionally or alternatively, append these modifiers:

- **+c**: Write discrete palettes in categorical format. If *label* is appended
then we create labels for each category to be used when the CPT is plotted.
The *label* may be a comma-separated list of category names (you can skip a
category by not giving a name), or give *start*\ [-], where we automatically
build monotonically increasing labels from *start* (a single letter or an integer).
Append - to build ranges *start*\ -*start+1* instead.
- **+k**: Categorical CPT should have string keys instead of numerical entries.
Append keys, which is either a file with one key per record or a single letter
(e.g., D), then we build sequential letter keys (e.g., D, E, F, …) starting at that point.

**Notes**: (1) For comma-separated lists of keys, use |-T| instead.
(2) If **+cM** is given and the number of categories is 12, then we automatically create
a list of month names. (3) If **+cD** is given and the number of categories is 7 then we
make a list of weekday names. The format of these labels will depend on the
:term:`FORMAT_TIME_PRIMARY_MAP`, :term:`GMT_LANGUAGE` and possibly
:term:`TIME_WEEK_START` settings.
20 changes: 2 additions & 18 deletions doc/rst/source/grd2cpt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Synopsis
[ |-C|\ *cpt* ]
[ |-D|\ [**i**\|\ **o**] ]
[ |-E|\ [*nlevels*][**+c**][**+f**\ *file*] ]
[ |-F|\ [**R**\|\ **r**\|\ **h**\|\ **c**\|\ **x**][**+c**\ [*label*]] ]
[ |-F|\ [**R**\|\ **c**\|\ **g**\|\ **h**\|\ **r**\|\ **x**][**+c**\ [*label*]][**+k**\ *keys*] ]
[ |-G|\ *zlo*\ /\ *zhi* ]
[ |-H| ]
[ |-I|\ [**c**][**z**] ]
Expand Down Expand Up @@ -121,23 +121,7 @@ Optional Arguments

.. _-F:

**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**\|\ **x**][**+c**\ [*label*]]
Force output CPT to written with r/g/b codes, gray-scale values
or color name (**R**, default) or r/g/b codes only (**r**), or h-s-v
codes (**h**), or c/m/y/k codes (**c**), or #rrggbb hex codes (**x**).
Optionally or alternatively,
append **+c** to write discrete palettes in categorical format.
If *label* is appended then we create labels for each category to be used
when the CPT is plotted. The *label* may be a comma-separated list of
category names (you can skip a category by not giving a name), or give
*start*\ [-], where we automatically build monotonically increasing labels
from *start* (a single letter or an integer). Append - to build ranges
*start*-*start+1* instead. **Note**: If **+cM** is given and the number
of categories is 12, then we automatically create a list of month names.
Likewise, if **+cD** is given and the number of categories is 7 then we
make a list of weekday names. The format of these labels will depend on the
:term:`FORMAT_TIME_PRIMARY_MAP`, :term:`GMT_LANGUAGE` and possibly
:term:`TIME_WEEK_START` settings.
.. include:: explain_cpt_output.rst_

.. _-G:

Expand Down
26 changes: 3 additions & 23 deletions doc/rst/source/makecpt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Synopsis
[ |-C|\ *cpt* ]
[ |-D|\ [**i**\|\ **o**] ]
[ |-E|\ [*nlevels*] ]
[ |-F|\ [**R**\|\ **r**\|\ **h**\|\ **c**\|\ **x**][**+c**\ [*label*]][**+k**\ *keys*] ]
[ |-F|\ [**R**\|\ **c**\|\ **g**\|\ **h**\|\ **r**\|\ **x**][**+c**\ [*label*]][**+k**\ *keys*]]
[ |-G|\ *zlo*\ /\ *zhi* ]
[ |-H| ]
[ |-I|\ [**c**][**z**] ]
Expand Down Expand Up @@ -62,7 +62,7 @@ and :term:`COLOR_NAN` from
the :doc:`gmt.conf` file or the command line will be used. This default
behavior can be overruled using the options |-D|, |-M| or |-N|.

The color model (RGB, HSV or CMYK) of the palette created by **makecpt**
The color model (GRAY, RGB, HSV or CMYK) of the palette created by **makecpt**
will be the same as specified in the header of the master CPT. When
there is no :term:`COLOR_MODEL` entry in the master CPT, the
:term:`COLOR_MODEL` specified in the :doc:`gmt.conf` file or on the command
Expand Down Expand Up @@ -110,27 +110,7 @@ Optional Arguments

.. _-F:

**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**\|\ **x**][**+c**\ [*label*]][**+k**\ *keys*]
Force output CPT to be written with r/g/b codes, gray-scale values
or color name (**R**, default) or r/g/b codes only (**r**), or h-s-v
codes (**h**), or c/m/y/k codes (**c**), or #rrggbb hex codes (**x**).
Optionally or alternatively,
append **+c** to write discrete palettes in categorical format.
If *label* is appended then we create labels for each category to be used
when the CPT is plotted. The *label* may be a comma-separated list of
category names (you can skip a category by not giving a name), or give
*start*\ [-], where we automatically build monotonically increasing labels
from *start* (a single letter or an integer). Append - to build ranges
*start*\ -*start+1* instead. If the categorical CPT should have string
keys instead of numerical entries then append **+k**\ *keys*, where
*keys* is either a file with one key per record or a single letter (e.g., D),
then we build sequential letter keys (e.g., D, E, F, ...) starting at that point.
For comma-separated lists of keys, use |-T| instead. **Note**: If **+cM** is given and the number
of categories is 12, then we automatically create a list of month names.
Likewise, if **+cD** is given and the number of categories is 7 then we
make a list of weekday names. The format of these labels will depend on the
:term:`FORMAT_TIME_PRIMARY_MAP`, :term:`GMT_LANGUAGE` and possibly
:term:`TIME_WEEK_START` settings.
.. include:: explain_cpt_output.rst_

.. _-G:

Expand Down
3 changes: 3 additions & 0 deletions src/gmt_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ enum GMT_time_period {
/* Directives and modifiers for -F interpolant option */
#define GMT_INTERPOLANT_OPT "a|c|e|l|n|s<p>[+d1|2]"

/* Directives and modifiers for -F cpt output option in makecpt/gdr2cpt */
#define GMT_COLORMODES_OPT "[R|c|g|h|r|x]][+c[<label>]][+k<keys>]"

/* Valid modifiers for various input files */

/* Valid modifiers for -Tmin/max/inc array creator */
Expand Down
30 changes: 24 additions & 6 deletions src/gmt_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -13977,7 +13977,7 @@ char *gmtlib_putfill (struct GMT_CTRL *GMT, struct GMT_FILL *F) {
strcat (text, add_mods);
}
}
else if (F->rgb[0] < -0.5)
else if (F->rgb[0] < -0.5) /* Skip it */
strcpy (text, "-");
else if ((i = gmtlib_getrgb_index (GMT, F->rgb)) >= 0)
snprintf (text, PATH_MAX+GMT_LEN256, "%s", gmt_M_color_name[i]);
Expand All @@ -13997,7 +13997,7 @@ char *gmt_putcolor (struct GMT_CTRL *GMT, double *rgb) {
static char text[GMT_LEN256] = {""};
int i;

if (rgb[0] < -0.5)
if (rgb[0] < -0.5) /* Skip it */
strcpy (text, "-");
else if ((i = gmtlib_getrgb_index (GMT, rgb)) >= 0)
snprintf (text, GMT_LEN256, "%s", gmt_M_color_name[i]);
Expand All @@ -14015,21 +14015,39 @@ char *gmt_putrgb (struct GMT_CTRL *GMT, double *rgb) {
static char text[GMT_LEN256] = {""};
gmt_M_unused(GMT);

if (rgb[0] < -0.5)
if (rgb[0] < -0.5) /* Skip it */
strcpy (text, "-");
else
snprintf (text, GMT_LEN256, "%.5g/%.5g/%.5g", gmt_M_t255(rgb,0), gmt_M_t255(rgb,1), gmt_M_t255(rgb,2));
gmtinit_append_trans (text, rgb[3]);
return (text);
}

/*! Creates t the string shade corresponding to the RGB triplet */
char *gmt_putgray (struct GMT_CTRL *GMT, double *rgb) {

static char text[GMT_LEN256] = {""};
gmt_M_unused(GMT);

if (rgb[0] < -0.5) /* Skip it */
strcpy (text, "-");
else if (gmt_M_is_gray(rgb))
snprintf (text, GMT_LEN256, "%.5g", gmt_M_t255(rgb,0));
else { /* Must convert to gray */
double gray = gmt_M_yiq (rgb);
snprintf (text, GMT_LEN256, "%.5g", gray);
}
gmtinit_append_trans (text, rgb[3]);
return (text);
}

/*! Creates t the string #rrggbb corresponding to the RGB triplet */
char *gmt_puthex (struct GMT_CTRL *GMT, double *rgb) {

static char text[GMT_LEN256] = {""};
gmt_M_unused(GMT);

if (rgb[0] < -0.5)
if (rgb[0] < -0.5) /* Skip it */
strcpy (text, "-");
else
snprintf (text, GMT_LEN256, "#%02x%02x%02x", urint (gmt_M_t255(rgb,0)), urint (gmt_M_t255(rgb,1)), urint (gmt_M_t255(rgb,2)));
Expand All @@ -14043,7 +14061,7 @@ char *gmtlib_putcmyk (struct GMT_CTRL *GMT, double *cmyk) {
static char text[GMT_LEN256] = {""};
gmt_M_unused(GMT);

if (cmyk[0] < -0.5)
if (cmyk[0] < -0.5) /* Skip it */
strcpy (text, "-");
else
snprintf (text, GMT_LEN256, "%.5g/%.5g/%.5g/%.5g", 100.0*gmt_M_q(cmyk[0]), 100.0*gmt_M_q(cmyk[1]), 100.0*gmt_M_q(cmyk[2]), 100.0*gmt_M_q(cmyk[3]));
Expand All @@ -14057,7 +14075,7 @@ char *gmtlib_puthsv (struct GMT_CTRL *GMT, double *hsv) {
static char text[GMT_LEN256] = {""};
gmt_M_unused(GMT);

if (hsv[0] < -0.5)
if (hsv[0] < -0.5) /* Skip it */
strcpy (text, "-");
else
snprintf (text, GMT_LEN256, "%.5g-%.5g-%.5g", gmt_M_q(hsv[0]), gmt_M_q(hsv[1]), gmt_M_q(hsv[2]));
Expand Down
3 changes: 3 additions & 0 deletions src/gmt_prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ EXTERN_MSC int gmt_set_measure_unit (struct GMT_CTRL *GMT, char unit);
EXTERN_MSC char * gmt_putcolor (struct GMT_CTRL *GMT, double *rgb);
EXTERN_MSC char * gmt_putrgb (struct GMT_CTRL *GMT, double *rgb);
EXTERN_MSC char * gmt_puthex (struct GMT_CTRL *GMT, double *rgb);
EXTERN_MSC char * gmt_putgray (struct GMT_CTRL *GMT, double *rgb);
EXTERN_MSC double gmt_convert_units (struct GMT_CTRL *GMT, char *value, unsigned int from_default, unsigned int target_unit);
EXTERN_MSC unsigned int gmt_check_scalingopt (struct GMT_CTRL *GMT, char option, char unit, char *unit_name);
EXTERN_MSC int gmt_parse_common_options (struct GMT_CTRL *GMT, char *list, char option, char *item);
Expand Down Expand Up @@ -553,6 +554,8 @@ EXTERN_MSC int gmt_grd_BC_set (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigne
EXTERN_MSC int gmt_cube_BC_set (struct GMT_CTRL *GMT, struct GMT_CUBE *U, unsigned int direction);
EXTERN_MSC unsigned int gmt_parse_inv_cpt (struct GMT_CTRL *GMT, char *arg);
EXTERN_MSC struct GMT_PALETTE * gmt_truncate_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double z_low, double z_high);
EXTERN_MSC void gmt_explain_cpt_output (struct GMTAPI_CTRL *API, char option);
EXTERN_MSC int gmt_prepare_categorical_cpt (struct GMT_CTRL *GMT, char *label, char *key, struct GMT_PALETTE *Pout);
EXTERN_MSC void gmt_free_int_selection (struct GMT_CTRL *GMT, struct GMT_INT_SELECTION **S);
EXTERN_MSC struct GMT_INT_SELECTION * gmt_set_int_selection (struct GMT_CTRL *GMT, char *item);
EXTERN_MSC bool gmt_get_int_selection (struct GMT_CTRL *GMT, struct GMT_INT_SELECTION *S, uint64_t this);
Expand Down
4 changes: 3 additions & 1 deletion src/gmt_resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ enum GMT_enum_color {
GMT_HSV = 2,
GMT_COLORINT = 4,
GMT_NO_COLORNAMES = 8,
GMT_HEX_COLOR = 16
GMT_HEX_COLOR = 16,
GMT_GRAY = 32,
};

enum GMT_enum_bfn {
Expand All @@ -597,6 +598,7 @@ enum GMT_enum_cptflags {
GMT_CPT_SOFT_HINGE = 8,
GMT_CPT_TIME = 16,
GMT_CPT_COLORLIST = 32,
GMT_CPT_GRAY_SET = 64,
GMT_CPT_HINGED = 4 /* Backwards compatibility with 6.0 API */
};

Expand Down
77 changes: 77 additions & 0 deletions src/gmt_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,68 @@ GMT_LOCAL void gmtsupport_truncate_cpt_slice (struct GMT_LUT *S, bool do_hsv, do
S->i_dz = 1.0 / (S->z_high - S->z_low);
}

void gmt_explain_cpt_output (struct GMTAPI_CTRL *API, char option) {
/* Display usage synopsis for -F cpt output/categorical option in makecpt/grd2cpt */

GMT_Usage (API, 1, "\n-%c%s", option, GMT_COLORMODES_OPT);
GMT_Usage (API, -2, "Select the color model for output [Default uses the input model]:");
GMT_Usage (API, 3, "R: Output r/g/b or colornames.");
GMT_Usage (API, 3, "c: Output c/m/y/k.");
GMT_Usage (API, 3, "g: Output gray value (Will apply the YIQ if not a gray value).");
GMT_Usage (API, 3, "h: Output h-s-v.");
GMT_Usage (API, 3, "r: Output r/g/b only.");
GMT_Usage (API, 3, "x: Output hex #rrggbb only.");
GMT_Usage (API, -2, "Two modifiers control generation of categorical labels:");
GMT_Usage (API, 3, "+c Output a discrete CPT in categorical CPT format. "
"The <label>, if appended, sets the labels for each category. It may be a "
"comma-separated list of category names, or <start>[-] where we automatically build "
"labels from <start> (a letter or an integer). Append - to build range labels <start>-<start+1>.");
GMT_Usage (API, 3, "+k Set categorical keys rather than numerical values. "
"<keys> may be a file with one key per line or a comma-separated list of keys. "
"If <keys> is a single letter then we build sequential alphabetical keys from that letter. "
"If number of categories is 12 and label is M then we auto-create month name labels, and "
"if it is 7 and label is D then we auto-create weekday name labels.");
}

int gmt_prepare_categorical_cpt (struct GMT_CTRL *GMT, char *label, char *key, struct GMT_PALETTE *Pout) {
bool got_key_file = (key && !gmt_access (GMT, key, R_OK)); /* Want categorical labels read from file */
unsigned int ns = 0;
Pout->categorical = GMT_CPT_CATEGORICAL_VAL;
if (label || (key && !got_key_file)) { /* Want auto-categorical labels appended to each CPT record */
char **Plabel = gmt_cat_cpt_strings (GMT, (label) ? label : key, Pout->n_colors, &ns);
for (unsigned int k = 0; k < MIN (Pout->n_colors, ns); k++) {
if (Pout->data[k].label) gmt_M_str_free (Pout->data[k].label);
if (Plabel[k]) Pout->data[k].label = Plabel[k]; /* Now the job of the CPT to free these strings */
}
gmt_M_free (GMT, Plabel); /* But the master array can go */
}
if (key) { /* Want categorical labels */
char **keys = NULL;
if (got_key_file) { /* Got a file with category keys */
ns = gmt_read_list (GMT, key, &keys);
if (ns < Pout->n_colors) {
GMT_Report (GMT->parent, GMT_MSG_ERROR, "The categorical keys file %s had %d entries but CPT has %d categories\n", key, ns, Pout->n_colors);
return (GMT_RUNTIME_ERROR);
}
else if (ns > Pout->n_colors)
GMT_Report (GMT->parent, GMT_MSG_WARNING, "The categorical keys file %s had %d entries but only %d are needed - skipping the extra keys\n", key, ns, Pout->n_colors);
}
else /* Got comma-separated keys */
keys = gmt_cat_cpt_strings (GMT, key, Pout->n_colors, &ns);
for (unsigned int k = 0; k < MIN (Pout->n_colors, ns); k++) {
if (Pout->data[k].key) gmt_M_str_free (Pout->data[k].key);
if (k < ns && keys[k]) {
Pout->data[k].key = keys[k]; /* Now the job of the CPT to free these strings */
if (Pout->data[k].label) gmt_M_str_free (Pout->data[k].label);
Pout->data[k].label = strdup (keys[k]);
}
}
gmt_M_free (GMT, keys); /* But the master array can go */
Pout->categorical = GMT_CPT_CATEGORICAL_KEY;
}
return (GMT_NOERROR);
}

bool gmt_consider_current_cpt (struct GMTAPI_CTRL *API, bool *active, char **arg) {
/* Modern mode only: Detect if no CPT is given but -C was set.
* If -C[+u|U<arg>] is given (i.e., no cpt) then we take that to mean
Expand Down Expand Up @@ -8330,6 +8392,8 @@ struct GMT_PALETTE * gmtlib_read_cpt (struct GMT_CTRL *GMT, void *source, unsign
X->model = GMT_RGB | GMT_COLORINT;
else if (strstr (line, "RGB"))
X->model = GMT_RGB;
else if (strstr (line, "+GRAY") || strstr (line, "gray"))
X->model = GMT_GRAY | GMT_COLORINT;
else if (strstr (line, "+HSV") || strstr (line, "hsv"))
X->model = GMT_HSV | GMT_COLORINT;
else if (strstr (line, "HSV"))
Expand Down Expand Up @@ -9473,6 +9537,9 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
struct CPT_Z_SCALE *Z = NULL; /* For unit manipulations */
struct GMT_PALETTE_HIDDEN *PH = NULL;

if (cpt_flags & GMT_CPT_GRAY_SET && !P->is_gray)
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Colors in the CPT file %s were converted to gray via the YIQ-translation.\n", &cpt_file[append]);

/* When writing the CPT to file it is no longer a normalized CPT with a hinge */
P->has_range = P->has_hinge = 0;

Expand Down Expand Up @@ -9537,6 +9604,8 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
gmtlib_write_newheaders (GMT, fp, 0); /* Write general header block */

if (!(P->model & GMT_COLORINT)) {} /* Write nothing when color interpolation is not forced */
else if (P->model & GMT_GRAY)
fprintf (fp, "# COLOR_MODEL = gray\n");
else if (P->model & GMT_HSV)
fprintf (fp, "# COLOR_MODEL = hsv\n");
else if (P->model & GMT_CMYK)
Expand All @@ -9562,6 +9631,8 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,

if (P->categorical) {
if (P->categorical & GMT_CPT_CATEGORICAL_KEY) strncpy (lo, P->data[i].key, GMT_LEN64-1);
if (P->model & GMT_GRAY)
fprintf (fp, format, lo, gmt_putgray (GMT, P->data[i].hsv_low), '\t');
if (P->model & GMT_HSV)
fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
else if (P->model & GMT_CMYK) {
Expand All @@ -9575,6 +9646,10 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
else
fprintf (fp, format, lo, gmt_putcolor (GMT, P->data[i].rgb_low), '\t');
}
else if (P->model & GMT_GRAY) {
fprintf (fp, format, lo, gmt_putgray (GMT, P->data[i].rgb_low), '\t');
fprintf (fp, format, hi, gmt_putgray (GMT, P->data[i].rgb_high), '\t');
}
else if (P->model & GMT_HSV) {
fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
fprintf (fp, format, hi, gmtlib_puthsv (GMT, P->data[i].hsv_high), '\t');
Expand Down Expand Up @@ -9615,6 +9690,8 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
if (!use[i]) continue; /* This BNF entry does not apply to this CPT */
if (P->bfn[i].skip)
fprintf (fp, "%c\t-\n", code[i]);
else if (P->model & GMT_GRAY)
fprintf (fp, "%c\t%s\n", code[i], gmt_putgray (GMT, P->bfn[i].rgb));
else if (P->model & GMT_HSV)
fprintf (fp, "%c\t%s\n", code[i], gmtlib_puthsv (GMT, P->bfn[i].hsv));
else if (P->model & GMT_CMYK) {
Expand Down
Loading

0 comments on commit e67d46b

Please sign in to comment.