main.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /****************************************************************
  2. *
  3. * MODULE: v.net.steiner
  4. *
  5. * AUTHOR(S): Radim Blazek
  6. *
  7. * PURPOSE: Find Steiner tree for network
  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/vector.h>
  22. #include <grass/dbmi.h>
  23. #include <grass/glocale.h>
  24. /* costs between 2 terminals */
  25. typedef struct
  26. {
  27. int term1, term2;
  28. double cost;
  29. } COST;
  30. int nterms; /* number of terminals */
  31. int nnodes; /* number of nodes */
  32. int *terms; /* array of 1. terminals; 2. accepted steiner points; 3. tested steiner point */
  33. COST *term_costs; /* costs between terminals */
  34. COST *sp_costs; /* costs between StP and terminals */
  35. int *comps; /* numbers of component, the terminal was assigned to */
  36. /* pointer to array of pointers to costs to other nodes, matrix: from-to, from < to:
  37. * 1-2 1-3 1-4 ... 1-nnodes
  38. * 2-3 2-4 ... 2-nnodes
  39. * 3-4 ... 3-nnodes
  40. * ...
  41. * (nnodes - 1)-nnodes
  42. * !!! init costs for from or to node, before this cost is read by init_node_costs();
  43. */
  44. double **nodes_costs;
  45. int cmp(const void *, const void *);
  46. /* Init all costs to/from given node */
  47. int init_node_costs(struct Map_info *Map, int from)
  48. {
  49. int to, ret, row, col;
  50. double cost;
  51. G_message(_("Init costs from node %d"), from);
  52. for (to = 1; to <= nnodes; to++) {
  53. if (from == to)
  54. continue;
  55. ret = Vect_net_shortest_path(Map, from, to, NULL, &cost);
  56. if (ret == -1) {
  57. /* G_warning ( "Destination node %d is unreachable from node %d\n", to, from); */
  58. cost = -2;
  59. }
  60. if (from < to) {
  61. row = from - 1;
  62. col = to - from - 1;
  63. }
  64. else {
  65. row = to - 1;
  66. col = from - to - 1;
  67. }
  68. G_debug(3, "init costs %d - > %d = %f\n", from, to, cost);
  69. nodes_costs[row][col] = cost;
  70. }
  71. return 1;
  72. }
  73. /* Get cost from node to node.
  74. * From or to costs must be init before!!!
  75. * Returns: 1 - ok, 0 - not reachable
  76. */
  77. int get_node_costs(int from, int to, double *cost)
  78. {
  79. int row, col, tmp;
  80. if (from == to) {
  81. *cost = 0;
  82. return 1;
  83. }
  84. if (from > to) {
  85. tmp = from;
  86. from = to;
  87. to = tmp;
  88. }
  89. row = from - 1;
  90. col = to - from - 1;
  91. if (nodes_costs[row][col] == -2) {
  92. *cost = PORT_DOUBLE_MAX;
  93. return 0;
  94. }
  95. *cost = nodes_costs[row][col];
  96. return 1;
  97. }
  98. /* Calculate costs for MST on given set of terminals.
  99. * If AList / NList is not NULL, list of arcs / nodes in MST is created
  100. * Note: qsort() from more (say >30) terminals, takes long time (most of mst()).
  101. * To improve the speed, there are used two sorted queues of costs:
  102. * 1. for all combinations of in trms
  103. * 2. from 'sp' to all other terminals
  104. * Because 1. is sorted only if new sp as added to list of terminals,
  105. * and 2. is much shorter than 1., a lot of time is saved.
  106. */
  107. int mst(struct Map_info *Map, int *trms, int ntrms, /* array of terminal, number of terminals */
  108. double *cst, double max_cst, /* cost, maximum cost */
  109. struct ilist *AList, struct ilist *NList, /* list of arcs/nodes in ST */
  110. int sp, /* Steiner point (node) to be tested with terminals, (0 = ignore) */
  111. int rebuild)
  112. { /* rebuild the sorted list of costs for terminals */
  113. int i, j, node1, node2, ret, com1, com2, t1, t2, line;
  114. static int k;
  115. int tcpos, scpos; /* current position in the term_costs / sp_costs */
  116. double tcst;
  117. struct ilist *List;
  118. int nsteps, quse;
  119. int nall; /* number of terminals + sp ( if used ) */
  120. if (AList != NULL) {
  121. Vect_reset_list(AList);
  122. }
  123. List = Vect_new_list();
  124. /* Create sorted array for all combinations of terms */
  125. if (rebuild) {
  126. k = 0;
  127. for (i = 0; i < ntrms; i++) {
  128. for (j = i + 1; j < ntrms; j++) {
  129. term_costs[k].term1 = i;
  130. term_costs[k].term2 = j;
  131. ret = get_node_costs(trms[i], trms[j], &tcst);
  132. term_costs[k].cost = tcst;
  133. k++;
  134. }
  135. }
  136. qsort((void *)term_costs, k, sizeof(COST), cmp); /* this takes most of a time in mst() */
  137. for (i = 0; i < k; i++) {
  138. G_debug(3, " %d - %d cost = %f\n", term_costs[i].term1,
  139. term_costs[i].term2, term_costs[i].cost);
  140. }
  141. }
  142. /* Create sorted array for all combinations of sp -> terms */
  143. if (sp > 0) {
  144. for (i = 0; i < ntrms; i++) {
  145. sp_costs[i].term1 = -1; /* not needed */
  146. sp_costs[i].term2 = i;
  147. ret = get_node_costs(sp, trms[i], &tcst);
  148. sp_costs[i].cost = tcst;
  149. }
  150. qsort((void *)sp_costs, ntrms, sizeof(COST), cmp);
  151. for (i = 0; i < ntrms; i++) {
  152. G_debug(3, " %d - %d cost = %f\n", sp_costs[i].term1,
  153. sp_costs[i].term2, sp_costs[i].cost);
  154. }
  155. }
  156. tcst = 0;
  157. /* MST has number_of_terminals-1 arcs */
  158. if (sp > 0) {
  159. nall = ntrms + 1;
  160. nsteps = ntrms; /* i.e. + one StP */
  161. }
  162. else {
  163. nall = ntrms;
  164. nsteps = ntrms - 1;
  165. }
  166. G_debug(1, "nall = %d\n", nall);
  167. for (i = 0; i < nall; i++)
  168. comps[i] = 0;
  169. tcpos = 0;
  170. scpos = 0;
  171. G_debug(2, "nsteps = %d\n", nsteps);
  172. for (i = 0; i < nsteps; i++) {
  173. G_debug(2, "step = %d\n", i);
  174. /* Take the best (lowest costs, no cycle) from both queues */
  175. /* For both queues go to next lowest costs without cycle */
  176. /* treminal costs */
  177. for (j = tcpos; j < k; j++) {
  178. t1 = term_costs[j].term1;
  179. t2 = term_costs[j].term2;
  180. com1 = comps[t1];
  181. com2 = comps[t2];
  182. if (com1 != com2 || com1 == 0) { /* will not create cycle -> candidate */
  183. tcpos = j;
  184. break;
  185. }
  186. }
  187. if (j == k) { /* arc without cycle not found */
  188. tcpos = -1;
  189. }
  190. /* StP costs */
  191. if (sp > 0) {
  192. for (j = scpos; j < ntrms; j++) {
  193. t1 = ntrms; /* StP is on first fre position */
  194. t2 = sp_costs[j].term2;
  195. com1 = comps[t1];
  196. com2 = comps[t2];
  197. G_debug(3, "scpos: j = %d comps(%d) = %d coms(%d) = %d\n", j,
  198. t1, com1, t2, com2);
  199. if (com1 != com2 || com1 == 0) { /* will not create cycle -> candidate */
  200. scpos = j;
  201. G_debug(3, " ok -> scpos = %d\n", scpos);
  202. break;
  203. }
  204. }
  205. if (j == ntrms) { /* arc without cycle not found */
  206. scpos = -1;
  207. }
  208. }
  209. else {
  210. scpos = -1;
  211. }
  212. G_debug(3, "tcpos = %d, scpos = %d\n", tcpos, scpos);
  213. G_debug(3, "tcost = %f, scost = %f\n", term_costs[tcpos].cost,
  214. sp_costs[scpos].cost);
  215. /* Now we have positions set on lowest costs in each queue or -1 if no more/not used */
  216. if (tcpos >= 0 && scpos >= 0) {
  217. if (term_costs[tcpos].cost < sp_costs[scpos].cost)
  218. quse = 1; /* use terms queue */
  219. else
  220. quse = 2; /* use sp queue */
  221. }
  222. else if (tcpos >= 0) {
  223. quse = 1; /* use terms queue */
  224. }
  225. else {
  226. quse = 2; /* use sp queue */
  227. }
  228. /* Now we know from which queue take next arc -> add arc to components */
  229. if (quse == 1) {
  230. t1 = term_costs[tcpos].term1;
  231. t2 = term_costs[tcpos].term2;
  232. tcst += term_costs[tcpos].cost;
  233. tcpos++;
  234. }
  235. else {
  236. t1 = ntrms;
  237. t2 = sp_costs[scpos].term2;
  238. tcst += sp_costs[scpos].cost;
  239. scpos++;
  240. }
  241. G_debug(3, "quse = %d t1 = %d t2 = %d\n", quse, t1, t2);
  242. G_debug(3, "tcst = %f (max = %f)\n", tcst, max_cst);
  243. com1 = comps[t1];
  244. com2 = comps[t2];
  245. comps[t1] = i + 1;
  246. comps[t2] = i + 1;
  247. G_debug(3, "comps(%d) = %d coms(%d) = %d\n", t1, i + 1, t2, i + 1);
  248. /* reset connected branches */
  249. for (j = 0; j < nall; j++) {
  250. if (comps[j] == com1 && com1 != 0)
  251. comps[j] = i + 1;
  252. if (comps[j] == com2 && com2 != 0)
  253. comps[j] = i + 1;
  254. }
  255. if (tcst > max_cst) {
  256. G_debug(3, "cost > max -> return\n");
  257. *cst = PORT_DOUBLE_MAX;
  258. return 1;
  259. }
  260. /* add to list of arcs */
  261. if (AList != NULL) {
  262. node1 = trms[t1];
  263. node2 = trms[t2];
  264. ret = Vect_net_shortest_path(Map, node1, node2, List, NULL);
  265. for (j = 0; j < List->n_values; j++) {
  266. Vect_list_append(AList, abs(List->value[j]));
  267. }
  268. }
  269. }
  270. /* create list of nodes */
  271. if (NList != NULL) {
  272. Vect_reset_list(NList);
  273. for (i = 0; i < AList->n_values; i++) {
  274. line = AList->value[i];
  275. Vect_get_line_nodes(Map, line, &node1, &node2);
  276. Vect_list_append(NList, node1);
  277. Vect_list_append(NList, node2);
  278. }
  279. }
  280. *cst = tcst;
  281. Vect_destroy_list(List);
  282. return 1;
  283. }
  284. int main(int argc, char **argv)
  285. {
  286. int i, j, k, ret;
  287. int nlines, type, ltype, afield, tfield, geo, cat;
  288. int sp, nsp, nspused, node, line;
  289. struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *type_opt,
  290. *term_opt, *nsp_opt;
  291. struct Flag *geo_f;
  292. struct GModule *module;
  293. struct Map_info Map, Out;
  294. int *testnode; /* array all nodes: 1 - should be tested as Steiner,
  295. * 0 - no need to test (unreachable or terminal) */
  296. struct ilist *TList; /* list of terminal nodes */
  297. struct ilist *StArcs; /* list of arcs on Steiner tree */
  298. struct ilist *StNodes; /* list of nodes on Steiner tree */
  299. double cost, tmpcost;
  300. struct cat_list *Clist;
  301. struct line_cats *Cats;
  302. struct line_pnts *Points;
  303. /* Initialize the GIS calls */
  304. G_gisinit(argv[0]);
  305. module = G_define_module();
  306. G_add_keyword(_("vector"));
  307. G_add_keyword(_("networking"));
  308. module->label =
  309. _("Create Steiner tree for the network and given terminals");
  310. module->description =
  311. _("Note that 'Minimum Steiner Tree' problem is NP-hard "
  312. "and heuristic algorithm is used in this module so "
  313. "the result may be sub optimal");
  314. map = G_define_standard_option(G_OPT_V_INPUT);
  315. output = G_define_standard_option(G_OPT_V_OUTPUT);
  316. type_opt = G_define_standard_option(G_OPT_V_TYPE);
  317. type_opt->options = "line,boundary";
  318. type_opt->answer = "line,boundary";
  319. type_opt->description = _("Arc type");
  320. afield_opt = G_define_standard_option(G_OPT_V_FIELD);
  321. afield_opt->key = "alayer";
  322. afield_opt->answer = "1";
  323. afield_opt->description = _("Arc layer");
  324. tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
  325. tfield_opt->key = "nlayer";
  326. tfield_opt->answer = "2";
  327. tfield_opt->description = _("Node layer (used for terminals)");
  328. afcol = G_define_option();
  329. afcol->key = "acolumn";
  330. afcol->type = TYPE_STRING;
  331. afcol->required = NO;
  332. afcol->description = _("Arcs' cost column (for both directions)");
  333. term_opt = G_define_standard_option(G_OPT_V_CATS);
  334. term_opt->key = "tcats";
  335. term_opt->required = YES;
  336. term_opt->description =
  337. _("Categories of points on terminals (layer is specified by nlayer)");
  338. nsp_opt = G_define_option();
  339. nsp_opt->key = "nsp";
  340. nsp_opt->type = TYPE_INTEGER;
  341. nsp_opt->required = NO;
  342. nsp_opt->multiple = NO;
  343. nsp_opt->answer = "-1";
  344. nsp_opt->description = _("Number of steiner points (-1 for all possible)");
  345. geo_f = G_define_flag();
  346. geo_f->key = 'g';
  347. geo_f->description =
  348. _("Use geodesic calculation for longitude-latitude locations");
  349. if (G_parser(argc, argv))
  350. exit(EXIT_FAILURE);
  351. Cats = Vect_new_cats_struct();
  352. Points = Vect_new_line_struct();
  353. type = Vect_option_to_types(type_opt);
  354. afield = atoi(afield_opt->answer);
  355. TList = Vect_new_list();
  356. StArcs = Vect_new_list();
  357. StNodes = Vect_new_list();
  358. Clist = Vect_new_cat_list();
  359. tfield = atoi(tfield_opt->answer);
  360. Vect_str_to_cat_list(term_opt->answer, Clist);
  361. G_debug(1, "Imput categories:\n");
  362. for (i = 0; i < Clist->n_ranges; i++) {
  363. G_debug(1, "%d - %d\n", Clist->min[i], Clist->max[i]);
  364. }
  365. if (geo_f->answer)
  366. geo = 1;
  367. else
  368. geo = 0;
  369. Vect_check_input_output_name(map->answer, output->answer, GV_FATAL_EXIT);
  370. Vect_set_open_level(2);
  371. Vect_open_old(&Map, map->answer, "");
  372. nnodes = Vect_get_num_nodes(&Map);
  373. /* Create list of terminals based on list of categories */
  374. for (i = 1; i <= nnodes; i++) {
  375. nlines = Vect_get_node_n_lines(&Map, i);
  376. for (j = 0; j < nlines; j++) {
  377. line = abs(Vect_get_node_line(&Map, i, j));
  378. ltype = Vect_read_line(&Map, NULL, Cats, line);
  379. if (!(ltype & GV_POINT))
  380. continue;
  381. if (!(Vect_cat_get(Cats, tfield, &cat)))
  382. continue;
  383. if (Vect_cat_in_cat_list(cat, Clist)) {
  384. Vect_list_append(TList, i);
  385. }
  386. }
  387. }
  388. nterms = TList->n_values;
  389. fprintf(stdout, "Number of terminals: %d\n", nterms);
  390. if (nterms < 2)
  391. G_fatal_error(_("Not enough terminals (< 2)"));
  392. /* Number of steiner points */
  393. nsp = atoi(nsp_opt->answer);
  394. if (nsp > nterms - 2) {
  395. nsp = nterms - 2;
  396. G_warning(_("Requested number of Steiner points > than possible"));
  397. }
  398. else if (nsp == -1) {
  399. nsp = nterms - 2;
  400. }
  401. fprintf(stdout, "Number of Steiner points set to %d\n", nsp);
  402. testnode = (int *)G_malloc((nnodes + 1) * sizeof(int));
  403. for (i = 1; i <= nnodes; i++)
  404. testnode[i] = 1;
  405. /* Alloc arrays of costs for nodes, first node at 1 (0 not used) */
  406. nodes_costs = (double **)G_malloc((nnodes - 1) * sizeof(double *));
  407. for (i = 0; i < nnodes; i++) {
  408. nodes_costs[i] =
  409. (double *)G_malloc((nnodes - i - 1) * sizeof(double));
  410. for (j = 0; j < nnodes - i - 1; j++)
  411. nodes_costs[i][j] = -1; /* init, i.e. cost was not calculated yet */
  412. }
  413. /* alloc memory from each to each other (not directed) terminal */
  414. i = nterms + nterms - 2; /* max number of terms + Steiner points */
  415. comps = (int *)G_malloc(i * sizeof(int));
  416. i = i * (i - 1) / 2; /* number of combinations */
  417. term_costs = (COST *) G_malloc(i * sizeof(COST));
  418. /* alloc memory for costs from Stp to each other terminal */
  419. i = nterms + nterms - 2 - 1; /* max number of terms + Steiner points - 1 */
  420. sp_costs = (COST *) G_malloc(i * sizeof(COST));
  421. terms = (int *)G_malloc((nterms + nterms - 2) * sizeof(int)); /* i.e. +(nterms - 2) St Points */
  422. /* Create initial parts from list of terminals */
  423. G_debug(1, "List of terminal nodes (%d):\n", nterms);
  424. for (i = 0; i < nterms; i++) {
  425. G_debug(1, "%d\n", TList->value[i]);
  426. terms[i] = TList->value[i];
  427. testnode[terms[i]] = 0; /* do not test as Steiner */
  428. }
  429. /* Build graph */
  430. Vect_net_build_graph(&Map, type, afield, 0, afcol->answer, NULL, NULL,
  431. geo, 0);
  432. /* Init costs for all terminals */
  433. for (i = 0; i < nterms; i++)
  434. init_node_costs(&Map, terms[i]);
  435. /* Test if all terminal may be connected */
  436. for (i = 1; i < nterms; i++) {
  437. ret = get_node_costs(terms[0], terms[i], &cost);
  438. if (ret == 0) {
  439. G_fatal_error(_("Terminal at node [%d] cannot be connected "
  440. "to terminal at node [%d]"), terms[0], terms[i]);
  441. }
  442. }
  443. /* Remove not reachable from list of SP candidates */
  444. j = 0;
  445. for (i = 1; i <= nnodes; i++) {
  446. ret = get_node_costs(terms[0], i, &cost);
  447. if (ret == 0) {
  448. testnode[i] = 0;
  449. /* fprintf (stderr, "node %d removed from list of Steiner point candidates\n", i ); */
  450. j++;
  451. }
  452. }
  453. G_message(_("[%d] (not reachable) nodes removed from list "
  454. "of Steiner point candidates"), j);
  455. /* calc costs for terminals MST */
  456. ret = mst(&Map, terms, nterms, &cost, PORT_DOUBLE_MAX, NULL, NULL, 0, 1); /* no StP, rebuild */
  457. G_message(_("MST costs = %f"), cost);
  458. /* Go through all nodes and try to use as steiner points -> find that which saves most costs */
  459. nspused = 0;
  460. for (j = 0; j < nsp; j++) {
  461. sp = 0;
  462. G_message(_("Search for [%d]. Steiner point"), j + 1);
  463. for (i = 1; i <= nnodes; i++) {
  464. G_percent(i, nnodes, 1);
  465. if (testnode[i] == 0) {
  466. G_debug(3, "skip test for %d\n", i);
  467. continue;
  468. }
  469. ret =
  470. mst(&Map, terms, nterms + j, &tmpcost, cost, NULL, NULL, i,
  471. 0);
  472. G_debug(2, "cost = %f x %f\n", tmpcost, cost);
  473. if (tmpcost < cost) { /* sp candidate */
  474. G_debug(3,
  475. " steiner candidate node = %d mst = %f (x last = %f)\n",
  476. i, tmpcost, cost);
  477. sp = i;
  478. cost = tmpcost;
  479. }
  480. }
  481. if (sp > 0) {
  482. G_message(_("Steiner point at node [%d] was added "
  483. "to terminals (MST costs = %f)"), sp, cost);
  484. terms[nterms + j] = sp;
  485. init_node_costs(&Map, sp);
  486. testnode[sp] = 0;
  487. nspused++;
  488. /* rebuild for nex cycle */
  489. ret =
  490. mst(&Map, terms, nterms + nspused, &tmpcost, PORT_DOUBLE_MAX,
  491. NULL, NULL, 0, 1);
  492. }
  493. else { /* no steiner found */
  494. G_message(_("No Steiner point found -> leaving cycle"));
  495. break;
  496. }
  497. }
  498. fprintf(stdout, "\nNumber of added Steiner points: %d "
  499. "(theoretic max is %d).\n", nspused, nterms - 2);
  500. /* Build lists of arcs and nodes for final version */
  501. ret =
  502. mst(&Map, terms, nterms + nspused, &cost, PORT_DOUBLE_MAX, StArcs,
  503. StNodes, 0, 0);
  504. /* Calculate true costs, which may be lower than MST if steiner points were not used */
  505. if (nsp < nterms - 2) {
  506. fprintf(stdout, "\nSpanning tree costs on complet graph = %f\n"
  507. "(may be higher than resulting Steiner tree costs!!!)\n",
  508. cost);
  509. }
  510. else
  511. fprintf(stdout, "\nSteiner tree costs = %f\n", cost);
  512. /* Write arcs to new map */
  513. Vect_open_new(&Out, output->answer, Vect_is_3d(&Map));
  514. Vect_hist_command(&Out);
  515. fprintf(stdout, "\nSteiner tree:\n");
  516. fprintf(stdout, "Arcs' categories (layer %d, %d arcs):\n", afield,
  517. StArcs->n_values);
  518. for (i = 0; i < StArcs->n_values; i++) {
  519. line = StArcs->value[i];
  520. ltype = Vect_read_line(&Map, Points, Cats, line);
  521. Vect_write_line(&Out, ltype, Points, Cats);
  522. Vect_cat_get(Cats, afield, &cat);
  523. if (i > 0)
  524. printf(",");
  525. fprintf(stdout, "%d", cat);
  526. }
  527. fprintf(stdout, "\n\n");
  528. fprintf(stdout, "Nodes' categories (layer %d, %d nodes):\n", tfield,
  529. StNodes->n_values);
  530. k = 0;
  531. for (i = 0; i < StNodes->n_values; i++) {
  532. node = StNodes->value[i];
  533. nlines = Vect_get_node_n_lines(&Map, node);
  534. for (j = 0; j < nlines; j++) {
  535. line = abs(Vect_get_node_line(&Map, node, j));
  536. ltype = Vect_read_line(&Map, Points, Cats, line);
  537. if (!(ltype & GV_POINT))
  538. continue;
  539. if (!(Vect_cat_get(Cats, tfield, &cat)))
  540. continue;
  541. Vect_write_line(&Out, ltype, Points, Cats);
  542. if (k > 0)
  543. fprintf(stdout, ",");
  544. fprintf(stdout, "%d", cat);
  545. k++;
  546. }
  547. }
  548. fprintf(stdout, "\n\n");
  549. Vect_build(&Out);
  550. /* Free, ... */
  551. Vect_destroy_list(StArcs);
  552. Vect_destroy_list(StNodes);
  553. Vect_close(&Map);
  554. Vect_close(&Out);
  555. exit(EXIT_SUCCESS);
  556. }
  557. int cmp(const void *pa, const void *pb)
  558. {
  559. COST *p1 = (COST *) pa;
  560. COST *p2 = (COST *) pb;
  561. if (p1->cost < p2->cost)
  562. return -1;
  563. if (p1->cost > p2->cost)
  564. return 1;
  565. return 0;
  566. }