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