@@ -1552,6 +1552,68 @@ GMT_LOCAL void gmtsupport_truncate_cpt_slice (struct GMT_LUT *S, bool do_hsv, do
15521552	S->i_dz = 1.0 / (S->z_high - S->z_low);
15531553}
15541554
1555+ void gmt_explain_cpt_output (struct GMTAPI_CTRL *API, char option) {
1556+ 	/* Display usage synopsis for -F cpt output/categorical option in makecpt/grd2cpt */
1557+ 
1558+ 	GMT_Usage (API, 1, "\n-%c%s", option, GMT_COLORMODES_OPT);
1559+ 	GMT_Usage (API, -2, "Select the color model for output [Default uses the input model]:");
1560+ 	GMT_Usage (API, 3, "R: Output r/g/b or colornames.");
1561+ 	GMT_Usage (API, 3, "c: Output c/m/y/k.");
1562+ 	GMT_Usage (API, 3, "g: Output gray value (Will apply the YIQ if not a gray value).");
1563+ 	GMT_Usage (API, 3, "h: Output h-s-v.");
1564+ 	GMT_Usage (API, 3, "r: Output r/g/b only.");
1565+ 	GMT_Usage (API, 3, "x: Output hex #rrggbb only.");
1566+ 	GMT_Usage (API, -2, "Two modifiers control generation of categorical labels:");
1567+ 	GMT_Usage (API, 3, "+c Output a discrete CPT in categorical CPT format. "
1568+ 		"The <label>, if appended, sets the labels for each category. It may be a "
1569+ 		"comma-separated list of category names, or <start>[-] where we automatically build "
1570+ 		"labels from <start> (a letter or an integer). Append - to build range labels <start>-<start+1>.");
1571+ 	GMT_Usage (API, 3, "+k Set categorical keys rather than numerical values. "
1572+ 		"<keys> may be a file with one key per line or a comma-separated list of keys. "
1573+ 		"If <keys> is a single letter then we build sequential alphabetical keys from that letter. "
1574+ 		"If number of categories is 12 and label is M then we auto-create month name labels, and "
1575+ 		"if it is 7 and label is D then we auto-create weekday name labels.");
1576+ }
1577+ 
1578+ int gmt_prepare_categorical_cpt (struct GMT_CTRL *GMT, char *label, char *key, struct GMT_PALETTE *Pout) {
1579+ 		bool got_key_file = (key && !gmt_access (GMT, key, R_OK));	/* Want categorical labels read from file */
1580+ 		unsigned int ns = 0;
1581+ 		Pout->categorical = GMT_CPT_CATEGORICAL_VAL;
1582+ 		if (label || (key && !got_key_file)) {	/* Want auto-categorical labels appended to each CPT record */
1583+ 			char **Plabel = gmt_cat_cpt_strings (GMT, (label) ? label : key, Pout->n_colors, &ns);
1584+ 			for (unsigned int k = 0; k < MIN (Pout->n_colors, ns); k++) {
1585+ 				if (Pout->data[k].label) gmt_M_str_free (Pout->data[k].label);
1586+ 				if (Plabel[k]) Pout->data[k].label = Plabel[k];	/* Now the job of the CPT to free these strings */
1587+ 			}
1588+ 			gmt_M_free (GMT, Plabel);	/* But the master array can go */
1589+ 		}
1590+ 		if (key) {	/* Want categorical labels */
1591+ 			char **keys = NULL;
1592+ 			if (got_key_file) {	/* Got a file with category keys */
1593+ 				ns = gmt_read_list (GMT, key, &keys);
1594+ 				if (ns < Pout->n_colors) {
1595+ 					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);
1596+ 					return (GMT_RUNTIME_ERROR);
1597+ 				}
1598+ 				else if (ns > Pout->n_colors)
1599+ 					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);
1600+ 			}
1601+ 			else	/* Got comma-separated keys */
1602+ 				keys = gmt_cat_cpt_strings (GMT, key, Pout->n_colors, &ns);
1603+ 			for (unsigned int k = 0; k < MIN (Pout->n_colors, ns); k++) {
1604+ 				if (Pout->data[k].key) gmt_M_str_free (Pout->data[k].key);
1605+ 				if (k < ns && keys[k]) {
1606+ 					Pout->data[k].key = keys[k];	/* Now the job of the CPT to free these strings */
1607+ 					if (Pout->data[k].label) gmt_M_str_free (Pout->data[k].label);
1608+ 					Pout->data[k].label = strdup (keys[k]);
1609+ 				}
1610+ 			}
1611+ 			gmt_M_free (GMT, keys);	/* But the master array can go */
1612+ 			Pout->categorical = GMT_CPT_CATEGORICAL_KEY;
1613+ 		}
1614+ 		return (GMT_NOERROR);
1615+ }
1616+ 
15551617bool gmt_consider_current_cpt (struct GMTAPI_CTRL *API, bool *active, char **arg) {
15561618	/* Modern mode only: Detect if no CPT is given but -C was set.
15571619	 * If -C[+u|U<arg>] 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
83308392				X->model = GMT_RGB | GMT_COLORINT;
83318393			else if (strstr (line, "RGB"))
83328394				X->model = GMT_RGB;
8395+ 			else if (strstr (line, "+GRAY") || strstr (line, "gray"))
8396+ 				X->model = GMT_GRAY | GMT_COLORINT;
83338397			else if (strstr (line, "+HSV") || strstr (line, "hsv"))
83348398				X->model = GMT_HSV | GMT_COLORINT;
83358399			else if (strstr (line, "HSV"))
@@ -9473,6 +9537,9 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
94739537	struct CPT_Z_SCALE *Z = NULL;	/* For unit manipulations */
94749538	struct GMT_PALETTE_HIDDEN *PH = NULL;
94759539
9540+ 	if (cpt_flags & GMT_CPT_GRAY_SET && !P->is_gray)
9541+ 		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]);
9542+ 
94769543	/* When writing the CPT to file it is no longer a normalized CPT with a hinge */
94779544	P->has_range = P->has_hinge = 0;
94789545
@@ -9537,6 +9604,8 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
95379604	gmtlib_write_newheaders (GMT, fp, 0);	/* Write general header block */
95389605
95399606	if (!(P->model & GMT_COLORINT)) {}	/* Write nothing when color interpolation is not forced */
9607+ 	else if (P->model & GMT_GRAY)
9608+ 		fprintf (fp, "# COLOR_MODEL = gray\n");
95409609	else if (P->model & GMT_HSV)
95419610		fprintf (fp, "# COLOR_MODEL = hsv\n");
95429611	else if (P->model & GMT_CMYK)
@@ -9562,6 +9631,8 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
95629631
95639632		if (P->categorical) {
95649633			if (P->categorical & GMT_CPT_CATEGORICAL_KEY) strncpy (lo, P->data[i].key, GMT_LEN64-1);
9634+ 			if (P->model & GMT_GRAY)
9635+ 				fprintf (fp, format, lo, gmt_putgray (GMT, P->data[i].hsv_low), '\t');
95659636			if (P->model & GMT_HSV)
95669637				fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
95679638			else if (P->model & GMT_CMYK) {
@@ -9575,6 +9646,10 @@ int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type,
95759646			else
95769647				fprintf (fp, format, lo, gmt_putcolor (GMT, P->data[i].rgb_low), '\t');
95779648		}
9649+ 		else if (P->model & GMT_GRAY) {
9650+ 			fprintf (fp, format, lo, gmt_putgray (GMT, P->data[i].rgb_low), '\t');
9651+ 			fprintf (fp, format, hi, gmt_putgray (GMT, P->data[i].rgb_high), '\t');
9652+ 		}
95789653		else if (P->model & GMT_HSV) {
95799654			fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
95809655			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,
96159690		if (!use[i]) continue;	/* This BNF entry does not apply to this CPT */
96169691		if (P->bfn[i].skip)
96179692			fprintf (fp, "%c\t-\n", code[i]);
9693+ 		else if (P->model & GMT_GRAY)
9694+ 			fprintf (fp, "%c\t%s\n", code[i], gmt_putgray (GMT, P->bfn[i].rgb));
96189695		else if (P->model & GMT_HSV)
96199696			fprintf (fp, "%c\t%s\n", code[i], gmtlib_puthsv (GMT, P->bfn[i].hsv));
96209697		else if (P->model & GMT_CMYK) {
0 commit comments