main.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /****************************************************************
  2. *
  3. * MODULE: v.generalize
  4. *
  5. * AUTHOR(S): Daniel Bundala
  6. * OGR support by Martin Landa <landa.martin gmail.com> (2009)
  7. * Markus Metz: preserve areas and area attributes
  8. *
  9. * PURPOSE: Module for line simplification and smoothing
  10. *
  11. * COPYRIGHT: (C) 2007-2010, 2012 by the GRASS Development Team
  12. *
  13. * This program is free software under the GNU General
  14. * Public License (>=v2). Read the file COPYING that
  15. * comes with GRASS for details.
  16. *
  17. ****************************************************************/
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <grass/gis.h>
  22. #include <grass/vector.h>
  23. #include <grass/glocale.h>
  24. #include "misc.h"
  25. #include "operators.h"
  26. #define DOUGLAS 0
  27. #define LANG 1
  28. #define VERTEX_REDUCTION 2
  29. #define REUMANN 3
  30. #define BOYLE 4
  31. #define DISTANCE_WEIGHTING 5
  32. #define CHAIKEN 6
  33. #define HERMITE 7
  34. #define SNAKES 8
  35. #define DOUGLAS_REDUCTION 9
  36. #define SLIDING_AVERAGING 10
  37. #define NETWORK 100
  38. #define DISPLACEMENT 101
  39. int main(int argc, char *argv[])
  40. {
  41. struct Map_info In, Out;
  42. struct line_pnts *Points;
  43. struct line_cats *Cats;
  44. int i, type, iter;
  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 *notab_flag, *loop_support_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 loop_support;
  61. int layer;
  62. int n_lines;
  63. int simplification, mask_type;
  64. struct cat_list *cat_list = NULL;
  65. char *s, *descriptions;
  66. /* initialize GIS environment */
  67. G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */
  68. /* initialize module */
  69. module = G_define_module();
  70. G_add_keyword(_("vector"));
  71. G_add_keyword(_("generalization"));
  72. G_add_keyword(_("simplification"));
  73. G_add_keyword(_("smoothing"));
  74. G_add_keyword(_("displacement"));
  75. G_add_keyword(_("network generalization"));
  76. module->description = _("Performs vector based generalization.");
  77. /* Define the different options as defined in gis.h */
  78. map_in = G_define_standard_option(G_OPT_V_INPUT);
  79. field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL);
  80. type_opt = G_define_standard_option(G_OPT_V_TYPE);
  81. type_opt->options = "line,boundary,area";
  82. type_opt->answer = "line,boundary,area";
  83. type_opt->guisection = _("Selection");
  84. map_out = G_define_standard_option(G_OPT_V_OUTPUT);
  85. method_opt = G_define_option();
  86. method_opt->key = "method";
  87. method_opt->type = TYPE_STRING;
  88. method_opt->required = YES;
  89. method_opt->multiple = NO;
  90. method_opt->options =
  91. "douglas,douglas_reduction,lang,reduction,reumann,boyle,sliding_averaging,distance_weighting,chaiken,hermite,snakes,network,displacement";
  92. descriptions = NULL;
  93. G_asprintf(&descriptions,
  94. "douglas;%s;"
  95. "douglas_reduction;%s;"
  96. "lang;%s;"
  97. "reduction;%s;"
  98. "reumann;%s;"
  99. "boyle;%s;"
  100. "sliding_averaging;%s;"
  101. "distance_weighting;%s;"
  102. "chaiken;%s;"
  103. "hermite;%s;"
  104. "snakes;%s;"
  105. "network;%s;"
  106. "displacement;%s;",
  107. _("Douglas-Peucker Algorithm"),
  108. _("Douglas-Peucker Algorithm with reduction parameter"),
  109. _("Lang Simplification Algorithm"),
  110. _("Vertex Reduction Algorithm eliminates points close to each other"),
  111. _("Reumann-Witkam Algorithm"),
  112. _("Boyle's Forward-Looking Algorithm"),
  113. _("McMaster's Sliding Averaging Algorithm"),
  114. _("McMaster's Distance-Weighting Algorithm"),
  115. _("Chaiken's Algorithm"),
  116. _("Interpolation by Cubic Hermite Splines"),
  117. _("Snakes method for line smoothing"),
  118. _("Network generalization"),
  119. _("Displacement of lines close to each other"));
  120. method_opt->descriptions = G_store(descriptions);
  121. method_opt->description = _("Generalization algorithm");
  122. thresh_opt = G_define_option();
  123. thresh_opt->key = "threshold";
  124. thresh_opt->type = TYPE_DOUBLE;
  125. thresh_opt->required = YES;
  126. thresh_opt->options = "0-1000000000";
  127. thresh_opt->description = _("Maximal tolerance value");
  128. look_ahead_opt = G_define_option();
  129. look_ahead_opt->key = "look_ahead";
  130. look_ahead_opt->type = TYPE_INTEGER;
  131. look_ahead_opt->required = NO;
  132. look_ahead_opt->answer = "7";
  133. look_ahead_opt->description = _("Look-ahead parameter");
  134. reduction_opt = G_define_option();
  135. reduction_opt->key = "reduction";
  136. reduction_opt->type = TYPE_DOUBLE;
  137. reduction_opt->required = NO;
  138. reduction_opt->answer = "50";
  139. reduction_opt->options = "0-100";
  140. reduction_opt->description =
  141. _("Percentage of the points in the output of 'douglas_reduction' algorithm");
  142. slide_opt = G_define_option();
  143. slide_opt->key = "slide";
  144. slide_opt->type = TYPE_DOUBLE;
  145. slide_opt->required = NO;
  146. slide_opt->answer = "0.5";
  147. slide_opt->options = "0-1";
  148. slide_opt->description =
  149. _("Slide of computed point toward the original point");
  150. angle_thresh_opt = G_define_option();
  151. angle_thresh_opt->key = "angle_thresh";
  152. angle_thresh_opt->type = TYPE_DOUBLE;
  153. angle_thresh_opt->required = NO;
  154. angle_thresh_opt->answer = "3";
  155. angle_thresh_opt->options = "0-180";
  156. angle_thresh_opt->description =
  157. _("Minimum angle between two consecutive segments in Hermite method");
  158. degree_thresh_opt = G_define_option();
  159. degree_thresh_opt->key = "degree_thresh";
  160. degree_thresh_opt->type = TYPE_INTEGER;
  161. degree_thresh_opt->required = NO;
  162. degree_thresh_opt->answer = "0";
  163. degree_thresh_opt->description =
  164. _("Degree threshold in network generalization");
  165. closeness_thresh_opt = G_define_option();
  166. closeness_thresh_opt->key = "closeness_thresh";
  167. closeness_thresh_opt->type = TYPE_DOUBLE;
  168. closeness_thresh_opt->required = NO;
  169. closeness_thresh_opt->answer = "0";
  170. closeness_thresh_opt->options = "0-1";
  171. closeness_thresh_opt->description =
  172. _("Closeness threshold in network generalization");
  173. betweeness_thresh_opt = G_define_option();
  174. betweeness_thresh_opt->key = "betweeness_thresh";
  175. betweeness_thresh_opt->type = TYPE_DOUBLE;
  176. betweeness_thresh_opt->required = NO;
  177. betweeness_thresh_opt->answer = "0";
  178. betweeness_thresh_opt->description =
  179. _("Betweeness threshold in network generalization");
  180. alpha_opt = G_define_option();
  181. alpha_opt->key = "alpha";
  182. alpha_opt->type = TYPE_DOUBLE;
  183. alpha_opt->required = NO;
  184. alpha_opt->answer = "1.0";
  185. alpha_opt->description = _("Snakes alpha parameter");
  186. beta_opt = G_define_option();
  187. beta_opt->key = "beta";
  188. beta_opt->type = TYPE_DOUBLE;
  189. beta_opt->required = NO;
  190. beta_opt->answer = "1.0";
  191. beta_opt->description = _("Snakes beta parameter");
  192. iterations_opt = G_define_option();
  193. iterations_opt->key = "iterations";
  194. iterations_opt->type = TYPE_INTEGER;
  195. iterations_opt->required = NO;
  196. iterations_opt->answer = "1";
  197. iterations_opt->description = _("Number of iterations");
  198. cat_opt = G_define_standard_option(G_OPT_V_CATS);
  199. cat_opt->guisection = _("Selection");
  200. where_opt = G_define_standard_option(G_OPT_DB_WHERE);
  201. where_opt->guisection = _("Selection");
  202. loop_support_flag = G_define_flag();
  203. loop_support_flag->key = 'l';
  204. loop_support_flag->label = _("Loop support");
  205. loop_support_flag->description = _("Modify end points of lines forming a closed loop");
  206. notab_flag = G_define_standard_flag(G_FLG_V_TABLE);
  207. notab_flag->description = _("Do not copy attributes");
  208. notab_flag->guisection = _("Attributes");
  209. /* options and flags parser */
  210. if (G_parser(argc, argv))
  211. exit(EXIT_FAILURE);
  212. thresh = atof(thresh_opt->answer);
  213. look_ahead = atoi(look_ahead_opt->answer);
  214. alpha = atof(alpha_opt->answer);
  215. beta = atof(beta_opt->answer);
  216. reduction = atof(reduction_opt->answer);
  217. iterations = atoi(iterations_opt->answer);
  218. slide = atof(slide_opt->answer);
  219. angle_thresh = atof(angle_thresh_opt->answer);
  220. degree_thresh = atof(degree_thresh_opt->answer);
  221. closeness_thresh = atof(closeness_thresh_opt->answer);
  222. betweeness_thresh = atof(betweeness_thresh_opt->answer);
  223. mask_type = type_mask(type_opt);
  224. G_debug(3, "Method: %s", method_opt->answer);
  225. s = method_opt->answer;
  226. if (strcmp(s, "douglas") == 0)
  227. method = DOUGLAS;
  228. else if (strcmp(s, "lang") == 0)
  229. method = LANG;
  230. else if (strcmp(s, "reduction") == 0)
  231. method = VERTEX_REDUCTION;
  232. else if (strcmp(s, "reumann") == 0)
  233. method = REUMANN;
  234. else if (strcmp(s, "boyle") == 0)
  235. method = BOYLE;
  236. else if (strcmp(s, "distance_weighting") == 0)
  237. method = DISTANCE_WEIGHTING;
  238. else if (strcmp(s, "chaiken") == 0)
  239. method = CHAIKEN;
  240. else if (strcmp(s, "hermite") == 0)
  241. method = HERMITE;
  242. else if (strcmp(s, "snakes") == 0)
  243. method = SNAKES;
  244. else if (strcmp(s, "douglas_reduction") == 0)
  245. method = DOUGLAS_REDUCTION;
  246. else if (strcmp(s, "sliding_averaging") == 0)
  247. method = SLIDING_AVERAGING;
  248. else if (strcmp(s, "network") == 0)
  249. method = NETWORK;
  250. else if (strcmp(s, "displacement") == 0) {
  251. method = DISPLACEMENT;
  252. /* we can displace only the lines */
  253. mask_type = GV_LINE;
  254. }
  255. else {
  256. G_fatal_error(_("Unknown method"));
  257. exit(EXIT_FAILURE);
  258. }
  259. /* simplification or smoothing? */
  260. switch (method) {
  261. case DOUGLAS:
  262. case DOUGLAS_REDUCTION:
  263. case LANG:
  264. case VERTEX_REDUCTION:
  265. case REUMANN:
  266. simplification = 1;
  267. break;
  268. default:
  269. simplification = 0;
  270. break;
  271. }
  272. Points = Vect_new_line_struct();
  273. Cats = Vect_new_cats_struct();
  274. Vect_check_input_output_name(map_in->answer, map_out->answer,
  275. G_FATAL_EXIT);
  276. Vect_set_open_level(2);
  277. if (Vect_open_old2(&In, map_in->answer, "", field_opt->answer) < 1)
  278. G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer);
  279. with_z = Vect_is_3d(&In);
  280. if (0 > Vect_open_new(&Out, map_out->answer, with_z)) {
  281. Vect_close(&In);
  282. G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer);
  283. }
  284. Vect_copy_head_data(&In, &Out);
  285. Vect_hist_copy(&In, &Out);
  286. Vect_hist_command(&Out);
  287. total_input = total_output = 0;
  288. layer = Vect_get_field_number(&In, field_opt->answer);
  289. /* parse filter options */
  290. if (layer > 0)
  291. cat_list = Vect_cats_set_constraint(&In, layer,
  292. where_opt->answer, cat_opt->answer);
  293. if (method == DISPLACEMENT) {
  294. /* modifies only lines, all other features including boundaries are preserved */
  295. /* options where, cats, and layer are respected */
  296. G_message(_("Displacement..."));
  297. snakes_displacement(&In, &Out, thresh, alpha, beta, 1.0, 10.0,
  298. iterations, cat_list, layer);
  299. }
  300. /* TODO: rearrange code below. It's really messy */
  301. if (method == NETWORK) {
  302. /* extracts lines of selected type, all other features are discarded */
  303. /* options where, cats, and layer are ignored */
  304. G_message(_("Network generalization..."));
  305. total_output =
  306. graph_generalization(&In, &Out, mask_type, degree_thresh,
  307. closeness_thresh, betweeness_thresh);
  308. }
  309. /* copy tables here because method == NETWORK is complete and
  310. * tables for Out may be needed for parse_filter_options() below */
  311. if (!notab_flag->answer) {
  312. if (method == NETWORK)
  313. copy_tables_by_cats(&In, &Out);
  314. else
  315. Vect_copy_tables(&In, &Out, -1);
  316. }
  317. else if (where_opt->answer && method < NETWORK) {
  318. G_warning(_("Attributes are needed for 'where' option, copying table"));
  319. Vect_copy_tables(&In, &Out, -1);
  320. }
  321. /* smoothing/simplification */
  322. if (method < NETWORK) {
  323. /* modifies only lines of selected type, all other features are preserved */
  324. int not_modified_boundaries = 0, n_oversimplified = 0;
  325. struct line_pnts *APoints; /* original Points */
  326. Vect_copy_map_lines(&In, &Out);
  327. Vect_build_partial(&Out, GV_BUILD_CENTROIDS);
  328. if ((mask_type & GV_AREA) && !(mask_type & GV_BOUNDARY))
  329. mask_type |= GV_BOUNDARY;
  330. G_message("-----------------------------------------------------");
  331. G_message(_("Generalization (%s)..."), method_opt->answer);
  332. G_percent_reset();
  333. APoints = Vect_new_line_struct();
  334. n_lines = Vect_get_num_lines(&Out);
  335. for (i = 1; i <= n_lines; i++) {
  336. int after = 0;
  337. G_percent(i, n_lines, 1);
  338. type = Vect_read_line(&Out, APoints, Cats, i);
  339. if (!(type & GV_LINES) || !(mask_type & type))
  340. continue;
  341. if (layer > 0) {
  342. if ((type & GV_LINE) &&
  343. !Vect_cats_in_constraint(Cats, layer, cat_list))
  344. continue;
  345. else if ((type & GV_BOUNDARY)) {
  346. int do_line = 0;
  347. int left, right;
  348. do_line = Vect_cats_in_constraint(Cats, layer, cat_list);
  349. if (!do_line) {
  350. /* check if any of the centroids is selected */
  351. Vect_get_line_areas(&Out, i, &left, &right);
  352. if (left > 0) {
  353. Vect_get_area_cats(&Out, left, Cats);
  354. do_line = Vect_cats_in_constraint(Cats, layer, cat_list);
  355. }
  356. else if (left < 0) {
  357. left = Vect_get_isle_area(&Out, abs(left));
  358. if (left > 0) {
  359. Vect_get_area_cats(&Out, left, Cats);
  360. do_line = Vect_cats_in_constraint(Cats, layer, cat_list);
  361. }
  362. }
  363. if (!do_line) {
  364. if (right > 0) {
  365. Vect_get_area_cats(&Out, right, Cats);
  366. do_line = Vect_cats_in_constraint(Cats, layer, cat_list);
  367. }
  368. else if (right < 0) {
  369. right = Vect_get_isle_area(&Out, abs(right));
  370. if (right > 0) {
  371. Vect_get_area_cats(&Out, right, Cats);
  372. do_line = Vect_cats_in_constraint(Cats, layer, cat_list);
  373. }
  374. }
  375. }
  376. }
  377. if (!do_line)
  378. continue;
  379. }
  380. }
  381. Vect_line_prune(APoints);
  382. if (APoints->n_points < 2)
  383. /* Line of length zero, delete if boundary ? */
  384. continue;
  385. total_input += APoints->n_points;
  386. /* copy points */
  387. Vect_reset_line(Points);
  388. Vect_append_points(Points, APoints, GV_FORWARD);
  389. loop_support = 0;
  390. if (loop_support_flag->answer) {
  391. int n1, n2;
  392. Vect_get_line_nodes(&Out, i, &n1, &n2);
  393. if (n1 == n2) {
  394. if (Vect_get_node_n_lines(&Out, n1) == 2) {
  395. if (abs(Vect_get_node_line(&Out, n1, 0)) == i &&
  396. abs(Vect_get_node_line(&Out, n1, 1)) == i)
  397. loop_support = 1;
  398. }
  399. }
  400. }
  401. for (iter = 0; iter < iterations; iter++) {
  402. switch (method) {
  403. case DOUGLAS:
  404. douglas_peucker(Points, thresh, with_z);
  405. break;
  406. case DOUGLAS_REDUCTION:
  407. douglas_peucker_reduction(Points, thresh, reduction,
  408. with_z);
  409. break;
  410. case LANG:
  411. lang(Points, thresh, look_ahead, with_z);
  412. break;
  413. case VERTEX_REDUCTION:
  414. vertex_reduction(Points, thresh, with_z);
  415. break;
  416. case REUMANN:
  417. reumann_witkam(Points, thresh, with_z);
  418. break;
  419. case BOYLE:
  420. boyle(Points, look_ahead, loop_support, with_z);
  421. break;
  422. case SLIDING_AVERAGING:
  423. sliding_averaging(Points, slide, look_ahead, loop_support, with_z);
  424. break;
  425. case DISTANCE_WEIGHTING:
  426. distance_weighting(Points, slide, look_ahead, loop_support, with_z);
  427. break;
  428. case CHAIKEN:
  429. chaiken(Points, thresh, loop_support, with_z);
  430. break;
  431. case HERMITE:
  432. hermite(Points, thresh, angle_thresh, loop_support, with_z);
  433. break;
  434. case SNAKES:
  435. snakes(Points, alpha, beta, loop_support, with_z);
  436. break;
  437. }
  438. }
  439. if (loop_support == 0) {
  440. /* safety check, BUG in method if not passed */
  441. if (APoints->x[0] != Points->x[0] ||
  442. APoints->y[0] != Points->y[0] ||
  443. APoints->z[0] != Points->z[0])
  444. G_fatal_error(_("Method '%s' did not preserve first point"), method_opt->answer);
  445. if (APoints->x[APoints->n_points - 1] != Points->x[Points->n_points - 1] ||
  446. APoints->y[APoints->n_points - 1] != Points->y[Points->n_points - 1] ||
  447. APoints->z[APoints->n_points - 1] != Points->z[Points->n_points - 1])
  448. G_fatal_error(_("Method '%s' did not preserve last point"), method_opt->answer);
  449. }
  450. else {
  451. /* safety check, BUG in method if not passed */
  452. if (Points->x[0] != Points->x[Points->n_points - 1] ||
  453. Points->y[0] != Points->y[Points->n_points - 1] ||
  454. Points->z[0] != Points->z[Points->n_points - 1])
  455. G_fatal_error(_("Method '%s' did not preserve loop"), method_opt->answer);
  456. }
  457. Vect_line_prune(Points);
  458. /* oversimplified line */
  459. if (Points->n_points < 2) {
  460. after = APoints->n_points;
  461. n_oversimplified++;
  462. }
  463. /* check for topology corruption */
  464. else if (type == GV_BOUNDARY) {
  465. if (!check_topo(&Out, i, APoints, Points, Cats)) {
  466. after = APoints->n_points;
  467. not_modified_boundaries++;
  468. }
  469. else
  470. after = Points->n_points;
  471. }
  472. else {
  473. /* type == GV_LINE */
  474. Vect_rewrite_line(&Out, i, type, Points, Cats);
  475. after = Points->n_points;
  476. }
  477. total_output += after;
  478. }
  479. if (not_modified_boundaries > 0)
  480. G_message(_("%d boundaries were not modified because modification would damage topology"),
  481. not_modified_boundaries);
  482. if (n_oversimplified > 0)
  483. G_message(_("%d lines/boundaries were not modified due to over-simplification"),
  484. n_oversimplified);
  485. G_message("-----------------------------------------------------");
  486. /* make sure that clean topo is built at the end */
  487. Vect_build_partial(&Out, GV_BUILD_NONE);
  488. }
  489. Vect_build(&Out);
  490. Vect_close(&In);
  491. Vect_close(&Out);
  492. G_message("-----------------------------------------------------");
  493. if (total_input != 0 && total_input != total_output)
  494. G_done_msg(_("Number of vertices for selected features %s from %d to %d (%d%%)."),
  495. simplification ? _("reduced") : _("changed"),
  496. total_input, total_output,
  497. (total_output * 100) / total_input);
  498. else
  499. G_done_msg(" ");
  500. exit(EXIT_SUCCESS);
  501. }