PostGIS  2.2.8dev-r@@SVN_REVISION@@
lwgeom_backend_api.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * Wrapper around external librairies functions (GEOS/CGAL...)
7  *
8  * Copyright 2012-2013 Oslandia <infos@oslandia.com>
9  *
10  * This is free software; you can redistribute and/or modify it under
11  * the terms of the GNU General Public Licence. See the COPYING file.
12  *
13  **********************************************************************/
14 
15 #include "postgres.h"
16 #include "fmgr.h"
17 #include "utils/guc.h" /* for custom variables */
18 
19 #include "../postgis_config.h"
20 #include "lwgeom_pg.h"
21 #include "liblwgeom.h"
22 
23 #include "lwgeom_backend_api.h"
24 #include "lwgeom_geos.h"
25 #if HAVE_SFCGAL
26 #include "lwgeom_sfcgal.h"
27 #endif
28 
29 Datum intersects(PG_FUNCTION_ARGS);
30 Datum intersects3d(PG_FUNCTION_ARGS);
31 Datum intersection(PG_FUNCTION_ARGS);
32 Datum difference(PG_FUNCTION_ARGS);
33 Datum geomunion(PG_FUNCTION_ARGS);
34 Datum area(PG_FUNCTION_ARGS);
35 Datum distance(PG_FUNCTION_ARGS);
36 Datum distance3d(PG_FUNCTION_ARGS);
37 
38 Datum intersects3d_dwithin(PG_FUNCTION_ARGS);
39 
40 
42 {
43  const char* name;
44  Datum (*intersects_fn) (PG_FUNCTION_ARGS);
45  Datum (*intersects3d_fn) (PG_FUNCTION_ARGS);
46  Datum (*intersection_fn) (PG_FUNCTION_ARGS);
47  Datum (*difference_fn) (PG_FUNCTION_ARGS);
48  Datum (*union_fn) (PG_FUNCTION_ARGS);
49  Datum (*area_fn) (PG_FUNCTION_ARGS);
50  Datum (*distance_fn) (PG_FUNCTION_ARGS);
51  Datum (*distance3d_fn) (PG_FUNCTION_ARGS);
52 };
53 
54 #if HAVE_SFCGAL
55 #define LWGEOM_NUM_BACKENDS 2
56 #else
57 #define LWGEOM_NUM_BACKENDS 1
58 #endif
59 
61  { .name = "geos",
62  .intersects_fn = geos_intersects,
63  .intersects3d_fn = intersects3d_dwithin,
64  .intersection_fn = geos_intersection,
65  .difference_fn = geos_difference,
66  .union_fn = geos_geomunion,
67  .area_fn = LWGEOM_area_polygon,
68  .distance_fn = LWGEOM_mindistance2d,
69  .distance3d_fn = LWGEOM_mindistance3d
70  },
71 #if HAVE_SFCGAL
72  { .name = "sfcgal",
73  .intersects_fn = sfcgal_intersects,
74  .intersects3d_fn = sfcgal_intersects3D,
75  .intersection_fn = sfcgal_intersection,
76  .difference_fn = sfcgal_difference,
77  .union_fn = sfcgal_union,
78  .area_fn = sfcgal_area,
79  .distance_fn = sfcgal_distance,
80  .distance3d_fn = sfcgal_distance3D
81  }
82 #endif
83 };
84 
85 
86 /* Geometry Backend */
88 struct lwgeom_backend_definition* lwgeom_backend = &lwgeom_backends[0];
89 
90 static void lwgeom_backend_switch( const char* newvalue, void* extra )
91 {
92  int i;
93 
94  if (!newvalue) { return; }
95 
96  for ( i = 0; i < LWGEOM_NUM_BACKENDS; ++i ) {
97  if ( !strcmp(lwgeom_backends[i].name, newvalue) ) {
98  lwgeom_backend = &lwgeom_backends[i];
99  return;
100  }
101  }
102  lwpgerror("Can't find %s geometry backend", newvalue );
103 }
104 
105 
106 #include "utils/guc.h"
107 #include "utils/guc_tables.h"
108 
109 
110 /*
111  * the bare comparison function for GUC names
112  */
113 static int
114 guc_name_compare(const char *namea, const char *nameb)
115 {
116  /*
117  * The temptation to use strcasecmp() here must be resisted, because the
118  * array ordering has to remain stable across setlocale() calls. So, build
119  * our own with a simple ASCII-only downcasing.
120  */
121  while (*namea && *nameb)
122  {
123  char cha = *namea++;
124  char chb = *nameb++;
125 
126  if (cha >= 'A' && cha <= 'Z')
127  cha += 'a' - 'A';
128  if (chb >= 'A' && chb <= 'Z')
129  chb += 'a' - 'A';
130  if (cha != chb)
131  return cha - chb;
132  }
133  if (*namea)
134  return 1; /* a is longer */
135  if (*nameb)
136  return -1; /* b is longer */
137  return 0;
138 }
139 
140 /*
141  * comparator for qsorting and bsearching guc_variables array
142  */
143 static int
144 guc_var_compare(const void *a, const void *b)
145 {
146  const struct config_generic *confa = *(struct config_generic * const *) a;
147  const struct config_generic *confb = *(struct config_generic * const *) b;
148 
149  return guc_name_compare(confa->name, confb->name);
150 }
151 
152 /*
153 * This is copied from the top half of the find_option function
154 * in postgresql's guc.c. We search the guc_variables for our option.
155 * Then we make sure it's not a placeholder. Only then are we sure
156 * we have a potential conflict, of the sort experienced during an
157 * extension upgrade.
158 */
159 static int
160 guc_find_option(const char *name)
161 {
162  const char **key = &name;
163  struct config_generic **res;
164 
165  /*
166  * By equating const char ** with struct config_generic *, we are assuming
167  * the name field is first in config_generic.
168  */
169  res = (struct config_generic **) bsearch((void *) &key,
170  (void *) get_guc_variables(),
171  GetNumConfigOptions(),
172  sizeof(struct config_generic *),
174 
175  /* Found nothing? Good */
176  if ( ! res ) return 0;
177 
178  /* Hm, you found something, but maybe it's just a placeholder? */
179  /* We'll consider a placehold a "not found" */
180  if ( (*res)->flags & GUC_CUSTOM_PLACEHOLDER )
181  return 0;
182 
183  return 1;
184 }
185 
186 
188 {
189  /* #2382 Before trying to create a user GUC, make sure */
190  /* that the name is not already in use. Why would it be in use? */
191  /* During an upgrade, a prior copy of the PostGIS library will */
192  /* already be loaded in memory and the GUC already defined. We */
193  /* can skip GUC definition in this case, so we just return. */
194  static const char *guc_name = "postgis.backend";
195  // const char *guc_installed = GetConfigOption(guc_name, TRUE, FALSE);
196 
197  /* Uh oh, this GUC name already exists. Ordinarily we could just go on */
198  /* our way, but the way the postgis.backend works is by using the "assign" */
199  /* callback to change which backend is in use by flipping a global variable */
200  /* over. This saves the overhead of looking up the engine every time, at */
201  /* the expense of the extra complexity. */
202  if ( guc_find_option(guc_name) )
203  {
204  /* In this narrow case the previously installed GUC is tied to the callback in */
205  /* the previously loaded library. Probably this is happening during an */
206  /* upgrade, so the old library is where the callback ties to. */
207  elog(WARNING, "'%s' is already set and cannot be changed until you reconnect", guc_name);
208  return;
209  }
210 
211  /* Good, the GUC name is not already in use, so this must be a fresh */
212  /* and clean new load of the library, and we can define the user GUC */
213  DefineCustomStringVariable( guc_name, /* name */
214  "Sets the PostGIS Geometry Backend.", /* short_desc */
215  "Sets the PostGIS Geometry Backend (allowed values are 'geos' or 'sfcgal')", /* long_desc */
216  &lwgeom_backend_name, /* valueAddr */
217  (char *)lwgeom_backends[0].name, /* bootValue */
218  PGC_USERSET, /* GucContext context */
219  0, /* int flags */
220 #if POSTGIS_PGSQL_VERSION >= 91
221  NULL, /* GucStringCheckHook check_hook */
222 #endif
223  lwgeom_backend_switch, /* GucStringAssignHook assign_hook */
224  NULL /* GucShowHook show_hook */
225  );
226 }
227 
228 #if 0
229 
230 backend/utils/misc/guc.h
231 int GetNumConfigOptions(void) returns num_guc_variables
232 
233 backend/utils/misc/guc_tables.h
234 struct config_generic ** get_guc_variables(void)
235 
236 
237 
238 
239 #endif
240 
242 Datum intersects(PG_FUNCTION_ARGS)
243 {
244  return (*lwgeom_backend->intersects_fn)( fcinfo );
245 }
246 
248 Datum intersection(PG_FUNCTION_ARGS)
249 {
250  return (*lwgeom_backend->intersection_fn)( fcinfo );
251 }
252 
254 Datum difference(PG_FUNCTION_ARGS)
255 {
256  return (*lwgeom_backend->difference_fn)( fcinfo );
257 }
258 
260 Datum geomunion(PG_FUNCTION_ARGS)
261 {
262  return (*lwgeom_backend->union_fn)( fcinfo );
263 }
264 
266 Datum area(PG_FUNCTION_ARGS)
267 {
268  return (*lwgeom_backend->area_fn)( fcinfo );
269 }
270 
272 Datum distance(PG_FUNCTION_ARGS)
273 {
274  return (*lwgeom_backend->distance_fn)( fcinfo );
275 }
276 
278 Datum distance3d(PG_FUNCTION_ARGS)
279 {
280  return (*lwgeom_backend->distance3d_fn)( fcinfo );
281 }
282 
284 Datum intersects3d(PG_FUNCTION_ARGS)
285 {
286  return (*lwgeom_backend->intersects3d_fn)( fcinfo );
287 }
288 
289 
290 
291 /* intersects3d through dwithin
292  * used by the 'geos' backend
293  */
295 Datum intersects3d_dwithin(PG_FUNCTION_ARGS)
296 {
297  double mindist;
298  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
299  GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
300  LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
301  LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
302 
303  error_if_srid_mismatch(lwgeom1->srid, lwgeom2->srid);
304 
305  mindist = lwgeom_mindistance3d_tolerance(lwgeom1,lwgeom2,0.0);
306 
307  PG_FREE_IF_COPY(geom1, 0);
308  PG_FREE_IF_COPY(geom2, 1);
309  /*empty geometries cases should be right handled since return from underlying
310  functions should be FLT_MAX which causes false as answer*/
311  PG_RETURN_BOOL(0.0 == mindist);
312 }
double lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance)
Function handling 3d min distance calculations and dwithin calculations.
Definition: measures3d.c:339
Datum(* distance_fn)(PG_FUNCTION_ARGS)
tuple res
Definition: window.py:78
Datum geomunion(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(intersects)
Datum distance3d(PG_FUNCTION_ARGS)
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
static int guc_find_option(const char *name)
Datum(* difference_fn)(PG_FUNCTION_ARGS)
Datum area(PG_FUNCTION_ARGS)
Datum intersects3d_dwithin(PG_FUNCTION_ARGS)
Datum LWGEOM_mindistance3d(PG_FUNCTION_ARGS)
static int guc_name_compare(const char *namea, const char *nameb)
static int guc_var_compare(const void *a, const void *b)
char * lwgeom_backend_name
Datum sfcgal_distance3D(PG_FUNCTION_ARGS)
void error_if_srid_mismatch(int srid1, int srid2)
Definition: lwutil.c:341
Datum(* union_fn)(PG_FUNCTION_ARGS)
Datum intersection(PG_FUNCTION_ARGS)
int32_t srid
Definition: liblwgeom.h:383
Datum geos_geomunion(PG_FUNCTION_ARGS)
void lwgeom_init_backend()
Datum(* distance3d_fn)(PG_FUNCTION_ARGS)
Datum intersects(PG_FUNCTION_ARGS)
Datum sfcgal_intersects3D(PG_FUNCTION_ARGS)
#define POSTGIS_PGSQL_VERSION
Definition: sqldefines.h:9
Datum(* intersects3d_fn)(PG_FUNCTION_ARGS)
struct lwgeom_backend_definition * lwgeom_backend
Datum intersects3d(PG_FUNCTION_ARGS)
Datum(* area_fn)(PG_FUNCTION_ARGS)
Datum distance(PG_FUNCTION_ARGS)
Datum sfcgal_union(PG_FUNCTION_ARGS)
Datum sfcgal_intersects(PG_FUNCTION_ARGS)
Datum LWGEOM_area_polygon(PG_FUNCTION_ARGS)
#define LWGEOM_NUM_BACKENDS
Datum geos_intersects(PG_FUNCTION_ARGS)
static void lwgeom_backend_switch(const char *newvalue, void *extra)
Datum LWGEOM_mindistance2d(PG_FUNCTION_ARGS)
Datum sfcgal_distance(PG_FUNCTION_ARGS)
Datum geos_difference(PG_FUNCTION_ARGS)
Datum sfcgal_intersection(PG_FUNCTION_ARGS)
Datum sfcgal_area(PG_FUNCTION_ARGS)
Datum(* intersection_fn)(PG_FUNCTION_ARGS)
Datum sfcgal_difference(PG_FUNCTION_ARGS)
Datum(* intersects_fn)(PG_FUNCTION_ARGS)
Datum geos_intersection(PG_FUNCTION_ARGS)
struct lwgeom_backend_definition lwgeom_backends[LWGEOM_NUM_BACKENDS]
This library is the generic geometry handling section of PostGIS.
Datum difference(PG_FUNCTION_ARGS)