Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify addition of category labels to CPT files #4390

Merged
merged 3 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 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**][**+c**] ]
[ |-F|\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+c**\ [*label*]] ]
[ |-G|\ *zlo*\ /\ *zhi* ]
[ |-H| ]
[ |-I|\ [**c**][**z**] ]
Expand Down Expand Up @@ -120,11 +120,17 @@ Optional Arguments

.. _-F:

**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+c**]
**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+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**). 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.

.. _-G:

Expand Down
19 changes: 17 additions & 2 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**][**+c**]]
[ |-F|\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+c**\ [*label*]]]
[ |-G|\ *zlo*\ /\ *zhi* ]
[ |-H| ]
[ |-I|\ [**c**][**z**] ]
Expand Down Expand Up @@ -111,11 +111,17 @@ Optional Arguments

.. _-F:

**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+c**]]
**-F**\ [**R**\|\ **r**\|\ **h**\|\ **c**][**+c**\ [*label*]]
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**). 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.

.. _-G:

Expand Down Expand Up @@ -313,6 +319,15 @@ we always get a color regardless of the *z* value, try

gmt makecpt -Cjet -T0/500 -Ww > wrapped.cpt

To build a categorical table with 3 categories and add specific category
names to them, try::

gmt makecpt -Ccubhelix -T0/3/1 -F+cClouds,Trees,Water > cat.cpt

To instead add unique category labels A, B, C, ... to a 10-item categorical CPT, try::

gmt makecpt -Cjet -T0/q0/1 -F+cA

.. include:: cpt_notes.rst_

Bugs
Expand Down
1 change: 1 addition & 0 deletions src/gmt_prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ EXTERN_MSC bool gmt_this_alloc_level (struct GMT_CTRL *GMT, unsigned int alloc_l

/* gmt_support.c: */

EXTERN_MSC char ** gmt_cat_cpt_labels (struct GMT_CTRL *GMT, char *label, unsigned int n);
EXTERN_MSC bool gmt_same_fill (struct GMT_CTRL *GMT, struct GMT_FILL *F1, struct GMT_FILL *F2);
EXTERN_MSC unsigned int gmt_contour_first_pos (struct GMT_CTRL *GMT, char *arg);
EXTERN_MSC unsigned int gmt_contour_A_arg_parsing (struct GMT_CTRL *GMT, char *arg, struct CONTOUR_ARGS *A);
Expand Down
49 changes: 49 additions & 0 deletions src/gmt_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -7407,6 +7407,55 @@ unsigned int gmt_validate_cpt_parameters (struct GMT_CTRL *GMT, struct GMT_PALET
return GMT_NOERROR;
}

char ** gmt_cat_cpt_labels (struct GMT_CTRL *GMT, char *label, unsigned int n) {
/* Generate categorical labels for n categories from the label magic argument */
unsigned int k = 0;
char **Clabel = gmt_M_memory (GMT, NULL, n, char *);

if (strchr (label, ',')) { /* Got list of category names */
char *word = NULL, *trail = NULL, *orig = strdup (label);
trail = orig;
while ((word = strsep (&trail, ",")) != NULL && k < n) {
if (*word != '\0') /* Skip empty strings */
Clabel[k] = strdup (word);
k++;
}
gmt_M_str_free (orig);
}
else { /* Auto-build the labels */
unsigned int mode;
int start;
char string[GMT_LEN64] = {""};
if (isdigit (label[0])) { /* Integer categories */
mode = 1;
start = atoi (label);
}
else { /* Letter categories */
mode = 3;
start = label[0];
}
if (label[strlen(label)-1] == '-') mode++; /* Wants a range */
for (k = 0; k < n; k++) {
switch (mode) {
case 1: /* Single integer label */
sprintf (string, "%d", start+k);
break;
case 2: /* Integer range label */
sprintf (string, "%d-%d", start+k, start+k+1);
break;
case 3: /* Single letter label */
sprintf (string, "%c", start+k);
break;
case 4: /* Character range label */
sprintf (string, "%c-%c", start+k, start+k+1);
break;
}
Clabel[k] = strdup (string);
}
}
return Clabel;
}

/*! . */
struct GMT_PALETTE * gmtlib_read_cpt (struct GMT_CTRL *GMT, void *source, unsigned int source_type, unsigned int cpt_flags) {
/* Opens and reads a color palette file in RGB, HSV, or CMYK of arbitrary length.
Expand Down
25 changes: 20 additions & 5 deletions src/grd2cpt.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ struct GRD2CPT_CTRL {
char *file;
unsigned int levels;
} E;
struct GRD2CPT_F { /* -F[r|R|h|c][+c] */
struct GRD2CPT_F { /* -F[r|R|h|c][+c[<label>]] */
bool active;
bool cat;
unsigned int model;
char *label;
} F;
struct GRD2CPT_G { /* -Glow/high for input CPT truncation */
bool active;
Expand Down Expand Up @@ -137,6 +138,7 @@ static void Free_Ctrl (struct GMT_CTRL *GMT, struct GRD2CPT_CTRL *C) { /* Deallo
gmt_M_str_free (C->Out.file);
gmt_M_str_free (C->C.file);
gmt_M_str_free (C->E.file);
gmt_M_str_free (C->F.label);
gmt_M_str_free (C->T.file);
gmt_M_free (GMT, C);
}
Expand All @@ -146,7 +148,7 @@ 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_Message (API, GMT_TIME_NONE, "usage: %s <grid> [-A<transparency>[+a]] [-C<cpt>] [-D[i|o]] [-E[<nlevels>][+c][+f<file>]]\n", name);
GMT_Message (API, GMT_TIME_NONE, "\t[-F[R|r|h|c][+c]] [-G<zlo>/<zhi>]%s [-I[c][z]] [-L<min_limit>/<max_limit>] [-M] [-N] [-Q[i|o]]\n", H_OPT);
GMT_Message (API, GMT_TIME_NONE, "\t[-F[R|r|h|c][+c[<label>]]] [-G<zlo>/<zhi>]%s [-I[c][z]] [-L<min_limit>/<max_limit>] [-M] [-N] [-Q[i|o]]\n", H_OPT);
GMT_Message (API, GMT_TIME_NONE, "\t[%s] [-T<start>/<stop>/<inc> or -T<n>] [-Sh|l|m|u]\n\t[%s] [-W[w]] [-Z] [%s] [%s]\n\t[%s] [%s]\n\n", 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);
Expand All @@ -164,7 +166,10 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
GMT_Message (API, GMT_TIME_NONE, "\t Append +f<file> to save the CDF table to a file.\n");
GMT_Message (API, GMT_TIME_NONE, "\t-F Select the color model for output (R for r/g/b or grayscale or colorname,\n");
GMT_Message (API, GMT_TIME_NONE, "\t r for r/g/b only, h for h-s-v, c for c/m/y/k) [Default uses the input model]\n");
GMT_Message (API, GMT_TIME_NONE, "\t Append +c to output a discrete CPT in categorical CPT format.\n");
GMT_Message (API, GMT_TIME_NONE, "\t Append +c[label>] to output a discrete CPT in categorical CPT format.\n");
GMT_Message (API, GMT_TIME_NONE, "\t The <label>, if present, sets the labels for each category. It may be a\n");
GMT_Message (API, GMT_TIME_NONE, "\t comma-separated list of category names, or <start>[-], where we automatically build\n");
GMT_Message (API, GMT_TIME_NONE, "\t labels from <start> (a letter or an integer). Append - to build ranges <start>-<start+1>.\n");
GMT_Message (API, GMT_TIME_NONE, "\t-G Truncate incoming CPT to be limited to the z-range <zlo>/<zhi>.\n");
GMT_Message (API, GMT_TIME_NONE, "\t To accept one of the incoming limits, set that limit to NaN.\n");
if (API->GMT->current.setting.run_mode == GMT_MODERN)
Expand Down Expand Up @@ -279,7 +284,7 @@ static int parse (struct GMT_CTRL *GMT, struct GRD2CPT_CTRL *Ctrl, struct GMT_OP
if (gmt_validate_modifiers (GMT, opt->arg, 'F', "c", GMT_MSG_ERROR)) n_errors++;
if (gmt_get_modifier (opt->arg, 'c', txt_a)) {
Ctrl->F.cat = true;
if (txt_a[0] == '\0') break;
Ctrl->F.label = strdup (txt_a);
}
Ctrl->F.active = true;
switch (txt_a[0]) {
Expand Down Expand Up @@ -833,7 +838,17 @@ 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) Pout->categorical = 1;
if (Ctrl->F.cat) { /* Flag as a categorical CPT */
Pout->categorical = 1;
if (Ctrl->F.label[0]) { /* Want categorical labels */
char **label = gmt_cat_cpt_labels (GMT, Ctrl->F.label, Pout->n_colors);
for (unsigned int k = 0; k < Pout->n_colors; 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 */

Expand Down
30 changes: 24 additions & 6 deletions src/makecpt.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ struct MAKECPT_CTRL {
bool active;
unsigned int levels;
} E;
struct MAKECPT_F { /* -F[r|R|h|c][+c] */
struct MAKECPT_F { /* -F[r|R|h|c][+c[<label>]] */
bool active;
bool cat;
unsigned int model;
char *label;
} F;
struct MAKECPT_G { /* -Glow/high for input CPT truncation */
bool active;
Expand Down Expand Up @@ -132,6 +133,7 @@ static void Free_Ctrl (struct GMT_CTRL *GMT, struct MAKECPT_CTRL *C) { /* Deallo
if (!C) return;
gmt_M_str_free (C->Out.file);
gmt_M_str_free (C->C.file);
gmt_M_str_free (C->F.label);
gmt_free_array (GMT, &(C->T.T));
gmt_M_free (GMT, C);
}
Expand All @@ -140,7 +142,7 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
const char *name = gmt_show_name_and_purpose (API, THIS_MODULE_LIB, THIS_MODULE_CLASSIC_NAME, THIS_MODULE_PURPOSE);
const char *H_OPT = (API->GMT->current.setting.run_mode == GMT_MODERN) ? " [-H]" : "";
if (level == GMT_MODULE_PURPOSE) return (GMT_NOERROR);
GMT_Message (API, GMT_TIME_NONE, "usage: %s [-A<transparency>[+a]] [-C<cpt>|colors] [-D[i|o]] [-E<nlevels>] [-F[R|r|h|c][+c]] [-G<zlo>/<zhi>]%s\n", name, H_OPT);
GMT_Message (API, GMT_TIME_NONE, "usage: %s [-A<transparency>[+a]] [-C<cpt>|colors] [-D[i|o]] [-E<nlevels>] [-F[R|r|h|c][+c[<label>]]] [-G<zlo>/<zhi>]%s\n", name, H_OPT);
GMT_Message (API, GMT_TIME_NONE, "\t[-I[c][z]] [-M] [-N] [-Q] [-S<mode>] [-T<min>/<max>[/<inc>[+b|l|n]] | -T<table> | -T<z1,z2,...zn>] [%s] [-W[w]]\n\t[-Z] [%s] [%s] [%s]\n\t[%s] [%s]\n\n",
GMT_V_OPT, GMT_bi_OPT, GMT_di_OPT, GMT_i_OPT, GMT_ho_OPT, GMT_PAR_OPT);

Expand All @@ -158,7 +160,10 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
GMT_Message (API, GMT_TIME_NONE, "\t If <nlevels> is not set we use the number of color slices in the chosen CPT.\n");
GMT_Message (API, GMT_TIME_NONE, "\t-F Select the color model for output (R for r/g/b or grayscale or colorname,\n");
GMT_Message (API, GMT_TIME_NONE, "\t r for r/g/b only, h for h-s-v, c for c/m/y/k) [Default uses the input model]\n");
GMT_Message (API, GMT_TIME_NONE, "\t Append +c to output a discrete CPT in categorical CPT format.\n");
GMT_Message (API, GMT_TIME_NONE, "\t Append +c[label>] to output a discrete CPT in categorical CPT format.\n");
GMT_Message (API, GMT_TIME_NONE, "\t The <label>, if present, sets the labels for each category. It may be a\n");
GMT_Message (API, GMT_TIME_NONE, "\t comma-separated list of category names, or <start>[-], where we automatically build\n");
GMT_Message (API, GMT_TIME_NONE, "\t labels from <start> (a letter or an integer). Append - to build ranges <start>-<start+1>.\n");
GMT_Message (API, GMT_TIME_NONE, "\t-G Truncate incoming CPT to be limited to the z-range <zlo>/<zhi>.\n");
GMT_Message (API, GMT_TIME_NONE, "\t To accept one of the incoming limits, set that limit to NaN.\n");
if (API->GMT->current.setting.run_mode == GMT_MODERN)
Expand Down Expand Up @@ -200,7 +205,7 @@ static int parse (struct GMT_CTRL *GMT, struct MAKECPT_CTRL *Ctrl, struct GMT_OP

int n;
unsigned int n_errors = 0, n_files[2] = {0, 0};
char txt_a[GMT_LEN32] = {""}, txt_b[GMT_LEN32] = {""}, *c = NULL;
char txt_a[GMT_LEN512] = {""}, txt_b[GMT_LEN32] = {""}, *c = NULL;
struct GMT_OPTION *opt = NULL;

for (opt = options; opt; opt = opt->next) if (opt->option == 'Q') Ctrl->Q.active = true;; /* If -T given before -Q we need to flag -T+l */
Expand Down Expand Up @@ -251,7 +256,10 @@ static int parse (struct GMT_CTRL *GMT, struct MAKECPT_CTRL *Ctrl, struct GMT_OP
case 'F': /* Sets format for color reporting */
Ctrl->F.active = true;
if (gmt_validate_modifiers (GMT, opt->arg, 'F', "c", GMT_MSG_ERROR)) n_errors++;
if (gmt_get_modifier (opt->arg, 'c', txt_a)) Ctrl->F.cat = true;
if (gmt_get_modifier (opt->arg, 'c', txt_a)) {
Ctrl->F.cat = true;
Ctrl->F.label = strdup (txt_a);
}
switch (opt->arg[0]) {
case 'r': Ctrl->F.model = GMT_RGB + GMT_NO_COLORNAMES; break;
case 'h': Ctrl->F.model = GMT_HSV; break;
Expand Down Expand Up @@ -585,7 +593,17 @@ 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) Pout->categorical = 1;
if (Ctrl->F.cat) { /* Flag as a categorical CPT */
Pout->categorical = 1;
if (Ctrl->F.label[0]) { /* Want categorical labels */
char **label = gmt_cat_cpt_labels (GMT, Ctrl->F.label, Pout->n_colors);
for (unsigned int k = 0; k < Pout->n_colors; 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);
}
}

write = (GMT->current.setting.run_mode == GMT_CLASSIC || Ctrl->H.active); /* Only output to stdout in classic mode and with -H in modern mode */

Expand Down
Binary file added test/psscale/catlabels.ps
Binary file not shown.
19 changes: 19 additions & 0 deletions test/psscale/catlabels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Testing the auto-labeling of categorical CPTs.
ps=catlabels.ps
gmt set FONT_ANNOT_PRIMARY 9p,Helvetica,black
gmt makecpt -T0/4/1 -Ccubhelix -F+c > t.cpt
gmt psxy -R0/20/0/30 -Jx1c -P -K -Y0 -T > $ps
gmt psscale -O -K -Ct.cpt -Dx8c/22c+w12c/0.5c+jTC+h -Y2.5c >> $ps
gmt psscale -O -K -Ct.cpt -Dx8c/19c+w12c/0.5c+jTC+h -Bxaf >> $ps
gmt psscale -O -K -Ct.cpt -Dx8c/16c+w12c/0.5c+jTC+h -Li0.4c >> $ps
gmt makecpt -T0/4/1 -Ccubhelix -F+cNight,Trees,Sediment,Water > t.cpt
gmt psscale -O -K -Ct.cpt -Dx8c/13c+w12c/0.5c+jTC+h -Li0.4c >> $ps
gmt makecpt -T0/4/1 -Ccubhelix -F+cA > t.cpt
gmt psscale -O -K -Ct.cpt -Dx8c/10c+w12c/0.5c+jTC+h >> $ps
gmt makecpt -T0/4/1 -Ccubhelix -F+c1 > t.cpt
gmt psscale -O -K -Ct.cpt -Dx8c/7c+w12c/0.5c+jTC+h >> $ps
gmt makecpt -T0/4/1 -Ccubhelix -F+c9- > t.cpt
gmt psscale -O -K -Ct.cpt -Dx8c/4c+w12c/0.5c+jTC+h >> $ps
gmt makecpt -T0/4/1 -Ccubhelix -F+cG- > t.cpt
gmt psscale -O -Ct.cpt -Dx8c/1c+w12c/0.5c+jTC+h -Li0.4c >> $ps