pgRouting

root/tags/release-1.0-beta/shooting_star.c

Revision 39, 14.1 kB (checked in by anton, 1 year ago)

1.0.0b tag added

Line 
1 /*
2  * Shooting* Shortest path algorithm for PostgreSQL
3  *
4  * Copyright (c) 2007 Anton A. Patrushev, Orkney, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */
21
22 #include "postgres.h"
23 #include "executor/spi.h"
24 #include "funcapi.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <search.h>
29
30 #include <string.h>
31 #include <time.h>
32
33 #include "shooting_star.h"
34
35 //-------------------------------------------------------------------------
36
37 Datum shortest_path_shooting_star(PG_FUNCTION_ARGS);
38
39 #undef DEBUG
40 //#define DEBUG 1
41
42 #ifdef DEBUG
43 #define DBG(format, arg...)                     \
44     elog(NOTICE, format , ## arg)
45 #else
46 #define DBG(format, arg...) do { ; } while (0)
47 #endif
48
49 // The number of tuples to fetch from the SPI cursor at each iteration
50 #define TUPLIMIT 1000
51
52 static char *
53 text2char(text *in)
54 {
55   char *out = palloc(VARSIZE(in));
56
57   memcpy(out, VARDATA(in), VARSIZE(in) - VARHDRSZ);
58   out[VARSIZE(in) - VARHDRSZ] = '\0';
59   return out;
60 }
61
62 static int
63 finish(int code, int ret)
64 {
65   code = SPI_finish();
66   if (code  != SPI_OK_FINISH )
67     {
68       elog(ERROR,"couldn't disconnect from SPI");
69       return -1 ;
70     }
71  
72   return ret;
73 }
74  
75 typedef struct edge_shooting_star_columns
76 {
77   int id;
78   int source;
79   int target;
80   int cost;
81   int reverse_cost;
82   int s_x;
83   int s_y;
84   int t_x;
85   int t_y;
86   int to_cost;//cost of transit to adjacent edge
87   int rule;
88 } edge_shooting_star_columns_t;
89
90 static int
91 fetch_edge_shooting_star_columns(SPITupleTable *tuptable,
92                          edge_shooting_star_columns_t *edge_columns,
93                          bool has_reverse_cost)
94 {
95   edge_columns->id = SPI_fnumber(SPI_tuptable->tupdesc, "id");
96   edge_columns->source = SPI_fnumber(SPI_tuptable->tupdesc, "source");
97   edge_columns->target = SPI_fnumber(SPI_tuptable->tupdesc, "target");
98   edge_columns->cost = SPI_fnumber(SPI_tuptable->tupdesc, "cost");
99   if (edge_columns->id == SPI_ERROR_NOATTRIBUTE ||
100       edge_columns->source == SPI_ERROR_NOATTRIBUTE ||
101       edge_columns->target == SPI_ERROR_NOATTRIBUTE ||
102       edge_columns->cost == SPI_ERROR_NOATTRIBUTE)
103     {
104       elog(ERROR, "Error, query must return columns "
105            "'id', 'source', 'target' and 'cost'");
106       return -1;
107     }
108
109   if (SPI_gettypeid(SPI_tuptable->tupdesc,
110                     edge_columns->source) != INT4OID ||
111       SPI_gettypeid(SPI_tuptable->tupdesc,
112                     edge_columns->target) != INT4OID ||
113       SPI_gettypeid(SPI_tuptable->tupdesc, edge_columns->cost) != FLOAT8OID)
114     {
115       elog(ERROR, "Error, columns 'source', 'target' must be of type int4, "
116            "'cost' must be of type float8");
117       return -1;
118     }
119
120   DBG("columns: id %i source %i target %i cost %i",
121       edge_columns->id, edge_columns->source,
122       edge_columns->target, edge_columns->cost);
123
124   if (has_reverse_cost)
125     {
126       edge_columns->reverse_cost = SPI_fnumber(SPI_tuptable->tupdesc,
127                                                "reverse_cost");
128
129       if (edge_columns->reverse_cost == SPI_ERROR_NOATTRIBUTE)
130         {
131           elog(ERROR, "Error, reverse_cost is used, but query did't return "
132                "'reverse_cost' column");
133           return -1;
134         }
135
136       if (SPI_gettypeid(SPI_tuptable->tupdesc,
137                         edge_columns->reverse_cost) != FLOAT8OID)
138         {
139           elog(ERROR, "Error, columns 'reverse_cost' must be of type float8");
140           return -1;
141         }
142
143       DBG("columns: reverse_cost cost %i", edge_columns->reverse_cost);
144     }
145
146   edge_columns->s_x = SPI_fnumber(SPI_tuptable->tupdesc, "x1");
147   edge_columns->s_y = SPI_fnumber(SPI_tuptable->tupdesc, "y1");
148   edge_columns->t_x = SPI_fnumber(SPI_tuptable->tupdesc, "x2");
149   edge_columns->t_y = SPI_fnumber(SPI_tuptable->tupdesc, "y2");
150
151   if (edge_columns->s_x == SPI_ERROR_NOATTRIBUTE ||
152       edge_columns->s_y == SPI_ERROR_NOATTRIBUTE ||
153       edge_columns->t_x == SPI_ERROR_NOATTRIBUTE ||
154       edge_columns->t_y == SPI_ERROR_NOATTRIBUTE)
155     {
156       elog(ERROR, "Error, query must return columns "
157            "'x1', 'x2', 'y1' and 'y2'");
158       return -1;
159     }
160
161   DBG("columns: x1 %i y1 %i x2 %i y2 %i",
162       edge_columns->s_x, edge_columns->s_y,
163       edge_columns->t_x,edge_columns->t_y);
164    
165
166   edge_columns->to_cost = SPI_fnumber(SPI_tuptable->tupdesc, "to_cost");
167   edge_columns->rule = SPI_fnumber(SPI_tuptable->tupdesc, "rule");
168
169   if (edge_columns->to_cost == SPI_ERROR_NOATTRIBUTE ||
170       edge_columns->rule == SPI_ERROR_NOATTRIBUTE)
171     {
172       elog(ERROR, "Error, query must return columns "
173            "'to_cost' and 'rule'");
174       return -1;
175     }
176
177   return 0;
178 }
179
180 //edges should be ordered by id or else we have to search for
181 //existing edges every time we want to add adjacent edge
182 static void
183 fetch_edge_shooting_star(HeapTuple *tuple, TupleDesc *tupdesc,
184                  edge_shooting_star_columns_t *edge_columns,
185                  edge_shooting_star_t *target_edge)
186 {
187   Datum binval;
188   bool isnull;
189   int t;
190
191   for(t=0; t<MAX_RULE_LENGTH;++t)
192     target_edge->rule[t] = -1;
193    
194   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->id, &isnull);
195   if (isnull)
196     elog(ERROR, "id contains a null value");
197   target_edge->id = DatumGetInt32(binval);
198  
199   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->source, &isnull);
200   if (isnull)
201     elog(ERROR, "source contains a null value");
202   target_edge->source = DatumGetInt32(binval);
203
204   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->target, &isnull);
205   if (isnull)
206     elog(ERROR, "target contains a null value");
207   target_edge->target = DatumGetInt32(binval);
208
209   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->cost, &isnull);
210   if (isnull)
211     elog(ERROR, "cost contains a null value");
212   target_edge->cost = DatumGetFloat8(binval);
213
214   if (edge_columns->reverse_cost != -1)
215     {
216       binval = SPI_getbinval(*tuple, *tupdesc,
217                              edge_columns->reverse_cost, &isnull);
218       if (isnull)
219         elog(ERROR, "reverse_cost contains a null value");
220       target_edge->reverse_cost =  DatumGetFloat8(binval);
221     }
222
223   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->s_x, &isnull);
224   if (isnull)
225     elog(ERROR, "source x contains a null value");
226   target_edge->s_x = DatumGetFloat8(binval);
227
228   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->s_y, &isnull);
229   if (isnull)
230     elog(ERROR, "source y contains a null value");
231   target_edge->s_y = DatumGetFloat8(binval);
232  
233   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->t_x, &isnull);
234   if (isnull)
235     elog(ERROR, "target x contains a null value");
236   target_edge->t_x = DatumGetFloat8(binval);
237  
238   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->t_y, &isnull);
239   if (isnull)
240     elog(ERROR, "target y contains a null value");
241   target_edge->t_y = DatumGetFloat8(binval);
242
243   binval = SPI_getbinval(*tuple, *tupdesc, edge_columns->to_cost, &isnull);
244   if (isnull)
245     target_edge->to_cost = 0;
246    
247   else
248     target_edge->to_cost = DatumGetFloat8(binval);
249
250   char *str = DatumGetCString(SPI_getvalue(*tuple, *tupdesc, edge_columns->rule));
251
252   char* pch;
253   int ci = MAX_RULE_LENGTH;
254
255   pch = (char *)strtok (str," ,");
256  
257   while (pch != NULL)
258   {
259     --ci;
260     target_edge->rule[ci] = atoi(pch);
261     pch = (char *)strtok (NULL, " ,");
262   }
263
264 }
265
266
267 static int compute_shortest_path_shooting_star(char* sql, int source_edge_id,
268                                        int target_edge_id, bool directed,
269                                        bool has_reverse_cost,
270                                        path_element_t **path, int *path_count)
271 {
272  
273   int SPIcode;
274   void *SPIplan;
275   Portal SPIportal;
276   bool moredata = TRUE;
277   int ntuples;
278   edge_shooting_star_t *edges = NULL;
279   int total_tuples = 0;
280  
281 //  int v_max_id=0;
282 //  int v_min_id=INT_MAX; 
283   int e_max_id=0;
284   int e_min_id=INT_MAX; 
285    
286   edge_shooting_star_columns_t edge_columns = {id: -1, source: -1, target: -1,
287                                        cost: -1, reverse_cost: -1,
288                                        s_x: -1, s_y: -1, t_x: -1, t_y: -1,
289                                        to_cost: -1, rule: -1};
290   char *err_msg;
291   int ret = -1;
292   register int z, t;
293  
294   int s_count=0;
295   int t_count=0;
296  
297   DBG("start shortest_path_shooting_star\n");
298        
299   SPIcode = SPI_connect();
300   if (SPIcode  != SPI_OK_CONNECT)
301     {
302       elog(ERROR, "shortest_path_shooting_star: couldn't open a connection to SPI");
303       return -1;
304     }
305
306   SPIplan = SPI_prepare(sql, 0, NULL);
307   if (SPIplan  == NULL)
308     {
309       elog(ERROR, "shortest_path_shooting_star: couldn't create query plan via SPI");
310       return -1;
311     }
312
313   if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL)
314     {
315       elog(ERROR, "shortest_path_shooting_star: SPI_cursor_open('%s') returns NULL",
316            sql);
317       return -1;
318     }
319
320   while (moredata == TRUE)
321     {
322       SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT);
323
324       if (edge_columns.id == -1)
325         {
326           if (fetch_edge_shooting_star_columns(SPI_tuptable, &edge_columns,
327                                        has_reverse_cost) == -1)
328             return finish(SPIcode, ret);
329         }
330        
331         //DBG("***%i***", ret);
332
333       ntuples = SPI_processed;
334       total_tuples += ntuples;
335
336       if (!edges)
337         edges = palloc(total_tuples * sizeof(edge_shooting_star_t));
338       else
339         edges = repalloc(edges, total_tuples * sizeof(edge_shooting_star_t));
340
341       if (edges == NULL)
342         {
343           elog(ERROR, "Out of memory");
344           return finish(SPIcode, ret);
345         }
346
347       if (ntuples > 0)
348         {
349           int t;
350           SPITupleTable *tuptable = SPI_tuptable;
351           TupleDesc tupdesc = SPI_tuptable->tupdesc;
352          
353           for (t = 0; t < ntuples; t++)
354             {
355               HeapTuple tuple = tuptable->vals[t];
356               fetch_edge_shooting_star(&tuple, &tupdesc, &edge_columns,
357                                &edges[total_tuples - ntuples + t]);
358             }
359           SPI_freetuptable(tuptable);
360         }
361       else
362         {
363           moredata = FALSE;
364         }
365     }
366    
367      
368   DBG("Total %i tuples", total_tuples);
369
370    
371
372   for(z=0; z<total_tuples; z++)
373   {
374     if(edges[z].id<e_min_id)
375       e_min_id=edges[z].id;
376
377     if(edges[z].id>e_max_id)
378       e_max_id=edges[z].id;
379
380   }
381
382     DBG("E : %i <-> %i", e_min_id, e_max_id);
383
384   for(z=0; z<total_tuples; ++z)
385   {
386
387     //check if edges[] contains source and target
388     if(edges[z].id == source_edge_id ||
389        edges[z].id == source_edge_id)
390       ++s_count;
391     if(edges[z].id == target_edge_id ||
392        edges[z].id == target_edge_id)
393       ++t_count;
394
395
396     //edges[z].source-=v_min_id;
397     //edges[z].target-=v_min_id;
398    
399   }
400    
401   DBG("Total %i tuples", total_tuples);
402
403   if(s_count == 0)
404   {
405     elog(ERROR, "Start edge was not found.");
406     return -1;
407   }
408              
409   if(t_count == 0)
410   {
411     elog(ERROR, "Target edge was not found.");
412     return -1;
413   }
414                            
415   DBG("Total %i tuples", total_tuples);
416
417   DBG("Calling boost_shooting_star <%i>\n", total_tuples);
418
419   //time_t stime = time(NULL);   
420
421   ret = boost_shooting_star(edges, total_tuples, source_edge_id,
422                     target_edge_id,
423                     directed, has_reverse_cost,
424                     path, path_count, &err_msg, e_max_id);
425
426   //time_t etime = time(NULL);   
427
428   //DBG("Path was calculated in %f seconds. \n", difftime(etime, stime));
429
430   DBG("SIZE %i\n",*path_count);
431
432   DBG("ret =  %i\n",ret);
433  
434
435   if (ret < 0)
436     {
437       ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED),
438         errmsg("Error computing path: %s", err_msg)));
439     }
440   return finish(SPIcode, ret);
441 }
442
443
444 PG_FUNCTION_INFO_V1(shortest_path_shooting_star);
445 Datum
446 shortest_path_shooting_star(PG_FUNCTION_ARGS)
447 {
448   FuncCallContext     *funcctx;
449   int                  call_cntr;
450   int                  max_calls;
451   TupleDesc            tuple_desc;
452   path_element_t      *path;
453  
454   /* stuff done only on the first call of the function */
455   if (SRF_IS_FIRSTCALL())
456     {
457       MemoryContext   oldcontext;
458       int path_count = 0;
459       int ret;
460
461       /* create a function context for cross-call persistence */
462       funcctx = SRF_FIRSTCALL_INIT();
463      
464       /* switch to memory context appropriate for multiple function calls */
465       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
466
467
468       ret = compute_shortest_path_shooting_star(text2char(PG_GETARG_TEXT_P(0)),
469                                         PG_GETARG_INT32(1),
470                                         PG_GETARG_INT32(2),
471                                         PG_GETARG_BOOL(3),
472                                         PG_GETARG_BOOL(4),
473                                         &path, &path_count);
474
475 #ifdef DEBUG
476       DBG("Ret is %i", ret);
477       if (ret >= 0)
478         {
479           int i;
480           for (i = 0; i < path_count; i++)
481             {
482               DBG("Step # %i vertex_id  %i ", i, path[i].vertex_id);
483               DBG("        edge_id    %i ", path[i].edge_id);
484               DBG("        cost       %f ", path[i].cost);
485             }
486         }
487 #endif
488
489       /* total number of tuples to be returned */
490       DBG("Conting tuples number\n");
491       funcctx->max_calls = path_count;
492       funcctx->user_fctx = path;
493      
494       DBG("Path count %i", path_count);
495      
496       funcctx->tuple_desc =
497         BlessTupleDesc(RelationNameGetTupleDesc("path_result"));
498
499       MemoryContextSwitchTo(oldcontext);
500     }
501
502   /* stuff done on every call of the function */
503
504   funcctx = SRF_PERCALL_SETUP();
505  
506   call_cntr = funcctx->call_cntr;
507   max_calls = funcctx->max_calls;
508   tuple_desc = funcctx->tuple_desc;
509   path = (path_element_t*) funcctx->user_fctx;
510  
511   DBG("Trying to allocate some memory\n");
512
513   if (call_cntr < max_calls)    /* do when there is more left to send */
514     {
515       HeapTuple    tuple;
516       Datum        result;
517       Datum *values;
518       char* nulls;
519      
520       values = palloc(3 * sizeof(Datum));
521       nulls = palloc(3 * sizeof(char));
522  
523       values[0] = Int32GetDatum(path[call_cntr].vertex_id);
524       nulls[0] = ' ';
525       values[1] = Int32GetDatum(path[call_cntr].edge_id);
526       nulls[1] = ' ';
527       values[2] = Float8GetDatum(path[call_cntr].cost);
528       nulls[2] = ' ';
529
530       tuple = heap_formtuple(tuple_desc, values, nulls);
531      
532       /* make the tuple into a datum */
533       result = HeapTupleGetDatum(tuple);
534      
535
536       /* clean up (this is not really necessary) */
537       pfree(values);
538       pfree(nulls);
539      
540       SRF_RETURN_NEXT(funcctx, result);
541     }
542   else    /* do when there is no more left */
543     {
544       SRF_RETURN_DONE(funcctx);
545     }
546 }
Note: See TracBrowser for help on using the browser.