main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /*
  2. ****************************************************************************
  3. *
  4. * MODULE: d.area.thematic
  5. * AUTHOR(S): Moritz Lennert, based on d.vect
  6. * PURPOSE: display a thematic vector area map
  7. * on top of the current image.
  8. * COPYRIGHT: (C) 2007 by the GRASS Development Team
  9. *
  10. * This program is free software under the GNU General Public
  11. * License (>=v2). Read the file COPYING that comes with GRASS
  12. * for details.
  13. *
  14. *****************************************************************************/
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <sys/types.h>
  18. #include <dirent.h>
  19. #include <grass/config.h>
  20. #include <grass/gis.h>
  21. #include <grass/raster.h>
  22. #include <grass/display.h>
  23. #include <grass/vector.h>
  24. #include <grass/colors.h>
  25. #include <grass/dbmi.h>
  26. #include <grass/glocale.h>
  27. #include <grass/arraystats.h>
  28. #include "plot.h"
  29. #include "local_proto.h"
  30. int main(int argc, char **argv)
  31. {
  32. int ret, level;
  33. int i, stat = 0;
  34. int nclass = 0, nbreaks, *frequencies, boxsize, textsize, ypos;
  35. int chcat = 0;
  36. int r, g, b;
  37. int has_color = 0;
  38. struct color_rgb *colors, bcolor;
  39. int default_width;
  40. int verbose = FALSE;
  41. char map_name[128];
  42. struct GModule *module;
  43. struct Option *map_opt;
  44. struct Option *column_opt;
  45. struct Option *breaks_opt;
  46. struct Option *algo_opt;
  47. struct Option *nbclass_opt;
  48. struct Option *colors_opt;
  49. struct Option *bcolor_opt;
  50. struct Option *bwidth_opt;
  51. struct Option *where_opt;
  52. struct Option *field_opt;
  53. struct Option *legend_file_opt;
  54. struct Flag *legend_flag, *algoinfo_flag, *nodraw_flag;
  55. char *desc;
  56. struct cat_list *Clist;
  57. int *cats, ncat, nrec, ctype;
  58. struct Map_info Map;
  59. struct field_info *fi;
  60. dbDriver *driver;
  61. dbHandle handle;
  62. dbCatValArray cvarr;
  63. struct Cell_head window;
  64. struct bound_box box;
  65. double overlap, *breakpoints, *data = NULL, class_info = 0.0;
  66. struct GASTATS stats;
  67. FILE *fd;
  68. /* Initialize the GIS calls */
  69. G_gisinit(argv[0]);
  70. module = G_define_module();
  71. G_add_keyword(_("display"));
  72. G_add_keyword(_("cartography"));
  73. G_add_keyword(_("choropleth map"));
  74. module->description =
  75. _("Displays a thematic vector area map in the active "
  76. "frame on the graphics monitor.");
  77. map_opt = G_define_standard_option(G_OPT_V_MAP);
  78. column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
  79. column_opt->required = YES;
  80. column_opt->description =
  81. _("Name of attribute column to be classified");
  82. breaks_opt = G_define_option();
  83. breaks_opt->key = "breaks";
  84. breaks_opt->type = TYPE_STRING;
  85. breaks_opt->required = NO;
  86. breaks_opt->multiple = YES;
  87. breaks_opt->description = _("Class breaks, without minimum and maximum");
  88. algo_opt = G_define_option();
  89. algo_opt->key = "algorithm";
  90. algo_opt->type = TYPE_STRING;
  91. algo_opt->required = NO;
  92. algo_opt->multiple = NO;
  93. algo_opt->options = "int,std,qua,equ,dis";
  94. algo_opt->description = _("Algorithm to use for classification");
  95. desc = NULL;
  96. G_asprintf(&desc,
  97. "int;%s;std;%s;qua;%s;equ;%s",
  98. _("simple intervals"),
  99. _("standard deviations"),
  100. _("quantiles"),
  101. _("equiprobable (normal distribution)"));
  102. algo_opt->descriptions = desc;
  103. /*currently disabled because of bugs "dis;discontinuities"); */
  104. nbclass_opt = G_define_option();
  105. nbclass_opt->key = "nbclasses";
  106. nbclass_opt->type = TYPE_INTEGER;
  107. nbclass_opt->required = NO;
  108. nbclass_opt->multiple = NO;
  109. nbclass_opt->description = _("Number of classes to define");
  110. colors_opt = G_define_option();
  111. colors_opt->key = "colors";
  112. colors_opt->type = TYPE_STRING;
  113. colors_opt->required = YES;
  114. colors_opt->multiple = YES;
  115. colors_opt->description = _("Colors (one per class)");
  116. colors_opt->gisprompt = "old_color,color,color";
  117. field_opt = G_define_standard_option(G_OPT_V_FIELD);
  118. field_opt->description =
  119. _("Layer number. If -1, all layers are displayed.");
  120. field_opt->guisection = _("Selection");
  121. where_opt = G_define_standard_option(G_OPT_DB_WHERE);
  122. where_opt->guisection = _("Selection");
  123. bwidth_opt = G_define_option();
  124. bwidth_opt->key = "boundary_width";
  125. bwidth_opt->type = TYPE_INTEGER;
  126. bwidth_opt->answer = "0";
  127. bwidth_opt->guisection = _("Boundaries");
  128. bwidth_opt->description = _("Boundary width");
  129. bcolor_opt = G_define_standard_option(G_OPT_C_FG);
  130. bcolor_opt->key = "boundary_color";
  131. bcolor_opt->label = _("Boundary color");
  132. bcolor_opt->guisection = _("Boundaries");
  133. legend_file_opt = G_define_standard_option(G_OPT_F_OUTPUT);
  134. legend_file_opt->key = "legendfile";
  135. legend_file_opt->description =
  136. _("File in which to save d.graph instructions for legend display");
  137. legend_file_opt->required = NO;
  138. legend_flag = G_define_flag();
  139. legend_flag->key = 'l';
  140. legend_flag->description =
  141. _("Create legend information and send to stdout");
  142. algoinfo_flag = G_define_flag();
  143. algoinfo_flag->key = 'e';
  144. algoinfo_flag->description =
  145. _("When printing legend info, include extended statistical info from classification algorithm");
  146. nodraw_flag = G_define_flag();
  147. nodraw_flag->key = 'n';
  148. nodraw_flag->description = _("Do not draw map, only output the legend");
  149. /* Check command line */
  150. if (G_parser(argc, argv))
  151. exit(EXIT_FAILURE);
  152. if (G_verbose() > G_verbose_std())
  153. verbose = TRUE;
  154. G_get_set_window(&window);
  155. /* Read map options */
  156. strcpy(map_name, map_opt->answer);
  157. /* open vector */
  158. level = Vect_open_old(&Map, map_name, "");
  159. if (level < 2)
  160. G_fatal_error(_("%s: You must build topology on vector map. Run v.build."),
  161. map_name);
  162. /* Check database connection and open it */
  163. Clist = Vect_new_cat_list();
  164. Clist->field = atoi(field_opt->answer);
  165. if (Clist->field < 1)
  166. G_fatal_error(_("'layer' must be > 0"));
  167. if ((fi = Vect_get_field(&Map, Clist->field)) == NULL)
  168. G_fatal_error(_("Database connection not defined"));
  169. if (fi != NULL) {
  170. driver = db_start_driver(fi->driver);
  171. if (driver == NULL)
  172. G_fatal_error(_("Unable to start driver <%s>"), fi->driver);
  173. db_init_handle(&handle);
  174. db_set_handle(&handle, fi->database, NULL);
  175. if (db_open_database(driver, &handle) != DB_OK)
  176. G_fatal_error(_("Unable to open database <%s>"), fi->database);
  177. }
  178. /*Get CatValArray needed for plotting and for legend calculations */
  179. db_CatValArray_init(&cvarr);
  180. nrec = db_select_CatValArray(driver, fi->table, fi->key,
  181. column_opt->answer, where_opt->answer,
  182. &cvarr);
  183. G_debug(3, "nrec (%s) = %d", column_opt->answer, nrec);
  184. if (cvarr.ctype != DB_C_TYPE_INT && cvarr.ctype != DB_C_TYPE_DOUBLE)
  185. G_fatal_error(_("Data (%s) not numeric. "
  186. "Column must be numeric."), column_opt->answer);
  187. if (nrec < 0)
  188. G_fatal_error(_("Cannot select data (%s) from table"),
  189. column_opt->answer);
  190. for (i = 0; i < cvarr.n_values; i++) {
  191. G_debug(4, "cat = %d %s = %d", cvarr.value[i].cat,
  192. column_opt->answer,
  193. (cvarr.ctype ==
  194. DB_C_TYPE_INT ? cvarr.value[i].val.i : (int)cvarr.value[i].
  195. val.d));
  196. }
  197. /*Get the sorted data */
  198. ret = db_CatValArray_sort_by_value(&cvarr);
  199. if (ret == DB_FAILED)
  200. G_fatal_error("Could not sort array of values..");
  201. data = (double *)G_malloc((nrec) * sizeof(double));
  202. for (i = 0; i < nrec; i++)
  203. data[i] = 0.0;
  204. ctype = cvarr.ctype;
  205. if (ctype == DB_C_TYPE_INT) {
  206. for (i = 0; i < nrec; i++)
  207. data[i] = cvarr.value[i].val.i;
  208. }
  209. else {
  210. for (i = 0; i < nrec; i++)
  211. data[i] = cvarr.value[i].val.d;
  212. }
  213. db_CatValArray_sort(&cvarr);
  214. /*Get the list of relevant cats if where option is given */
  215. if (where_opt->answer) {
  216. ncat = db_select_int(driver, fi->table, fi->key, where_opt->answer,
  217. &cats);
  218. chcat = 1;
  219. Vect_array_to_cat_list(cats, ncat, Clist);
  220. }
  221. db_close_database(driver);
  222. db_shutdown_driver(driver);
  223. /*get border line width */
  224. default_width = atoi(bwidth_opt->answer);
  225. if (default_width < 0)
  226. default_width = 0;
  227. /*get border line color */
  228. bcolor = G_standard_color_rgb(WHITE);
  229. ret = G_str_to_color(bcolor_opt->answer, &r, &g, &b);
  230. if (ret == 1) {
  231. has_color = 1;
  232. bcolor.r = r;
  233. bcolor.g = g;
  234. bcolor.b = b;
  235. }
  236. else if (ret == 2) { /* none */
  237. has_color = 0;
  238. }
  239. else if (ret == 0) { /* error */
  240. G_fatal_error(_("Unknown color: [%s]"), bcolor_opt->answer);
  241. }
  242. /* if both class breaks and (algorithm or classnumber) are given, give precedence to class
  243. * breaks
  244. */
  245. if (breaks_opt->answers) {
  246. if (algo_opt->answer || nbclass_opt->answer)
  247. 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."));
  248. /*Get class breaks */
  249. nbreaks = 0;
  250. while (breaks_opt->answers[nbreaks] != NULL)
  251. nbreaks++;
  252. nclass = nbreaks + 1; /*add one since breaks do not include min and max values */
  253. G_debug(3, "nclass = %d", nclass);
  254. breakpoints = (double *)G_malloc((nbreaks) * sizeof(double));
  255. for (i = 0; i < nbreaks; i++)
  256. breakpoints[i] = atof(breaks_opt->answers[i]);
  257. }
  258. else {
  259. if (algo_opt->answer && nbclass_opt->answer) {
  260. nclass = atoi(nbclass_opt->answer);
  261. nbreaks = nclass - 1; /* we need one less classbreaks (min and
  262. * max exluded) than classes */
  263. breakpoints = (double *)G_malloc((nbreaks) * sizeof(double));
  264. for (i = 0; i < nbreaks; i++)
  265. breakpoints[i] = 0;
  266. /* Get classbreaks for given algorithm and number of classbreaks.
  267. * class_info takes any info coming from the classification algorithms */
  268. class_info =
  269. class_apply_algorithm(algo_opt->answer, data, nrec, &nbreaks,
  270. breakpoints);
  271. }
  272. else {
  273. G_fatal_error(_("You must either give classbreaks or a classification algorithm"));
  274. }
  275. };
  276. /* Fill colors */
  277. colors = (struct color_rgb *)G_malloc(nclass * sizeof(struct color_rgb));
  278. if (colors_opt->answers != NULL) {
  279. for (i = 0; i < nclass; i++) {
  280. if (colors_opt->answers[i] == NULL)
  281. G_fatal_error(_("Not enough colors or error in color specifications.\nNeed %i colors."),
  282. nclass);
  283. ret = G_str_to_color(colors_opt->answers[i], &r, &g, &b);
  284. if (!ret)
  285. G_fatal_error(_("Error interpreting color %s"),
  286. colors_opt->answers[i]);
  287. colors[i].r = r;
  288. colors[i].g = g;
  289. colors[i].b = b;
  290. }
  291. }
  292. if (!nodraw_flag->answer) {
  293. /* Now's let's prepare the actual plotting */
  294. D_open_driver();
  295. D_setup(0);
  296. if (verbose)
  297. G_message(_("Plotting ..."));
  298. Vect_get_map_box(&Map, &box);
  299. if (window.north < box.S || window.south > box.N ||
  300. window.east < box.W ||
  301. window.west > G_adjust_easting(box.E, &window)) {
  302. G_message(_("The bounding box of the map is outside the current region, "
  303. "nothing drawn."));
  304. stat = 0;
  305. }
  306. else {
  307. overlap =
  308. G_window_percentage_overlap(&window, box.N, box.S, box.E,
  309. box.W);
  310. G_debug(1, "overlap = %f \n", overlap);
  311. if (overlap < 1)
  312. Vect_set_constraint_region(&Map, window.north, window.south,
  313. window.east, window.west,
  314. PORT_DOUBLE_MAX, -PORT_DOUBLE_MAX);
  315. /* default line width */
  316. D_line_width(default_width);
  317. stat =
  318. dareatheme(&Map, Clist, &cvarr, breakpoints, nbreaks, colors,
  319. has_color ? &bcolor : NULL, chcat, &window,
  320. default_width);
  321. /* reset line width: Do we need to get line width from display
  322. * driver (not implemented)? It will help restore previous line
  323. * width (not just 0) determined by another module (e.g.,
  324. * d.linewidth). */
  325. D_line_width(0);
  326. } /* end window check if */
  327. D_save_command(G_recreate_command());
  328. D_close_driver();
  329. } /* end of nodraw_flag condition */
  330. frequencies = (int *)G_malloc((nbreaks + 1) * sizeof(int));
  331. for (i = 0; i < nbreaks + 1; i++)
  332. frequencies[i] = 0.0;
  333. class_frequencies(data, nrec, nbreaks, breakpoints, frequencies);
  334. /*Get basic statistics about the data */
  335. basic_stats(data, nrec, &stats);
  336. if (legend_flag->answer) {
  337. if (algoinfo_flag->answer) {
  338. fprintf(stdout, _("\nTotal number of records: %.0f\n"),
  339. stats.count);
  340. fprintf(stdout, _("Classification of %s into %i classes\n"),
  341. column_opt->answer, nbreaks + 1);
  342. fprintf(stdout, _("Using algorithm: *** %s ***\n"),
  343. algo_opt->answer);
  344. fprintf(stdout, _("Mean: %f\tStandard deviation = %f\n"),
  345. stats.mean, stats.stdev);
  346. if (G_strcasecmp(algo_opt->answer, "dis") == 0)
  347. fprintf(stdout, _("Last chi2 = %f\n"), class_info);
  348. if (G_strcasecmp(algo_opt->answer, "std") == 0)
  349. fprintf(stdout,
  350. _("Stdev multiplied by %.4f to define step\n"),
  351. class_info);
  352. fprintf(stdout, "\n");
  353. }
  354. if(stats.min > breakpoints[0]){
  355. fprintf(stdout, "<%f|%i|%d:%d:%d\n",
  356. breakpoints[0], frequencies[0], colors[0].r,
  357. colors[0].g, colors[0].b);
  358. } else {
  359. fprintf(stdout, "%f|%f|%i|%d:%d:%d\n",
  360. stats.min, breakpoints[0], frequencies[0], colors[0].r,
  361. colors[0].g, colors[0].b);
  362. }
  363. for (i = 1; i < nbreaks; i++) {
  364. fprintf(stdout, "%f|%f|%i|%d:%d:%d\n",
  365. breakpoints[i - 1], breakpoints[i], frequencies[i],
  366. colors[i].r, colors[i].g, colors[i].b);
  367. }
  368. if(stats.max < breakpoints[nbreaks-1]){
  369. fprintf(stdout, ">%f|%i|%d:%d:%d\n",
  370. breakpoints[nbreaks - 1], frequencies[nbreaks],
  371. colors[nbreaks].r, colors[nbreaks].g, colors[nbreaks].b);
  372. } else {
  373. fprintf(stdout, "%f|%f|%i|%d:%d:%d\n",
  374. breakpoints[nbreaks - 1], stats.max, frequencies[nbreaks],
  375. colors[nbreaks].r, colors[nbreaks].g, colors[nbreaks].b);
  376. }
  377. }
  378. if (legend_file_opt->answer) {
  379. fd = fopen(legend_file_opt->answer, "w");
  380. boxsize = 25;
  381. textsize = 8;
  382. fprintf(fd, "size %i %i\n", textsize, textsize);
  383. ypos = 10;
  384. fprintf(fd, "symbol basic/box %i 5 %i black %d:%d:%d\n", boxsize,
  385. ypos, colors[0].r, colors[0].g, colors[0].b);
  386. fprintf(fd, "move 8 %i \n", ypos-1);
  387. if(stats.min > breakpoints[0]){
  388. fprintf(fd, "text <%f | %i\n", breakpoints[0],
  389. frequencies[0]);
  390. } else {
  391. fprintf(fd, "text %f - %f | %i\n", stats.min, breakpoints[0],
  392. frequencies[0]);
  393. }
  394. for (i = 1; i < nbreaks; i++) {
  395. ypos = 10 + i * 6;
  396. fprintf(fd, "symbol basic/box %i 5 %i black %d:%d:%d\n", boxsize,
  397. ypos, colors[i].r, colors[i].g, colors[i].b);
  398. fprintf(fd, "move 8 %i\n", ypos-1);
  399. fprintf(fd, "text %f - %f | %i\n", breakpoints[i - 1],
  400. breakpoints[i], frequencies[i]);
  401. }
  402. ypos = 10 + i * 6;
  403. fprintf(fd, "symbol basic/box %i 5 %i black %d:%d:%d\n", boxsize,
  404. ypos, colors[nbreaks].r, colors[nbreaks].g,
  405. colors[nbreaks].b);
  406. fprintf(fd, "move 8 %i\n", ypos -1);
  407. if(stats.max < breakpoints[nbreaks-1]){
  408. fprintf(fd, "text >%f | %i\n", breakpoints[nbreaks - 1],
  409. frequencies[nbreaks]);
  410. } else {
  411. fprintf(fd, "text %f - %f | %i\n", breakpoints[nbreaks - 1],
  412. stats.max, frequencies[nbreaks]);
  413. }
  414. fclose(fd);
  415. }
  416. if (verbose)
  417. G_done_msg(" ");
  418. Vect_close(&Map);
  419. Vect_destroy_cat_list(Clist);
  420. exit(stat);
  421. }