|
32 | 32 | #define THIS_MODULE_MODERN_NAME "text" |
33 | 33 | #define THIS_MODULE_LIB "core" |
34 | 34 | #define THIS_MODULE_PURPOSE "Plot or typeset text" |
35 | | -#define THIS_MODULE_KEYS "<D{,>X},>DL" |
| 35 | +#define THIS_MODULE_KEYS "<D{,DD(=f,>X},>DL" |
36 | 36 | #define THIS_MODULE_NEEDS "JR" |
37 | 37 | #define THIS_MODULE_OPTIONS "-:>BJKOPRUVXYaefhpqtxyw" GMT_OPT("Ec") |
38 | 38 |
|
@@ -60,12 +60,14 @@ struct PSTEXT_CTRL { |
60 | 60 | double dx, dy; |
61 | 61 | char mode; |
62 | 62 | } C; |
63 | | - struct PSTEXT_D { /* -D[j]<dx>[/<dy>][v[<pen>]] */ |
| 63 | + struct PSTEXT_D { /* -D[j|J]<dx>[/<dy>][+f<file>][+v[<pen>]] */ |
64 | 64 | bool active; |
65 | 65 | bool line; |
| 66 | + bool variable; /* True if per-record offsets are read from +f<file> */ |
66 | 67 | int justify; |
67 | 68 | double dx, dy; |
68 | 69 | struct GMT_PEN pen; |
| 70 | + char *file; /* Name of file with per-record dx,dy offsets */ |
69 | 71 | } D; |
70 | 72 | struct PSTEXT_F { /* -F[+c+f<fontinfo>+a<angle>+j<justification>+l|h|r|z|t] */ |
71 | 73 | bool active; |
@@ -160,6 +162,7 @@ static void *New_Ctrl (struct GMT_CTRL *GMT) { /* Allocate and initialize a new |
160 | 162 |
|
161 | 163 | static void Free_Ctrl (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *C) { /* Deallocate control structure */ |
162 | 164 | if (!C) return; |
| 165 | + gmt_M_str_free (C->D.file); |
163 | 166 | gmt_M_str_free (C->F.text); |
164 | 167 | gmt_M_free (GMT, C); |
165 | 168 | } |
@@ -292,7 +295,7 @@ static int usage (struct GMTAPI_CTRL *API, int level) { |
292 | 295 | const char *name = gmt_show_name_and_purpose (API, THIS_MODULE_LIB, THIS_MODULE_CLASSIC_NAME, THIS_MODULE_PURPOSE); |
293 | 296 | if (level & PSTEXT_SHOW_FONTS) show_fonts = true, level -= PSTEXT_SHOW_FONTS; /* Deal with the special bitflag for showing the fonts */ |
294 | 297 | if (level == GMT_MODULE_PURPOSE) return (GMT_NOERROR); |
295 | | - GMT_Usage (API, 0, "usage: %s [<table>] %s %s [-A] [%s] [-C[<dx>[/<dy>]][+tc|C|o|O]] [-D[j|J]<dx>[/<dy>][+v[<pen>]]] " |
| 298 | + GMT_Usage (API, 0, "usage: %s [<table>] %s %s [-A] [%s] [-C[<dx>[/<dy>]][+tc|C|o|O]] [-D[j|J]<dx>[/<dy>][+f<file>][+v[<pen>]]] " |
296 | 299 | "[-F[+a[<angle>]][+c[<justify>]][+f[<font>]][+h|l|r[<first>]|+t<text>|+z[<fmt>]][+j[<justify>]]] %s " |
297 | 300 | "[-G[<color>][+n]] [-L] [-M] [-N] %s%s[-Ql|u] [-S[<dx>/<dy>/][<shade>]] [%s] [%s] [-W<pen>] [%s] [%s] [-Z] " |
298 | 301 | "[%s] %s[%s] [%s] [%s] [-it<word>] [%s] [%s] [%s] [%s] [%s] [%s]\n", |
@@ -349,10 +352,11 @@ static int usage (struct GMTAPI_CTRL *API, int level) { |
349 | 352 | GMT_Usage (API, 3, "C: Convex rectangle (requires -M)."); |
350 | 353 | GMT_Usage (API, 3, "o: Rectangle [Default]."); |
351 | 354 | GMT_Usage (API, 3, "O: Rectangle with rounded corners."); |
352 | | - GMT_Usage (API, 1, "\n-D[j|J]<dx>[/<dy>][+v[<pen>]]"); |
| 355 | + GMT_Usage (API, 1, "\n-D[j|J]<dx>[/<dy>][+f<file>][+v[<pen>]]"); |
353 | 356 | GMT_Usage (API, -2, "Add <dx>,<dy> to the text origin AFTER projecting with -J. If <dy> is not given it equals <dx> [0/0]. " |
354 | 357 | "Use -Dj to move text origin away from point (direction determined by text's justification). " |
355 | | - "Upper case -DJ will shorten diagonal shifts at corners by sqrt(2). Cannot be used with -M. Optional modifier:"); |
| 358 | + "Upper case -DJ will shorten diagonal shifts at corners by sqrt(2). Cannot be used with -M. Optional modifiers:"); |
| 359 | + GMT_Usage (API, 3, "+f: Read per-record offsets (dx dy) from <file> (two columns in current distance units); overrides fixed <dx>/<dy>."); |
356 | 360 | GMT_Usage (API, 3, "+v: Draw line from text to original point; optionally append a <pen> [%s].", gmt_putpen (API->GMT, &API->GMT->current.setting.map_default_pen)); |
357 | 361 | GMT_Usage (API, 1, "\n-F[+a[<angle>]][+c[<justify>]][+f[<font>]][+h|l|r[<first>]|+t<text>|+z[<fmt>]][+j[<justify>]]"); |
358 | 362 | GMT_Usage (API, -2, "Specify values for text attributes that apply to all text records:"); |
@@ -453,22 +457,41 @@ static int parse (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *Ctrl, struct GMT_OPT |
453 | 457 | if (c) c[0] = '+'; /* Restore */ |
454 | 458 | break; |
455 | 459 | case 'D': |
456 | | - n_errors += gmt_M_repeated_module_option (API, Ctrl->D.active); |
| 460 | + n_errors += gmt_M_repeated_module_option(API, Ctrl->D.active); |
457 | 461 | k = 0; |
458 | 462 | if (opt->arg[k] == 'j') { Ctrl->D.justify = 1, k++; } |
459 | 463 | else if (opt->arg[k] == 'J') { Ctrl->D.justify = 2, k++; } |
| 464 | + /* Check for +f<file> modifier (per-record variable offsets); extract before other parsing */ |
| 465 | + if ((c = strstr(&opt->arg[k], "+f"))) { |
| 466 | + char *end = strchr (&c[2], '+'); /* Look for next modifier (+v) after filename */ |
| 467 | + if (end) { /* Another modifier follows after +f<file> */ |
| 468 | + char saved = *end; |
| 469 | + *end = '\0'; |
| 470 | + Ctrl->D.file = strdup(&c[2]); |
| 471 | + *end = saved; |
| 472 | + memmove(c, end, strlen(end) + 1); /* Remove +f<file> from option string */ |
| 473 | + } |
| 474 | + else { |
| 475 | + Ctrl->D.file = strdup(&c[2]); |
| 476 | + c[0] = '\0'; /* Truncate option string at +f */ |
| 477 | + } |
| 478 | + Ctrl->D.variable = true; |
| 479 | + } |
| 480 | + /* Handle +v modifier (new-style or old-style) */ |
460 | 481 | for (j = k; opt->arg[j] && opt->arg[j] != 'v'; j++); |
461 | 482 | if (opt->arg[j] == 'v') { /* Want to draw a line from point to offset point */ |
462 | 483 | Ctrl->D.line = true; |
463 | | - n_errors += gmt_M_check_condition (GMT, opt->arg[j+1] && gmt_getpen (GMT, &opt->arg[j+1], &Ctrl->D.pen), "Option -D: Give pen after +v\n"); |
| 484 | + n_errors += gmt_M_check_condition(GMT, opt->arg[j+1] && gmt_getpen (GMT, &opt->arg[j+1], &Ctrl->D.pen), "Option -D: Give pen after +v\n"); |
464 | 485 | if (opt->arg[j-1] == '+') /* New-style syntax */ |
465 | 486 | opt->arg[j-1] = 0; |
466 | 487 | else |
467 | 488 | opt->arg[j] = 0; |
468 | 489 | } |
469 | 490 | j = sscanf (&opt->arg[k], "%[^/]/%s", txt_a, txt_b); |
470 | | - Ctrl->D.dx = gmt_M_to_inch (GMT, txt_a); |
471 | | - Ctrl->D.dy = (j == 2) ? gmt_M_to_inch (GMT, txt_b) : Ctrl->D.dx; |
| 491 | + if (j >= 1 && txt_a[0]) { /* Have explicit dx[/dy] */ |
| 492 | + Ctrl->D.dx = gmt_M_to_inch(GMT, txt_a); |
| 493 | + Ctrl->D.dy = (j == 2) ? gmt_M_to_inch(GMT, txt_b) : Ctrl->D.dx; |
| 494 | + } |
472 | 495 | break; |
473 | 496 | case 'F': |
474 | 497 | n_errors += gmt_M_repeated_module_option (API, Ctrl->F.active); |
@@ -708,7 +731,7 @@ static int parse (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *Ctrl, struct GMT_OPT |
708 | 731 | n_errors += gmt_M_check_condition (GMT, !Ctrl->L.active && !GMT->common.J.active, "Must specify a map projection with the -J option\n"); |
709 | 732 | n_errors += gmt_M_check_condition (GMT, Ctrl->C.dx < 0.0 || Ctrl->C.dy < 0.0, "Option -C: clearances cannot be negative!\n"); |
710 | 733 | n_errors += gmt_M_check_condition (GMT, Ctrl->C.dx == 0.0 && Ctrl->C.dy == 0.0 && Ctrl->C.mode != 'o', "Option -C: Non-rectangular text boxes require a non-zero clearance\n"); |
711 | | - n_errors += gmt_M_check_condition (GMT, Ctrl->D.dx == 0.0 && Ctrl->D.dy == 0.0 && Ctrl->D.line, "-D<x/y>v requires one nonzero <x/y>\n"); |
| 734 | + n_errors += gmt_M_check_condition (GMT, Ctrl->D.dx == 0.0 && Ctrl->D.dy == 0.0 && Ctrl->D.line && !Ctrl->D.variable, "-D<x/y>v requires one nonzero <x/y>\n"); |
712 | 735 | n_errors += gmt_M_check_condition (GMT, Ctrl->Q.active && abs (Ctrl->Q.mode) > 1, "Option -Q: Use l or u for lower/upper-case.\n"); |
713 | 736 | n_errors += gmt_M_check_condition (GMT, Ctrl->G.mode && Ctrl->M.active, "Option -Gc: Cannot be used with -M.\n"); |
714 | 737 | n_errors += gmt_M_check_condition (GMT, Ctrl->G.mode && Ctrl->W.active, "Option -Gc: Cannot be used with -W.\n"); |
@@ -824,6 +847,8 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { |
824 | 847 | unsigned int n_read = 0, n_processed = 0, txt_alloc = 0, add, n_expected_cols, z_col = GMT_Z, n_skipped = 0; |
825 | 848 |
|
826 | 849 | size_t n_alloc = 0; |
| 850 | + uint64_t n_D_offsets = 0, d_rec_no = 0; |
| 851 | + double *D_off_x = NULL, *D_off_y = NULL; |
827 | 852 |
|
828 | 853 | double plot_x = 0.0, plot_y = 0.0, save_angle = 0.0, xx[2] = {0.0, 0.0}, yy[2] = {0.0, 0.0}, *in = NULL; |
829 | 854 | double offset[2], tmp, *c_x = NULL, *c_y = NULL, *c_angle = NULL; |
@@ -892,7 +917,30 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { |
892 | 917 | Ctrl->F.font.size = GMT->current.setting.font_annot[GMT_PRIMARY].size; |
893 | 918 |
|
894 | 919 | pstext_load_parameters_pstext (GMT, &T, Ctrl); /* Pass info from Ctrl to T */ |
| 920 | + if (Ctrl->D.variable) { /* Read per-record offsets from file */ |
| 921 | + uint64_t tbl, seg, row; |
| 922 | + double unit_scale = GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH]; |
| 923 | + struct GMT_DATASET *D_file = NULL; |
| 924 | + if ((D_file = GMT_Read_Data (API, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, |
| 925 | + GMT_READ_NORMAL, NULL, Ctrl->D.file, NULL)) == NULL) |
| 926 | + Return (API->error); |
| 927 | + for (tbl = 0; tbl < D_file->n_tables; tbl++) |
| 928 | + for (seg = 0; seg < D_file->table[tbl]->n_segments; seg++) |
| 929 | + n_D_offsets += D_file->table[tbl]->segment[seg]->n_rows; |
| 930 | + D_off_x = gmt_M_memory (GMT, NULL, n_D_offsets, double); |
| 931 | + D_off_y = gmt_M_memory (GMT, NULL, n_D_offsets, double); |
| 932 | + n_D_offsets = 0; |
| 933 | + for (tbl = 0; tbl < D_file->n_tables; tbl++) |
| 934 | + for (seg = 0; seg < D_file->table[tbl]->n_segments; seg++) |
| 935 | + for (row = 0; row < D_file->table[tbl]->segment[seg]->n_rows; row++, n_D_offsets++) { |
| 936 | + D_off_x[n_D_offsets] = D_file->table[tbl]->segment[seg]->data[GMT_X][row] * unit_scale; |
| 937 | + D_off_y[n_D_offsets] = D_file->table[tbl]->segment[seg]->data[GMT_Y][row] * unit_scale; |
| 938 | + } |
| 939 | + if (GMT_Destroy_Data (API, &D_file) != GMT_NOERROR) |
| 940 | + Return (API->error); |
| 941 | + } |
895 | 942 | add = !(T.x_offset == 0.0 && T.y_offset == 0.0); |
| 943 | + if (Ctrl->D.variable) add = 1; /* Variable offsets always enable displacement */ |
896 | 944 | if (add && Ctrl->D.justify) T.boxflag |= 64; |
897 | 945 |
|
898 | 946 | if (!(Ctrl->N.active || Ctrl->Z.active)) { |
@@ -937,11 +985,13 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { |
937 | 985 | else if (T.paragraph_angle < -90.0) T.paragraph_angle += 180.0; |
938 | 986 | } |
939 | 987 | if (add) { |
| 988 | + double xoff = (Ctrl->D.variable && n_D_offsets > 0) ? D_off_x[0] : T.x_offset; |
| 989 | + double yoff = (Ctrl->D.variable && n_D_offsets > 0) ? D_off_y[0] : T.y_offset; |
940 | 990 | if (Ctrl->D.justify) /* Smart offset according to justification (from Dave Huang) */ |
941 | | - gmt_smart_justify (GMT, T.block_justify, T.paragraph_angle, T.x_offset, T.y_offset, &plot_x, &plot_y, Ctrl->D.justify); |
| 991 | + gmt_smart_justify(GMT, T.block_justify, T.paragraph_angle, xoff, yoff, &plot_x, &plot_y, Ctrl->D.justify); |
942 | 992 | else { /* Default hard offset */ |
943 | | - plot_x += T.x_offset; |
944 | | - plot_y += T.y_offset; |
| 993 | + plot_x += xoff; |
| 994 | + plot_y += yoff; |
945 | 995 | } |
946 | 996 | xx[1] = plot_x; yy[1] = plot_y; |
947 | 997 | } |
@@ -1350,14 +1400,17 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { |
1350 | 1400 | else if (T.paragraph_angle < -90.0) T.paragraph_angle += 180.0; |
1351 | 1401 | } |
1352 | 1402 | if (add) { |
| 1403 | + double xoff = (Ctrl->D.variable && d_rec_no < n_D_offsets) ? D_off_x[d_rec_no] : T.x_offset; |
| 1404 | + double yoff = (Ctrl->D.variable && d_rec_no < n_D_offsets) ? D_off_y[d_rec_no] : T.y_offset; |
1353 | 1405 | if (Ctrl->D.justify) /* Smart offset according to justification (from Dave Huang) */ |
1354 | | - gmt_smart_justify (GMT, T.block_justify, T.paragraph_angle, T.x_offset, T.y_offset, &plot_x, &plot_y, Ctrl->D.justify); |
| 1406 | + gmt_smart_justify(GMT, T.block_justify, T.paragraph_angle, xoff, yoff, &plot_x, &plot_y, Ctrl->D.justify); |
1355 | 1407 | else { /* Default hard offset */ |
1356 | | - plot_x += T.x_offset; |
1357 | | - plot_y += T.y_offset; |
| 1408 | + plot_x += xoff; |
| 1409 | + plot_y += yoff; |
1358 | 1410 | } |
1359 | 1411 | xx[1] = plot_x; yy[1] = plot_y; |
1360 | 1412 | } |
| 1413 | + if (Ctrl->D.variable) d_rec_no++; |
1361 | 1414 | n_paragraphs++; |
1362 | 1415 |
|
1363 | 1416 | if (GMT->common.t.variable) { /* Update the transparencies for current string (if -t was given) */ |
@@ -1518,6 +1571,10 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { |
1518 | 1571 |
|
1519 | 1572 | GMT_Report (API, GMT_MSG_INFORMATION, Ctrl->M.active ? "pstext: Plotted %d text blocks\n" : "pstext: Plotted %d text strings\n", n_paragraphs); |
1520 | 1573 |
|
| 1574 | + if (Ctrl->D.variable) { |
| 1575 | + gmt_M_free(GMT, D_off_x); |
| 1576 | + gmt_M_free(GMT, D_off_y); |
| 1577 | + } |
1521 | 1578 | Return (GMT_NOERROR); |
1522 | 1579 | } |
1523 | 1580 |
|
|
0 commit comments