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