diff --git a/doc/rst/source/explain_cpt_output.rst_ b/doc/rst/source/explain_cpt_output.rst_
new file mode 100644
index 00000000000..78692e2e74e
--- /dev/null
+++ b/doc/rst/source/explain_cpt_output.rst_
@@ -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.
diff --git a/doc/rst/source/grd2cpt.rst b/doc/rst/source/grd2cpt.rst
index b5684b18bd1..b871034f0fd 100644
--- a/doc/rst/source/grd2cpt.rst
+++ b/doc/rst/source/grd2cpt.rst
@@ -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**] ]
@@ -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:
diff --git a/doc/rst/source/makecpt.rst b/doc/rst/source/makecpt.rst
index 74bb0dc40e1..339bcf57bc0 100644
--- a/doc/rst/source/makecpt.rst
+++ b/doc/rst/source/makecpt.rst
@@ -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**] ]
@@ -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
@@ -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:
diff --git a/src/gmt_constants.h b/src/gmt_constants.h
index df8adc826f2..2f69c779e90 100644
--- a/src/gmt_constants.h
+++ b/src/gmt_constants.h
@@ -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
[+d1|2]"
+/* Directives and modifiers for -F cpt output option in makecpt/gdr2cpt */
+#define GMT_COLORMODES_OPT "[R|c|g|h|r|x]][+c[]][+k]"
+
/* Valid modifiers for various input files */
/* Valid modifiers for -Tmin/max/inc array creator */
diff --git a/src/gmt_init.c b/src/gmt_init.c
index 9d5ed5adfac..e036ef5838e 100644
--- a/src/gmt_init.c
+++ b/src/gmt_init.c
@@ -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]);
@@ -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]);
@@ -14015,7 +14015,7 @@ 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));
@@ -14023,13 +14023,31 @@ char *gmt_putrgb (struct GMT_CTRL *GMT, double *rgb) {
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)));
@@ -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]));
@@ -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]));
diff --git a/src/gmt_prototypes.h b/src/gmt_prototypes.h
index dd4b40ea27c..70833a66880 100644
--- a/src/gmt_prototypes.h
+++ b/src/gmt_prototypes.h
@@ -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);
@@ -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);
diff --git a/src/gmt_resources.h b/src/gmt_resources.h
index f80c3e93889..d3f9bce5d99 100644
--- a/src/gmt_resources.h
+++ b/src/gmt_resources.h
@@ -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 {
@@ -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 */
};
diff --git a/src/gmt_support.c b/src/gmt_support.c
index 4c86626502f..de0f8bf498a 100644
--- a/src/gmt_support.c
+++ b/src/gmt_support.c
@@ -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 , if appended, sets the labels for each category. It may be a "
+ "comma-separated list of category names, or [-] where we automatically build "
+ "labels from (a letter or an integer). Append - to build range labels -.");
+ GMT_Usage (API, 3, "+k Set categorical keys rather than numerical values. "
+ " may be a file with one key per line or a comma-separated list of keys. "
+ "If 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] is given (i.e., no cpt) then we take that to mean
@@ -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"))
@@ -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;
@@ -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)
@@ -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) {
@@ -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');
@@ -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) {
diff --git a/src/grd2cpt.c b/src/grd2cpt.c
index 1533112c43b..4690338f84c 100644
--- a/src/grd2cpt.c
+++ b/src/grd2cpt.c
@@ -73,11 +73,12 @@ struct GRD2CPT_CTRL {
char *file;
unsigned int levels;
} E;
- struct GRD2CPT_F { /* -F[r|R|h|c|x][+c[]] */
+ struct GRD2CPT_F { /* -F[R|c|g|h|r|x][+c[]][+k] */
bool active;
bool cat;
unsigned int model;
char *label;
+ char *key;
} F;
struct GRD2CPT_G { /* -Glow/high for input CPT truncation */
bool active;
@@ -151,9 +152,9 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
const char *H_OPT = (API->GMT->current.setting.run_mode == GMT_MODERN) ? " [-H]" : "";
if (level == GMT_MODULE_PURPOSE) return (GMT_NOERROR);
GMT_Usage (API, 0, "usage: %s %s [-A[+a]] [-C] [-D[i|o]] [-E[][+c][+f]] "
- "[-F[R|r|h|c|x][+c[]]] [-G/]%s [-I[c][z]] [-L/] [-M] [-N] [-Q[i|o]] "
+ "[-F%s] [-G/]%s [-I[c][z]] [-L/] [-M] [-N] [-Q[i|o]] "
"[%s] [-Sh|l|m|u] [-T//|] [%s] [-W[w]] [-Z] [%s] [%s] [%s] [%s]\n",
- name, GMT_INGRID, H_OPT, GMT_Rgeo_OPT, GMT_V_OPT, GMT_bo_OPT, GMT_ho_OPT, GMT_o_OPT, GMT_PAR_OPT);
+ name, GMT_INGRID, GMT_COLORMODES_OPT, H_OPT, GMT_Rgeo_OPT, GMT_V_OPT, GMT_bo_OPT, GMT_ho_OPT, GMT_o_OPT, GMT_PAR_OPT);
if (level == GMT_SYNOPSIS) return (GMT_MODULE_SYNOPSIS);
@@ -172,18 +173,7 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
"Alternatively, append to sample equidistant color levels from zmin to zmax. "
"Instead, append +c to use the cumulative density function (cdf) to set color bounds. "
"Append +f to save the CDF table to a file.");
- GMT_Usage (API, 1, "\n-F[R|r|h|c][+c[]]");
- 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 grayscale or colorname.");
- GMT_Usage (API, 3, "r: Output r/g/b only.");
- GMT_Usage (API, 3, "h: Output h-s-v.");
- GMT_Usage (API, 3, "c: Output c/m/y/k.");
- GMT_Usage (API, 3, "x: Output hex #rrggbb only.");
- GMT_Usage (API, -2, "One modifier controls generation of categorical labels:");
- GMT_Usage (API, 3, "+c Output a discrete CPT in categorical CPT format. "
- "The , if appended, sets the labels for each category. It may be a "
- "comma-separated list of category names, or [-] where we automatically build "
- "labels from (a letter or an integer). Append - to build range labels -.");
+ gmt_explain_cpt_output (API, 'F');
GMT_Usage (API, 1, "\n-G/");
GMT_Usage (API, -2, "Truncate incoming CPT to be limited to the z-range /. "
"To accept one of the incoming limits, set that limit to NaN.");
@@ -305,17 +295,26 @@ static int parse (struct GMT_CTRL *GMT, struct GRD2CPT_CTRL *Ctrl, struct GMT_OP
break;
case 'F': /* Set color model for output */
n_errors += gmt_M_repeated_module_option (API, Ctrl->F.active);
- if (gmt_validate_modifiers (GMT, opt->arg, 'F', "c", GMT_MSG_ERROR)) n_errors++;
+ if (gmt_validate_modifiers (GMT, opt->arg, 'F', "ck", GMT_MSG_ERROR)) n_errors++;
if (gmt_get_modifier (opt->arg, 'c', txt_a)) {
Ctrl->F.cat = true;
if (txt_a[0]) Ctrl->F.label = strdup (txt_a);
}
+ if (gmt_get_modifier (opt->arg, 'k', txt_a)) {
+ Ctrl->F.cat = true;
+ if (txt_a[0]) Ctrl->F.key = strdup (txt_a);
+ }
switch (opt->arg[0]) {
+ case '\0': case '+': case 'R': Ctrl->F.model = GMT_RGB; break;
case 'r': Ctrl->F.model = GMT_RGB + GMT_NO_COLORNAMES; break;
+ case 'g': Ctrl->F.model = GMT_GRAY; break;
case 'h': Ctrl->F.model = GMT_HSV; break;
case 'c': Ctrl->F.model = GMT_CMYK; break;
case 'x': Ctrl->F.model = GMT_RGB + GMT_HEX_COLOR; break;
- default: Ctrl->F.model = GMT_RGB; break;
+ default:
+ GMT_Report (API, GMT_MSG_ERROR, "Option -F: Unrecognized directive %c\n", opt->arg[0]);
+ n_errors++;
+ break;
}
break;
case 'G': /* truncate incoming CPT */
@@ -876,20 +875,14 @@ EXTERN_MSC int GMT_grd2cpt (void *V_API, int mode, void *args) {
if (Ctrl->N.active) cpt_flags |= GMT_CPT_NO_BNF; /* bit 0 controls if BFN will be written out */
if (Ctrl->D.mode == 1) cpt_flags |= GMT_CPT_EXTEND_BNF; /* bit 1 controls if BF will be set to equal bottom/top rgb value */
if (Ctrl->F.active) Pout->model = Ctrl->F.model;
- if (Ctrl->F.cat) { /* Flag as a categorical CPT */
- Pout->categorical = GMT_CPT_CATEGORICAL_VAL;
- if (Ctrl->F.label) { /* Want categorical labels */
- unsigned int ns = 0;
- char **label = gmt_cat_cpt_strings (GMT, Ctrl->F.label, 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);
- Pout->data[k].label = label[k]; /* Now the job of the CPT to free these strings */
- }
- gmt_M_free (GMT, label);
- }
- }
if (Ctrl->A.active) gmt_cpt_transparency (GMT, Pout, Ctrl->A.value, Ctrl->A.mode); /* Set transparency */
+ if (Ctrl->F.model & GMT_GRAY) cpt_flags = GMT_CPT_GRAY_SET; /* Controls if final CPT should be converted to gray via YIQ */
+
+ if (Ctrl->F.cat) { /* Flag as a categorical CPT */
+ if ((API->error = gmt_prepare_categorical_cpt (GMT, Ctrl->F.label, Ctrl->F.key, Pout)))
+ Return (API->error);
+ }
if (write && GMT_Write_Data (API, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, cpt_flags, NULL, Ctrl->Out.file, Pout) != GMT_NOERROR)
error = API->error;
diff --git a/src/makecpt.c b/src/makecpt.c
index 6d1e86b3a50..0bf223f1d99 100644
--- a/src/makecpt.c
+++ b/src/makecpt.c
@@ -72,7 +72,7 @@ struct MAKECPT_CTRL {
bool active;
unsigned int levels;
} E;
- struct MAKECPT_F { /* -F[r|R|h|c|x][+c[]][+k] */
+ struct MAKECPT_F { /* -F[R|c|g|h|r|x][+c[]][+k] */
bool active;
bool cat;
unsigned int model;
@@ -146,9 +146,9 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
const char *H_OPT = (API->GMT->current.setting.run_mode == GMT_MODERN) ? " [-H]" : "";
if (level == GMT_MODULE_PURPOSE) return (GMT_NOERROR);
GMT_Usage (API, 0, "usage: %s [-A[+a]] [-C|colors] [-D[i|o]] [-E[]] "
- "[-F[R|r|h|c|x][+c[]][+k]] [-G/]%s [-I[c][z]] [-M] [-N] [-Q] [-S] "
+ "[-F%s] [-G/]%s [-I[c][z]] [-M] [-N] [-Q] [-S] "
"[-T/[/[+b|i|l|n]] | -T | -T] [%s] [-W[w]] [-Z] [%s] [%s] [%s] [%s] [%s]\n",
- name, H_OPT, GMT_V_OPT, GMT_bi_OPT, GMT_di_OPT, GMT_ho_OPT, GMT_i_OPT, GMT_PAR_OPT);
+ name, GMT_COLORMODES_OPT, H_OPT, GMT_V_OPT, GMT_bi_OPT, GMT_di_OPT, GMT_ho_OPT, GMT_i_OPT, GMT_PAR_OPT);
if (level == GMT_SYNOPSIS) return (GMT_MODULE_SYNOPSIS);
@@ -165,23 +165,7 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
"This option implies we read data from given command-line files [or standard input] to "
"determine data range (use -i to select a data column, else last column is used). "
"If is not set we use the number of color slices in the chosen CPT.");
- GMT_Usage (API, 1, "\n-F[R|r|h|c][+c[]][+k]");
- 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 grayscale or colorname.");
- GMT_Usage (API, 3, "r: Output r/g/b only.");
- GMT_Usage (API, 3, "h: Output h-s-v.");
- GMT_Usage (API, 3, "c: Output c/m/y/k.");
- 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 , if appended, sets the labels for each category. It may be a "
- "comma-separated list of category names, or [-] where we automatically build "
- "labels from (a letter or an integer). Append - to build range labels -.");
- GMT_Usage (API, 3, "+k Set categorical keys rather than numerical values. "
- " may be a file with one key per line or a comma-separated list of keys. "
- "If 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.");
+ gmt_explain_cpt_output (API, 'F');
GMT_Usage (API, 1, "\n-G/");
GMT_Usage (API, -2, "Truncate incoming CPT to be limited to the z-range /. "
"To accept one of the incoming limits, set that limit to NaN.");
@@ -301,11 +285,16 @@ static int parse (struct GMT_CTRL *GMT, struct MAKECPT_CTRL *Ctrl, struct GMT_OP
if (txt_a[0]) Ctrl->F.key = strdup (txt_a);
}
switch (opt->arg[0]) {
+ case '\0': case '+': case 'R': Ctrl->F.model = GMT_RGB; break;
+ case 'c': Ctrl->F.model = GMT_CMYK; break;
case 'r': Ctrl->F.model = GMT_RGB + GMT_NO_COLORNAMES; break;
+ case 'g': Ctrl->F.model = GMT_GRAY; break;
case 'h': Ctrl->F.model = GMT_HSV; break;
- case 'c': Ctrl->F.model = GMT_CMYK; break;
case 'x': Ctrl->F.model = GMT_RGB + GMT_HEX_COLOR; break;
- default: Ctrl->F.model = GMT_RGB; break;
+ default:
+ GMT_Report (API, GMT_MSG_ERROR, "Option -F: Unrecognized directive %c\n", opt->arg[0]);
+ n_errors++;
+ break;
}
break;
case 'G': /* truncate incoming CPT */
@@ -668,45 +657,14 @@ EXTERN_MSC int GMT_makecpt (void *V_API, int mode, void *args) {
if (Ctrl->N.active) cpt_flags |= GMT_CPT_NO_BNF; /* bit 0 controls if BFN will be written out */
if (Ctrl->D.mode == 1) cpt_flags |= GMT_CPT_EXTEND_BNF; /* bit 1 controls if BF will be set to equal bottom/top rgb value */
if (Ctrl->F.active) Pout->model = Ctrl->F.model;
+
if (Ctrl->F.cat) { /* Flag as a categorical CPT */
- bool got_key_file= (Ctrl->F.key && !gmt_access (GMT, Ctrl->F.key, R_OK)); /* Want categorical labels read from file */
- unsigned int ns = 0;
- Pout->categorical = GMT_CPT_CATEGORICAL_VAL;
- if (Ctrl->F.label || (Ctrl->F.key && !got_key_file)) { /* Want auto-categorical labels appended to each CPT record */
- char **label = gmt_cat_cpt_strings (GMT, (Ctrl->F.label) ? Ctrl->F.label : Ctrl->F.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 (label[k]) Pout->data[k].label = label[k]; /* Now the job of the CPT to free these strings */
- }
- gmt_M_free (GMT, label); /* But the master array can go */
- }
- if (Ctrl->F.key) { /* Want categorical labels */
- char **keys = NULL;
- if (got_key_file) { /* Got a file with category keys */
- ns = gmt_read_list (GMT, Ctrl->F.key, &keys);
- if (ns < Pout->n_colors) {
- GMT_Report (API, GMT_MSG_ERROR, "The categorical keys file %s had %d entries but CPT has %d categories\n", Ctrl->F.key, ns, Pout->n_colors);
- Return (GMT_RUNTIME_ERROR);
- }
- else if (ns > Pout->n_colors)
- GMT_Report (API, GMT_MSG_WARNING, "The categorical keys file %s had %d entries but only %d are needed - skipping the extra keys\n", Ctrl->F.key, ns, Pout->n_colors);
- }
- else /* Got comma-separated keys */
- keys = gmt_cat_cpt_strings (GMT, Ctrl->F.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;
- }
+ if ((API->error = gmt_prepare_categorical_cpt (GMT, Ctrl->F.label, Ctrl->F.key, Pout)))
+ Return (API->error);
}
write = (GMT->current.setting.run_mode == GMT_CLASSIC || Ctrl->H.active); /* Only output to stdout in classic mode and with -H in modern mode */
+ if (Ctrl->F.model & GMT_GRAY) cpt_flags = GMT_CPT_GRAY_SET; /* Controls if final CPT should be converted to gray via YIQ */
if (write && GMT_Write_Data (API, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, cpt_flags, NULL, Ctrl->Out.file, Pout) != GMT_NOERROR) {
Return (API->error);