main.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. /*
  2. ****************************************************************************
  3. *
  4. * MODULE: d.vect.thematic
  5. * AUTHOR(S): Moritz Lennert, based on d.vect
  6. * PURPOSE: Display a thematic vector map
  7. * TODO: Common part of code merge with d.vect (similarly as r.colors
  8. * and r3.colors)
  9. * COPYRIGHT: (C) 2007-2014 by the GRASS Development Team
  10. *
  11. * This program is free software under the GNU General Public
  12. * License (>=v2). Read the file COPYING that comes with GRASS
  13. * for details.
  14. *
  15. *****************************************************************************/
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/types.h>
  19. #include <dirent.h>
  20. #include <grass/config.h>
  21. #include <grass/gis.h>
  22. #include <grass/raster.h>
  23. #include <grass/display.h>
  24. #include <grass/vector.h>
  25. #include <grass/colors.h>
  26. #include <grass/dbmi.h>
  27. #include <grass/glocale.h>
  28. #include <grass/arraystats.h>
  29. #include "plot.h"
  30. #include "local_proto.h"
  31. int main(int argc, char **argv)
  32. {
  33. int ret, level;
  34. int i, stat = 0;
  35. int nclass = 0, nbreaks, *frequencies;
  36. int chcat = 0;
  37. int r, g, b;
  38. int has_color = 0;
  39. struct color_rgb *colors, bcolor;
  40. int default_width;
  41. int verbose = FALSE;
  42. char map_name[128];
  43. struct GModule *module;
  44. struct Option *map_opt;
  45. struct Option *column_opt;
  46. struct Option *breaks_opt;
  47. struct Option *algo_opt;
  48. struct Option *nbclass_opt;
  49. struct Option *colors_opt;
  50. struct Option *bcolor_opt;
  51. struct Option *bwidth_opt;
  52. struct Option *where_opt;
  53. struct Option *field_opt;
  54. struct Option *legend_file_opt;
  55. struct Option *icon_opt;
  56. struct Option *icon_line_opt;
  57. struct Option *icon_area_opt;
  58. struct Option *size_opt;
  59. struct Option *title_opt;
  60. struct Flag *legend_flag, *algoinfo_flag, *nodraw_flag, *vlegend_flag;
  61. char *desc, *deprecated;
  62. struct cat_list *Clist;
  63. int *cats, ncat, nrec, ctype;
  64. struct Map_info Map;
  65. struct field_info *fi;
  66. dbDriver *driver;
  67. dbHandle handle;
  68. dbCatValArray cvarr;
  69. struct Cell_head window;
  70. struct bound_box box;
  71. double overlap, *breakpoints, *data = NULL, class_info = 0.0;
  72. struct GASTATS stats;
  73. int size;
  74. int nfeatures;
  75. char title[128];
  76. char *leg_file;
  77. /* Initialize the GIS calls */
  78. G_gisinit(argv[0]);
  79. module = G_define_module();
  80. G_add_keyword(_("display"));
  81. G_add_keyword(_("cartography"));
  82. G_add_keyword(_("choropleth map"));
  83. G_add_keyword(_("legend"));
  84. module->description =
  85. _("Displays a thematic vector map "
  86. "in the active graphics frame.");
  87. map_opt = G_define_standard_option(G_OPT_V_MAP);
  88. field_opt = G_define_standard_option(G_OPT_V_FIELD);
  89. field_opt->description =
  90. _("Layer number. If -1, all layers are displayed.");
  91. field_opt->guisection = _("Selection");
  92. column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
  93. column_opt->required = YES;
  94. column_opt->description =
  95. _("Name of attribute column to be classified");
  96. breaks_opt = G_define_option();
  97. breaks_opt->key = "breaks";
  98. breaks_opt->type = TYPE_STRING;
  99. breaks_opt->required = NO;
  100. breaks_opt->multiple = YES;
  101. breaks_opt->description = _("Class breaks, without minimum and maximum");
  102. breaks_opt->guisection = _("Classes");
  103. algo_opt = G_define_option();
  104. algo_opt->key = "algorithm";
  105. algo_opt->type = TYPE_STRING;
  106. algo_opt->required = NO;
  107. algo_opt->multiple = NO;
  108. algo_opt->options = "int,std,qua,equ,dis";
  109. algo_opt->description = _("Algorithm to use for classification");
  110. desc = NULL;
  111. G_asprintf(&desc,
  112. "int;%s;std;%s;qua;%s;equ;%s",
  113. _("simple intervals"),
  114. _("standard deviations"),
  115. _("quantiles"),
  116. _("equiprobable (normal distribution)"));
  117. algo_opt->descriptions = desc;
  118. /*currently disabled because of bugs "dis;discontinuities"); */
  119. algo_opt->guisection = _("Classes");
  120. nbclass_opt = G_define_option();
  121. nbclass_opt->key = "nclasses";
  122. nbclass_opt->type = TYPE_INTEGER;
  123. nbclass_opt->required = NO;
  124. nbclass_opt->multiple = NO;
  125. nbclass_opt->description = _("Number of classes to define");
  126. nbclass_opt->guisection = _("Classes");
  127. colors_opt = G_define_option();
  128. colors_opt->key = "colors";
  129. colors_opt->type = TYPE_STRING;
  130. colors_opt->required = YES;
  131. colors_opt->multiple = YES;
  132. colors_opt->description = _("Colors (one per class)");
  133. colors_opt->gisprompt = "old_color,color,color";
  134. where_opt = G_define_standard_option(G_OPT_DB_WHERE);
  135. where_opt->guisection = _("Selection");
  136. bwidth_opt = G_define_option();
  137. bwidth_opt->key = "boundary_width";
  138. bwidth_opt->type = TYPE_INTEGER;
  139. bwidth_opt->answer = "1";
  140. bwidth_opt->guisection = _("Boundaries");
  141. bwidth_opt->description = _("Boundary width");
  142. bcolor_opt = G_define_standard_option(G_OPT_CN);
  143. bcolor_opt->key = "boundary_color";
  144. bcolor_opt->label = _("Boundary color");
  145. bcolor_opt->guisection = _("Boundaries");
  146. /* Symbols */
  147. icon_opt = G_define_option();
  148. icon_opt->key = "icon";
  149. icon_opt->type = TYPE_STRING;
  150. icon_opt->required = NO;
  151. icon_opt->multiple = NO;
  152. icon_opt->guisection = _("Symbols");
  153. icon_opt->answer = "basic/x";
  154. /* This could also use ->gisprompt = "old,symbol,symbol" instead of ->options */
  155. icon_opt->options = icon_files();
  156. icon_opt->description = _("Point and centroid symbol");
  157. size_opt = G_define_option();
  158. size_opt->key = "size";
  159. size_opt->type = TYPE_DOUBLE;
  160. size_opt->answer = "5";
  161. size_opt->guisection = _("Symbols");
  162. size_opt->label = _("Symbol size");
  163. icon_line_opt = G_define_option();
  164. icon_line_opt->key = "icon_line";
  165. icon_line_opt->type = TYPE_STRING;
  166. icon_line_opt->required = NO;
  167. icon_line_opt->multiple = NO;
  168. icon_line_opt->guisection = _("Legend");
  169. icon_line_opt->answer = "legend/line";
  170. /* This could also use ->gisprompt = "old,symbol,symbol" instead of ->options */
  171. icon_line_opt->options = icon_files();
  172. icon_line_opt->description = _("Legend symbol for lines");
  173. icon_area_opt = G_define_option();
  174. icon_area_opt->key = "icon_area";
  175. icon_area_opt->type = TYPE_STRING;
  176. icon_area_opt->required = NO;
  177. icon_area_opt->multiple = NO;
  178. icon_area_opt->guisection = _("Legend");
  179. icon_area_opt->answer = "legend/area";
  180. /* This could also use ->gisprompt = "old,symbol,symbol" instead of ->options */
  181. icon_area_opt->options = icon_files();
  182. icon_area_opt->description = _("Legend symbol for areas");
  183. title_opt = G_define_option();
  184. title_opt->key = "legend_title";
  185. title_opt->type = TYPE_STRING;
  186. title_opt->guisection = _("Legend");
  187. title_opt->description = _("Thematic map title");
  188. legend_file_opt = G_define_standard_option(G_OPT_F_OUTPUT);
  189. legend_file_opt->key = "legendfile";
  190. deprecated = NULL;
  191. G_asprintf(&deprecated,
  192. "[%s] %s",
  193. _("DEPRECATED"),
  194. _("Output legend file"));
  195. legend_file_opt->description = deprecated;
  196. legend_file_opt->required = NO;
  197. legend_file_opt->guisection = _("Legend");
  198. legend_flag = G_define_flag();
  199. legend_flag->key = 'l';
  200. legend_flag->description =
  201. _("Create legend information and send to stdout");
  202. legend_flag->guisection = _("Legend");
  203. nodraw_flag = G_define_flag();
  204. nodraw_flag->key = 'n';
  205. nodraw_flag->description = _("Do not draw map, only output the legend information");
  206. nodraw_flag->guisection = _("Legend");
  207. algoinfo_flag = G_define_flag();
  208. algoinfo_flag->key = 'e';
  209. deprecated = NULL;
  210. G_asprintf(&deprecated,
  211. "[%s] %s",
  212. _("DEPRECATED"),
  213. _("When printing legend info, include extended statistical info from classification algorithm"));
  214. algoinfo_flag->description = deprecated;
  215. algoinfo_flag->guisection = _("Legend");
  216. vlegend_flag = G_define_flag();
  217. vlegend_flag->key = 's';
  218. vlegend_flag->label = _("Do not show this layer in vector legend");
  219. vlegend_flag->guisection = _("Legend");
  220. G_option_required(algo_opt, breaks_opt, NULL);
  221. G_option_exclusive(algo_opt, breaks_opt, NULL);
  222. G_option_requires(algo_opt, nbclass_opt, NULL);
  223. /* Check command line */
  224. if (G_parser(argc, argv))
  225. exit(EXIT_FAILURE);
  226. if (algoinfo_flag->answer)
  227. G_warning(_("Flag -e is deprecated, set verbose mode with --v to get the extended statistical info."));
  228. if (legend_file_opt->answer)
  229. G_warning(_("Option legendfile is deprecated, either use flag -l "
  230. "to print legend to standard output, "
  231. "or set GRASS_LEGEND_FILE environment variable "
  232. "(see d.legend.vect for details)."));
  233. if (G_verbose() > G_verbose_std())
  234. verbose = TRUE;
  235. G_get_set_window(&window);
  236. size = atof(size_opt->answer);
  237. /* Read map options */
  238. strcpy(map_name, map_opt->answer);
  239. /* open vector */
  240. level = Vect_open_old(&Map, map_name, "");
  241. if (level < 2)
  242. G_fatal_error(_("%s: You must build topology on vector map. Run v.build."),
  243. map_name);
  244. if (title_opt->answer)
  245. strcpy(title, title_opt->answer);
  246. else
  247. strcpy(title, Map.name);
  248. /* Check database connection and open it */
  249. Clist = Vect_new_cat_list();
  250. Clist->field = atoi(field_opt->answer);
  251. if (Clist->field < 1)
  252. G_fatal_error(_("'layer' must be > 0"));
  253. if ((fi = Vect_get_field(&Map, Clist->field)) == NULL)
  254. G_fatal_error(_("Database connection not defined"));
  255. if (fi != NULL) {
  256. driver = db_start_driver(fi->driver);
  257. if (driver == NULL)
  258. G_fatal_error(_("Unable to start driver <%s>"), fi->driver);
  259. db_init_handle(&handle);
  260. db_set_handle(&handle, fi->database, NULL);
  261. if (db_open_database(driver, &handle) != DB_OK)
  262. G_fatal_error(_("Unable to open database <%s>"), fi->database);
  263. }
  264. /*Get CatValArray needed for plotting and for legend calculations */
  265. db_CatValArray_init(&cvarr);
  266. nrec = db_select_CatValArray(driver, fi->table, fi->key,
  267. column_opt->answer, where_opt->answer,
  268. &cvarr);
  269. G_debug(3, "nrec (%s) = %d", column_opt->answer, nrec);
  270. if (cvarr.ctype != DB_C_TYPE_INT && cvarr.ctype != DB_C_TYPE_DOUBLE)
  271. G_fatal_error(_("Data (%s) not numeric. "
  272. "Column must be numeric."), column_opt->answer);
  273. if (nrec < 0)
  274. G_fatal_error(_("Cannot select data (%s) from table"),
  275. column_opt->answer);
  276. for (i = 0; i < cvarr.n_values; i++) {
  277. G_debug(4, "cat = %d %s = %d", cvarr.value[i].cat,
  278. column_opt->answer,
  279. (cvarr.ctype ==
  280. DB_C_TYPE_INT ? cvarr.value[i].val.i : (int)cvarr.value[i].
  281. val.d));
  282. }
  283. /*Get the sorted data */
  284. ret = db_CatValArray_sort_by_value(&cvarr);
  285. if (ret == DB_FAILED)
  286. G_fatal_error("Could not sort array of values..");
  287. data = (double *)G_malloc((nrec) * sizeof(double));
  288. for (i = 0; i < nrec; i++)
  289. data[i] = 0.0;
  290. ctype = cvarr.ctype;
  291. if (ctype == DB_C_TYPE_INT) {
  292. for (i = 0; i < nrec; i++)
  293. data[i] = cvarr.value[i].val.i;
  294. }
  295. else {
  296. for (i = 0; i < nrec; i++)
  297. data[i] = cvarr.value[i].val.d;
  298. }
  299. db_CatValArray_sort(&cvarr);
  300. /*Get the list of relevant cats if where option is given */
  301. if (where_opt->answer) {
  302. ncat = db_select_int(driver, fi->table, fi->key, where_opt->answer,
  303. &cats);
  304. chcat = 1;
  305. Vect_array_to_cat_list(cats, ncat, Clist);
  306. }
  307. db_close_database(driver);
  308. db_shutdown_driver(driver);
  309. /*get border line width */
  310. default_width = atoi(bwidth_opt->answer);
  311. if (default_width < 0)
  312. default_width = 0;
  313. /*get border line color */
  314. bcolor = G_standard_color_rgb(WHITE);
  315. ret = G_str_to_color(bcolor_opt->answer, &r, &g, &b);
  316. if (ret == 1) {
  317. has_color = 1;
  318. bcolor.r = r;
  319. bcolor.g = g;
  320. bcolor.b = b;
  321. }
  322. else if (ret == 2) { /* none */
  323. has_color = 0;
  324. }
  325. else if (ret == 0) { /* error */
  326. G_fatal_error(_("Unknown color: [%s]"), bcolor_opt->answer);
  327. }
  328. /* if both class breaks and (algorithm or classnumber) are given, give precedence to class
  329. * breaks
  330. */
  331. if (breaks_opt->answers) {
  332. if (algo_opt->answer || nbclass_opt->answer)
  333. G_warning(_("You gave both manual breaks and a classification algorithm or a number of classes. The manual breaks have precedence and will thus be used."));
  334. /*Get class breaks */
  335. nbreaks = 0;
  336. while (breaks_opt->answers[nbreaks] != NULL)
  337. nbreaks++;
  338. nclass = nbreaks + 1; /*add one since breaks do not include min and max values */
  339. G_debug(3, "nclass = %d", nclass);
  340. breakpoints = (double *)G_malloc((nbreaks) * sizeof(double));
  341. for (i = 0; i < nbreaks; i++)
  342. breakpoints[i] = atof(breaks_opt->answers[i]);
  343. }
  344. else {
  345. if (algo_opt->answer && nbclass_opt->answer) {
  346. nclass = atoi(nbclass_opt->answer);
  347. nbreaks = nclass - 1; /* we need one less classbreaks (min and
  348. * max exluded) than classes */
  349. breakpoints = (double *)G_malloc((nbreaks) * sizeof(double));
  350. for (i = 0; i < nbreaks; i++)
  351. breakpoints[i] = 0;
  352. /* Get classbreaks for given algorithm and number of classbreaks.
  353. * class_info takes any info coming from the classification algorithms */
  354. class_info = AS_class_apply_algorithm(AS_option_to_algorithm(algo_opt),
  355. data, nrec, &nbreaks,
  356. breakpoints);
  357. }
  358. else {
  359. G_fatal_error(_("You must either give classbreaks or a classification algorithm"));
  360. }
  361. };
  362. /* Fill colors */
  363. colors = (struct color_rgb *)G_malloc(nclass * sizeof(struct color_rgb));
  364. if (colors_opt->answers != NULL) {
  365. for (i = 0; i < nclass; i++) {
  366. if (colors_opt->answers[i] == NULL)
  367. G_fatal_error(_("Not enough colors or error in color specifications.\nNeed %i entries for 'colors' parameter"),
  368. nclass);
  369. ret = G_str_to_color(colors_opt->answers[i], &r, &g, &b);
  370. if (!ret)
  371. G_fatal_error(_("Error interpreting color %s"),
  372. colors_opt->answers[i]);
  373. colors[i].r = r;
  374. colors[i].g = g;
  375. colors[i].b = b;
  376. }
  377. }
  378. if (!nodraw_flag->answer) {
  379. /* Now's let's prepare the actual plotting */
  380. D_open_driver();
  381. D_setup(0);
  382. if (verbose)
  383. G_message(_("Plotting ..."));
  384. overlap = 1;
  385. Vect_get_map_box(&Map, &box);
  386. if (window.proj != PROJECTION_LL) {
  387. overlap =
  388. G_window_percentage_overlap(&window, box.N, box.S,
  389. box.E, box.W);
  390. G_debug(1, "overlap = %f \n", overlap);
  391. }
  392. if (overlap == 0) {
  393. G_message(_("The bounding box of the map is outside the current region, "
  394. "nothing drawn."));
  395. stat = 0;
  396. }
  397. else {
  398. if (overlap < 1)
  399. Vect_set_constraint_region(&Map, window.north, window.south,
  400. window.east, window.west,
  401. PORT_DOUBLE_MAX, -PORT_DOUBLE_MAX);
  402. /* default line width */
  403. D_line_width(default_width);
  404. if (Vect_get_num_primitives(&Map, GV_BOUNDARY) > 0)
  405. stat =
  406. dareatheme(&Map, Clist, &cvarr, breakpoints, nbreaks, colors,
  407. has_color ? &bcolor : NULL, chcat, &window,
  408. default_width);
  409. else if ((Vect_get_num_primitives(&Map, GV_POINT) > 0) ||
  410. (Vect_get_num_primitives(&Map, GV_LINE) > 0)){
  411. stat = display_lines(&Map, Clist, chcat, icon_opt->answer, size,
  412. default_width, &cvarr, breakpoints, nbreaks, colors,
  413. has_color ? &bcolor : NULL);
  414. }
  415. /* reset line width: Do we need to get line width from display
  416. * driver (not implemented)? It will help restore previous line
  417. * width (not just 0) determined by another module (e.g.,
  418. * d.linewidth). */
  419. D_line_width(0);
  420. } /* end window check if */
  421. D_save_command(G_recreate_command());
  422. D_close_driver();
  423. } /* end of nodraw_flag condition */
  424. frequencies = (int *)G_malloc((nbreaks + 1) * sizeof(int));
  425. for (i = 0; i < nbreaks + 1; i++)
  426. frequencies[i] = 0.0;
  427. AS_class_frequencies(data, nrec, nbreaks, breakpoints, frequencies);
  428. /*Get basic statistics about the data */
  429. AS_basic_stats(data, nrec, &stats);
  430. /* Print statistics */
  431. G_verbose_message(_("\nTotal number of records: %.0f\n"),
  432. stats.count);
  433. G_verbose_message(_("Classification of %s into %i classes\n"),
  434. column_opt->answer, nbreaks + 1);
  435. G_verbose_message(_("Using algorithm: *** %s ***\n"),
  436. algo_opt->answer);
  437. G_verbose_message(_("Mean: %f\tStandard deviation = %f\n"),
  438. stats.mean, stats.stdev);
  439. if (G_strcasecmp(algo_opt->answer, "dis") == 0)
  440. G_verbose_message(_("Last chi2 = %f\n"), class_info);
  441. if (G_strcasecmp(algo_opt->answer, "std") == 0)
  442. G_verbose_message(_("Stdev multiplied by %.4f to define step\n"),
  443. class_info);
  444. G_verbose_message("\n");
  445. /* Print legfile to stdout */
  446. if ((legend_flag->answer) ||
  447. ((legend_file_opt->answer) && (strcmp(legend_file_opt->answer,"-") == 0))) {
  448. while (TRUE) {
  449. nfeatures = Vect_get_num_primitives(&Map, GV_POINT);
  450. if (nfeatures > 0) {
  451. write_into_legend_file("stdout", icon_opt->answer,
  452. title, stats.min, stats.max, breakpoints,
  453. nbreaks, size, bcolor, colors, default_width,
  454. frequencies, "point");
  455. break;
  456. }
  457. nfeatures = Vect_get_num_primitives(&Map, GV_LINE);
  458. if (nfeatures > 0) {
  459. write_into_legend_file("stdout", icon_line_opt->answer,
  460. title, stats.min, stats.max, breakpoints,
  461. nbreaks, size, bcolor, colors, default_width,
  462. frequencies, "line");
  463. break;
  464. }
  465. nfeatures = Vect_get_num_primitives(&Map, GV_BOUNDARY);
  466. if (nfeatures > 0) {
  467. write_into_legend_file("stdout", icon_area_opt->answer,
  468. title, stats.min, stats.max, breakpoints,
  469. nbreaks, size, bcolor, colors, default_width,
  470. frequencies, "area");
  471. break;
  472. }
  473. }
  474. }
  475. /* Write into default legfile */
  476. leg_file = getenv("GRASS_LEGEND_FILE");
  477. if (leg_file && !vlegend_flag->answer) {
  478. while (TRUE) {
  479. nfeatures = Vect_get_num_primitives(&Map, GV_POINT);
  480. if (nfeatures > 0) {
  481. write_into_legend_file(leg_file, icon_opt->answer,
  482. title, stats.min, stats.max, breakpoints,
  483. nbreaks, size, bcolor, colors, default_width,
  484. frequencies, "point");
  485. break;
  486. }
  487. nfeatures = Vect_get_num_primitives(&Map, GV_LINE);
  488. if (nfeatures > 0) {
  489. write_into_legend_file(leg_file, icon_line_opt->answer,
  490. title, stats.min, stats.max, breakpoints,
  491. nbreaks, size, bcolor, colors, default_width,
  492. frequencies, "line");
  493. break;
  494. }
  495. nfeatures = Vect_get_num_primitives(&Map, GV_BOUNDARY);
  496. if (nfeatures > 0) {
  497. write_into_legend_file(leg_file, icon_area_opt->answer,
  498. title, stats.min, stats.max, breakpoints,
  499. nbreaks, size, bcolor, colors, default_width,
  500. frequencies, "area");
  501. break;
  502. }
  503. }
  504. }
  505. /* Write into user-specified output file */
  506. if (legend_file_opt->answer) {
  507. while (TRUE) {
  508. nfeatures = Vect_get_num_primitives(&Map, GV_POINT);
  509. if (nfeatures > 0) {
  510. write_into_legend_file(legend_file_opt->answer, icon_opt->answer,
  511. title, stats.min, stats.max, breakpoints,
  512. nbreaks, size, bcolor, colors, default_width,
  513. frequencies, "point");
  514. break;
  515. }
  516. nfeatures = Vect_get_num_primitives(&Map, GV_LINE);
  517. if (nfeatures > 0) {
  518. write_into_legend_file(legend_file_opt->answer, icon_line_opt->answer,
  519. title, stats.min, stats.max, breakpoints,
  520. nbreaks, size, bcolor, colors, default_width,
  521. frequencies, "line");
  522. break;
  523. }
  524. nfeatures = Vect_get_num_primitives(&Map, GV_BOUNDARY);
  525. if (nfeatures > 0) {
  526. write_into_legend_file(legend_file_opt->answer, icon_area_opt->answer,
  527. title, stats.min, stats.max, breakpoints,
  528. nbreaks, size, bcolor, colors, default_width,
  529. frequencies, "area");
  530. break;
  531. }
  532. }
  533. }
  534. if (verbose)
  535. G_done_msg(" ");
  536. Vect_close(&Map);
  537. Vect_destroy_cat_list(Clist);
  538. exit(stat);
  539. }
  540. int cmp(const void *a, const void *b)
  541. {
  542. return (strcmp(*(char **)a, *(char **)b));
  543. }
  544. /* adopted from r.colors */
  545. char *icon_files(void)
  546. {
  547. char **list, *ret;
  548. char buf[GNAME_MAX], path[GPATH_MAX], path_i[GPATH_MAX];
  549. int i, count;
  550. size_t len;
  551. DIR *dir, *dir_i;
  552. struct dirent *d, *d_i;
  553. list = NULL;
  554. len = 0;
  555. sprintf(path, "%s/etc/symbol", G_gisbase());
  556. dir = opendir(path);
  557. if (!dir)
  558. return NULL;
  559. count = 0;
  560. /* loop over etc/symbol */
  561. while ((d = readdir(dir))) {
  562. if (d->d_name[0] == '.')
  563. continue;
  564. sprintf(path_i, "%s/etc/symbol/%s", G_gisbase(), d->d_name);
  565. dir_i = opendir(path_i);
  566. if (!dir_i)
  567. continue;
  568. /* loop over each directory in etc/symbols */
  569. while ((d_i = readdir(dir_i))) {
  570. if (d_i->d_name[0] == '.')
  571. continue;
  572. list = G_realloc(list, (count + 1) * sizeof(char *));
  573. sprintf(buf, "%s/%s", d->d_name, d_i->d_name);
  574. list[count++] = G_store(buf);
  575. len += strlen(d->d_name) + strlen(d_i->d_name) + 2; /* '/' + ',' */
  576. }
  577. closedir(dir_i);
  578. }
  579. closedir(dir);
  580. qsort(list, count, sizeof(char *), cmp);
  581. if (len > 0) {
  582. ret = G_malloc((len + 1) * sizeof(char)); /* \0 */
  583. *ret = '\0';
  584. for (i = 0; i < count; i++) {
  585. if (i > 0)
  586. strcat(ret, ",");
  587. strcat(ret, list[i]);
  588. G_free(list[i]);
  589. }
  590. G_free(list);
  591. }
  592. else {
  593. ret = G_store("");
  594. }
  595. return ret;
  596. }