main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /****************************************************************
  2. *
  3. * MODULE: v.generalize
  4. *
  5. * AUTHOR(S): Daniel Bundala
  6. *
  7. * PURPOSE: Module for line simplification and smoothing
  8. *
  9. * COPYRIGHT: (C) 2002-2005 by the GRASS Development Team
  10. *
  11. * This program is free software under the
  12. * GNU General Public License (>=v2).
  13. * Read the file COPYING that comes with GRASS
  14. * for details.
  15. *
  16. ****************************************************************/
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <grass/gis.h>
  20. #include <grass/Vect.h>
  21. #include <grass/glocale.h>
  22. #include "misc.h"
  23. #include "operators.h"
  24. #define DOUGLAS 0
  25. #define LANG 1
  26. #define VERTEX_REDUCTION 2
  27. #define REUMANN 3
  28. #define BOYLE 4
  29. #define DISTANCE_WEIGHTING 5
  30. #define CHAIKEN 6
  31. #define HERMITE 7
  32. #define SNAKES 8
  33. #define DOUGLAS_REDUCTION 9
  34. #define SLIDING_AVERAGING 10
  35. #define REMOVE_SMALL 11
  36. #define NETWORK 100
  37. #define DISPLACEMENT 101
  38. int main(int argc, char *argv[])
  39. {
  40. struct Map_info In, Out;
  41. static struct line_pnts *Points;
  42. struct line_cats *Cats;
  43. int i, type, iter;
  44. char *mapset;
  45. struct GModule *module; /* GRASS module for parsing arguments */
  46. struct Option *map_in, *map_out, *thresh_opt, *method_opt,
  47. *look_ahead_opt;
  48. struct Option *iterations_opt, *cat_opt, *alpha_opt, *beta_opt, *type_opt;
  49. struct Option *field_opt, *where_opt, *reduction_opt, *slide_opt;
  50. struct Option *angle_thresh_opt, *degree_thresh_opt,
  51. *closeness_thresh_opt;
  52. struct Option *betweeness_thresh_opt;
  53. struct Flag *ca_flag, *rs_flag;
  54. int with_z;
  55. int total_input, total_output; /* Number of points in the input/output map respectively */
  56. double thresh, alpha, beta, reduction, slide, angle_thresh;
  57. double degree_thresh, closeness_thresh, betweeness_thresh;
  58. int method;
  59. int look_ahead, iterations;
  60. int chcat;
  61. int ret, layer;
  62. int n_areas, n_lines;
  63. double x, y;
  64. int simplification, mask_type;
  65. VARRAY *varray;
  66. char *s;
  67. int left, right;
  68. /* initialize GIS environment */
  69. G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */
  70. /* initialize module */
  71. module = G_define_module();
  72. module->keywords =
  73. _
  74. ("vector, generalization, simplification, smoothing, displacement, network generalization");
  75. module->description = _("Vector based generalization.");
  76. /* Define the different options as defined in gis.h */
  77. map_in = G_define_standard_option(G_OPT_V_INPUT);
  78. map_out = G_define_standard_option(G_OPT_V_OUTPUT);
  79. type_opt = G_define_standard_option(G_OPT_V_TYPE);
  80. type_opt->options = "line,boundary,area";
  81. type_opt->answer = "line,boundary,area";
  82. method_opt = G_define_option();
  83. method_opt->key = "method";
  84. method_opt->type = TYPE_STRING;
  85. method_opt->required = YES;
  86. method_opt->multiple = NO;
  87. method_opt->options =
  88. "douglas,douglas_reduction,lang,reduction,reumann,remove_small,boyle,sliding_averaging,distance_weighting,chaiken,hermite,snakes,network,displacement";
  89. method_opt->answer = "douglas";
  90. method_opt->descriptions = _("douglas;Douglas-Peucker Algorithm;"
  91. "douglas_reduction;Douglas-Peucker Algorithm with reduction parameter;"
  92. "lang;Lang Simplification Algorithm;"
  93. "reduction;Vertex Reduction Algorithm eliminates points close to each other;"
  94. "reumann;Reumann-Witkam Algorithm;"
  95. "remove_small;Removes lines shorter than threshold and areas of area less than threshold;"
  96. "boyle;Boyle's Forward-Looking Algorithm;"
  97. "sliding_averaging;McMaster's Sliding Averaging Algorithm;"
  98. "distance_weighting;McMaster's Distance-Weighting Algorithm;"
  99. "chaiken;Chaiken's Algorithm;"
  100. "hermite;Interpolation by Cubic Hermite Splines;"
  101. "snakes;Snakes method for line smoothing;"
  102. "network;Network generalization;"
  103. "displacement;Displacement of lines close to each other;");
  104. method_opt->description = _("Generalization algorithm");
  105. thresh_opt = G_define_option();
  106. thresh_opt->key = "threshold";
  107. thresh_opt->type = TYPE_DOUBLE;
  108. thresh_opt->required = YES;
  109. thresh_opt->options = "0-1000000000";
  110. thresh_opt->answer = "1.0";
  111. thresh_opt->description = _("Maximal tolerance value");
  112. look_ahead_opt = G_define_option();
  113. look_ahead_opt->key = "look_ahead";
  114. look_ahead_opt->type = TYPE_INTEGER;
  115. look_ahead_opt->required = YES;
  116. look_ahead_opt->answer = "7";
  117. look_ahead_opt->description = _("Look-ahead parameter");
  118. reduction_opt = G_define_option();
  119. reduction_opt->key = "reduction";
  120. reduction_opt->type = TYPE_DOUBLE;
  121. reduction_opt->required = YES;
  122. reduction_opt->answer = "50";
  123. reduction_opt->options = "0-100";
  124. reduction_opt->description =
  125. _
  126. ("Percentage of the points in the output of 'douglas_reduction' algorithm");
  127. slide_opt = G_define_option();
  128. slide_opt->key = "slide";
  129. slide_opt->type = TYPE_DOUBLE;
  130. slide_opt->required = YES;
  131. slide_opt->answer = "0.5";
  132. slide_opt->options = "0-1";
  133. slide_opt->description =
  134. _("Slide of computed point toward the original point");
  135. angle_thresh_opt = G_define_option();
  136. angle_thresh_opt->key = "angle_thresh";
  137. angle_thresh_opt->type = TYPE_DOUBLE;
  138. angle_thresh_opt->required = YES;
  139. angle_thresh_opt->answer = "3";
  140. angle_thresh_opt->options = "0-180";
  141. angle_thresh_opt->description =
  142. _("Minimum angle between two consecutive segments in Hermite method");
  143. degree_thresh_opt = G_define_option();
  144. degree_thresh_opt->key = "degree_thresh";
  145. degree_thresh_opt->type = TYPE_INTEGER;
  146. degree_thresh_opt->required = YES;
  147. degree_thresh_opt->answer = "0";
  148. degree_thresh_opt->description =
  149. _("Degree threshold in network generalization");
  150. closeness_thresh_opt = G_define_option();
  151. closeness_thresh_opt->key = "closeness_thresh";
  152. closeness_thresh_opt->type = TYPE_DOUBLE;
  153. closeness_thresh_opt->required = YES;
  154. closeness_thresh_opt->answer = "0";
  155. closeness_thresh_opt->options = "0-1";
  156. closeness_thresh_opt->description =
  157. _("Closeness threshold in network generalization");
  158. betweeness_thresh_opt = G_define_option();
  159. betweeness_thresh_opt->key = "betweeness_thresh";
  160. betweeness_thresh_opt->type = TYPE_DOUBLE;
  161. betweeness_thresh_opt->required = YES;
  162. betweeness_thresh_opt->answer = "0";
  163. betweeness_thresh_opt->description =
  164. _("Betweeness threshold in network generalization");
  165. alpha_opt = G_define_option();
  166. alpha_opt->key = "alpha";
  167. alpha_opt->type = TYPE_DOUBLE;
  168. alpha_opt->required = YES;
  169. alpha_opt->answer = "1.0";
  170. alpha_opt->description = _("Snakes alpha parameter");
  171. beta_opt = G_define_option();
  172. beta_opt->key = "beta";
  173. beta_opt->type = TYPE_DOUBLE;
  174. beta_opt->required = YES;
  175. beta_opt->answer = "1.0";
  176. beta_opt->description = _("Snakes beta parameter");
  177. iterations_opt = G_define_option();
  178. iterations_opt->key = "iterations";
  179. iterations_opt->type = TYPE_INTEGER;
  180. iterations_opt->required = YES;
  181. iterations_opt->answer = "1";
  182. iterations_opt->description = _("Number of iterations");
  183. field_opt = G_define_standard_option(G_OPT_V_FIELD);
  184. cat_opt = G_define_standard_option(G_OPT_V_CATS);
  185. where_opt = G_define_standard_option(G_OPT_WHERE);
  186. ca_flag = G_define_flag();
  187. ca_flag->key = 'c';
  188. ca_flag->description = _("Copy attributes");
  189. rs_flag = G_define_flag();
  190. rs_flag->key = 'r';
  191. rs_flag->description = _("Remove lines and areas smaller than threshold");
  192. /* options and flags parser */
  193. if (G_parser(argc, argv))
  194. exit(EXIT_FAILURE);
  195. thresh = atof(thresh_opt->answer);
  196. look_ahead = atoi(look_ahead_opt->answer);
  197. alpha = atof(alpha_opt->answer);
  198. beta = atof(beta_opt->answer);
  199. reduction = atof(reduction_opt->answer);
  200. iterations = atoi(iterations_opt->answer);
  201. slide = atof(slide_opt->answer);
  202. angle_thresh = atof(angle_thresh_opt->answer);
  203. degree_thresh = atof(degree_thresh_opt->answer);
  204. closeness_thresh = atof(closeness_thresh_opt->answer);
  205. betweeness_thresh = atof(betweeness_thresh_opt->answer);
  206. mask_type = type_mask(type_opt);
  207. G_debug(3, "Method: %s", method_opt->answer);
  208. s = method_opt->answer;
  209. if (strcmp(s, "douglas") == 0)
  210. method = DOUGLAS;
  211. else if (strcmp(s, "lang") == 0)
  212. method = LANG;
  213. else if (strcmp(s, "reduction") == 0)
  214. method = VERTEX_REDUCTION;
  215. else if (strcmp(s, "reumann") == 0)
  216. method = REUMANN;
  217. else if (strcmp(s, "boyle") == 0)
  218. method = BOYLE;
  219. else if (strcmp(s, "distance_weighting") == 0)
  220. method = DISTANCE_WEIGHTING;
  221. else if (strcmp(s, "chaiken") == 0)
  222. method = CHAIKEN;
  223. else if (strcmp(s, "hermite") == 0)
  224. method = HERMITE;
  225. else if (strcmp(s, "snakes") == 0)
  226. method = SNAKES;
  227. else if (strcmp(s, "douglas_reduction") == 0)
  228. method = DOUGLAS_REDUCTION;
  229. else if (strcmp(s, "sliding_averaging") == 0)
  230. method = SLIDING_AVERAGING;
  231. else if (strcmp(s, "network") == 0)
  232. method = NETWORK;
  233. else if (strcmp(s, "displacement") == 0) {
  234. method = DISPLACEMENT;
  235. /* we can displace only the lines */
  236. mask_type = GV_LINE;
  237. }
  238. else if (strcmp(s, "remove_small") == 0) {
  239. method = REMOVE_SMALL;
  240. /* switch -r flag on */
  241. rs_flag->answer = 1;
  242. }
  243. else {
  244. G_fatal_error(_("Unknown method"));
  245. exit(EXIT_FAILURE);
  246. }
  247. /* simplification or smoothing? */
  248. switch (method) {
  249. case DOUGLAS:
  250. case DOUGLAS_REDUCTION:
  251. case LANG:
  252. case VERTEX_REDUCTION:
  253. case REUMANN:
  254. case REMOVE_SMALL:
  255. simplification = 1;
  256. break;
  257. default:
  258. simplification = 0;
  259. break;
  260. }
  261. Points = Vect_new_line_struct();
  262. Cats = Vect_new_cats_struct();
  263. Vect_check_input_output_name(map_in->answer, map_out->answer,
  264. GV_FATAL_EXIT);
  265. if ((mapset = G_find_vector2(map_in->answer, "")) == NULL)
  266. G_fatal_error(_("Vector map <%s> not found"), map_in->answer);
  267. Vect_set_open_level(2);
  268. if (1 > Vect_open_old(&In, map_in->answer, mapset))
  269. G_fatal_error(_("Unable to open vector map <%s>"),
  270. G_fully_qualified_name(map_in->answer, mapset));
  271. with_z = Vect_is_3d(&In);
  272. if (0 > Vect_open_new(&Out, map_out->answer, with_z)) {
  273. Vect_close(&In);
  274. G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer);
  275. }
  276. /* parse filter option and select appropriate lines */
  277. layer = atoi(field_opt->answer);
  278. if (where_opt->answer) {
  279. if (layer < 1)
  280. G_fatal_error(_("'%s' must be > 0 for '%s'"), "layer", "where");
  281. if (cat_opt->answer)
  282. G_warning(_("'where' and 'cats' parameters were supplied, cat will be ignored"));
  283. chcat = 1;
  284. varray = Vect_new_varray(Vect_get_num_lines(&In));
  285. if (Vect_set_varray_from_db
  286. (&In, layer, where_opt->answer, mask_type, 1, varray) == -1) {
  287. G_warning(_("Unable to load data from database"));
  288. }
  289. }
  290. else if (cat_opt->answer) {
  291. if (layer < 1)
  292. G_fatal_error(_("'%s' must be > 0 for '%s'"), "layer", "cat");
  293. varray = Vect_new_varray(Vect_get_num_lines(&In));
  294. chcat = 1;
  295. if (Vect_set_varray_from_cat_string
  296. (&In, layer, cat_opt->answer, mask_type, 1, varray) == -1) {
  297. G_warning(_("Problem loading category values"));
  298. }
  299. }
  300. else {
  301. chcat = 0;
  302. varray = NULL;
  303. }
  304. Vect_copy_head_data(&In, &Out);
  305. Vect_hist_copy(&In, &Out);
  306. Vect_hist_command(&Out);
  307. total_input = total_output = 0;
  308. if (method == DISPLACEMENT) {
  309. snakes_displacement(&In, &Out, thresh, alpha, beta, 1.0, 10.0,
  310. iterations, varray);
  311. }
  312. /* TODO: rearrange code below. It's really messy */
  313. if (method == NETWORK) {
  314. total_output =
  315. graph_generalization(&In, &Out, degree_thresh, closeness_thresh,
  316. betweeness_thresh);
  317. }
  318. else {
  319. G_message(_("Generalization (%s)..."), method_opt->answer);
  320. G_percent_reset();
  321. }
  322. i = 0;
  323. n_lines = Vect_get_num_lines(&In);
  324. while (method < NETWORK &&
  325. (type = Vect_read_next_line(&In, Points, Cats)) > 0) {
  326. i++;
  327. G_percent(i, n_lines, 1);
  328. if (type == GV_CENTROID && (mask_type & GV_BOUNDARY))
  329. continue; /* skip old centroids,
  330. * we calculate new if we generalize boundarie */
  331. total_input += Points->n_points;
  332. if ((type & mask_type) && (!chcat || varray->c[i])) {
  333. int after = 0;
  334. for (iter = 0; iter < iterations; iter++) {
  335. switch (method) {
  336. case DOUGLAS:
  337. douglas_peucker(Points, thresh, with_z);
  338. break;
  339. case DOUGLAS_REDUCTION:
  340. douglas_peucker_reduction(Points, thresh, reduction,
  341. with_z);
  342. break;
  343. case LANG:
  344. lang(Points, thresh, look_ahead, with_z);
  345. break;
  346. case VERTEX_REDUCTION:
  347. vertex_reduction(Points, thresh, with_z);
  348. break;
  349. case REUMANN:
  350. reumann_witkam(Points, thresh, with_z);
  351. break;
  352. case BOYLE:
  353. boyle(Points, look_ahead, with_z);
  354. break;
  355. case SLIDING_AVERAGING:
  356. sliding_averaging(Points, slide, look_ahead, with_z);
  357. break;
  358. case DISTANCE_WEIGHTING:
  359. distance_weighting(Points, slide, look_ahead, with_z);
  360. break;
  361. case CHAIKEN:
  362. chaiken(Points, thresh, with_z);
  363. break;
  364. case HERMITE:
  365. hermite(Points, thresh, angle_thresh, with_z);
  366. break;
  367. case SNAKES:
  368. snakes(Points, alpha, beta, with_z);
  369. break;
  370. }
  371. }
  372. /* remove "oversimplified" lines */
  373. if (rs_flag->answer && simplification && type == GV_LINE &&
  374. Vect_line_length(Points) < thresh)
  375. continue;
  376. after = Points->n_points;
  377. total_output += after;
  378. Vect_write_line(&Out, type, Points, Cats);
  379. }
  380. else {
  381. total_output += Points->n_points;
  382. Vect_write_line(&Out, type, Points, Cats);
  383. }
  384. }
  385. /* remove incorrect boundaries
  386. * they may occur only if they were generalized */
  387. if (mask_type & GV_BOUNDARY) {
  388. Vect_build_partial(&Out, GV_BUILD_ATTACH_ISLES, NULL);
  389. n_lines = Vect_get_num_lines(&Out);
  390. for (i = 1; i <= n_lines; i++) {
  391. type = Vect_read_line(&Out, Points, Cats, i);
  392. if (type != GV_BOUNDARY)
  393. continue;
  394. Vect_get_line_areas(&Out, i, &left, &right);
  395. if (left == 0 || right == 0) {
  396. Vect_delete_line(&Out, i);
  397. total_output -= Points->n_points;
  398. }
  399. }
  400. }
  401. /* calculate new centroids
  402. * We need to calculate them only if the boundaries
  403. * were generalized
  404. */
  405. if ((mask_type & GV_BOUNDARY) && method != DISPLACEMENT) {
  406. Vect_build_partial(&Out, GV_BUILD_ATTACH_ISLES, NULL);
  407. n_areas = Vect_get_num_areas(&Out);
  408. for (i = 1; i <= n_areas; i++) {
  409. /* skip dead area */
  410. if (!Vect_area_alive(&Out, i))
  411. continue;
  412. Vect_get_area_cats(&In, i, Cats);
  413. ret = Vect_get_point_in_area(&Out, i, &x, &y);
  414. if (ret < 0) {
  415. G_warning(_("Unable to calculate centroid for area %d"), i);
  416. continue;
  417. }
  418. Vect_reset_line(Points);
  419. Vect_append_point(Points, x, y, 0.0);
  420. Vect_write_line(&Out, GV_CENTROID, Points, Cats);
  421. }
  422. }
  423. /* remove small areas */
  424. if (rs_flag->answer && simplification && (mask_type & GV_AREA)) {
  425. Vect_build_partial(&Out, GV_BUILD_CENTROIDS, NULL);
  426. Vect_remove_small_areas(&Out, thresh, NULL, NULL, &slide);
  427. }
  428. if (G_verbose() > G_verbose_min())
  429. Vect_build(&Out, stderr);
  430. else
  431. Vect_build(&Out, NULL);
  432. /* finally copy tables */
  433. if (ca_flag->answer)
  434. copy_tables_by_cats(&In, &Out);
  435. if (total_input != 0)
  436. G_message(_("Number of vertices was reduced from %d to %d [%d%%]"),
  437. total_input, total_output,
  438. (total_output * 100) / total_input);
  439. Vect_close(&In);
  440. Vect_close(&Out);
  441. exit(EXIT_SUCCESS);
  442. }