main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /****************************************************************
  2. *
  3. * MODULE: v.net.iso
  4. *
  5. * AUTHOR(S): Radim Blazek
  6. *
  7. * PURPOSE: Split net to bands between isolines.
  8. *
  9. * COPYRIGHT: (C) 2001 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 <stdlib.h>
  18. #include <string.h>
  19. #include <time.h>
  20. #include <grass/gis.h>
  21. #include <grass/Vect.h>
  22. #include <grass/dbmi.h>
  23. #include <grass/glocale.h>
  24. typedef struct
  25. {
  26. int cat; /* category number */
  27. int node; /* node number */
  28. } CENTER;
  29. typedef struct
  30. {
  31. int centre; /* neares centre, initialy -1 *//* currently not used */
  32. double cost; /* costs from this centre, initialy not undefined */
  33. } NODE;
  34. typedef struct
  35. { /* iso point along the line */
  36. int iso; /* index of iso line in iso array of costs */
  37. double distance; /* distance along the line from the beginning for both directions */
  38. } ISOPOINT;
  39. int main(int argc, char **argv)
  40. {
  41. int i, j, ret, centre, line, centre1, centre2;
  42. int nlines, nnodes, type, ltype, afield, nfield, geo, cat;
  43. int node, node1, node2;
  44. double cost, e1cost, e2cost, n1cost, n2cost, s1cost, s2cost, l, l1;
  45. struct Option *map, *output;
  46. struct Option *afield_opt, *nfield_opt, *afcol, *abcol, *ncol, *type_opt,
  47. *term_opt, *cost_opt;
  48. struct Flag *geo_f;
  49. struct GModule *module;
  50. char *mapset;
  51. struct Map_info Map, Out;
  52. struct cat_list *catlist;
  53. CENTER *Centers = NULL;
  54. int acentres = 0, ncentres = 0;
  55. NODE *Nodes;
  56. struct line_cats *Cats;
  57. struct line_pnts *Points, *SPoints;
  58. int niso, aiso;
  59. double *iso;
  60. int npnts1, apnts1 = 0, npnts2, apnts2 = 0;
  61. ISOPOINT *pnts1 = NULL, *pnts2 = NULL;
  62. int next_iso;
  63. G_gisinit(argv[0]);
  64. module = G_define_module();
  65. module->label = _("Split net by cost isolines");
  66. module->keywords = _("vector, networking");
  67. module->description =
  68. _
  69. ("Splits net to bands between cost isolines (direction from centre). "
  70. "Centre node must be opened (costs >= 0). "
  71. "Costs of centre node are used in calculation.");
  72. map = G_define_standard_option(G_OPT_V_INPUT);
  73. output = G_define_standard_option(G_OPT_V_OUTPUT);
  74. type_opt = G_define_standard_option(G_OPT_V_TYPE);
  75. type_opt->options = "line,boundary";
  76. type_opt->answer = "line,boundary";
  77. type_opt->description = _("Arc type");
  78. afield_opt = G_define_standard_option(G_OPT_V_FIELD);
  79. afield_opt->key = "alayer";
  80. afield_opt->description = _("Arc layer");
  81. nfield_opt = G_define_standard_option(G_OPT_V_FIELD);
  82. nfield_opt->key = "nlayer";
  83. nfield_opt->answer = "2";
  84. nfield_opt->description = _("Node layer");
  85. afcol = G_define_option();
  86. afcol->key = "afcolumn";
  87. afcol->type = TYPE_STRING;
  88. afcol->required = NO;
  89. afcol->description =
  90. _("Arc forward/both direction(s) cost column (number)");
  91. abcol = G_define_option();
  92. abcol->key = "abcolumn";
  93. abcol->type = TYPE_STRING;
  94. abcol->required = NO;
  95. abcol->description = _("Arc backward direction cost column (number)");
  96. ncol = G_define_option();
  97. ncol->key = "ncolumn";
  98. ncol->type = TYPE_STRING;
  99. ncol->required = NO;
  100. ncol->description = _("Node cost column (number)");
  101. term_opt = G_define_standard_option(G_OPT_V_CATS);
  102. term_opt->key = "ccats";
  103. term_opt->required = YES;
  104. term_opt->description =
  105. _("Categories of centres (points on nodes) to which net "
  106. "will be allocated. "
  107. "Layer for this categories is given by nlayer option.");
  108. cost_opt = G_define_option();
  109. cost_opt->key = "costs";
  110. cost_opt->type = TYPE_INTEGER;
  111. cost_opt->multiple = YES;
  112. cost_opt->required = YES;
  113. cost_opt->description = _("Costs for isolines");
  114. geo_f = G_define_flag();
  115. geo_f->key = 'g';
  116. geo_f->description =
  117. _("Use geodesic calculation for longitude-latitude locations");
  118. if (G_parser(argc, argv))
  119. exit(EXIT_FAILURE);
  120. Cats = Vect_new_cats_struct();
  121. Points = Vect_new_line_struct();
  122. SPoints = Vect_new_line_struct();
  123. type = Vect_option_to_types(type_opt);
  124. afield = atoi(afield_opt->answer);
  125. nfield = atoi(nfield_opt->answer);
  126. catlist = Vect_new_cat_list();
  127. Vect_str_to_cat_list(term_opt->answer, catlist);
  128. Vect_check_input_output_name(map->answer, output->answer, GV_FATAL_EXIT);
  129. /* Iso costs */
  130. aiso = 1;
  131. iso = (double *)G_malloc(aiso * sizeof(double));
  132. /* Set first iso to 0 */
  133. iso[0] = 0;
  134. niso = 1;
  135. i = 0;
  136. while (cost_opt->answers[i]) {
  137. if (niso == aiso) {
  138. aiso += 1;
  139. iso = (double *)G_realloc(iso, aiso * sizeof(double));
  140. }
  141. iso[niso] = atof(cost_opt->answers[i]);
  142. if (iso[niso] <= 0)
  143. G_fatal_error(_("Wrong iso cost: %f"), iso[niso]);
  144. if (iso[niso] <= iso[niso - 1])
  145. G_fatal_error(_("Iso cost: %f less than previous"), iso[niso]);
  146. G_message(_("Iso cost [%d] : [%f]"), niso, iso[niso]);
  147. niso++;
  148. i++;
  149. }
  150. /* Should not happen: */
  151. if (niso < 2)
  152. G_warning(_("Not enough costs, everything reachable falls to first band"));
  153. if (geo_f->answer)
  154. geo = 1;
  155. else
  156. geo = 0;
  157. mapset = G_find_vector2(map->answer, NULL);
  158. if (mapset == NULL)
  159. G_fatal_error(_("Vector map <%s> not found"), map->answer);
  160. Vect_set_open_level(2);
  161. Vect_open_old(&Map, map->answer, mapset);
  162. /* Build graph */
  163. Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer,
  164. abcol->answer, ncol->answer, geo, 0);
  165. nnodes = Vect_get_num_nodes(&Map);
  166. /* Create list of centres based on list of categories */
  167. for (node = 1; node <= nnodes; node++) {
  168. nlines = Vect_get_node_n_lines(&Map, node);
  169. for (j = 0; j < nlines; j++) {
  170. line = abs(Vect_get_node_line(&Map, node, j));
  171. ltype = Vect_read_line(&Map, NULL, Cats, line);
  172. if (!(ltype & GV_POINT))
  173. continue;
  174. if (!(Vect_cat_get(Cats, nfield, &cat)))
  175. continue;
  176. if (Vect_cat_in_cat_list(cat, catlist)) {
  177. Vect_net_get_node_cost(&Map, node, &n1cost);
  178. if (n1cost == -1) { /* closed */
  179. G_warning(_("Centre at closed node (costs = -1) ignored"));
  180. }
  181. else {
  182. if (acentres == ncentres) {
  183. acentres += 1;
  184. Centers =
  185. (CENTER *) G_realloc(Centers,
  186. acentres * sizeof(CENTER));
  187. }
  188. Centers[ncentres].cat = cat;
  189. Centers[ncentres].node = node;
  190. G_debug(2, "centre = %d node = %d cat = %d", ncentres,
  191. node, cat);
  192. ncentres++;
  193. }
  194. }
  195. }
  196. }
  197. G_message(_("Number of centres: [%d] (nlayer: [%d])"), ncentres, nfield);
  198. if (ncentres == 0)
  199. G_warning(_("Not enough centres for selected nlayer. Nothing will be allocated."));
  200. /* alloc and reset space for all nodes */
  201. Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE));
  202. for (i = 1; i <= nnodes; i++) {
  203. Nodes[i].centre = -1;
  204. }
  205. apnts1 = 1;
  206. pnts1 = (ISOPOINT *) G_malloc(apnts1 * sizeof(ISOPOINT));
  207. apnts2 = 1;
  208. pnts2 = (ISOPOINT *) G_malloc(apnts2 * sizeof(ISOPOINT));
  209. /* Fill Nodes by neares centre and costs from that centre */
  210. G_message(_("Calculating costs from centres..."));
  211. for (centre = 0; centre < ncentres; centre++) {
  212. G_percent(centre, ncentres, 1);
  213. node1 = Centers[centre].node;
  214. Vect_net_get_node_cost(&Map, node1, &n1cost);
  215. G_debug(2, "centre = %d node = %d cat = %d", centre, node1,
  216. Centers[centre].cat);
  217. for (node2 = 1; node2 <= nnodes; node2++) {
  218. G_debug(5, " node1 = %d node2 = %d", node1, node2);
  219. Vect_net_get_node_cost(&Map, node2, &n2cost);
  220. if (n2cost == -1) {
  221. continue;
  222. } /* closed, left it as not attached */
  223. ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost);
  224. if (ret == -1) {
  225. continue;
  226. } /* node unreachable */
  227. /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but
  228. * only if centre and node are not identical, because at the end node cost is add later */
  229. if (node1 != node2)
  230. cost += n1cost;
  231. G_debug(5,
  232. "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
  233. node1, node2, cost, Nodes[node2].centre,
  234. Nodes[node2].cost);
  235. if (Nodes[node2].centre == -1 || cost < Nodes[node2].cost) {
  236. Nodes[node2].cost = cost;
  237. Nodes[node2].centre = centre;
  238. }
  239. }
  240. }
  241. G_percent(1, 1, 1);
  242. /* Write arcs to new map */
  243. Vect_open_new(&Out, output->answer, Vect_is_3d(&Map));
  244. Vect_hist_command(&Out);
  245. nlines = Vect_get_num_lines(&Map);
  246. for (line = 1; line <= nlines; line++) {
  247. ltype = Vect_read_line(&Map, Points, NULL, line);
  248. if (!(ltype & type)) {
  249. continue;
  250. }
  251. Vect_get_line_nodes(&Map, line, &node1, &node2);
  252. centre1 = Nodes[node1].centre;
  253. centre2 = Nodes[node2].centre;
  254. s1cost = Nodes[node1].cost;
  255. s2cost = Nodes[node2].cost;
  256. l = Vect_line_length(Points);
  257. if (l == 0)
  258. continue;
  259. G_debug(3, "Line %d : length = %f", line, l);
  260. G_debug(3, "Arc centres: %d %d (nodes: %d %d)", centre1, centre2,
  261. node1, node2);
  262. Vect_net_get_node_cost(&Map, node1, &n1cost);
  263. Vect_net_get_node_cost(&Map, node2, &n2cost);
  264. Vect_net_get_line_cost(&Map, line, GV_FORWARD, &e1cost);
  265. Vect_net_get_line_cost(&Map, line, GV_BACKWARD, &e2cost);
  266. G_debug(3, " s1cost = %f n1cost = %f e1cost = %f", s1cost, n1cost,
  267. e1cost);
  268. G_debug(3, " s2cost = %f n2cost = %f e2cost = %f", s2cost, n2cost,
  269. e2cost);
  270. /* First check if arc is reachable from at least one side */
  271. if ((centre1 != -1 && n1cost != -1 && e1cost != -1) ||
  272. (centre2 != -1 && n2cost != -1 && e2cost != -1)) {
  273. /* Line is reachable at least from one side */
  274. G_debug(3, " -> arc is reachable");
  275. /* Add costs of node to starting costs */
  276. s1cost += n1cost;
  277. s2cost += n2cost;
  278. e1cost /= l;
  279. e2cost /= l;
  280. /* Find points on isolines along the line in both directions, add them to array,
  281. * first point is placed at the beginning/end of line */
  282. /* Forward */
  283. npnts1 = 0; /* in case this direction is closed */
  284. if (centre1 != -1 && n1cost != -1 && e1cost != -1) {
  285. /* Find iso for beginning of the line */
  286. next_iso = 0;
  287. for (i = niso - 1; i >= 0; i--) {
  288. if (iso[i] <= s1cost) {
  289. next_iso = i;
  290. break;
  291. }
  292. }
  293. /* Add first */
  294. pnts1[0].iso = next_iso;
  295. pnts1[0].distance = 0;
  296. npnts1++;
  297. next_iso++;
  298. /* Calculate distances for points along line */
  299. while (next_iso < niso) {
  300. if (e1cost == 0)
  301. break; /* Outside line */
  302. l1 = (iso[next_iso] - s1cost) / e1cost;
  303. if (l1 >= l)
  304. break; /* Outside line */
  305. if (npnts1 == apnts1) {
  306. apnts1 += 1;
  307. pnts1 =
  308. (ISOPOINT *) G_realloc(pnts1,
  309. apnts1 * sizeof(ISOPOINT));
  310. }
  311. pnts1[npnts1].iso = next_iso;
  312. pnts1[npnts1].distance = l1;
  313. G_debug(3,
  314. " forward %d : iso %d : distance %f : cost %f",
  315. npnts1, next_iso, l1, iso[next_iso]);
  316. npnts1++;
  317. next_iso++;
  318. }
  319. }
  320. G_debug(3, " npnts1 = %d", npnts1);
  321. /* Backward */
  322. npnts2 = 0;
  323. if (centre2 != -1 && n2cost != -1 && e2cost != -1) {
  324. /* Find iso for beginning of the line */
  325. next_iso = 0;
  326. for (i = niso - 1; i >= 0; i--) {
  327. if (iso[i] <= s2cost) {
  328. next_iso = i;
  329. break;
  330. }
  331. }
  332. /* Add first */
  333. pnts2[0].iso = next_iso;
  334. pnts2[0].distance = l;
  335. npnts2++;
  336. next_iso++;
  337. /* Calculate distances for points along line */
  338. while (next_iso < niso) {
  339. if (e2cost == 0)
  340. break; /* Outside line */
  341. l1 = (iso[next_iso] - s2cost) / e2cost;
  342. if (l1 >= l)
  343. break; /* Outside line */
  344. if (npnts2 == apnts2) {
  345. apnts2 += 1;
  346. pnts2 =
  347. (ISOPOINT *) G_realloc(pnts2,
  348. apnts2 * sizeof(ISOPOINT));
  349. }
  350. pnts2[npnts2].iso = next_iso;
  351. pnts2[npnts2].distance = l - l1;
  352. G_debug(3,
  353. " backward %d : iso %d : distance %f : cost %f",
  354. npnts2, next_iso, l - l1, iso[next_iso]);
  355. npnts2++;
  356. next_iso++;
  357. }
  358. }
  359. G_debug(3, " npnts2 = %d", npnts2);
  360. /* Limit number of points by maximum costs in reverse direction, this may remove
  361. * also the first point in one direction, but not in both */
  362. /* Forward */
  363. if (npnts2 > 0) {
  364. for (i = 0; i < npnts1; i++) {
  365. G_debug(3,
  366. " pnt1 = %d dist1 = %f iso1 = %d max iso2 = %d",
  367. i, pnts1[i].distance, pnts1[i].iso,
  368. pnts2[npnts2 - 1].iso);
  369. if (pnts2[npnts2 - 1].iso < pnts1[i].iso) {
  370. G_debug(3, " -> cut here");
  371. npnts1 = i;
  372. break;
  373. }
  374. }
  375. }
  376. G_debug(3, " npnts1 cut = %d", npnts1);
  377. /* Backward */
  378. if (npnts1 > 0) {
  379. for (i = 0; i < npnts2; i++) {
  380. G_debug(3,
  381. " pnt2 = %d dist2 = %f iso2 = %d max iso1 = %d",
  382. i, pnts2[i].distance, pnts2[i].iso,
  383. pnts1[npnts1 - 1].iso);
  384. if (pnts1[npnts1 - 1].iso < pnts2[i].iso) {
  385. G_debug(3, " -> cut here");
  386. npnts2 = i;
  387. break;
  388. }
  389. }
  390. }
  391. G_debug(3, " npnts2 cut = %d", npnts2);
  392. /* Biggest cost shoud be equal if exist (npnts > 0). Cut out overlaping segments,
  393. * this can cut only points on line but not first points */
  394. if (npnts1 > 1 && npnts2 > 1) {
  395. while (npnts1 > 1 && npnts2 > 1) {
  396. if (pnts1[npnts1 - 1].distance >= pnts2[npnts2 - 1].distance) { /* overlap */
  397. npnts1--;
  398. npnts2--;
  399. }
  400. else {
  401. break;
  402. }
  403. }
  404. }
  405. G_debug(3, " npnts1 2. cut = %d", npnts1);
  406. G_debug(3, " npnts2 2. cut = %d", npnts2);
  407. /* Now we have points in both directions which may not overlap, npoints in one
  408. * direction may be 0 but not both */
  409. /* Join both arrays, iso of point is for next segment (point is at the beginning) */
  410. /* In case npnts1 == 0 add point at distance 0 */
  411. if (npnts1 == 0) {
  412. G_debug(3,
  413. " npnts1 = 0 -> add first at distance 0, cat = %d",
  414. pnts2[npnts2 - 1].iso);
  415. pnts1[0].iso = pnts2[npnts2 - 1].iso; /* use last point iso in reverse direction */
  416. pnts1[0].distance = 0;
  417. npnts1++;
  418. }
  419. for (i = npnts2 - 1; i >= 0; i--) {
  420. /* Check if identical */
  421. if (pnts1[npnts1 - 1].distance == pnts2[i].distance)
  422. continue;
  423. if (npnts1 == apnts1) {
  424. apnts1 += 1;
  425. pnts1 =
  426. (ISOPOINT *) G_realloc(pnts1,
  427. apnts1 * sizeof(ISOPOINT));
  428. }
  429. pnts1[npnts1].iso = pnts2[i].iso - 1; /* last may be -1, but it is not used */
  430. pnts1[npnts1].distance = pnts2[i].distance;
  431. npnts1++;
  432. }
  433. /* In case npnts2 == 0 add point at the end */
  434. if (npnts2 == 0) {
  435. pnts1[npnts1].iso = 0; /* not used */
  436. pnts1[npnts1].distance = l;
  437. npnts1++;
  438. }
  439. /* Create line segments. */
  440. for (i = 1; i < npnts1; i++) {
  441. cat = pnts1[i - 1].iso + 1;
  442. G_debug(3, " segment %f - %f cat %d", pnts1[i - 1].distance,
  443. pnts1[i].distance, cat);
  444. ret =
  445. Vect_line_segment(Points, pnts1[i - 1].distance,
  446. pnts1[i].distance, SPoints);
  447. if (ret == 0) {
  448. G_warning(_("Cannot get line segment, segment out of line"));
  449. }
  450. else {
  451. Vect_reset_cats(Cats);
  452. Vect_cat_set(Cats, 1, cat);
  453. Vect_write_line(&Out, ltype, SPoints, Cats);
  454. }
  455. }
  456. }
  457. else {
  458. /* arc is not reachable */
  459. G_debug(3, " -> arc is not reachable");
  460. Vect_reset_cats(Cats);
  461. Vect_write_line(&Out, ltype, Points, Cats);
  462. }
  463. }
  464. Vect_build(&Out, stderr);
  465. /* Free, ... */
  466. G_free(Nodes);
  467. G_free(Centers);
  468. Vect_close(&Map);
  469. Vect_close(&Out);
  470. exit(EXIT_SUCCESS);
  471. }