PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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*/
35typedef struct {
36 GeomCache gcache;
39
40
41
45static int
46CircTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
47{
48 CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache;
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
63static int
64CircTreeFreer(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
76static GeomCache*
78{
79 CircTreeGeomCache* cache = palloc(sizeof(CircTreeGeomCache));
80 memset(cache, 0, sizeof(CircTreeGeomCache));
81 return (GeomCache*)cache;
82}
83
84static GeomCacheMethods CircTreeCacheMethods =
85{
86 CIRC_CACHE_ENTRY,
90};
91
92static CircTreeGeomCache *
93GetCircTreeGeomCache(FunctionCallInfo fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2)
94{
95 return (CircTreeGeomCache*)GetGeomCache(fcinfo, &CircTreeCacheMethods, g1, g2);
96}
97
98static int
99CircTreePIP(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
156static int
158 const GSERIALIZED *g1,
159 const GSERIALIZED *g2,
160 const SPHEROID *s,
161 double tolerance,
162 double *distance)
163{
164 CircTreeGeomCache* tree_cache = NULL;
165
166 int type1 = gserialized_get_type(g1);
167 int type2 = gserialized_get_type(g2);
168
169 Assert(distance);
170
171 /* Two points? Get outa here... */
172 if ( type1 == POINTTYPE && type2 == POINTTYPE )
173 return LW_FAILURE;
174
175 /* Fetch/build our cache, if appropriate, etc... */
176 tree_cache = GetCircTreeGeomCache(fcinfo, g1, g2);
177
178 /* OK, we have an index at the ready! Use it for the one tree argument and */
179 /* fill in the other tree argument */
180 if ( tree_cache && tree_cache->gcache.argnum && tree_cache->index )
181 {
182 CIRC_NODE* circtree_cached = tree_cache->index;
183 CIRC_NODE* circtree = NULL;
184 const GSERIALIZED* g_cached;
185 const GSERIALIZED* g;
186 LWGEOM* lwgeom = NULL;
187 int geomtype_cached;
188 int geomtype;
189 POINT4D p4d;
190
191 /* We need to dynamically build a tree for the uncached side of the function call */
192 if ( tree_cache->gcache.argnum == 1 )
193 {
194 g_cached = g1;
195 g = g2;
196 geomtype_cached = type1;
197 geomtype = type2;
198 }
199 else if ( tree_cache->gcache.argnum == 2 )
200 {
201 g_cached = g2;
202 g = g1;
203 geomtype_cached = type2;
204 geomtype = type1;
205 }
206 else
207 {
208 lwpgerror("geography_distance_cache this cannot happen!");
209 return LW_FAILURE;
210 }
211
212 lwgeom = lwgeom_from_gserialized(g);
213 if ( geomtype_cached == POLYGONTYPE || geomtype_cached == MULTIPOLYGONTYPE )
214 {
215 lwgeom_startpoint(lwgeom, &p4d);
216 if ( CircTreePIP(circtree_cached, g_cached, &p4d) )
217 {
218 *distance = 0.0;
219 lwgeom_free(lwgeom);
220 return LW_SUCCESS;
221 }
222 }
223
224 circtree = lwgeom_calculate_circ_tree(lwgeom);
225 if ( geomtype == POLYGONTYPE || geomtype == MULTIPOLYGONTYPE )
226 {
227 POINT2D p2d;
228 circ_tree_get_point(circtree_cached, &p2d);
229 p4d.x = p2d.x;
230 p4d.y = p2d.y;
231 if ( CircTreePIP(circtree, g, &p4d) )
232 {
233 *distance = 0.0;
234 circ_tree_free(circtree);
235 lwgeom_free(lwgeom);
236 return LW_SUCCESS;
237 }
238 }
239
240 *distance = circ_tree_distance_tree(circtree_cached, circtree, s, tolerance);
241 circ_tree_free(circtree);
242 lwgeom_free(lwgeom);
243 return LW_SUCCESS;
244 }
245 else
246 {
247 return LW_FAILURE;
248 }
249}
250
251int
252geography_distance_cache(FunctionCallInfo fcinfo,
253 const GSERIALIZED *g1,
254 const GSERIALIZED *g2,
255 const SPHEROID *s,
256 double *distance)
257{
259}
260
261int
262geography_dwithin_cache(FunctionCallInfo fcinfo,
263 const GSERIALIZED *g1,
264 const GSERIALIZED *g2,
265 const SPHEROID *s,
266 double tolerance,
267 int *dwithin)
268{
269 double distance;
270 /* Ticket #2422, difference between sphere and spheroid distance can trip up the */
271 /* threshold shortcircuit (stopping a calculation before the spheroid distance is actually */
272 /* below the threshold. Lower in the code line, we actually reduce the threshold a little to */
273 /* avoid this. */
274 /* Correct fix: propogate the spheroid information all the way to the bottom of the calculation */
275 /* so the "right thing" can be done in all cases. */
276 if ( LW_SUCCESS == geography_distance_cache_tolerance(fcinfo, g1, g2, s, tolerance, &distance) )
277 {
278 *dwithin = (distance <= (tolerance + FP_TOLERANCE) ? LW_TRUE : LW_FALSE);
279 return LW_SUCCESS;
280 }
281 return LW_FAILURE;
282}
283
284int
285geography_tree_distance(const GSERIALIZED* g1, const GSERIALIZED* g2, const SPHEROID* s, double tolerance, double* distance)
286{
287 CIRC_NODE* circ_tree1 = NULL;
288 CIRC_NODE* circ_tree2 = NULL;
289 LWGEOM* lwgeom1 = NULL;
290 LWGEOM* lwgeom2 = NULL;
291 POINT4D pt1, pt2;
292
293 lwgeom1 = lwgeom_from_gserialized(g1);
294 lwgeom2 = lwgeom_from_gserialized(g2);
295 circ_tree1 = lwgeom_calculate_circ_tree(lwgeom1);
296 circ_tree2 = lwgeom_calculate_circ_tree(lwgeom2);
297 lwgeom_startpoint(lwgeom1, &pt1);
298 lwgeom_startpoint(lwgeom2, &pt2);
299
300 if ( CircTreePIP(circ_tree1, g1, &pt2) || CircTreePIP(circ_tree2, g2, &pt1) )
301 {
302 *distance = 0.0;
303 }
304 else
305 {
306 /* Calculate tree/tree distance */
307 *distance = circ_tree_distance_tree(circ_tree1, circ_tree2, s, tolerance);
308 }
309
310 circ_tree_free(circ_tree1);
311 circ_tree_free(circ_tree2);
312 lwgeom_free(lwgeom1);
313 lwgeom_free(lwgeom2);
314 return LW_SUCCESS;
315}
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
int geography_dwithin_cache(FunctionCallInfo fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, int *dwithin)
static GeomCacheMethods CircTreeCacheMethods
static GeomCache * CircTreeAllocator(void)
static int CircTreeFreer(GeomCache *cache)
int geography_tree_distance(const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, double *distance)
static int geography_distance_cache_tolerance(FunctionCallInfo fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double tolerance, double *distance)
int geography_distance_cache(FunctionCallInfo fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2, const SPHEROID *s, double *distance)
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.
static CircTreeGeomCache * GetCircTreeGeomCache(FunctionCallInfo fcinfo, const GSERIALIZED *g1, const GSERIALIZED *g2)
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
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
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:108
int lwgeom_startpoint(const LWGEOM *lwgeom, POINT4D *pt)
Definition lwgeom.c:2113
#define LW_FAILURE
Definition liblwgeom.h:110
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
int gbox_pt_outside(const GBOX *gbox, POINT2D *pt_outside)
Calculate a spherical point that falls outside the geocentric gbox.
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
#define LW_SUCCESS
Definition liblwgeom.h:111
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:121
#define POLYGONTYPE
Definition liblwgeom.h:118
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:107
#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)
CIRC_NODE * lwgeom_calculate_circ_tree(const LWGEOM *lwgeom)
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.
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:376
double x
Definition liblwgeom.h:376
double x
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400
Note that p1 and p2 are pointers into an independent POINTARRAY, do not free them.