GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-f22b1278f8
net_build.c
Go to the documentation of this file.
1 /*!
2  * \file lib/vector/Vlib/net_build.c
3  *
4  * \brief Vector library - related fns for vector network building
5  *
6  * Higher level functions for reading/writing/manipulating vectors.
7  *
8  * (C) 2001-2009, 2014 by the GRASS Development Team
9  *
10  * This program is free software under the GNU General Public License
11  * (>=v2). Read the file COPYING that comes with GRASS for details.
12  *
13  * \author Radim Blazek
14  * \author Stepan Turek stepan.turek seznam.cz (turns support)
15  */
16 
17 #include <grass/dbmi.h>
18 #include <grass/vector.h>
19 #include <grass/glocale.h>
20 
21 /*!
22  \brief Build network graph with turntable.
23 
24  Internal format for edge costs is integer, costs are multiplied
25  before conversion to int by 1000 and for lengths LL without geo flag by
26  1000000. The same multiplication factor is used for nodes. Costs in database
27  column may be 'integer' or 'double precision' number >= 0 or -1 for infinity
28  i.e. arc or node is closed and cannot be traversed If record in table is not
29  found for arcs, costs for arc are set to 0. If record in table is not found
30  for node, costs for node are set to 0.
31 
32  \param Map vector map
33  \param ltype line type for arcs
34  \param afield arc costs field (if 0, use length)
35  \param nfield node costs field (if 0, do not use node costs)
36  \param tfield field where turntable is attached
37  \param tucfield field with unique categories used in the turntable
38  \param afcol column with forward costs for arc
39  \param abcol column with backward costs for arc (if NULL, back costs =
40  forward costs)
41  \param ncol column with costs for nodes (if NULL, do not use
42  node costs)
43  \param geo use geodesic calculation for length (LL)
44  \param algorithm not used (in future code for algorithm)
45 
46  \return 0 on success, 1 on error
47  */
48 
49 int Vect_net_ttb_build_graph(struct Map_info *Map, int ltype, int afield,
50  int nfield, int tfield, int tucfield,
51  const char *afcol, const char *abcol,
52  const char *ncol, int geo, int algorithm UNUSED)
53 {
54  /* TODO very long function, split into smaller ones */
55  int i, j, from, to, line, nlines, nnodes, ret, type, cat, skipped, cfound;
56  struct line_pnts *Points;
57  struct line_cats *Cats;
58  double dcost, bdcost, ll;
59  int cost, bcost;
60  dglGraph_s *gr;
61  dglInt32_t opaqueset[16] = {360000, 0, 0, 0, 0, 0, 0, 0,
62  0, 0, 0, 0, 0, 0, 0, 0};
63  struct field_info *Fi = NULL;
64  dbDriver *driver = NULL;
65  dbDriver *ttbdriver = NULL;
66  dbHandle handle;
67  dbString stmt;
68  dbColumn *Column = NULL;
69  dbCatValArray fvarr, bvarr;
70  int fctype = 0, bctype = 0, nrec, nturns;
71 
72  int ln_cat, nnode_lns, i_line, line_id, i_virt_edge;
73  struct line_cats *ln_Cats;
74  double x, y, z;
75  struct bound_box box;
76  struct boxlist *List;
77 
78  dglInt32_t dgl_cost;
79 
80  /*TODO attributes of turntable should be stored in one place */
81  const char *tcols[] = {"cat", "ln_from", "ln_to", "cost", "isec", NULL};
82  dbCatValArray tvarrs[5];
83  int tctype[5] = {0};
84  int tucfield_idx;
85 
86  int t, f;
87  int node_pt_id, turn_cat, tucfound;
88  int isec;
89 
90  /* TODO int costs -> double (waiting for dglib) */
91  G_debug(1,
92  "Vect_net_ttb_build_graph(): "
93  "ltype = %d, afield = %d, nfield = %d, tfield = %d, tucfield = %d ",
94  ltype, afield, nfield, tfield, tucfield);
95  G_debug(1, " afcol = %s, abcol = %s, ncol = %s", afcol, abcol, ncol);
96 
97  G_message(_("Building graph..."));
98 
99  Map->dgraph.line_type = ltype;
100 
101  Points = Vect_new_line_struct();
102  Cats = Vect_new_cats_struct();
103 
104  ll = 0;
105  if (G_projection() == 3)
106  ll = 1; /* LL */
107 
108  if (afcol == NULL && ll && !geo)
109  Map->dgraph.cost_multip = 1000000;
110  else
111  Map->dgraph.cost_multip = 1000;
112 
113  nlines = Vect_get_num_lines(Map);
114  nnodes = Vect_get_num_nodes(Map);
115 
116  gr = &(Map->dgraph.graph_s);
117 
118  /* Allocate space for costs, later replace by functions reading costs from
119  * graph */
120  Map->dgraph.edge_fcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
121  Map->dgraph.edge_bcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
122  Map->dgraph.node_costs = (double *)G_malloc((nnodes + 1) * sizeof(double));
123 
124  /* Set to -1 initially */
125  for (i = 1; i <= nlines; i++) {
126  Map->dgraph.edge_fcosts[i] = -1; /* forward */
127  Map->dgraph.edge_bcosts[i] = -1; /* backward */
128  }
129  for (i = 1; i <= nnodes; i++) {
130  Map->dgraph.node_costs[i] = 0;
131  }
132 
133  dglInitialize(gr, (dglByte_t)1, sizeof(dglInt32_t), (dglInt32_t)0,
134  opaqueset);
135 
136  if (gr == NULL)
137  G_fatal_error(_("Unable to build network graph"));
138 
139  db_init_handle(&handle);
140  db_init_string(&stmt);
141 
142  if (abcol != NULL && afcol == NULL)
143  G_fatal_error(_("Forward costs column not specified"));
144 
145  /* --- Add arcs --- */
146  /* Open db connection */
147 
148  /* Get field info */
149  if (tfield < 1)
150  G_fatal_error(_("Turntable field < 1"));
151  Fi = Vect_get_field(Map, tfield);
152  if (Fi == NULL)
153  G_fatal_error(_("Database connection not defined for layer %d"),
154  tfield);
155 
156  /* Open database */
157  ttbdriver = db_start_driver_open_database(Fi->driver, Fi->database);
158  if (ttbdriver == NULL)
159  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
160  Fi->database, Fi->driver);
161 
162  i = 0;
163  while (tcols[i]) {
164  /* Load costs to array */
165  if (db_get_column(ttbdriver, Fi->table, tcols[i], &Column) != DB_OK)
166  G_fatal_error(_("Turntable column <%s> not found in table <%s>"),
167  tcols[i], Fi->table);
168 
169  tctype[i] = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
170  db_free_column(Column);
171 
172  if ((tctype[i] == DB_C_TYPE_INT || tctype[i] == DB_C_TYPE_DOUBLE) &&
173  !strcmp(tcols[i], "cost"))
174  ;
175  else if (tctype[i] == DB_C_TYPE_INT)
176  ;
177  else
179  _("Data type of column <%s> not supported (must be numeric)"),
180  tcols[i]);
181 
182  db_CatValArray_init(&tvarrs[i]);
183  nturns = db_select_CatValArray(ttbdriver, Fi->table, Fi->key, tcols[i],
184  NULL, &tvarrs[i]);
185  ++i;
186  }
187 
189 
190  G_debug(1, "forward costs: nrec = %d", nturns);
191 
192  /* Set node attributes */
193  G_message("Register nodes");
194  if (ncol != NULL) {
195 
196  G_debug(2, "Set nodes' costs");
197  if (nfield < 1)
198  G_fatal_error("Node field < 1");
199 
200  G_message(_("Setting node costs..."));
201 
202  Fi = Vect_get_field(Map, nfield);
203  if (Fi == NULL)
204  G_fatal_error(_("Database connection not defined for layer %d"),
205  nfield);
206 
208  if (driver == NULL)
209  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
210  Fi->database, Fi->driver);
211 
212  /* Load costs to array */
213  if (db_get_column(driver, Fi->table, ncol, &Column) != DB_OK)
214  G_fatal_error(_("Column <%s> not found in table <%s>"), ncol,
215  Fi->table);
216 
217  fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
218  db_free_column(Column);
219 
220  if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
222  _("Data type of column <%s> not supported (must be numeric)"),
223  ncol);
224 
225  db_CatValArray_init(&fvarr);
226 
227  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, ncol, NULL,
228  &fvarr);
229  G_debug(1, "node costs: nrec = %d", nrec);
231 
232  tucfield_idx = Vect_cidx_get_field_index(Map, tucfield);
233  }
234 
235  List = Vect_new_boxlist(0);
236  ln_Cats = Vect_new_cats_struct();
237 
238  G_message("Building turns graph...");
239 
240  i_virt_edge = -1;
241  for (i = 1; i <= nnodes; i++) {
242  /* TODO: what happens if we set attributes of non existing node (skipped
243  * lines, nodes without lines) */
244 
245  /* select points at node */
246  Vect_get_node_coor(Map, i, &x, &y, &z);
247  box.E = box.W = x;
248  box.N = box.S = y;
249  box.T = box.B = z;
250  Vect_select_lines_by_box(Map, &box, GV_POINT, List);
251 
252  G_debug(2, " node = %d nlines = %d", i, List->n_values);
253  cfound = 0;
254  dcost = 0;
255  tucfound = 0;
256 
257  for (j = 0; j < List->n_values; j++) {
258  line = List->id[j];
259  G_debug(2, " line (%d) = %d", j, line);
260  type = Vect_read_line(Map, NULL, Cats, line);
261  if (!(type & GV_POINT))
262  continue;
263  /* get node column costs */
264  if (ncol != NULL && !cfound &&
265  Vect_cat_get(Cats, nfield,
266  &cat)) { /* point with category of field found */
267  /* Set costs */
268  if (fctype == DB_C_TYPE_INT) {
269  ret = db_CatValArray_get_value_int(&fvarr, cat, &cost);
270  dcost = cost;
271  }
272  else { /* DB_C_TYPE_DOUBLE */
273  ret = db_CatValArray_get_value_double(&fvarr, cat, &dcost);
274  }
275  if (ret != DB_OK) {
276  G_warning(
277  _("Database record for node %d (cat = %d) not found "
278  "(cost set to 0)"),
279  i, cat);
280  }
281  cfound = 1;
282  Map->dgraph.node_costs[i] = dcost;
283  }
284 
285  /* add virtual nodes and lines, which represents the intersections
286  there are added two nodes for every intersection, which are
287  linked with the nodes (edges in primal graph). the positive node
288  - when we are going from the intersection the negative node -
289  when we are going to the intersection
290 
291  TODO There are more possible approaches in virtual nodes
292  management. We can also add and remove them dynamically as they
293  are needed for analysis when Vect_net_ttb_shortest_path is called
294  (problem of flattening graph).
295  Currently this static solution was chosen, because it cost
296  time only when graph is build. However it costs more memory
297  space. For Dijkstra algorithm this expansion should not be
298  serious problem because we can only get into positive node or go
299  from the negative node.
300 
301  */
302 
303  ret = Vect_cat_get(Cats, tucfield, &cat);
304  if (!tucfound && ret) { /* point with category of field found */
305  /* find lines which belongs to the intersection */
306  nnode_lns = Vect_get_node_n_lines(Map, i);
307 
308  for (i_line = 0; i_line < nnode_lns; i_line++) {
309 
310  line_id = Vect_get_node_line(Map, i, i_line);
311  Vect_read_line(Map, NULL, ln_Cats, abs(line_id));
312  Vect_cat_get(ln_Cats, tucfield, &ln_cat);
313 
314  if (line_id < 0)
315  ln_cat *= -1;
316  f = cat * 2;
317 
318  if (ln_cat < 0)
319  t = ln_cat * -2 + 1;
320  else
321  t = ln_cat * 2;
322 
323  G_debug(
324  5,
325  "Add arc %d for virtual node from %d to %d cost = %d",
326  i_virt_edge, f, t, 0);
327 
328  /* positive, start virtual node */
329  ret = dglAddEdge(gr, (dglInt32_t)f, (dglInt32_t)t,
330  (dglInt32_t)0, (dglInt32_t)(i_virt_edge));
331  if (ret < 0)
332  G_fatal_error(_("Cannot add network arc for virtual "
333  "node connection."));
334 
335  t = cat * 2 + 1;
336  i_virt_edge--;
337 
338  if (-ln_cat < 0)
339  f = ln_cat * 2 + 1;
340  else
341  f = ln_cat * -2;
342 
343  G_debug(
344  5,
345  "Add arc %d for virtual node from %d to %d cost = %d",
346  i_virt_edge, f, t, 0);
347 
348  /* negative, destination virtual node */
349  ret = dglAddEdge(gr, (dglInt32_t)f, (dglInt32_t)t,
350  (dglInt32_t)0, (dglInt32_t)(i_virt_edge));
351  if (ret < 0)
352  G_fatal_error(_("Cannot add network arc for virtual "
353  "node connection."));
354 
355  i_virt_edge--;
356  }
357  tucfound++;
358  }
359  else if (ret)
360  tucfound++;
361  }
362 
363  if (tucfound > 1)
364  G_warning(_("There exists more than one point of node <%d> with "
365  "unique category field <%d>.\n"
366  "The unique categories layer is not valid therefore "
367  "you will probably get incorrect results."),
368  tucfield, i);
369 
370  if (ncol != NULL && !cfound)
371  G_debug(
372  2,
373  "Category of field %d is not attached to any points in node %d"
374  "(costs set to 0)",
375  nfield, i);
376  }
377 
378  Vect_destroy_cats_struct(ln_Cats);
379 
380  for (i = 1; i <= nturns; i++) {
381  /* select points at node */
382 
383  /* TODO use cursors */
384  db_CatValArray_get_value_int(&tvarrs[0], i, &turn_cat);
385 
386  db_CatValArray_get_value_int(&tvarrs[1], i, &from);
387  db_CatValArray_get_value_int(&tvarrs[2], i, &to);
388 
389  db_CatValArray_get_value_int(&tvarrs[4], i, &isec);
390  dcost = 0.0;
391  if (ncol != NULL) {
392  /* TODO optimization do not do it for every turn in intersection
393  * again */
394  if (Vect_cidx_find_next(Map, tucfield_idx, isec, GV_POINT, 0, &type,
395  &node_pt_id) == -1) {
396  G_warning(
397  _("Unable to find point representing intersection <%d> in "
398  "unique categories field <%d>.\n"
399  "Cost for the intersection was set to 0.\n"
400  "The unique categories layer is not valid therefore you "
401  "will probably get incorrect results."),
402  isec, tucfield);
403  }
404  else {
405  Vect_read_line(Map, Points, Cats, node_pt_id);
406 
407  node_pt_id = Vect_find_node(Map, *Points->x, *Points->y,
408  *Points->z, 0.0, WITHOUT_Z);
409 
410  if (node_pt_id == 0) {
411  G_warning(
412  _("Unable to find node for point representing "
413  "intersection <%d> in unique categories field <%d>.\n"
414  "Cost for the intersection was set to 0.\n"
415  "The unique categories layer is not valid therefore "
416  "you will probably get incorrect results."),
417  isec, tucfield);
418  }
419  else {
420  G_debug(2, " node = %d", node_pt_id);
421  dcost = Map->dgraph.node_costs[node_pt_id];
422  }
423  }
424  }
425 
426  G_debug(2, "Set node's cost to %f", dcost);
427 
428  if (dcost >= 0) {
429  /* Set costs from turntable */
430  if (tctype[3] == DB_C_TYPE_INT) {
431  ret = db_CatValArray_get_value_int(&tvarrs[3], i, &cost);
432  dcost = cost;
433  }
434  else /* DB_C_TYPE_DOUBLE */
435  ret = db_CatValArray_get_value_double(&tvarrs[3], i, &dcost);
436 
437  if (ret != DB_OK) {
438  G_warning(
439  _("Database record for turn with cat = %d is not found. "
440  "(The turn was skipped."),
441  i);
442  continue;
443  }
444 
445  if (dcost >= 0) {
446 
447  if (ncol != NULL)
448  cost = (Map->dgraph.node_costs[node_pt_id] + dcost) *
450  else
451  cost = dcost * (dglInt32_t)Map->dgraph.cost_multip;
452 
453  /* dglib does not like negative id's of nodes */
454  if (from < 0)
455  f = from * -2 + 1;
456  else
457  f = from * 2;
458 
459  if (to < 0)
460  t = to * -2 + 1;
461  else
462  t = to * 2;
463 
464  G_debug(5, "Add arc/turn %d for turn from %d to %d cost = %d",
465  turn_cat, f, t, cost);
466 
467  ret = dglAddEdge(gr, (dglInt32_t)f, (dglInt32_t)t,
468  (dglInt32_t)cost, (dglInt32_t)(turn_cat));
469 
470  if (ret < 0)
472  _("Cannot add network arc representing turn."));
473  }
474  }
475  }
476 
477  Vect_destroy_boxlist(List);
478 
479  i = 0;
480  while (tcols[i]) {
481  db_CatValArray_free(&tvarrs[i]);
482  ++i;
483  }
484 
485  if (ncol != NULL) {
487  db_CatValArray_free(&fvarr);
488  }
489 
490  /* Open db connection */
491  if (afcol != NULL) {
492  /* Get field info */
493  if (afield < 1)
494  G_fatal_error(_("Arc field < 1"));
495  Fi = Vect_get_field(Map, afield);
496  if (Fi == NULL)
497  G_fatal_error(_("Database connection not defined for layer %d"),
498  afield);
499 
500  /* Open database */
502  if (driver == NULL)
503  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
504  Fi->database, Fi->driver);
505 
506  /* Load costs to array */
507  if (db_get_column(driver, Fi->table, afcol, &Column) != DB_OK)
508  G_fatal_error(_("Column <%s> not found in table <%s>"), afcol,
509  Fi->table);
510 
511  fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
512  db_free_column(Column);
513 
514  if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
516  _("Data type of column <%s> not supported (must be numeric)"),
517  afcol);
518 
519  db_CatValArray_init(&fvarr);
520  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, afcol, NULL,
521  &fvarr);
522  G_debug(1, "forward costs: nrec = %d", nrec);
523 
524  if (abcol != NULL) {
525  if (db_get_column(driver, Fi->table, abcol, &Column) != DB_OK)
526  G_fatal_error(_("Column <%s> not found in table <%s>"), abcol,
527  Fi->table);
528 
529  bctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
530  db_free_column(Column);
531 
532  if (bctype != DB_C_TYPE_INT && bctype != DB_C_TYPE_DOUBLE)
533  G_fatal_error(_("Data type of column <%s> not supported (must "
534  "be numeric)"),
535  abcol);
536 
537  db_CatValArray_init(&bvarr);
538  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, abcol,
539  NULL, &bvarr);
540  G_debug(1, "backward costs: nrec = %d", nrec);
541  }
543  }
544 
545  skipped = 0;
546 
547  G_message(_("Registering arcs..."));
548 
549  for (i = 1; i <= nlines; i++) {
550  G_percent(i, nlines, 1); /* must be before any continue */
551 
552  type = Vect_read_line(Map, Points, Cats, i);
553  if (!(type & ltype & (GV_LINE | GV_BOUNDARY)))
554  continue;
555 
556  Vect_get_line_nodes(Map, i, &from, &to);
557 
558  dcost = bdcost = 0;
559 
560  cfound = Vect_cat_get(Cats, tucfield, &cat);
561  if (!cfound)
562  continue;
563 
564  if (cfound > 1)
565  G_warning(_("Line with id <%d> has more unique categories defined "
566  "in field <%d>.\n"
567  "The unique categories layer is not valid therefore "
568  "you will probably get incorrect results."),
569  i, tucfield);
570 
571  if (afcol != NULL) {
572  if (!(Vect_cat_get(Cats, afield, &cat))) {
573  G_debug(2,
574  "Category of field %d not attached to the line %d -> "
575  "cost was set to 0",
576  afield, i);
577  skipped += 2; /* Both directions */
578  }
579  else {
580  if (fctype == DB_C_TYPE_INT) {
581  ret = db_CatValArray_get_value_int(&fvarr, cat, &cost);
582  dcost = cost;
583  }
584  else { /* DB_C_TYPE_DOUBLE */
585  ret = db_CatValArray_get_value_double(&fvarr, cat, &dcost);
586  }
587  if (ret != DB_OK) {
588  G_warning(_("Database record for line %d (cat = %d, "
589  "forward/both direction(s)) not found "
590  "(cost was set to 0)"),
591  i, cat);
592  }
593 
594  if (abcol != NULL) {
595  if (bctype == DB_C_TYPE_INT) {
596  ret = db_CatValArray_get_value_int(&bvarr, cat, &bcost);
597  bdcost = bcost;
598  }
599  else { /* DB_C_TYPE_DOUBLE */
600  ret = db_CatValArray_get_value_double(&bvarr, cat,
601  &bdcost);
602  }
603  if (ret != DB_OK) {
604  G_warning(_("Database record for line %d (cat = %d, "
605  "backword direction) not found"
606  "(cost was set to 0)"),
607  i, cat);
608  }
609  }
610  else
611  bdcost = dcost;
612 
613  Vect_cat_get(Cats, tucfield, &cat);
614  }
615  }
616  else {
617  if (ll) {
618  if (geo)
619  dcost = Vect_line_geodesic_length(Points);
620  else
621  dcost = Vect_line_length(Points);
622  }
623  else
624  dcost = Vect_line_length(Points);
625 
626  bdcost = dcost;
627  }
628 
629  cost = (dglInt32_t)Map->dgraph.cost_multip * dcost;
630  dgl_cost = cost;
631 
632  cat = cat * 2;
633 
634  G_debug(5, "Setinng node %d cost: %d", cat, cost);
635  dglNodeSet_Attr(gr, dglGetNode(gr, (dglInt32_t)cat), &dgl_cost);
636 
637  Map->dgraph.edge_fcosts[i] = dcost;
638 
639  cost = (dglInt32_t)Map->dgraph.cost_multip * bdcost;
640  dgl_cost = cost;
641 
642  ++cat;
643 
644  G_debug(5, "Setinng node %d cost: %d", cat, cost);
645  dglNodeSet_Attr(gr, dglGetNode(gr, (dglInt32_t)cat), &dgl_cost);
646 
647  Map->dgraph.edge_bcosts[i] = bdcost;
648  }
649 
650  if (afcol != NULL && skipped > 0)
651  G_debug(2, "%d lines missing category of field %d skipped", skipped,
652  afield);
653 
654  if (afcol != NULL) {
656  db_CatValArray_free(&fvarr);
657 
658  if (abcol != NULL) {
659  db_CatValArray_free(&bvarr);
660  }
661  }
663 
664  Vect_destroy_line_struct(Points);
666 
667  G_message(_("Flattening the graph..."));
668  ret = dglFlatten(gr);
669  if (ret < 0)
670  G_fatal_error(_("GngFlatten error"));
671 
672  /* init SP cache */
673  /* disable to debug dglib cache */
674  dglInitializeSPCache(gr, &(Map->dgraph.spCache));
675 
676  G_message(_("Graph was built"));
677 
678  return 0;
679 }
680 
681 /*!
682  \brief Build network graph.
683 
684  Internal format for edge costs is integer, costs are multiplied
685  before conversion to int by 1000 and for lengths LL without geo flag by
686  1000000. The same multiplication factor is used for nodes. Costs in database
687  column may be 'integer' or 'double precision' number >= 0 or -1 for infinity
688  i.e. arc or node is closed and cannot be traversed If record in table is not
689  found for arcs, arc is skip. If record in table is not found for node, costs
690  for node are set to 0.
691 
692  \param Map vector map
693  \param ltype line type for arcs
694  \param afield arc costs field (if 0, use length)
695  \param nfield node costs field (if 0, do not use node costs)
696  \param afcol column with forward costs for arc
697  \param abcol column with backward costs for arc (if NULL, back costs =
698  forward costs), \param ncol column with costs for nodes (if NULL, do not use
699  node costs), \param geo use geodesic calculation for length (LL), \param
700  version graph version to create (1, 2, 3)
701 
702  \return 0 on success, 1 on error
703  */
704 int Vect_net_build_graph(struct Map_info *Map, int ltype, int afield,
705  int nfield, const char *afcol, const char *abcol,
706  const char *ncol, int geo, int version)
707 {
708  /* TODO long function, split into smaller ones */
709  int i, j, from, to, line, nlines, nnodes, ret, type, cat, skipped, cfound;
710  int dofw, dobw;
711  struct line_pnts *Points;
712  struct line_cats *Cats;
713  double dcost, bdcost, ll;
714  int cost, bcost;
715  dglGraph_s *gr;
716  dglInt32_t dgl_cost;
717  dglInt32_t opaqueset[16] = {360000, 0, 0, 0, 0, 0, 0, 0,
718  0, 0, 0, 0, 0, 0, 0, 0};
719  struct field_info *Fi = NULL;
720  dbDriver *driver = NULL;
721  dbHandle handle;
722  dbString stmt;
723  dbColumn *Column = NULL;
724  dbCatValArray fvarr, bvarr;
725  int fctype = 0, bctype = 0, nrec;
726 
727  /* TODO int costs -> double (waiting for dglib) */
728  G_debug(1, "Vect_net_build_graph(): ltype = %d, afield = %d, nfield = %d",
729  ltype, afield, nfield);
730  G_debug(1, " afcol = %s, abcol = %s, ncol = %s", afcol, abcol, ncol);
731 
732  G_message(_("Building graph..."));
733 
734  Map->dgraph.line_type = ltype;
735 
736  Points = Vect_new_line_struct();
737  Cats = Vect_new_cats_struct();
738 
739  ll = 0;
740  if (G_projection() == 3)
741  ll = 1; /* LL */
742 
743  if (afcol == NULL && ll && !geo)
744  Map->dgraph.cost_multip = 1000000;
745  else
746  Map->dgraph.cost_multip = 1000;
747 
748  nlines = Vect_get_num_lines(Map);
749  nnodes = Vect_get_num_nodes(Map);
750 
751  gr = &(Map->dgraph.graph_s);
752 
753  /* Allocate space for costs, later replace by functions reading costs from
754  * graph */
755  Map->dgraph.edge_fcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
756  Map->dgraph.edge_bcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
757  Map->dgraph.node_costs = (double *)G_malloc((nnodes + 1) * sizeof(double));
758  /* Set to -1 initially */
759  for (i = 1; i <= nlines; i++) {
760  Map->dgraph.edge_fcosts[i] = -1; /* forward */
761  Map->dgraph.edge_bcosts[i] = -1; /* backward */
762  }
763  for (i = 1; i <= nnodes; i++) {
764  Map->dgraph.node_costs[i] = 0;
765  }
766 
767  if (version < 1 || version > 3)
768  version = 1;
769 
770  if (ncol != NULL)
771  dglInitialize(gr, (dglByte_t)version, sizeof(dglInt32_t), (dglInt32_t)0,
772  opaqueset);
773  else
774  dglInitialize(gr, (dglByte_t)version, (dglInt32_t)0, (dglInt32_t)0,
775  opaqueset);
776 
777  if (gr == NULL)
778  G_fatal_error(_("Unable to build network graph"));
779 
780  db_init_handle(&handle);
781  db_init_string(&stmt);
782 
783  if (abcol != NULL && afcol == NULL)
784  G_fatal_error(_("Forward costs column not specified"));
785 
786  /* --- Add arcs --- */
787  /* Open db connection */
788  if (afcol != NULL) {
789  /* Get field info */
790  if (afield < 1)
791  G_fatal_error(_("Arc field < 1"));
792  Fi = Vect_get_field(Map, afield);
793  if (Fi == NULL)
794  G_fatal_error(_("Database connection not defined for layer %d"),
795  afield);
796 
797  /* Open database */
799  if (driver == NULL)
800  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
801  Fi->database, Fi->driver);
802 
803  /* Load costs to array */
804  if (db_get_column(driver, Fi->table, afcol, &Column) != DB_OK)
805  G_fatal_error(_("Column <%s> not found in table <%s>"), afcol,
806  Fi->table);
807 
808  fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
809  db_free_column(Column);
810 
811  if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
813  _("Data type of column <%s> not supported (must be numeric)"),
814  afcol);
815 
816  db_CatValArray_init(&fvarr);
817  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, afcol, NULL,
818  &fvarr);
819  G_debug(1, "forward costs: nrec = %d", nrec);
820 
821  if (abcol != NULL) {
822  if (db_get_column(driver, Fi->table, abcol, &Column) != DB_OK)
823  G_fatal_error(_("Column <%s> not found in table <%s>"), abcol,
824  Fi->table);
825 
826  bctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
827  db_free_column(Column);
828 
829  if (bctype != DB_C_TYPE_INT && bctype != DB_C_TYPE_DOUBLE)
830  G_fatal_error(_("Data type of column <%s> not supported (must "
831  "be numeric)"),
832  abcol);
833 
834  db_CatValArray_init(&bvarr);
835  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, abcol,
836  NULL, &bvarr);
837  G_debug(1, "backward costs: nrec = %d", nrec);
838  }
840  }
841 
842  skipped = 0;
843 
844  G_message(_("Registering arcs..."));
845 
846  for (i = 1; i <= nlines; i++) {
847  G_percent(i, nlines, 1); /* must be before any continue */
848  dofw = dobw = 1;
849  type = Vect_read_line(Map, Points, Cats, i);
850  if (!(type & ltype & (GV_LINE | GV_BOUNDARY)))
851  continue;
852 
853  Vect_get_line_nodes(Map, i, &from, &to);
854 
855  if (afcol != NULL) {
856  if (!(Vect_cat_get(Cats, afield, &cat))) {
857  G_debug(2,
858  "Category of field %d not attached to the line %d -> "
859  "line skipped",
860  afield, i);
861  skipped += 2; /* Both directions */
862  continue;
863  }
864  else {
865  if (fctype == DB_C_TYPE_INT) {
866  ret = db_CatValArray_get_value_int(&fvarr, cat, &cost);
867  dcost = cost;
868  }
869  else { /* DB_C_TYPE_DOUBLE */
870  ret = db_CatValArray_get_value_double(&fvarr, cat, &dcost);
871  }
872  if (ret != DB_OK) {
873  G_warning(_("Database record for line %d (cat = %d, "
874  "forward/both direction(s)) not found "
875  "(forward/both direction(s) of line skipped)"),
876  i, cat);
877  dofw = 0;
878  }
879 
880  if (abcol != NULL) {
881  if (bctype == DB_C_TYPE_INT) {
882  ret = db_CatValArray_get_value_int(&bvarr, cat, &bcost);
883  bdcost = bcost;
884  }
885  else { /* DB_C_TYPE_DOUBLE */
886  ret = db_CatValArray_get_value_double(&bvarr, cat,
887  &bdcost);
888  }
889  if (ret != DB_OK) {
890  G_warning(_("Database record for line %d (cat = %d, "
891  "backword direction) not found"
892  "(direction of line skipped)"),
893  i, cat);
894  dobw = 0;
895  }
896  }
897  else {
898  if (dofw)
899  bdcost = dcost;
900  else
901  dobw = 0;
902  }
903  }
904  }
905  else {
906  if (ll) {
907  if (geo)
908  dcost = Vect_line_geodesic_length(Points);
909  else
910  dcost = Vect_line_length(Points);
911  }
912  else
913  dcost = Vect_line_length(Points);
914 
915  bdcost = dcost;
916  }
917  if (dofw && dcost != -1) {
918  cost = (dglInt32_t)Map->dgraph.cost_multip * dcost;
919  G_debug(5, "Add arc %d from %d to %d cost = %d", i, from, to, cost);
920  ret = dglAddEdge(gr, (dglInt32_t)from, (dglInt32_t)to,
921  (dglInt32_t)cost, (dglInt32_t)i);
922  Map->dgraph.edge_fcosts[i] = dcost;
923  if (ret < 0)
924  G_fatal_error("Cannot add network arc");
925  }
926 
927  G_debug(5, "bdcost = %f edge_bcosts = %f", bdcost,
928  Map->dgraph.edge_bcosts[i]);
929  if (dobw && bdcost != -1) {
930  bcost = (dglInt32_t)Map->dgraph.cost_multip * bdcost;
931  G_debug(5, "Add arc %d from %d to %d bcost = %d", -i, to, from,
932  bcost);
933  ret = dglAddEdge(gr, (dglInt32_t)to, (dglInt32_t)from,
934  (dglInt32_t)bcost, (dglInt32_t)-i);
935  Map->dgraph.edge_bcosts[i] = bdcost;
936  if (ret < 0)
937  G_fatal_error(_("Cannot add network arc"));
938  }
939  }
940 
941  if (afcol != NULL && skipped > 0)
942  G_debug(2, "%d lines missing category of field %d skipped", skipped,
943  afield);
944 
945  if (afcol != NULL) {
947  db_CatValArray_free(&fvarr);
948 
949  if (abcol != NULL) {
950  db_CatValArray_free(&bvarr);
951  }
952  }
953 
954  /* Set node attributes */
955  G_debug(2, "Register nodes");
956  if (ncol != NULL) {
957  double x, y, z;
958  struct bound_box box;
959  struct boxlist *List;
960 
961  List = Vect_new_boxlist(0);
962 
963  G_debug(2, "Set nodes' costs");
964  if (nfield < 1)
965  G_fatal_error("Node field < 1");
966 
967  G_message(_("Setting node costs..."));
968 
969  Fi = Vect_get_field(Map, nfield);
970  if (Fi == NULL)
971  G_fatal_error(_("Database connection not defined for layer %d"),
972  nfield);
973 
975  if (driver == NULL)
976  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
977  Fi->database, Fi->driver);
978 
979  /* Load costs to array */
980  if (db_get_column(driver, Fi->table, ncol, &Column) != DB_OK)
981  G_fatal_error(_("Column <%s> not found in table <%s>"), ncol,
982  Fi->table);
983 
984  fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
985  db_free_column(Column);
986 
987  if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
989  _("Data type of column <%s> not supported (must be numeric)"),
990  ncol);
991 
992  db_CatValArray_init(&fvarr);
993  nrec = db_select_CatValArray(driver, Fi->table, Fi->key, ncol, NULL,
994  &fvarr);
995  G_debug(1, "node costs: nrec = %d", nrec);
997 
998  for (i = 1; i <= nnodes; i++) {
999  /* TODO: what happens if we set attributes of not existing node
1000  * (skipped lines, nodes without lines) */
1001 
1002  /* select points at node */
1003  Vect_get_node_coor(Map, i, &x, &y, &z);
1004  box.E = box.W = x;
1005  box.N = box.S = y;
1006  box.T = box.B = z;
1007  Vect_select_lines_by_box(Map, &box, GV_POINT, List);
1008 
1009  G_debug(2, " node = %d nlines = %d", i, List->n_values);
1010  cfound = 0;
1011  dcost = 0;
1012 
1013  for (j = 0; j < List->n_values; j++) {
1014  line = List->id[j];
1015  G_debug(2, " line (%d) = %d", j, line);
1016  type = Vect_read_line(Map, NULL, Cats, line);
1017  if (!(type & GV_POINT))
1018  continue;
1019  if (Vect_cat_get(
1020  Cats, nfield,
1021  &cat)) { /* point with category of field found */
1022  /* Set costs */
1023  if (fctype == DB_C_TYPE_INT) {
1024  ret = db_CatValArray_get_value_int(&fvarr, cat, &cost);
1025  dcost = cost;
1026  }
1027  else { /* DB_C_TYPE_DOUBLE */
1028  ret = db_CatValArray_get_value_double(&fvarr, cat,
1029  &dcost);
1030  }
1031  if (ret != DB_OK) {
1032  G_warning(_("Database record for node %d (cat = %d) "
1033  "not found "
1034  "(cost set to 0)"),
1035  i, cat);
1036  }
1037  cfound = 1;
1038  break;
1039  }
1040  }
1041  if (!cfound) {
1042  G_debug(
1043  2,
1044  "Category of field %d not attached to any points in node %d"
1045  "(costs set to 0)",
1046  nfield, i);
1047  }
1048  if (dcost == -1) { /* closed */
1049  cost = -1;
1050  }
1051  else {
1052  cost = (dglInt32_t)Map->dgraph.cost_multip * dcost;
1053  }
1054 
1055  dgl_cost = cost;
1056  G_debug(3, "Set node's cost to %d", cost);
1057 
1058  dglNodeSet_Attr(gr, dglGetNode(gr, (dglInt32_t)i), &dgl_cost);
1059 
1060  Map->dgraph.node_costs[i] = dcost;
1061  }
1063  db_CatValArray_free(&fvarr);
1064 
1065  Vect_destroy_boxlist(List);
1066  }
1067 
1068  G_message(_("Flattening the graph..."));
1069  ret = dglFlatten(gr);
1070  if (ret < 0)
1071  G_fatal_error(_("GngFlatten error"));
1072 
1073  /* init SP cache */
1074  /* disable to debug dglib cache */
1075  dglInitializeSPCache(gr, &(Map->dgraph.spCache));
1076 
1077  G_message(_("Graph was built"));
1078 
1079  return 0;
1080 }
#define NULL
Definition: ccmath.h:32
#define DB_C_TYPE_INT
Definition: dbmi.h:108
#define DB_C_TYPE_DOUBLE
Definition: dbmi.h:109
#define DB_OK
Definition: dbmi.h:71
void db_CatValArray_free(dbCatValArray *)
Free allocated dbCatValArray.
Definition: value.c:373
int db_CatValArray_get_value_int(dbCatValArray *, int, int *)
Find value (integer) by key.
void db_CatValArray_init(dbCatValArray *)
Initialize dbCatValArray.
Definition: value.c:361
int db_get_column(dbDriver *, const char *, const char *, dbColumn **)
Get column structure by table and column name.
int db_sqltype_to_Ctype(int)
Get C data type based on given SQL data type.
Definition: sqlCtype.c:24
int db_get_column_sqltype(dbColumn *)
Returns column sqltype for column.
dbDriver * db_start_driver_open_database(const char *, const char *)
Open driver/database connection.
Definition: db.c:28
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition: db.c:61
int db_select_CatValArray(dbDriver *, const char *, const char *, const char *, const char *, dbCatValArray *)
Select pairs key/value to array, values are sorted by key (must be integer)
void db_free_column(dbColumn *)
Frees column structure.
void db_init_handle(dbHandle *)
Initialize handle (i.e database/schema)
Definition: handle.c:23
void db_init_string(dbString *)
Initialize dbString.
Definition: string.c:25
int db_CatValArray_get_value_double(dbCatValArray *, int, double *)
Find value (double) by key.
void G_percent(long, long, int)
Print percent complete messages.
Definition: percent.c:61
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition: defs/gis.h:94
void G_message(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
int G_projection(void)
Query cartographic projection.
Definition: proj1.c:32
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition: line.c:77
int Vect_get_line_nodes(struct Map_info *, int, int *, int *)
Get line nodes.
Definition: level_two.c:303
int Vect_get_node_coor(struct Map_info *, int, double *, double *, double *)
Get node coordinates.
Definition: level_two.c:274
plus_t Vect_get_num_lines(struct Map_info *)
Fetch number of features (points, lines, boundaries, centroids) in vector map.
Definition: level_two.c:75
double Vect_line_length(const struct line_pnts *)
Calculate line length, 3D-length in case of 3D vector line.
Definition: line.c:575
int Vect_cidx_get_field_index(struct Map_info *, int)
Get layer index for given layer number.
Definition: Vlib/cindex.c:115
double Vect_line_geodesic_length(const struct line_pnts *)
Calculate line length.
Definition: line.c:602
int Vect_cidx_find_next(struct Map_info *, int, int, int, int, int *, int *)
Find next line/area id for given category, start_index and type_mask.
Definition: Vlib/cindex.c:327
struct boxlist * Vect_new_boxlist(int)
Creates and initializes a struct boxlist.
int Vect_cat_get(const struct line_cats *, int, int *)
Get first found category of given field.
void Vect_destroy_boxlist(struct boxlist *)
Frees all memory associated with a struct boxlist, including the struct itself.
void Vect_destroy_cats_struct(struct line_cats *)
Frees all memory associated with line_cats structure, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
struct field_info * Vect_get_field(struct Map_info *, int)
Get information about link to database (by layer number)
Definition: field.c:515
void Vect_destroy_field_info(struct field_info *)
Free a struct field_info and all memory associated with it.
Definition: field.c:633
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
Definition: sindex.c:32
int Vect_get_node_n_lines(struct Map_info *, int)
Get number of lines for node.
Definition: level_two.c:380
plus_t Vect_get_num_nodes(struct Map_info *)
Get number of nodes in vector map.
Definition: level_two.c:34
int Vect_find_node(struct Map_info *, double, double, double, double, int)
Find the nearest node.
int Vect_get_node_line(struct Map_info *, int, int)
Get line id for node line index.
Definition: level_two.c:396
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
#define GV_LINE
Definition: dig_defines.h:184
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:183
#define GV_BOUNDARY
Definition: dig_defines.h:185
#define WITHOUT_Z
2D/3D vector data
Definition: dig_defines.h:171
#define UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition: gis.h:47
#define _(str)
Definition: glocale.h:10
int Vect_net_build_graph(struct Map_info *Map, int ltype, int afield, int nfield, const char *afcol, const char *abcol, const char *ncol, int geo, int version)
Build network graph.
Definition: net_build.c:704
int Vect_net_ttb_build_graph(struct Map_info *Map, int ltype, int afield, int nfield, int tfield, int tucfield, const char *afcol, const char *abcol, const char *ncol, int geo, int algorithm UNUSED)
Build network graph with turntable.
Definition: net_build.c:49
double t
Definition: r_raster.c:39
if(!(yy_init))
Definition: sqlp.yy.c:775
dglSPCache_s spCache
Shortest path cache.
Definition: dig_structs.h:1216
int line_type
Line type used to build the graph.
Definition: dig_structs.h:1208
int cost_multip
Edge and node costs multiplicator.
Definition: dig_structs.h:1234
double * edge_fcosts
Forward costs used for graph.
Definition: dig_structs.h:1222
double * node_costs
Node costs used for graph.
Definition: dig_structs.h:1230
dglGraph_s graph_s
Graph structure.
Definition: dig_structs.h:1212
double * edge_bcosts
backward costs used for graph
Definition: dig_structs.h:1226
Vector map info.
Definition: dig_structs.h:1243
struct Graph_info dgraph
Graph info (built for network analysis)
Definition: dig_structs.h:1383
Bounding box.
Definition: dig_structs.h:64
double W
West.
Definition: dig_structs.h:80
double T
Top.
Definition: dig_structs.h:84
double S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
double B
Bottom.
Definition: dig_structs.h:88
List of bounding boxes with id.
Definition: dig_structs.h:1723
int * id
Array of ids.
Definition: dig_structs.h:1727
struct bound_box * box
Array of bounding boxes.
Definition: dig_structs.h:1731
int n_values
Number of items in the list.
Definition: dig_structs.h:1739
Definition: driver.h:21
Layer (old: field) information.
Definition: dig_structs.h:131
char * table
Name of DB table.
Definition: dig_structs.h:151
char * driver
Name of DB driver ('sqlite', 'dbf', ...)
Definition: dig_structs.h:143
char * database
Definition: dig_structs.h:147
char * key
Name of key column (usually 'cat')
Definition: dig_structs.h:155
Feature category info.
Definition: dig_structs.h:1677
Feature geometry info - coordinates.
Definition: dig_structs.h:1651
double * y
Array of Y coordinates.
Definition: dig_structs.h:1659
double * x
Array of X coordinates.
Definition: dig_structs.h:1655
double * z
Array of Z coordinates.
Definition: dig_structs.h:1663
unsigned char dglByte_t
Definition: type.h:35
long dglInt32_t
Definition: type.h:36
int dglAddEdge(dglGraph_s *pGraph, dglInt32_t nHead, dglInt32_t nTail, dglInt32_t nCost, dglInt32_t nEdge)
void dglNodeSet_Attr(dglGraph_s *pGraph, dglInt32_t *pnNode, dglInt32_t *pnAttr)
int dglInitialize(dglGraph_s *pGraph, dglByte_t Version, dglInt32_t NodeAttrSize, dglInt32_t EdgeAttrSize, dglInt32_t *pOpaqueSet)
int dglInitializeSPCache(dglGraph_s *pGraph, dglSPCache_s *pCache)
int dglFlatten(dglGraph_s *pGraph)
dglInt32_t * dglGetNode(dglGraph_s *pGraph, dglInt32_t nNodeId)
#define x