PGROUTING  2.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
alpha.c
Go to the documentation of this file.
1 /*PGR-GNU*****************************************************************
2 FILE: alpha.c
3 
4 Copyright (c) 2006 Anton A. Patrushev, Orkney, Inc.
5 Mail: project@pgrouting.org
6 
7 ------
8 
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 
23 ********************************************************************PGR-GNU*/
24 
25 #include "./../../common/src/postgres_connection.h"
26 
27 #include "catalog/pg_type.h"
28 
29 #include "../../common/src/pgr_types.h"
30 #include "alpha_driver.h"
31 
32 
33 
34 /*
35  * Define this to have profiling enabled
36  */
37 // #define PROFILE
38 
39 
40 PGDLLEXPORT Datum alphashape(PG_FUNCTION_ARGS);
41 
42 #undef DEBUG
43 #include "../../common/src/debug_macro.h"
44 
45 // The number of tuples to fetch from the SPI cursor at each iteration
46 #define TUPLIMIT 1000
47 
48 
49 static int
50 finish(int code, int ret) {
51  code = SPI_finish();
52  if (code != SPI_OK_FINISH) {
53  elog(ERROR, "couldn't disconnect from SPI");
54  return -1;
55  }
56  return ret;
57 }
58 
59 
60 typedef struct vertex_columns {
61  int id;
62  int x;
63  int y;
65 
66 
67 
68 static int
69 fetch_vertices_columns(SPITupleTable *tuptable,
71  if (tuptable) {}; // TODO this is unused parameter
72  vertex_columns->id = SPI_fnumber(SPI_tuptable->tupdesc, "id");
73  vertex_columns->x = SPI_fnumber(SPI_tuptable->tupdesc, "x");
74  vertex_columns->y = SPI_fnumber(SPI_tuptable->tupdesc, "y");
75 
76  if (vertex_columns->id == SPI_ERROR_NOATTRIBUTE ||
77  vertex_columns->x == SPI_ERROR_NOATTRIBUTE ||
78  vertex_columns->y == SPI_ERROR_NOATTRIBUTE) {
79  elog(ERROR, "Error, query must return columns "
80  "'id', 'x' and 'y'");
81  return -1;
82  }
83 
84  if (SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->id) != INT4OID ||
85  SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->x) != FLOAT8OID ||
86  SPI_gettypeid(SPI_tuptable->tupdesc, vertex_columns->y) != FLOAT8OID) {
87  elog(ERROR,
88  "Error, column 'id' must be of type int4, 'x' and 'y' must be of type float8");
89  return -1;
90  }
91 
92  return 0;
93 }
94 
95 static void
96 fetch_vertex(HeapTuple *tuple, TupleDesc *tupdesc,
97  vertex_columns_t *vertex_columns, vertex_t *target_vertex) {
98  Datum binval;
99  bool isnull;
100 
101  binval = SPI_getbinval(*tuple, *tupdesc, vertex_columns->x, &isnull);
102  if (isnull)
103  elog(ERROR, "x contains a null value");
104  target_vertex->x = DatumGetFloat8(binval);
105 
106  binval = SPI_getbinval(*tuple, *tupdesc, vertex_columns->y, &isnull);
107  if (isnull)
108  elog(ERROR, "y contains a null value");
109  target_vertex->y = DatumGetFloat8(binval);
110 }
111 
112 static int compute_alpha_shape(char* sql, float8 alpha, vertex_t **res, size_t *res_count) {
113  int SPIcode;
114  void *SPIplan;
115  Portal SPIportal;
116  bool moredata = TRUE;
117  size_t ntuples;
118  vertex_t *vertices = NULL;
119  size_t total_tuples = 0;
120 #ifndef _MSC_VER
121  vertex_columns_t vertex_columns = {.id = -1, .x = -1, .y = -1};
122 #else // _MSC_VER
123  vertex_columns_t vertex_columns = {-1, -1, -1};
124 #endif // _MSC_VER
125  char *err_msg;
126  int ret = -1;
127 
128  PGR_DBG("start alpha_shape\n");
129 
130  SPIcode = SPI_connect();
131  if (SPIcode != SPI_OK_CONNECT) {
132  elog(ERROR, "alpha_shape: couldn't open a connection to SPI");
133  return -1;
134  }
135 
136  SPIplan = SPI_prepare(sql, 0, NULL);
137  if (SPIplan == NULL) {
138  elog(ERROR, "alpha_shape: couldn't create query plan via SPI");
139  return -1;
140  }
141 
142  if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) {
143  elog(ERROR, "alpha_shape: SPI_cursor_open('%s') returns NULL", sql);
144  return -1;
145  }
146 
147  while (moredata == TRUE) {
148  SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT);
149 
150  if (vertex_columns.id == -1) {
151  if (fetch_vertices_columns(SPI_tuptable, &vertex_columns) == -1)
152  return finish(SPIcode, ret);
153  }
154 
155  ntuples = SPI_processed;
156  total_tuples += ntuples;
157  if (!vertices)
158  vertices = palloc(total_tuples * sizeof(vertex_t));
159  else
160  vertices = repalloc(vertices, total_tuples * sizeof(vertex_t));
161 
162  if (vertices == NULL) {
163  elog(ERROR, "Out of memory");
164  return finish(SPIcode, ret);
165  }
166 
167  if (ntuples > 0) {
168  uint32_t t;
169  SPITupleTable *tuptable = SPI_tuptable;
170  TupleDesc tupdesc = SPI_tuptable->tupdesc;
171 
172  for (t = 0; t < ntuples; t++) {
173  HeapTuple tuple = tuptable->vals[t];
174  fetch_vertex(&tuple, &tupdesc, &vertex_columns,
175  &vertices[total_tuples - ntuples + t]);
176  }
177  SPI_freetuptable(tuptable);
178  } else {
179  moredata = FALSE;
180  }
181  }
182 
183 
184  // if (total_tuples < 2) //this was the buggy code of the pgrouting project.
185  // TODO(someone): report this as a bug to the pgrouting project
186  // the CGAL alpha-shape function crashes if called with less than three points!!!
187 
188  if (total_tuples < 3) {
189  elog(ERROR, "Less than 3 vertices. Alpha shape calculation needs at least 3 vertices.");
190  return finish(SPIcode, ret);
191  }
192  if (total_tuples == 1) {
193  elog(ERROR, "Distance is too short. only 1 vertex for alpha shape calculation. alpha shape calculation needs at least 3 vertices.");
194  }
195  if (total_tuples == 2) {
196  elog(ERROR, "Distance is too short. only 2 vertices for alpha shape calculation. alpha shape calculation needs at least 3 vertices.");
197  }
198  if (total_tuples < 3) {
199  // elog(ERROR, "Distance is too short ....");
200  return finish(SPIcode, ret);
201  }
202 
203  PGR_DBG("Calling CGAL alpha-shape\n");
204 
205  ret = alpha_shape(vertices, total_tuples, alpha, res, res_count, &err_msg);
206 
207  if (ret < 0) {
208  // elog(ERROR, "Error computing shape: %s", err_msg);
209  ereport(ERROR, (errcode(ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED), errmsg("%s", err_msg)));
210  }
211 
212  return finish(SPIcode, ret);
213 }
214 
216 
217 PGDLLEXPORT
218 Datum alphashape(PG_FUNCTION_ARGS) {
219  FuncCallContext *funcctx;
220  TupleDesc tuple_desc;
221  vertex_t *res = NULL;
222 
223  /* stuff done only on the first call of the function */
224  if (SRF_IS_FIRSTCALL()) {
225  MemoryContext oldcontext;
226  size_t res_count;
227 
228 
229  /* create a function context for cross-call persistence */
230  funcctx = SRF_FIRSTCALL_INIT();
231 
232  /* switch to memory context appropriate for multiple function calls */
233  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
234 
235  compute_alpha_shape(text_to_cstring(PG_GETARG_TEXT_P(0)),
236  PG_GETARG_FLOAT8(1), &res, &res_count);
237 
238  /* total number of tuples to be returned */
239  PGR_DBG("Conting tuples number\n");
240 #if PGSQL_VERSION > 95
241  funcctx->max_calls = res_count;
242 #else
243  funcctx->max_calls = (uint32_t)res_count;
244 #endif
245  funcctx->user_fctx = res;
246 
247  PGR_DBG("Total count %lu", res_count);
248 
249  if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE)
250  ereport(ERROR,
251  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
252  errmsg("function returning record called in context "
253  "that cannot accept type record")));
254 
255  funcctx->tuple_desc = BlessTupleDesc(tuple_desc);
256 
257  MemoryContextSwitchTo(oldcontext);
258  }
259 
260  /* stuff done on every call of the function */
261  PGR_DBG("Strange stuff doing\n");
262  funcctx = SRF_PERCALL_SETUP();
263 
264  tuple_desc = funcctx->tuple_desc;
265  res = (vertex_t*)funcctx->user_fctx;
266 
267  PGR_DBG("Trying to allocate some memory\n");
268 
269  if (funcctx->call_cntr < funcctx->max_calls) {
270  /* do when there is more left to send */
271  HeapTuple tuple;
272  Datum result;
273  Datum *values;
274  bool* nulls;
275  double x;
276  double y;
277 
278  values = palloc(2 * sizeof(Datum));
279  nulls = palloc(2 * sizeof(bool));
280 
281  x = res[funcctx->call_cntr].x;
282  y = res[funcctx->call_cntr].y;
283  if (x == DBL_MAX && y == DBL_MAX) {
284  values[0] = 0;
285  values[1] = 0;
286  nulls[0] = true;
287  nulls[1] = true;
288  } else {
289  values[0] = Float8GetDatum(x);
290  values[1] = Float8GetDatum(y);
291  nulls[0] = false;
292  nulls[1] = false;
293  }
294 
295  PGR_DBG("Heap making\n");
296 
297  tuple = heap_form_tuple(tuple_desc, values, nulls);
298 
299  PGR_DBG("Datum making\n");
300 
301  /* make the tuple into a datum */
302  result = HeapTupleGetDatum(tuple);
303 
304  PGR_DBG("Trying to free some memory\n");
305 
306  /* clean up (this is not really necessary) */
307  pfree(values);
308  pfree(nulls);
309 
310  SRF_RETURN_NEXT(funcctx, result);
311  } else {
312  SRF_RETURN_DONE(funcctx);
313  }
314 }
PGDLLEXPORT Datum alphashape(PG_FUNCTION_ARGS)
Definition: alpha.c:218
double y
Definition: alpha_driver.h:35
static int finish(int code, int ret)
Definition: alpha.c:50
#define PGR_DBG(...)
Definition: debug_macro.h:34
PG_FUNCTION_INFO_V1(alphashape)
static int fetch_vertices_columns(SPITupleTable *tuptable, vertex_columns_t *vertex_columns)
Definition: alpha.c:69
static void fetch_vertex(HeapTuple *tuple, TupleDesc *tupdesc, vertex_columns_t *vertex_columns, vertex_t *target_vertex)
Definition: alpha.c:96
double float8
Definition: postgres.h:23
#define TUPLIMIT
Definition: alpha.c:46
struct vertex_columns vertex_columns_t
char * err_msg
Definition: BDATester.cpp:50
static int compute_alpha_shape(char *sql, float8 alpha, vertex_t **res, size_t *res_count)
Definition: alpha.c:112
int alpha_shape(vertex_t *vertices, size_t count, double alpha, vertex_t **res, size_t *res_count, char **err_msg)
double x
Definition: alpha_driver.h:34