do_labels.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #include <grass/gis.h>
  6. #include <grass/display.h>
  7. #include <grass/glocale.h>
  8. #include "local_proto.h"
  9. #define NL 012
  10. #define TAB 011
  11. #define BACK 0134
  12. #define MTEXT 1024
  13. #define TOP 0
  14. #define CENT 1
  15. #define BOT 2
  16. #define LEFT 0
  17. #define RITE 2
  18. #define YES 1
  19. #define NO 0
  20. static double east;
  21. static double north;
  22. static int xoffset;
  23. static int yoffset;
  24. static int xref;
  25. static int yref;
  26. static RGBA_Color color, highlight_color, background, border;
  27. static double size;
  28. static int fontsize;
  29. static int highlight_width;
  30. static int opaque;
  31. static double width, rotation;
  32. static char text[MTEXT];
  33. static char font[256];
  34. static const char *std_font;
  35. static int ymatch(char *);
  36. static int xmatch(char *);
  37. int initialize_options(void)
  38. {
  39. east = 0.0;
  40. north = 0.0;
  41. xoffset = 0;
  42. yoffset = 0;
  43. xref = CENT;
  44. yref = CENT;
  45. set_RGBA_from_str(&color, "black");
  46. set_RGBA_from_str(&highlight_color, "white");
  47. set_RGBA_from_str(&background, "white");
  48. set_RGBA_from_str(&border, "black");
  49. size = 1000.;
  50. fontsize = 0;
  51. width = 1.;
  52. highlight_width = 0;
  53. opaque = YES;
  54. rotation = 0.0;
  55. std_font = getenv("GRASS_FONT");
  56. if (!std_font)
  57. std_font = "romans";
  58. strcpy(font, std_font);
  59. return 0;
  60. }
  61. int do_labels(FILE * infile, int do_rotation)
  62. {
  63. char buff[128];
  64. initialize_options();
  65. while (G_getl2(text, MTEXT, infile)) {
  66. if (text[0] == '#')
  67. continue;
  68. if (!strncmp(text, "eas", 3))
  69. sscanf(text, "%*s %lf", &east);
  70. else if (!strncmp(text, "nor", 3))
  71. sscanf(text, "%*s %lf", &north);
  72. else if (!strncmp(text, "xof", 3))
  73. sscanf(text, "%*s %d", &xoffset);
  74. else if (!strncmp(text, "yof", 3))
  75. sscanf(text, "%*s %d", &yoffset);
  76. else if (!strncmp(text, "col", 3)) {
  77. sscanf(text, "%*s %s", buff);
  78. set_RGBA_from_str(&color, buff);
  79. }
  80. else if (!strncmp(text, "siz", 3))
  81. sscanf(text, "%*s %lf", &size);
  82. else if (!strncmp(text, "fontsize", 8))
  83. sscanf(text, "%*s %d", &fontsize);
  84. else if (!strncmp(text, "wid", 3))
  85. sscanf(text, "%*s %lf", &width);
  86. else if (!strncmp(text, "bac", 3)) {
  87. sscanf(text, "%*s %s", buff);
  88. set_RGBA_from_str(&background, buff);
  89. }
  90. else if (!strncmp(text, "bor", 3)) {
  91. sscanf(text, "%*s %s", buff);
  92. set_RGBA_from_str(&border, buff);
  93. }
  94. else if (!strncmp(text, "opa", 3)) {
  95. sscanf(text, "%*s %s", buff);
  96. if (!strncmp(buff, "YES", 3))
  97. opaque = YES;
  98. else
  99. opaque = NO;
  100. }
  101. else if (!strncmp(text, "ref", 3)) {
  102. if (sscanf(text, "%*s %16[^\n]", buff) < 1 || scan_ref(buff) == 0) {
  103. xref = CENT;
  104. yref = CENT;
  105. }
  106. }
  107. else if (!strncmp(text, "fon", 3)) {
  108. if (sscanf(text, "%*s %s", font) != 1
  109. || !strcmp(font, "standard"))
  110. strcpy(font, std_font);
  111. }
  112. else if (!strncmp(text, "rot", 3)) {
  113. if (do_rotation)
  114. sscanf(text, "%*s %lf", &rotation);
  115. }
  116. else if (!strncmp(text, "hco", 3)) {
  117. sscanf(text, "%*s %s", buff);
  118. set_RGBA_from_str(&highlight_color, buff);
  119. }
  120. else if (!strncmp(text, "hwi", 3))
  121. sscanf(text, "%*s %d", &highlight_width);
  122. else if (!strncmp(text, "tex", 3)) {
  123. show_it();
  124. rotation = 0.0; /* reset */
  125. }
  126. else {
  127. if (sscanf(text, "%1s", buff) == 1)
  128. fprintf(stderr, _("Error: %s\n"), text);
  129. }
  130. }
  131. return 0;
  132. }
  133. int show_it(void)
  134. {
  135. /*
  136. * The border+background box coords given by R_get_text_box() expand to
  137. * cover the area of the rotated text, but the bottom left corner of that
  138. * box is not always the ref=lower,left spot (rot>90), and middle|upper
  139. * left of the text do not match the middle|upper left of the expanded
  140. * text box when rotated.
  141. *
  142. * The solution is to calculate the position and dimensions of the text
  143. * without rotation, then rotate those points about the point's coord,
  144. * and replot. For text we must calculate the starting coord of the text
  145. * independent of the text box, once for each line of text (if multiline).
  146. *
  147. */
  148. int i, j;
  149. int n_lines;
  150. int n_chars;
  151. char line[256];
  152. char *lptr, *tptr;
  153. double line_size;
  154. double d_text_size;
  155. double text_size;
  156. double X, Y, Y0;
  157. double T, B, L, R;
  158. double t, b, l, r;
  159. double xarr[5];
  160. double yarr[5];
  161. double Xoffset, Yoffset; /* in XY plane */
  162. double X_just_offset, Y_just_offset; /* in rotated label plane */
  163. double ll_x, ll_y, ul_x, ul_y, lr_x, lr_y, ur_x, ur_y, text_x, text_y;
  164. G_debug(3, "Doing '%s'", text);
  165. X = east;
  166. Y0 = north;
  167. /* Set font */
  168. D_font(font);
  169. /* Set text size */
  170. if (fontsize) {
  171. d_text_size = fontsize;
  172. text_size = fontsize * D_get_d_to_u_yconv();
  173. }
  174. else {
  175. d_text_size = fabs(size * D_get_u_to_d_yconv());
  176. text_size = size;
  177. }
  178. line_size = text_size * 1.2;
  179. D_text_size(d_text_size, d_text_size);
  180. /* Find extent of all text (assume ref point is upper left) */
  181. T = -1e300;
  182. B = 1e300;
  183. L = 1e300;
  184. R = -1e300;
  185. /* Scan to beginning of text string */
  186. for (tptr = text; *tptr != ':'; tptr++) ;
  187. tptr++;
  188. /* get the box size for each line of text and expand the bounding box as needed */
  189. n_lines = 0;
  190. for (;;) {
  191. n_chars = 0;
  192. for (lptr = line; *tptr && *tptr != NL; *lptr++ = *tptr++) {
  193. /* R_get_text_box() seems to skip leading spaces?, so for
  194. multiline we need to append a space to secondary lines
  195. to get the placement right (??) */
  196. if ((lptr == line) && (n_lines > 0))
  197. *lptr++ = ' ';
  198. if ((*tptr == BACK) && (*(tptr + 1) == 'n'))
  199. break;
  200. n_chars++;
  201. }
  202. n_lines++;
  203. if (n_chars == 0)
  204. break;
  205. *lptr = '\0';
  206. G_debug(3, "line %d ='%s'", n_lines, line);
  207. Y = north - (line_size * 1.2) - ((n_lines - 1) * line_size);
  208. D_pos_abs(X, Y);
  209. D_text_rotation(0.0); /* reset */
  210. D_get_text_box(line, &t, &b, &l, &r);
  211. if (T < t)
  212. T = t;
  213. if (B > b)
  214. B = b;
  215. if (L > l)
  216. L = l;
  217. if (R < r)
  218. R = r;
  219. if ((*tptr == '\0') || (*tptr == NL))
  220. break;
  221. tptr++;
  222. tptr++;
  223. }
  224. G_debug(3, "nlines=%d", n_lines);
  225. /* enforce min/max width */
  226. if (width > 25.)
  227. width = 25.;
  228. if (width < 0.)
  229. width = 0.;
  230. /* Expand border 1/2 of text size */
  231. T = T + (text_size * 0.2);
  232. B = B - (text_size * 0.2);
  233. L = L - (text_size * 0.2);
  234. R = R + (text_size * 0.2);
  235. Xoffset = D_get_d_to_u_xconv() * xoffset;
  236. Yoffset = -D_get_d_to_u_yconv() * yoffset;
  237. X_just_offset = 0;
  238. Y_just_offset = 0;
  239. /* shift to match justification */
  240. if (xref == CENT)
  241. X_just_offset -= (R - L + text_size) / 2;
  242. if (xref == RITE)
  243. X_just_offset -= R - L + text_size;
  244. if (yref == CENT)
  245. Y_just_offset -= ((B - Y0) / 2) - (Y0 - T);
  246. if (yref == BOT)
  247. Y_just_offset -= (B - Y0) - (Y0 - T);
  248. /* get unrotated corners of text box, and rotate them */
  249. ul_y = ur_y = T + Y_just_offset;
  250. ll_y = lr_y = B + Y_just_offset;
  251. ll_x = ul_x = L + X_just_offset;
  252. lr_x = ur_x = R + X_just_offset;
  253. G_rotate_around_point(X, Y0, &ll_x, &ll_y, -1 * rotation);
  254. G_rotate_around_point(X, Y0, &ul_x, &ul_y, -1 * rotation);
  255. G_rotate_around_point(X, Y0, &ur_x, &ur_y, -1 * rotation);
  256. G_rotate_around_point(X, Y0, &lr_x, &lr_y, -1 * rotation);
  257. /* rotate lower starting corner of text */
  258. text_x = X + X_just_offset;
  259. text_y = Y + Y_just_offset;
  260. G_rotate_around_point(X, Y0, &text_x, &text_y, -1 * rotation);
  261. /* define rotated bounding box */
  262. xarr[0] = ll_x + Xoffset;
  263. xarr[1] = ul_x + Xoffset;
  264. xarr[2] = ur_x + Xoffset;
  265. xarr[3] = lr_x + Xoffset;
  266. xarr[4] = ll_x + Xoffset;
  267. yarr[0] = ll_y + Yoffset;
  268. yarr[1] = ul_y + Yoffset;
  269. yarr[2] = ur_y + Yoffset;
  270. yarr[3] = lr_y + Yoffset;
  271. yarr[4] = ll_y + Yoffset;
  272. /* skip labels which will go offscreen (even partially) */
  273. for (i = 0; i < 5; i++) {
  274. if ((xarr[i] > D_get_u_east()) || (xarr[i] < D_get_u_west()))
  275. return 0;
  276. if ((yarr[i] > D_get_u_north()) || (yarr[i] < D_get_u_south()))
  277. return 0;
  278. }
  279. #ifdef OUTPUT_ASCII
  280. fprintf(stdout, "L 5\n");
  281. for (i = 0; i < 5; i++)
  282. fprintf(stdout, " %f %f\n", xarr[i], yarr[i]);
  283. /* d.labels labfile | v.in.ascii -n out=labbox format=standard */
  284. #endif
  285. /* draw boxes */
  286. if (RGBA_has_color(&background)) {
  287. set_color_from_RGBA(&background);
  288. D_polygon_abs(xarr, yarr, 5);
  289. }
  290. if (RGBA_has_color(&border)) {
  291. set_color_from_RGBA(&border);
  292. D_line_width(width);
  293. D_polyline_abs(xarr, yarr, 5);
  294. D_line_width(0);
  295. }
  296. /* Set font rotation */
  297. D_text_rotation(rotation);
  298. G_debug(3, " rotation = %.2f", rotation);
  299. /**** draw highlighted text background ****/
  300. if (highlight_width && RGBA_has_color(&highlight_color)) {
  301. set_color_from_RGBA(&highlight_color);
  302. /* Scan to beginning of text string */
  303. for (tptr = text; *tptr != ':'; tptr++) ;
  304. tptr++;
  305. for (i = 1; i <= n_lines; i++) {
  306. /* get line of text from full label text string */
  307. n_chars = 0;
  308. for (lptr = line; *tptr && *tptr != NL; *lptr++ = *tptr++) {
  309. if ((lptr == line) && (i > 1)) /* see comment above */
  310. *lptr++ = ' ';
  311. if ((*tptr == BACK) && (*(tptr + 1) == 'n'))
  312. break;
  313. n_chars++;
  314. }
  315. if (n_chars == 0)
  316. break;
  317. *lptr = '\0';
  318. /* figure out text placement */
  319. Y = north - (line_size * 1.2) - ((i - 1) * line_size);
  320. text_x = X + X_just_offset; /* reset after G_rotate_around_point_int() */
  321. text_y = Y + Y_just_offset;
  322. G_rotate_around_point(X, Y0, &text_x, &text_y, -1 * rotation);
  323. for (j = 1; j <= highlight_width; j++) {
  324. /* smear it around. probably a better way (knight's move? rand?) */
  325. D_pos_abs(text_x + Xoffset, text_y + Yoffset + j);
  326. D_text(line);
  327. D_pos_abs(text_x + Xoffset, text_y + Yoffset - j);
  328. D_text(line);
  329. D_pos_abs(text_x + Xoffset + j, text_y + Yoffset);
  330. D_text(line);
  331. D_pos_abs(text_x + Xoffset - j, text_y + Yoffset);
  332. D_text(line);
  333. D_pos_abs(text_x + Xoffset + j, text_y + Yoffset + j);
  334. D_text(line);
  335. D_pos_abs(text_x + Xoffset - j, text_y + Yoffset - j);
  336. D_text(line);
  337. D_pos_abs(text_x + Xoffset + j, text_y + Yoffset - j);
  338. D_text(line);
  339. D_pos_abs(text_x + Xoffset - j, text_y + Yoffset + j);
  340. D_text(line);
  341. }
  342. if ((*tptr == '\0') || (*tptr == NL))
  343. break;
  344. tptr++;
  345. tptr++;
  346. }
  347. }
  348. /**** place the text ****/
  349. set_color_from_RGBA(&color);
  350. /* Scan to beginning of text string */
  351. for (tptr = text; *tptr != ':'; tptr++) ;
  352. tptr++;
  353. for (i = 1; i <= n_lines; i++) {
  354. /* get line of text from full label text string */
  355. n_chars = 0;
  356. for (lptr = line; *tptr && *tptr != NL; *lptr++ = *tptr++) {
  357. if ((lptr == line) && (i > 1)) /* see comment above */
  358. *lptr++ = ' ';
  359. if ((*tptr == BACK) && (*(tptr + 1) == 'n'))
  360. break;
  361. n_chars++;
  362. }
  363. if (n_chars == 0)
  364. break;
  365. *lptr = '\0';
  366. /* figure out text placement */
  367. Y = north - (line_size * 1.2) - ((i - 1) * line_size);
  368. text_x = X + X_just_offset; /* reset after G_rotate_around_point_int() */
  369. text_y = Y + Y_just_offset;
  370. G_rotate_around_point(X, Y0, &text_x, &text_y, -1 * rotation);
  371. D_pos_abs(text_x + Xoffset, text_y + Yoffset);
  372. D_text(line);
  373. if ((*tptr == '\0') || (*tptr == NL))
  374. break;
  375. tptr++;
  376. tptr++;
  377. }
  378. return 0;
  379. }
  380. static int xok, yok;
  381. int scan_ref(char *buf)
  382. {
  383. char word1[50], word2[50];
  384. int i;
  385. xok = yok = 0;
  386. for (i = 0; buf[i]; i++)
  387. if (buf[i] >= 'A' && buf[i] <= 'Z')
  388. buf[i] += 'a' - 'A';
  389. xref = yref = CENT;
  390. switch (sscanf(buf, "%s%s", word1, word2)) {
  391. case 2:
  392. if (!(xmatch(word2) || ymatch(word2)))
  393. return 0;
  394. case 1:
  395. if (xmatch(word1) || ymatch(word1))
  396. return 1;
  397. default:
  398. return 0;
  399. }
  400. }
  401. static int xmatch(char *word)
  402. {
  403. if (strcmp(word, "center") == 0)
  404. return 1;
  405. if (strcmp(word, "middle") == 0)
  406. return 1;
  407. if (xok)
  408. return 0;
  409. if (strcmp(word, "left") == 0)
  410. xref = LEFT;
  411. else if (strcmp(word, "right") == 0)
  412. xref = RITE;
  413. else
  414. return 0;
  415. xok = 1;
  416. return 1;
  417. }
  418. static int ymatch(char *word)
  419. {
  420. if (strcmp(word, "center") == 0)
  421. return 1;
  422. if (strcmp(word, "middle") == 0)
  423. return 1;
  424. if (yok)
  425. return 0;
  426. if (strcmp(word, "upper") == 0)
  427. yref = TOP;
  428. else if (strcmp(word, "top") == 0)
  429. yref = TOP;
  430. else if (strcmp(word, "lower") == 0)
  431. yref = BOT;
  432. else if (strcmp(word, "bottom") == 0)
  433. yref = BOT;
  434. else
  435. return 0;
  436. yok = 1;
  437. return 1;
  438. }