GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-ab016ef450
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
timetables.c
Go to the documentation of this file.
1 /*!
2  \file vector/neta/timetables.c
3 
4  \brief Network Analysis library - timetables
5 
6  Shortest path using timetables.
7 
8  (C) 2009-2010 by Daniel Bundala, and 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 Daniel Bundala (Google Summer of Code 2009)
14  */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <grass/gis.h>
19 #include <grass/vector.h>
20 #include <grass/dbmi.h>
21 #include <grass/glocale.h>
22 #include <grass/dgl/graph.h>
23 #include <grass/neta.h>
24 
25 /*!
26  \brief Get number of distinct elements
27 
28  \param driver DB driver
29  \param sql SQl string
30  \param[out] list of lengths
31  \param[out] list of ids
32 
33  \return number of distinct elements
34  \return -1 on failure
35  */
36 int NetA_init_distinct(dbDriver *driver, dbString *sql, int **lengths,
37  int **ids)
38 {
39  int count, last, cur, result, index, more;
40  dbCursor cursor;
41  dbTable *table;
42  dbColumn *column;
43  dbValue *value;
44 
45  if (db_open_select_cursor(driver, sql, &cursor, DB_SEQUENTIAL) != DB_OK) {
46  G_warning(_("Unable to open select cursor: %s"), db_get_string(sql));
47  return -1;
48  }
49  /*TODO: check column types */
50 
51  count = last = 0;
52  /*count number of distinct routes */
53  table = db_get_cursor_table(&cursor);
54  column = db_get_table_column(table, 0);
55  while (db_fetch(&cursor, DB_NEXT, &more) == DB_OK && more) {
56  value = db_get_column_value(column);
57  cur = db_get_value_int(value);
58  if (count == 0 || cur != last) {
59  last = cur;
60  count++;
61  }
62  }
63  result = count;
64  db_close_cursor(&cursor);
65 
66  *lengths = (int *)G_calloc(count, sizeof(int));
67  *ids = (int *)G_calloc(count, sizeof(int));
68  if (!*lengths || !*ids) {
69  G_warning(_("Out of memory"));
70  return -1;
71  }
72  if (db_open_select_cursor(driver, sql, &cursor, DB_SEQUENTIAL) != DB_OK) {
73  G_warning(_("Unable to open select cursor: %s"), db_get_string(sql));
74  return -1;
75  }
76  count = index = 0;
77  /*calculate the lengths of the routes */
78  table = db_get_cursor_table(&cursor);
79  column = db_get_table_column(table, 0);
80  while (db_fetch(&cursor, DB_NEXT, &more) == DB_OK && more) {
81  value = db_get_column_value(column);
82  cur = db_get_value_int(value);
83  if (count != 0 && cur != last)
84  index++;
85  if (count == 0 || cur != last)
86  (*ids)[index] = cur;
87  (*lengths)[index]++;
88  last = cur;
89  count++;
90  }
91  return result;
92 }
93 
94 static int cmp_int(const void *a, const void *b)
95 {
96  return *(int *)a - *(int *)b;
97 }
98 
99 /*!
100  \brief Initialises timetable from a database
101 
102  \param In pointer to Map_info structure
103  \param route_layer layer number of routes
104  \param walk_layer layer number of walkers
105  \param route_id id of route
106  \param times list of timestamps
107  \param to_stop ?
108  \param walk_length walk length as string
109  \param timetable pointer to neta_timetable
110  \param route_ids list of route ids
111  \param stop_ids list of stop ids
112 
113  \return 0 on success
114  \return non-zero value on failure
115  */
116 int NetA_init_timetable_from_db(struct Map_info *In, int route_layer,
117  int walk_layer, char *route_id, char *times,
118  char *to_stop, char *walk_length,
119  neta_timetable *timetable, int **route_ids,
120  int **stop_ids)
121 {
122  int more, i, stop, route, time, *stop_pnt, stop1, stop2;
123  dbString sql;
124  dbCursor cursor;
125  dbTable *table;
126  dbColumn *column1, *column2, *column3;
127  dbValue *value;
128  char buf[2000];
129 
130  dbDriver *driver;
131  struct field_info *Fi;
132 
133  Fi = Vect_get_field(In, route_layer);
134  if (Fi == NULL)
135  G_fatal_error(_("Database connection not defined for layer %d"),
136  route_layer);
137 
139  if (driver == NULL)
140  G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
141  Fi->database, Fi->driver);
142 
143  db_init_string(&sql);
144  snprintf(buf, sizeof(buf), "select %s from %s order by %s", route_id,
145  Fi->table, route_id);
146  db_set_string(&sql, buf);
147  timetable->routes =
148  NetA_init_distinct(driver, &sql, &(timetable->route_length), route_ids);
149  if (timetable->routes < 0)
150  return 1;
151 
152  snprintf(buf, sizeof(buf), "select %s from %s order by %s", Fi->key,
153  Fi->table, Fi->key);
154  db_set_string(&sql, buf);
155  timetable->stops =
156  NetA_init_distinct(driver, &sql, &(timetable->stop_length), stop_ids);
157  if (timetable->stops < 0)
158  return 1;
159 
160  timetable->route_stops = (int **)G_calloc(timetable->routes, sizeof(int *));
161  timetable->route_times = (int **)G_calloc(timetable->routes, sizeof(int *));
162  timetable->stop_routes = (int **)G_calloc(timetable->stops, sizeof(int *));
163  timetable->stop_times = (int **)G_calloc(timetable->stops, sizeof(int *));
164  timetable->walk_length = (int *)G_calloc(timetable->stops, sizeof(int));
165  timetable->walk_stops = (int **)G_calloc(timetable->stops, sizeof(int *));
166  timetable->walk_times = (int **)G_calloc(timetable->stops, sizeof(int *));
167  if (!timetable->route_stops || !timetable->route_times ||
168  !timetable->stop_routes || !timetable->stop_times ||
169  !timetable->walk_length) {
170  G_warning(_("Out of memory"));
171  return 2;
172  }
173 
174  for (i = 0; i < timetable->routes; i++) {
175  timetable->route_stops[i] =
176  (int *)G_calloc(timetable->route_length[i], sizeof(int));
177  timetable->route_times[i] =
178  (int *)G_calloc(timetable->route_length[i], sizeof(int));
179  if (!timetable->route_stops[i] || !timetable->route_times[i]) {
180  G_warning(_("Out of memory"));
181  return 2;
182  }
183 
184  timetable->route_length[i] = 0;
185  }
186 
187  for (i = 0; i < timetable->stops; i++) {
188  timetable->stop_routes[i] =
189  (int *)G_calloc(timetable->stop_length[i], sizeof(int));
190  timetable->stop_times[i] =
191  (int *)G_calloc(timetable->stop_length[i], sizeof(int));
192  if (!timetable->stop_routes[i] || !timetable->stop_times[i]) {
193  G_warning(_("Out of memory"));
194  return 2;
195  }
196  timetable->walk_length[i] = 0;
197  timetable->stop_length[i] = 0;
198  }
199 
200  snprintf(buf, sizeof(buf), "select %s, %s, %s from %s order by %s", Fi->key,
201  route_id, times, Fi->table, times);
202  db_set_string(&sql, buf);
203 
204  if (db_open_select_cursor(driver, &sql, &cursor, DB_SEQUENTIAL) != DB_OK) {
205  G_warning(_("Unable to open select cursor: %s"), db_get_string(&sql));
206  return 1;
207  }
208 
209  table = db_get_cursor_table(&cursor);
210  column1 = db_get_table_column(table, 0);
211  column2 = db_get_table_column(table, 1);
212  column3 = db_get_table_column(table, 2);
213  while (db_fetch(&cursor, DB_NEXT, &more) == DB_OK && more) {
214  value = db_get_column_value(column1);
215  stop = db_get_value_int(value);
216  value = db_get_column_value(column2);
217  route = db_get_value_int(value);
218  value = db_get_column_value(column3);
219  time = db_get_value_int(value);
220  stop = (int *)bsearch(&stop, *stop_ids, timetable->stops, sizeof(int),
221  cmp_int) -
222  (*stop_ids);
223  route = (int *)bsearch(&route, *route_ids, timetable->routes,
224  sizeof(int), cmp_int) -
225  (*route_ids);
226 
227  timetable->stop_routes[stop][timetable->stop_length[stop]] = route;
228  timetable->stop_times[stop][timetable->stop_length[stop]++] = time;
229 
230  timetable->route_stops[route][timetable->route_length[route]] = stop;
231  timetable->route_times[route][timetable->route_length[route]++] = time;
232  }
233  db_close_cursor(&cursor);
234 
235  if (walk_layer != -1) {
236 
237  Fi = Vect_get_field(In, walk_layer);
238  snprintf(buf, sizeof(buf), "select %s, %s, %s from %s", Fi->key,
239  to_stop, walk_length, Fi->table);
240  db_set_string(&sql, buf);
241 
242  if (db_open_select_cursor(driver, &sql, &cursor, DB_SEQUENTIAL) !=
243  DB_OK) {
244  G_warning(_("Unable to open select cursor: %s"),
245  db_get_string(&sql));
246  return 1;
247  }
248 
249  table = db_get_cursor_table(&cursor);
250  column1 = db_get_table_column(table, 0);
251  column2 = db_get_table_column(table, 1);
252  while (db_fetch(&cursor, DB_NEXT, &more) == DB_OK && more) {
253  value = db_get_column_value(column2);
254  stop = db_get_value_int(value);
255  stop_pnt = (int *)bsearch(&stop, *stop_ids, timetable->stops,
256  sizeof(int), cmp_int);
257  if (stop_pnt) {
258  value = db_get_column_value(column1);
259  stop = db_get_value_int(value);
260  stop_pnt = (int *)bsearch(&stop, *stop_ids, timetable->stops,
261  sizeof(int), cmp_int);
262  if (stop_pnt) {
263  stop = stop_pnt - (*stop_ids);
264  timetable->walk_length[stop]++;
265  }
266  }
267  }
268  db_close_cursor(&cursor);
269 
270  for (i = 0; i < timetable->stops; i++) {
271  timetable->walk_stops[i] =
272  (int *)G_calloc(timetable->walk_length[i], sizeof(int));
273  timetable->walk_times[i] =
274  (int *)G_calloc(timetable->walk_length[i], sizeof(int));
275  if (!timetable->walk_stops[i] || !timetable->walk_times[i]) {
276  G_warning(_("Out of memory"));
277  return 2;
278  }
279  timetable->walk_length[i] = 0;
280  }
281 
282  if (db_open_select_cursor(driver, &sql, &cursor, DB_SEQUENTIAL) !=
283  DB_OK) {
284  G_warning(_("Unable to open select cursor: %s"),
285  db_get_string(&sql));
286  return 1;
287  }
288 
289  table = db_get_cursor_table(&cursor);
290  column1 = db_get_table_column(table, 0);
291  column2 = db_get_table_column(table, 1);
292  column3 = db_get_table_column(table, 2);
293  while (db_fetch(&cursor, DB_NEXT, &more) == DB_OK && more) {
294  value = db_get_column_value(column2);
295  stop = db_get_value_int(value);
296  stop_pnt = (int *)bsearch(&stop, *stop_ids, timetable->stops,
297  sizeof(int), cmp_int);
298  if (stop_pnt) {
299  stop2 = stop_pnt - (*stop_ids);
300  value = db_get_column_value(column1);
301  stop = db_get_value_int(value);
302  stop_pnt = (int *)bsearch(&stop, *stop_ids, timetable->stops,
303  sizeof(int), cmp_int);
304  if (stop_pnt) {
305  stop1 = stop_pnt - (*stop_ids);
306  value = db_get_column_value(column3);
307  time = db_get_value_int(value);
308  timetable
309  ->walk_stops[stop1][timetable->walk_length[stop1]] =
310  stop2;
311  timetable
312  ->walk_times[stop1][timetable->walk_length[stop1]++] =
313  time;
314  }
315  }
316  }
317  db_close_cursor(&cursor);
318  }
321 
322  return 0;
323 }
324 
325 typedef struct {
326  int v;
327  int conns;
328 } neta_heap_data;
329 
330 static neta_heap_data *new_heap_data(int conns, int v)
331 {
332  neta_heap_data *d = (neta_heap_data *)G_calloc(1, sizeof(neta_heap_data));
333  d->v = v;
334  d->conns = conns;
335  return d;
336 }
337 
338 /*!
339  \brief Update Dijkstra structures
340 
341  \param olc_conns old connection
342  \param new_conns new connection
343  \param to old 'to' node
344  \param new_dst new 'to' node
345  \param v ?
346  \param route id of route
347  \param rows ? (unused)
348  \param update ?
349  \param[out] result pointer to neta_timetable_result structure
350  \param heap ?
351  */
352 void NetA_update_dijkstra(int old_conns, int new_conns, int to, int new_dst,
353  int v, int route, int rows UNUSED, int update,
354  neta_timetable_result *result, dglHeap_s *heap)
355 {
356  if (result->dst[new_conns][to] == -1 ||
357  result->dst[new_conns][to] > new_dst) {
358  result->dst[new_conns][to] = new_dst;
359  result->prev_stop[new_conns][to] = v;
360  result->prev_route[new_conns][to] = route;
361  result->prev_conn[new_conns][to] = old_conns;
362  if (update) {
363  dglHeapData_u heap_data;
364 
365  heap_data.pv = (void *)new_heap_data(new_conns, to);
366  dglHeapInsertMin(heap, new_dst, ' ', heap_data);
367  }
368  }
369 }
370 
371 /*!
372  \brief Computes the earliest arrival time.
373 
374  Computes the earliest arrival time to to_stop from from_stop
375  starting at start_time, or -1 if no path exists
376 
377  \param timetable pointer to neta_timetable structure
378  \param from_stop 'from' node
379  \param to_stop 'to' stop
380  \param start_time start timestamp
381  \param min_change ?
382  \param max_changes ?
383  \param walking_change ?
384  \param[out] result pointer to neta_timetable_result
385 
386  \return ?
387  \return -1 on error
388  */
389 int NetA_timetable_shortest_path(neta_timetable *timetable, int from_stop,
390  int to_stop, int start_time, int min_change,
391  int max_changes, int walking_change,
392  neta_timetable_result *result)
393 {
394  int i, j;
395  dglHeap_s heap;
396 
397  int opt_conns, rows = 1;
398 
399  if (max_changes != -1)
400  rows = max_changes + 2;
401 
402  result->rows = rows;
403  result->dst = (int **)G_calloc(rows, sizeof(int *));
404  result->prev_stop = (int **)G_calloc(rows, sizeof(int *));
405  result->prev_route = (int **)G_calloc(rows, sizeof(int *));
406  result->prev_conn = (int **)G_calloc(rows, sizeof(int *));
407 
408  if (!result->dst || !result->prev_stop || !result->prev_route ||
409  !result->prev_conn) {
410  G_warning(_("Out of memory"));
411  return -1;
412  }
413 
414  for (i = 0; i < rows; i++) {
415  result->dst[i] = (int *)G_calloc(timetable->stops, sizeof(int));
416  result->prev_stop[i] = (int *)G_calloc(timetable->stops, sizeof(int));
417  result->prev_route[i] = (int *)G_calloc(timetable->stops, sizeof(int));
418  result->prev_conn[i] = (int *)G_calloc(timetable->stops, sizeof(int));
419  if (!result->dst[i] || !result->prev_stop[i] ||
420  !result->prev_route[i] || !result->prev_conn[i]) {
421  G_warning(_("Out of memory"));
422  return -1;
423  }
424  }
425 
426  if (from_stop == to_stop) {
427  result->dst[0][to_stop] = start_time;
428  result->prev_route[0][to_stop] = result->prev_stop[0][to_stop] = -1;
429  result->routes = 0;
430  return start_time;
431  }
432 
433  result->routes = -1;
434  if (walking_change > 1)
435  walking_change = 1;
436  if (walking_change < 0 || max_changes == -1)
437  walking_change = 0;
438  dglHeapInit(&heap);
439 
440  for (i = 0; i < rows; i++)
441  for (j = 0; j < timetable->stops; j++)
442  result->dst[i][j] = result->prev_stop[i][j] =
443  result->prev_route[i][j] = -1;
444 
445  result->dst[0][from_stop] = start_time - min_change;
446  result->prev_stop[0][from_stop] = result->prev_route[0][from_stop] = -1;
447  dglHeapData_u heap_data;
448 
449  heap_data.pv = (void *)new_heap_data(0, from_stop);
450  dglHeapInsertMin(&heap, start_time - min_change, ' ', heap_data);
451 
452  while (1) {
453  dglInt32_t v, dist, conns;
454  dglHeapNode_s heap_node;
455  int new_conns, walk_conns, update;
456 
457  if (!dglHeapExtractMin(&heap, &heap_node))
458  break;
459  v = ((neta_heap_data *)(heap_node.value.pv))->v;
460  conns = ((neta_heap_data *)(heap_node.value.pv))->conns;
461  dist = heap_node.key;
462 
463  if (dist > result->dst[conns][v])
464  continue;
465  if (v == to_stop)
466  break;
467  new_conns = (max_changes == -1) ? 0 : (conns + 1);
468  walk_conns = conns + walking_change;
469 
470  /*walking */
471  if (walk_conns < rows) {
472  /* update = (max_changes == -1 || walk_conns <=
473  * max_changes); */
474  update = 1;
475  for (i = 0; i < timetable->walk_length[v]; i++) {
476  int to = timetable->walk_stops[v][i];
477  int new_dst = dist + timetable->walk_times[v][i];
478 
479  NetA_update_dijkstra(conns, walk_conns, to, new_dst, v, -2,
480  rows, update, result, &heap);
481  }
482  }
483 
484  if (new_conns >= rows)
485  continue;
486  /*process all routes arriving after dist+min_change */
487  for (i = 0; i < timetable->stop_length[v]; i++)
488  if (timetable->stop_times[v][i] >= dist + min_change) {
489  int route = timetable->stop_routes[v][i];
490 
491  /*find the index of v on the route */
492  for (j = 0; j < timetable->route_length[route]; j++)
493  if (timetable->route_stops[route][j] == v)
494  break;
495  j++;
496  for (; j < timetable->route_length[route]; j++) {
497  int to = timetable->route_stops[route][j];
498 
499  NetA_update_dijkstra(conns, new_conns, to,
500  timetable->route_times[route][j], v,
501  route, rows, 1, result, &heap);
502  }
503  }
504  }
505  dglHeapFree(&heap, NULL);
506  opt_conns = -1;
507  for (i = 0; i < rows; i++)
508  if (result->dst[i][to_stop] != -1 &&
509  (opt_conns == -1 ||
510  result->dst[opt_conns][to_stop] > result->dst[i][to_stop]))
511  opt_conns = i;
512  result->routes = opt_conns;
513 
514  if (opt_conns != -1)
515  return result->dst[opt_conns][to_stop];
516  return -1;
517 }
518 
519 /*!
520  \brief Get time
521 
522  Get time when route "route" arrives at stop "stop" or -1.
523 
524  \param timetable pointer to neta_timetable structure
525  \param stop 'stop' node id
526  \param route route id
527 
528  \return time
529  \return -1 if not found
530  */
532  int route)
533 {
534  int i;
535 
536  for (i = 0; i < timetable->route_length[route]; i++)
537  if (timetable->route_stops[route][i] == stop)
538  return timetable->route_times[route][i];
539  return -1;
540 }
541 
542 /*!
543  \brief Free neta_timetable_result structure
544 
545  \param result pointer to neta_timetable_result structure
546  */
548 {
549  int i;
550 
551  for (i = 0; i < result->rows; i++) {
552  G_free(result->dst[i]);
553  G_free(result->prev_stop[i]);
554  G_free(result->prev_route[i]);
555  }
556  G_free(result->dst);
557  G_free(result->prev_stop);
558  G_free(result->prev_route);
559 }
#define NULL
Definition: ccmath.h:32
#define DB_SEQUENTIAL
Definition: dbmi.h:123
#define DB_OK
Definition: dbmi.h:71
#define DB_NEXT
Definition: dbmi.h:114
dbValue * db_get_column_value(dbColumn *)
Returns column value for given column structure.
dbColumn * db_get_table_column(dbTable *, int)
Returns column structure for given table and column number.
dbDriver * db_start_driver_open_database(const char *, const char *)
Open driver/database connection.
Definition: db.c:28
dbTable * db_get_cursor_table(dbCursor *)
Get table allocated by cursor.
Definition: cursor.c:67
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition: db.c:61
int db_set_string(dbString *, const char *)
Inserts string to dbString (enlarge string)
Definition: string.c:41
int db_get_value_int(dbValue *)
Get integer value.
Definition: value.c:38
void db_init_string(dbString *)
Initialize dbString.
Definition: string.c:25
int db_close_cursor(dbCursor *)
Close cursor.
Definition: c_close_cur.c:27
int db_open_select_cursor(dbDriver *, dbString *, dbCursor *, int)
Open select cursor.
Definition: c_openselect.c:37
char * db_get_string(const dbString *)
Get string.
Definition: string.c:140
int db_fetch(dbCursor *, int, int *)
Fetch data from open cursor.
Definition: c_fetch.c:28
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
#define G_calloc(m, n)
Definition: defs/gis.h:95
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
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
const struct driver * driver
Definition: driver/init.c:25
#define UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition: gis.h:46
#define _(str)
Definition: glocale.h:10
void dglHeapInit(dglHeap_s *pheap)
Definition: heap.c:27
int dglHeapInsertMin(dglHeap_s *pheap, long key, unsigned char flags, dglHeapData_u value)
Definition: heap.c:50
void dglHeapFree(dglHeap_s *pheap, dglHeapCancelItem_fn pfnCancelItem)
Definition: heap.c:35
int dglHeapExtractMin(dglHeap_s *pheap, dglHeapNode_s *pnoderet)
Definition: heap.c:76
GRASS_INTERPFL_EXPORT int count
double b
Definition: r_raster.c:39
Vector map info.
Definition: dig_structs.h:1243
long key
Definition: heap.h:35
dglHeapData_u value
Definition: heap.h:36
Definition: heap.h:41
Definition: driver.h:27
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
int ** route_times
Definition: defs/neta.h:59
int * route_length
Definition: defs/neta.h:56
int ** walk_times
Definition: defs/neta.h:70
int ** stop_times
Definition: defs/neta.h:65
int ** walk_stops
Definition: defs/neta.h:69
int ** stop_routes
Definition: defs/neta.h:63
int * stop_length
Definition: defs/neta.h:62
int * walk_length
Definition: defs/neta.h:67
int ** route_stops
Definition: defs/neta.h:58
int NetA_init_timetable_from_db(struct Map_info *In, int route_layer, int walk_layer, char *route_id, char *times, char *to_stop, char *walk_length, neta_timetable *timetable, int **route_ids, int **stop_ids)
Initialises timetable from a database.
Definition: timetables.c:116
int NetA_init_distinct(dbDriver *driver, dbString *sql, int **lengths, int **ids)
Get number of distinct elements.
Definition: timetables.c:36
void NetA_timetable_result_release(neta_timetable_result *result)
Free neta_timetable_result structure.
Definition: timetables.c:547
int NetA_timetable_get_route_time(neta_timetable *timetable, int stop, int route)
Get time.
Definition: timetables.c:531
int NetA_timetable_shortest_path(neta_timetable *timetable, int from_stop, int to_stop, int start_time, int min_change, int max_changes, int walking_change, neta_timetable_result *result)
Computes the earliest arrival time.
Definition: timetables.c:389
void NetA_update_dijkstra(int old_conns, int new_conns, int to, int new_dst, int v, int route, int rows UNUSED, int update, neta_timetable_result *result, dglHeap_s *heap)
Update Dijkstra structures.
Definition: timetables.c:352
long dglInt32_t
Definition: type.h:36
void * pv
Definition: heap.h:26