PostGIS  3.4.0dev-r@@SVN_REVISION@@
geography_measurement_trees.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS 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  * PostGIS 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 PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * ^copyright^
22  *
23  **********************************************************************/
24 
26 
27 
28 /*
29 * Specific tree types include all the generic slots and
30 * their own slots for their trees. We put the implementation
31 * for the CircTreeGeomCache here because we can't shove
32 * the PgSQL specific bits of the code (fcinfo) back into
33 * liblwgeom, where most of the circtree logic lives.
34 */
35 typedef struct {
36  GeomCache gcache;
39 
40 
41 
45 static int
46 CircTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
47 {
48  CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache;
49  CIRC_NODE* tree = lwgeom_calculate_circ_tree(lwgeom);
50 
51  if ( circ_cache->index )
52  {
53  circ_tree_free(circ_cache->index);
54  circ_cache->index = 0;
55  }
56  if ( ! tree )
57  return LW_FAILURE;
58 
59  circ_cache->index = tree;
60  return LW_SUCCESS;
61 }
62 
63 static int
64 CircTreeFreer(GeomCache* cache)
65 {
66  CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache;
67  if ( circ_cache->index )
68  {
69  circ_tree_free(circ_cache->index);
70  circ_cache->index = 0;
71  circ_cache->gcache.argnum = 0;
72  }
73  return LW_SUCCESS;
74 }
75 
76 static GeomCache*
78 {
79  CircTreeGeomCache* cache = palloc(sizeof(CircTreeGeomCache));
80  memset(cache, 0, sizeof(CircTreeGeomCache));
81  return (GeomCache*)cache;
82 }
83 
84 static GeomCacheMethods CircTreeCacheMethods =
85 {
86  CIRC_CACHE_ENTRY,
90 };
91 
92 static CircTreeGeomCache *
93 GetCircTreeGeomCache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2)
94 {
95  return (CircTreeGeomCache*)GetGeomCache(fcinfo, &CircTreeCacheMethods, g1, g2);
96 }
97 
98 static int
99 CircTreePIP(const CIRC_NODE* tree1, const GSERIALIZED* g1, const POINT4D* in_point)
100 {
101  int tree1_type = gserialized_get_type(g1);
102  GBOX gbox1;
103  GEOGRAPHIC_POINT in_gpoint;
104  POINT3D in_point3d;
105 
106  POSTGIS_DEBUGF(3, "tree1_type=%d", tree1_type);
107 
108  /* If the tree'ed argument is a polygon, do the P-i-P using the tree-based P-i-P */
109  if ( tree1_type == POLYGONTYPE || tree1_type == MULTIPOLYGONTYPE )
110  {
111  POSTGIS_DEBUG(3, "tree is a polygon, using tree PiP");
112  /* Need a gbox to calculate an outside point */
113  if ( LW_FAILURE == gserialized_get_gbox_p(g1, &gbox1) )
114  {
115  LWGEOM* lwgeom1 = lwgeom_from_gserialized(g1);
116  POSTGIS_DEBUG(3, "unable to read gbox from gserialized, calculating from scratch");
117  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
118  lwgeom_free(lwgeom1);
119  }
120 
121  /* Flip the candidate point into geographics */
122  geographic_point_init(in_point->x, in_point->y, &in_gpoint);
123  geog2cart(&in_gpoint, &in_point3d);
124 
125  /* If the candidate isn't in the tree box, it's not in the tree area */
126  if ( ! gbox_contains_point3d(&gbox1, &in_point3d) )
127  {
128  POSTGIS_DEBUG(3, "in_point3d is not inside the tree gbox, CircTreePIP returning FALSE");
129  return LW_FALSE;
130  }
131  /* The candidate point is in the box, so it *might* be inside the tree */
132  else
133  {
134  POINT2D pt2d_outside; /* latlon */
135  POINT2D pt2d_inside;
136  pt2d_inside.x = in_point->x;
137  pt2d_inside.y = in_point->y;
138  /* Calculate a definitive outside point */
139  if (gbox_pt_outside(&gbox1, &pt2d_outside) == LW_FAILURE)
140  if (circ_tree_get_point_outside(tree1, &pt2d_outside) == LW_FAILURE)
141  lwpgerror("%s: Unable to generate outside point!", __func__);
142 
143  POSTGIS_DEBUGF(3, "p2d_inside=POINT(%g %g) p2d_outside=POINT(%g %g)", pt2d_inside.x, pt2d_inside.y, pt2d_outside.x, pt2d_outside.y);
144  /* Test the candidate point for strict containment */
145  POSTGIS_DEBUG(3, "calling circ_tree_contains_point for PiP test");
146  return circ_tree_contains_point(tree1, &pt2d_inside, &pt2d_outside, 0, NULL);
147  }
148  }
149  else
150  {
151  POSTGIS_DEBUG(3, "tree1 not polygonal, so CircTreePIP returning FALSE");
152  return LW_FALSE;
153  }
154 }
155 
156 static int
157 geography_distance_cache_tolerance(FunctionCallInfo fcinfo,
158  SHARED_GSERIALIZED *shared_g1,
159  SHARED_GSERIALIZED *shared_g2,
160  const SPHEROID *s,
161  double tolerance,
162  double *distance)
163 {
164  const GSERIALIZED *g1 = shared_gserialized_get(shared_g1);
165  const GSERIALIZED *g2 = shared_gserialized_get(shared_g2);
166  CircTreeGeomCache* tree_cache = NULL;
167 
168  int type1 = gserialized_get_type(g1);
169  int type2 = gserialized_get_type(g2);
170 
171  Assert(distance);
172 
173  /* Two points? Get outa here... */
174  if ( type1 == POINTTYPE && type2 == POINTTYPE )
175  return LW_FAILURE;
176 
177  /* Fetch/build our cache, if appropriate, etc... */
178  tree_cache = GetCircTreeGeomCache(fcinfo, shared_g1, shared_g2);
179 
180  /* OK, we have an index at the ready! Use it for the one tree argument and */
181  /* fill in the other tree argument */
182  if ( tree_cache && tree_cache->gcache.argnum && tree_cache->index )
183  {
184  CIRC_NODE* circtree_cached = tree_cache->index;
185  CIRC_NODE* circtree = NULL;
186  const GSERIALIZED* g_cached;
187  const GSERIALIZED* g;
188  LWGEOM* lwgeom = NULL;
189  int geomtype_cached;
190  int geomtype;
191  POINT4D p4d;
192 
193  /* We need to dynamically build a tree for the uncached side of the function call */
194  if ( tree_cache->gcache.argnum == 1 )
195  {
196  g_cached = g1;
197  g = g2;
198  geomtype_cached = type1;
199  geomtype = type2;
200  }
201  else if ( tree_cache->gcache.argnum == 2 )
202  {
203  g_cached = g2;
204  g = g1;
205  geomtype_cached = type2;
206  geomtype = type1;
207  }
208  else
209  {
210  lwpgerror("geography_distance_cache this cannot happen!");
211  return LW_FAILURE;
212  }
213 
214  lwgeom = lwgeom_from_gserialized(g);
215  if ( geomtype_cached == POLYGONTYPE || geomtype_cached == MULTIPOLYGONTYPE )
216  {
217  lwgeom_startpoint(lwgeom, &p4d);
218  if ( CircTreePIP(circtree_cached, g_cached, &p4d) )
219  {
220  *distance = 0.0;
221  lwgeom_free(lwgeom);
222  return LW_SUCCESS;
223  }
224  }
225 
226  circtree = lwgeom_calculate_circ_tree(lwgeom);
227  if ( geomtype == POLYGONTYPE || geomtype == MULTIPOLYGONTYPE )
228  {
229  POINT2D p2d;
230  circ_tree_get_point(circtree_cached, &p2d);
231  p4d.x = p2d.x;
232  p4d.y = p2d.y;
233  if ( CircTreePIP(circtree, g, &p4d) )
234  {
235  *distance = 0.0;
236  circ_tree_free(circtree);
237  lwgeom_free(lwgeom);
238  return LW_SUCCESS;
239  }
240  }
241 
242  *distance = circ_tree_distance_tree(circtree_cached, circtree, s, tolerance);
243  circ_tree_free(circtree);
244  lwgeom_free(lwgeom);
245  return LW_SUCCESS;
246  }
247  else
248  {
249  return LW_FAILURE;
250  }
251 }
252 
253 int
254 geography_distance_cache(FunctionCallInfo fcinfo,
255  SHARED_GSERIALIZED *g1,
256  SHARED_GSERIALIZED *g2,
257  const SPHEROID *s,
258  double *distance)
259 {
260  return geography_distance_cache_tolerance(fcinfo, g1, g2, s, FP_TOLERANCE, distance);
261 }
262 
263 int
264 geography_dwithin_cache(FunctionCallInfo fcinfo,
265  SHARED_GSERIALIZED *g1,
266  SHARED_GSERIALIZED *g2,
267  const SPHEROID *s,
268  double tolerance,
269  int *dwithin)
270 {
271  double distance;
272  /* Ticket #2422, difference between sphere and spheroid distance can trip up the */
273  /* threshold shortcircuit (stopping a calculation before the spheroid distance is actually */
274  /* below the threshold. Lower in the code line, we actually reduce the threshold a little to */
275  /* avoid this. */
276  /* Correct fix: propogate the spheroid information all the way to the bottom of the calculation */
277  /* so the "right thing" can be done in all cases. */
278  if ( LW_SUCCESS == geography_distance_cache_tolerance(fcinfo, g1, g2, s, tolerance, &distance) )
279  {
280  *dwithin = (distance <= (tolerance + FP_TOLERANCE) ? LW_TRUE : LW_FALSE);
281  return LW_SUCCESS;
282  }
283  return LW_FAILURE;
284 }
285 
286 int
287 geography_tree_distance(const GSERIALIZED* g1, const GSERIALIZED* g2, const SPHEROID* s, double tolerance, double* distance)
288 {
289  CIRC_NODE* circ_tree1 = NULL;
290  CIRC_NODE* circ_tree2 = NULL;
291  LWGEOM* lwgeom1 = NULL;
292  LWGEOM* lwgeom2 = NULL;
293  POINT4D pt1, pt2;
294 
295  lwgeom1 = lwgeom_from_gserialized(g1);
296  lwgeom2 = lwgeom_from_gserialized(g2);
297  circ_tree1 = lwgeom_calculate_circ_tree(lwgeom1);
298  circ_tree2 = lwgeom_calculate_circ_tree(lwgeom2);
299  lwgeom_startpoint(lwgeom1, &pt1);
300  lwgeom_startpoint(lwgeom2, &pt2);
301 
302  if ( CircTreePIP(circ_tree1, g1, &pt2) || CircTreePIP(circ_tree2, g2, &pt1) )
303  {
304  *distance = 0.0;
305  }
306  else
307  {
308  /* Calculate tree/tree distance */
309  *distance = circ_tree_distance_tree(circ_tree1, circ_tree2, s, tolerance);
310  }
311 
312  circ_tree_free(circ_tree1);
313  circ_tree_free(circ_tree2);
314  lwgeom_free(lwgeom1);
315  lwgeom_free(lwgeom2);
316  return LW_SUCCESS;
317 }
char * s
Definition: cu_in_wkt.c:23
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
Return true if the point is inside the gbox.
Definition: gbox.c:247
static GeomCache * CircTreeAllocator(void)
static CircTreeGeomCache * GetCircTreeGeomCache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2)
static GeomCacheMethods CircTreeCacheMethods
static int CircTreeFreer(GeomCache *cache)
int geography_tree_distance(const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, double *distance)
int geography_dwithin_cache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2, const SPHEROID *s, double tolerance, int *dwithin)
static int CircTreePIP(const CIRC_NODE *tree1, const GSERIALIZED *g1, const POINT4D *in_point)
static int CircTreeBuilder(const LWGEOM *lwgeom, GeomCache *cache)
Builder, freeer and public accessor for cached CIRC_NODE trees.
int geography_distance_cache(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1, SHARED_GSERIALIZED *g2, const SPHEROID *s, double *distance)
static int geography_distance_cache_tolerance(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *shared_g1, SHARED_GSERIALIZED *shared_g2, const SPHEROID *s, double tolerance, double *distance)
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition: gserialized.c:65
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
#define LW_FALSE
Definition: liblwgeom.h:94
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition: lwgeom.c:2135
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
Calculate a spherical point that falls outside the geocentric gbox.
Definition: lwgeodetic.c:1558
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3027
#define LW_SUCCESS
Definition: liblwgeom.h:97
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
#define POLYGONTYPE
Definition: liblwgeom.h:104
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define FP_TOLERANCE
Floating point comparators.
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
Definition: lwgeodetic.c:180
void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p)
Convert spherical coordinates to cartesian coordinates on unit sphere.
Definition: lwgeodetic.c:404
double circ_tree_distance_tree(const CIRC_NODE *n1, const CIRC_NODE *n2, const SPHEROID *spheroid, double threshold)
int circ_tree_get_point(const CIRC_NODE *node, POINT2D *pt)
Returns a POINT2D that is a vertex of the input shape.
void circ_tree_free(CIRC_NODE *node)
Recurse from top of node tree and free all children.
CIRC_NODE * lwgeom_calculate_circ_tree(const LWGEOM *lwgeom)
int circ_tree_get_point_outside(const CIRC_NODE *node, POINT2D *pt)
int circ_tree_contains_point(const CIRC_NODE *node, const POINT2D *pt, const POINT2D *pt_outside, int level, int *on_boundary)
Walk the tree and count intersections between the stab line and the edges.
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:54
double y
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:390
double x
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
Note that p1 and p2 are pointers into an independent POINTARRAY, do not free them.