main.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /* ****************************************************************************
  2. *
  3. * MODULE: v.label
  4. * AUTHOR(S): Philip Verhagen (original s.label), Radim Blazek, Hamish Bowman
  5. * PURPOSE: Create paint labels
  6. * COPYRIGHT: (C) 2000 by the GRASS Development Team
  7. *
  8. * This program is free software under the GNU General Public
  9. * License (>=v2). Read the file COPYING that comes with GRASS
  10. * for details.
  11. *
  12. *****************************************************************************/
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <math.h>
  17. #include <grass/gis.h>
  18. #include <grass/display.h>
  19. #include <grass/raster.h>
  20. #include <grass/Vect.h>
  21. #include <grass/dbmi.h>
  22. #include <grass/glocale.h>
  23. #define PI M_PI
  24. struct Option *Xoffset, *Yoffset, *Reference, *Font, *Color, *Size;
  25. struct Option *Width, *Hcolor, *Hwidth, *Bcolor, *Border, *Opaque;
  26. int fontsize;
  27. char ref_pt[24];
  28. void print_label(FILE *, double, double, double, char *);
  29. int main(int argc, char **argv)
  30. {
  31. int i, cnt, nrows, txtlength, field, more;
  32. int type, ltype;
  33. int cat, direction;
  34. double x, y, linlength, lablength, size, space, ldist;
  35. double rotate, rot;
  36. char *mapset;
  37. char *txt, buf[2000];
  38. struct line_pnts *Points;
  39. struct line_cats *Cats;
  40. FILE *labels;
  41. struct Map_info Map;
  42. struct GModule *module;
  43. struct Option *Vectfile, *Typopt, *Fieldopt, *Colopt, *whereopt;
  44. struct Option *Labelfile, *Space, *FontSize, *Rotation;
  45. struct Flag *Along_flag, *Curl_flag;
  46. struct field_info *fi;
  47. dbDriver *driver;
  48. dbString stmt, valstr;
  49. dbCursor cursor;
  50. dbTable *table;
  51. dbColumn *column;
  52. G_gisinit(argv[0]);
  53. module = G_define_module();
  54. module->keywords = _("vector, paint labels");
  55. module->description =
  56. _("Creates paint labels for a vector map from attached attributes.");
  57. Labelfile = G_define_option();
  58. Labelfile->key = "labels";
  59. Labelfile->label = _("Name for new paint-label file");
  60. Labelfile->description =
  61. _("If not given the name of the input map is used");
  62. Labelfile->type = TYPE_STRING;
  63. Labelfile->required = NO;
  64. Labelfile->key_desc = "name";
  65. Vectfile = G_define_standard_option(G_OPT_V_MAP);
  66. Colopt = G_define_standard_option(G_OPT_COLUMN);
  67. Colopt->required = YES;
  68. Colopt->description = _("Name of attribute column to be used for labels");
  69. Typopt = G_define_standard_option(G_OPT_V_TYPE);
  70. Typopt->options = "point,line,boundary,centroid";
  71. Typopt->answer = "point,line,boundary,centroid";
  72. Fieldopt = G_define_standard_option(G_OPT_V_FIELD);
  73. whereopt = G_define_standard_option(G_OPT_WHERE);
  74. Along_flag = G_define_flag();
  75. Along_flag->key = 'a';
  76. Along_flag->description = _("Rotate labels to align with lines");
  77. Along_flag->guisection = _("Effects");
  78. Curl_flag = G_define_flag();
  79. Curl_flag->key = 'c';
  80. Curl_flag->description = _("Curl labels along lines");
  81. Curl_flag->guisection = _("Effects");
  82. Xoffset = G_define_option();
  83. Xoffset->key = "xoffset";
  84. Xoffset->description = _("Offset label in x-direction");
  85. Xoffset->type = TYPE_DOUBLE;
  86. Xoffset->answer = "0";
  87. Xoffset->guisection = _("Placement");
  88. Yoffset = G_define_option();
  89. Yoffset->key = "yoffset";
  90. Yoffset->description = _("Offset label in y-direction");
  91. Yoffset->type = TYPE_DOUBLE;
  92. Yoffset->answer = "0";
  93. Yoffset->guisection = _("Placement");
  94. Reference = G_define_option();
  95. Reference->key = "reference";
  96. Reference->description = _("Reference position");
  97. Reference->type = TYPE_STRING;
  98. Reference->multiple = YES;
  99. Reference->answer = "center";
  100. Reference->options = "center,left,right,upper,lower";
  101. Reference->guisection = _("Placement");
  102. Font = G_define_option();
  103. Font->key = "font";
  104. Font->description = _("Font name");
  105. Font->type = TYPE_STRING;
  106. Font->answer = "standard";
  107. Font->guisection = _("Font");
  108. Size = G_define_option();
  109. Size->key = "size";
  110. Size->description = _("Label size (in map-units)");
  111. Size->type = TYPE_DOUBLE;
  112. Size->answer = "100";
  113. Size->guisection = _("Font");
  114. Space = G_define_option();
  115. Space->key = "space";
  116. Space->description =
  117. _("Space between letters for curled labels (in map-units)");
  118. Space->type = TYPE_DOUBLE;
  119. Space->required = NO;
  120. Space->guisection = _("Font");
  121. FontSize = G_define_option();
  122. FontSize->key = "fontsize";
  123. FontSize->description = _("Label size (in points)");
  124. FontSize->type = TYPE_INTEGER;
  125. FontSize->required = NO;
  126. FontSize->options = "1-1000";
  127. FontSize->guisection = _("Font");
  128. Color = G_define_standard_option(G_OPT_C_FG);
  129. Color->label = _("Text color");
  130. Color->guisection = _("Colors");
  131. Rotation = G_define_option();
  132. Rotation->key = "rotation";
  133. Rotation->description = _("Rotation angle (degrees counter-clockwise)");
  134. Rotation->type = TYPE_DOUBLE;
  135. Rotation->required = NO;
  136. Rotation->options = "0-360";
  137. Rotation->answer = "0";
  138. Rotation->key_desc = "angle";
  139. Rotation->guisection = _("Placement");
  140. Width = G_define_option();
  141. Width->key = "width";
  142. Width->description = _("Border width");
  143. Width->type = TYPE_DOUBLE;
  144. Width->answer = "1";
  145. Width->options = "0-25";
  146. Width->guisection = _("Effects");
  147. Hcolor = G_define_standard_option(G_OPT_C_BG);
  148. Hcolor->key = "hcolor";
  149. Hcolor->label = _("Highlight color for text");
  150. Hcolor->answer = "none";
  151. Hcolor->guisection = _("Colors");
  152. Hwidth = G_define_option();
  153. Hwidth->key = "hwidth";
  154. Hwidth->description = _("Width of highlight coloring");
  155. Hwidth->type = TYPE_DOUBLE;
  156. Hwidth->answer = "0";
  157. Hwidth->guisection = _("Effects");
  158. Bcolor = G_define_standard_option(G_OPT_C_BG);
  159. Bcolor->key = "background";
  160. Bcolor->label = _("Background color");
  161. Bcolor->answer = "none";
  162. Bcolor->guisection = _("Colors");
  163. Border = G_define_standard_option(G_OPT_C_BG);
  164. Border->key = "border";
  165. Border->label = _("Border color");
  166. Border->answer = "none";
  167. Border->guisection = _("Colors");
  168. Opaque = G_define_option();
  169. Opaque->key = "opaque";
  170. Opaque->description =
  171. _("Opaque to vector (only relevant if background color is selected)");
  172. Opaque->type = TYPE_STRING;
  173. Opaque->answer = "yes";
  174. Opaque->options = "yes,no";
  175. Opaque->key_desc = "yes|no";
  176. Opaque->guisection = _("Colors");
  177. if (G_parser(argc, argv))
  178. exit(EXIT_FAILURE);
  179. if (Curl_flag->answer)
  180. Along_flag->answer = 1;
  181. db_init_string(&stmt);
  182. db_init_string(&valstr);
  183. Points = Vect_new_line_struct();
  184. Cats = Vect_new_cats_struct();
  185. type = Vect_option_to_types(Typopt);
  186. size = atof(Size->answer);
  187. space = size; /* default: set spacing according to letter size (map units) */
  188. rotate = atof(Rotation->answer);
  189. if (FontSize->answer) {
  190. fontsize = atoi(FontSize->answer);
  191. /* figure out space param dynamically from current dispay */
  192. /* don't bother if Space was explicitly given (bypasses xmon req) */
  193. if (Along_flag->answer && !Space->answer) {
  194. if (R_open_driver() != 0) /* connect to the driver */
  195. G_fatal_error(_("No graphics device selected"));
  196. /* Read in the map region associated with graphics window */
  197. D_setup(0);
  198. space = fontsize / D_get_u_to_d_xconv(); /* in earth units */
  199. R_close_driver();
  200. }
  201. }
  202. else
  203. fontsize = 0;
  204. /* or if user explicitly gave a number for letter spacing, use that */
  205. if (Space->answer)
  206. space = atof(Space->answer);
  207. if (Along_flag->answer && !fontsize &&
  208. (size / space >= 2 || size / space <= 0.5))
  209. G_warning(_("size and space options vary significantly which may lead to crummy output"));
  210. /* parse reference answers */
  211. i = 0;
  212. strcpy(ref_pt, "");
  213. while (Reference->answers[i]) {
  214. if (i > 1)
  215. G_fatal_error(_("Too many parameters for <reference>"));
  216. if (i > 0)
  217. strcat(ref_pt, " ");
  218. strncat(ref_pt, Reference->answers[i], 7);
  219. i++;
  220. }
  221. /* open vector */
  222. mapset = G_find_vector2(Vectfile->answer, NULL);
  223. if (mapset == NULL)
  224. G_fatal_error(_("Vector map <%s> not found"), Vectfile->answer);
  225. Vect_open_old(&Map, Vectfile->answer, mapset);
  226. /* open database */
  227. field = atoi(Fieldopt->answer);
  228. fi = Vect_get_field(&Map, field);
  229. if (fi == NULL)
  230. G_fatal_error(_("Unable to get layer info for vector map"));
  231. driver = db_start_driver_open_database(fi->driver, fi->database);
  232. if (driver == NULL)
  233. G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
  234. fi->database, fi->driver);
  235. /* open labels */
  236. if (!Labelfile->answer)
  237. Labelfile->answer = Vectfile->answer;
  238. labels = G_fopen_new("paint/labels", Labelfile->answer);
  239. /* write label */
  240. cnt = 0;
  241. while (1) {
  242. ltype = Vect_read_next_line(&Map, Points, Cats);
  243. if (ltype == -1)
  244. G_fatal_error(_("Unable to read vector map"));
  245. if (ltype == -2)
  246. break; /* EOF */
  247. if (!(type & ltype))
  248. continue;
  249. Vect_cat_get(Cats, field, &cat);
  250. if (cat < 0)
  251. continue; /* no cat for this field */
  252. /* Read label from database */
  253. if (whereopt->answer) {
  254. sprintf(buf, "select %s from %s where %s = %d and %s",
  255. Colopt->answer, fi->table, fi->key, cat,
  256. whereopt->answer);
  257. }
  258. else {
  259. sprintf(buf, "select %s from %s where %s = %d",
  260. Colopt->answer, fi->table, fi->key, cat);
  261. }
  262. G_debug(3, "SQL: %s", buf);
  263. db_set_string(&stmt, buf);
  264. if (db_open_select_cursor(driver, &stmt, &cursor, DB_SEQUENTIAL) !=
  265. DB_OK)
  266. G_fatal_error(_("Unable to select attributes"));
  267. nrows = db_get_num_rows(&cursor);
  268. if (nrows < 1) {
  269. /* not optimal, but the warning isn't /that/ critical. */
  270. if (!whereopt->answer) {
  271. G_warning(_("No record for category %d in table <%s>"),
  272. cat, fi->table);
  273. }
  274. continue;
  275. }
  276. if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK || !more)
  277. continue;
  278. table = db_get_cursor_table(&cursor);
  279. column = db_get_table_column(table, 0); /* first column */
  280. db_convert_column_value_to_string(column, &valstr);
  281. db_close_cursor(&cursor);
  282. txt = db_get_string(&valstr);
  283. G_debug(3, "Label: %s", txt);
  284. txtlength = strlen(txt);
  285. if (txtlength == 0)
  286. continue;
  287. /* Line length */
  288. linlength = Vect_line_length(Points);
  289. if (ltype & GV_POINTS) {
  290. print_label(labels, Points->x[0], Points->y[0], rotate, txt);
  291. }
  292. else if (!Along_flag->answer) { /* Line, but not along */
  293. /* get centre */
  294. Vect_point_on_line(Points, linlength / 2, &x, &y, NULL, NULL,
  295. NULL);
  296. print_label(labels, x, y, rotate, txt);
  297. }
  298. else { /* Along line */
  299. /* find best orientation (most letters by bottom to down side */
  300. rotate = 0;
  301. for (i = 0; i < txtlength; i++) {
  302. /* distance of the letter from the beginning of line */
  303. lablength = txtlength * space;
  304. ldist = i * space + (linlength - lablength) / 2;
  305. if (ldist < 0)
  306. ldist = 0;
  307. if (ldist > linlength)
  308. ldist = linlength;
  309. Vect_point_on_line(Points, ldist, &x, &y, NULL, &rot, NULL);
  310. rot = rot * 180 / PI;
  311. if (rot > 90 || rot < -90)
  312. rotate += -1;
  313. else
  314. rotate += 1;
  315. }
  316. if (rotate >= 0) {
  317. direction = 0;
  318. }
  319. else {
  320. direction = 1;
  321. }
  322. if (Curl_flag->answer) {
  323. for (i = 0; i < txtlength; i++) {
  324. /* distance of the letter from the beginning of line */
  325. lablength = txtlength * space;
  326. ldist = i * space + (linlength - lablength) / 2;
  327. if (ldist < 0)
  328. ldist = 0;
  329. if (ldist > linlength)
  330. ldist = linlength;
  331. Vect_point_on_line(Points, ldist, &x, &y, NULL, &rotate,
  332. NULL);
  333. rotate = rotate * 180 / PI;
  334. if (direction == 0) {
  335. sprintf(buf, "%c", txt[i]);
  336. }
  337. else {
  338. sprintf(buf, "%c", txt[txtlength - i - 1]);
  339. rotate += 180;
  340. }
  341. print_label(labels, x, y, rotate, buf);
  342. }
  343. }
  344. else { /* same as above but take center value for placement & rotation */
  345. i = (int)(txtlength / 2.0 + 0.5);
  346. lablength = txtlength * space;
  347. ldist = i * space + (linlength - lablength) / 2;
  348. if (ldist < 0)
  349. ldist = 0;
  350. if (ldist > linlength)
  351. ldist = linlength;
  352. Vect_point_on_line(Points, ldist, &x, &y, NULL, &rotate,
  353. NULL);
  354. rotate = rotate * 180 / PI;
  355. if (direction != 0)
  356. rotate += 180;
  357. print_label(labels, x, y, rotate, txt);
  358. }
  359. }
  360. cnt++;
  361. }
  362. Vect_destroy_line_struct(Points);
  363. Vect_close(&Map);
  364. db_close_database_shutdown_driver(driver);
  365. fclose(labels);
  366. G_message(_("Labeled %d lines."), cnt);
  367. exit(EXIT_SUCCESS);
  368. }
  369. void print_label(FILE * labels, double x, double y, double rotate,
  370. char *label)
  371. {
  372. fprintf(labels, "east: %f\n", x);
  373. fprintf(labels, "north: %f\n", y);
  374. fprintf(labels, "xoffset: %s\n", Xoffset->answer);
  375. fprintf(labels, "yoffset: %s\n", Yoffset->answer);
  376. fprintf(labels, "ref: %s\n", ref_pt);
  377. fprintf(labels, "font: %s\n", Font->answer);
  378. fprintf(labels, "color: %s\n", Color->answer);
  379. if (fontsize)
  380. fprintf(labels, "fontsize: %d\n", fontsize);
  381. else
  382. fprintf(labels, "size: %s\n", Size->answer);
  383. fprintf(labels, "width: %s\n", Width->answer);
  384. fprintf(labels, "hcolor: %s\n", Hcolor->answer);
  385. fprintf(labels, "hwidth: %s\n", Hwidth->answer);
  386. fprintf(labels, "background: %s\n", Bcolor->answer);
  387. fprintf(labels, "border: %s\n", Border->answer);
  388. fprintf(labels, "opaque: %s\n", Opaque->answer);
  389. if (rotate != 0)
  390. fprintf(labels, "rotate: %f\n", rotate);
  391. fprintf(labels, "text: %s\n\n", label);
  392. }