PostGIS  3.7.0dev-r@@SVN_REVISION@@
lwgeom_itree.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 (C) 2024 Paul Ramsey <pramsey@cleverelephant.ca>
22  *
23  **********************************************************************/
24 
25 /* PostgreSQL */
26 #include "postgres.h"
27 #include "funcapi.h"
28 #include "fmgr.h"
29 
30 /* Liblwgeom */
31 #include "liblwgeom.h"
32 #include "liblwgeom_internal.h" /* For FP comparators. */
33 #include "intervaltree.h"
34 
35 /* PostGIS */
36 #include "lwgeom_pg.h"
37 #include "lwgeom_cache.h"
38 #include "lwgeom_itree.h"
39 
40 
41 /* Prototypes */
42 Datum ST_IntersectsIntervalTree(PG_FUNCTION_ARGS);
43 
44 
45 /**********************************************************************
46 * IntervalTree Caching support
47 **********************************************************************/
48 
49 /*
50  * Specific cache types include all the generic GeomCache slots and
51  * their own slots for their index trees. .
52  */
53 typedef struct {
54  GeomCache gcache;
57 
61 static int
62 IntervalTreeBuilder(const LWGEOM *lwgeom, GeomCache *geomcache)
63 {
64  IntervalTreeGeomCache *cache = (IntervalTreeGeomCache*)geomcache;
65  IntervalTree *itree = itree_from_lwgeom(lwgeom);
66 
67  if (cache->itree)
68  {
69  itree_free(cache->itree);
70  cache->itree = 0;
71  }
72  if (!itree)
73  return LW_FAILURE;
74 
75  cache->itree = itree;
76  return LW_SUCCESS;
77 }
78 
79 static int
80 IntervalTreeFreer(GeomCache *geomcache)
81 {
82  IntervalTreeGeomCache *cache = (IntervalTreeGeomCache*)geomcache;
83  if (cache->itree)
84  {
85  itree_free(cache->itree);
86  cache->itree = 0;
87  cache->gcache.argnum = 0;
88  }
89  return LW_SUCCESS;
90 }
91 
92 static GeomCache *
94 {
95  IntervalTreeGeomCache *cache = palloc(sizeof(IntervalTreeGeomCache));
96  memset(cache, 0, sizeof(IntervalTreeGeomCache));
97  return (GeomCache*)cache;
98 }
99 
100 static GeomCacheMethods IntervalTreeCacheMethods =
101 {
102  ITREE_CACHE_ENTRY,
106 };
107 
108 /*
109  * Always return an IntervalTree, even if we don't have one
110  * cached already so that the caller can use it to do a
111  * index assisted p-i-p every time.
112  */
113 IntervalTree *
114 GetIntervalTree(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1)
115 {
116  const GSERIALIZED *poly1;
117  LWGEOM *lwpoly1;
118 
119  IntervalTree *itree = NULL;
120  IntervalTreeGeomCache *cache = (IntervalTreeGeomCache*)GetGeomCache(fcinfo, &IntervalTreeCacheMethods, g1, NULL);
121 
122  /* Found a cached tree */
123  if (cache && cache->itree)
124  return cache->itree;
125 
126  /* Build one on the fly */
127  poly1 = shared_gserialized_get(g1);
128  lwpoly1 = lwgeom_from_gserialized(poly1);
129  itree = itree_from_lwgeom(lwpoly1);
130  lwgeom_free(lwpoly1);
131  return itree;
132 }
133 
134 /*
135  * A point must be fully inside (not on boundary) of
136  * a polygon to be contained. A multipoint must have
137  * at least one fully contained member and no members
138  * outside the polygon to be contained.
139  */
140 bool itree_pip_contains(const IntervalTree *itree, const LWGEOM *lwpoints)
141 {
142  if (lwgeom_get_type(lwpoints) == POINTTYPE)
143  {
144  return itree_point_in_multipolygon(itree, lwgeom_as_lwpoint(lwpoints)) == ITREE_INSIDE;
145  }
146  else if (lwgeom_get_type(lwpoints) == MULTIPOINTTYPE)
147  {
148  bool found_completely_inside = false;
149  LWMPOINT *mpoint = lwgeom_as_lwmpoint(lwpoints);
150  for (uint32_t i = 0; i < mpoint->ngeoms; i++)
151  {
152  IntervalTreeResult pip_result;
153  const LWPOINT* pt = mpoint->geoms[i];
154 
155  if (lwpoint_is_empty(pt))
156  continue;
157  /*
158  * We need to find at least one point that's completely inside the
159  * polygons (pip_result == 1). As long as we have one point that's
160  * completely inside, we can have as many as we want on the boundary
161  * itself. (pip_result == 0)
162  */
163  pip_result = itree_point_in_multipolygon(itree, pt);
164 
165  if (pip_result == ITREE_INSIDE)
166  found_completely_inside = true;
167 
168  if (pip_result == ITREE_OUTSIDE)
169  return false;
170  }
171  return found_completely_inside;
172  }
173  else
174  {
175  elog(ERROR, "%s got a non-point input", __func__);
176  return false;
177  }
178 }
179 
180 
181 /*
182  * If any point in the point/multipoint is outside
183  * the polygon, then the polygon does not cover the point/multipoint.
184  */
185 bool itree_pip_covers(const IntervalTree *itree, const LWGEOM *lwpoints)
186 {
187  if (lwgeom_get_type(lwpoints) == POINTTYPE)
188  {
189  return itree_point_in_multipolygon(itree, lwgeom_as_lwpoint(lwpoints)) != ITREE_OUTSIDE;
190  }
191  else if (lwgeom_get_type(lwpoints) == MULTIPOINTTYPE)
192  {
193  LWMPOINT* mpoint = lwgeom_as_lwmpoint(lwpoints);
194  for (uint32_t i = 0; i < mpoint->ngeoms; i++)
195  {
196  const LWPOINT *pt = mpoint->geoms[i];
197 
198  if (lwpoint_is_empty(pt))
199  continue;
200 
201  if (itree_point_in_multipolygon(itree, pt) == ITREE_OUTSIDE)
202  return false;
203  }
204  return true;
205  }
206  else
207  {
208  elog(ERROR, "%s got a non-point input", __func__);
209  return false;
210  }
211 }
212 
213 /*
214  * A.intersects(B) implies if any member of the point/multipoint
215  * is not outside, then they intersect.
216  */
217 bool itree_pip_intersects(const IntervalTree *itree, const LWGEOM *lwpoints)
218 {
219  if (lwgeom_get_type(lwpoints) == POINTTYPE)
220  {
221  return itree_point_in_multipolygon(itree, lwgeom_as_lwpoint(lwpoints)) != ITREE_OUTSIDE;
222  }
223  else if (lwgeom_get_type(lwpoints) == MULTIPOINTTYPE)
224  {
225  LWMPOINT* mpoint = lwgeom_as_lwmpoint(lwpoints);
226  for (uint32_t i = 0; i < mpoint->ngeoms; i++)
227  {
228  const LWPOINT *pt = mpoint->geoms[i];
229 
230  if (lwpoint_is_empty(pt))
231  continue;
232 
233  if (itree_point_in_multipolygon(itree, pt) != ITREE_OUTSIDE)
234  return true;
235  }
236  return false;
237  }
238  else
239  {
240  elog(ERROR, "%s got a non-point input", __func__);
241  return false;
242  }
243 }
244 
245 
246 /**********************************************************************
247 * ST_IntersectsIntervalTree
248 **********************************************************************/
249 
251 Datum ST_IntersectsIntervalTree(PG_FUNCTION_ARGS)
252 {
253  GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
254  GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
255  LWGEOM *lwg1, *lwg2;
256  LWPOINT *lwpt;
257  IntervalTree *itree = NULL;
258  bool isPoly1, isPoly2, isPt1, isPt2;
259 
260  /* Return false on empty arguments. */
262  {
263  PG_FREE_IF_COPY(g1, 0);
264  PG_FREE_IF_COPY(g2, 1);
265  PG_RETURN_BOOL(false);
266  }
267 
268  lwg1 = lwgeom_from_gserialized(g1);
269  lwg2 = lwgeom_from_gserialized(g2);
270 
271  isPoly1 = lwg1->type == POLYGONTYPE || lwg1->type == MULTIPOLYGONTYPE;
272  isPoly2 = lwg2->type == POLYGONTYPE || lwg2->type == MULTIPOLYGONTYPE;
273  isPt1 = lwg1->type == POINTTYPE;
274  isPt2 = lwg2->type == POINTTYPE;
275 
276  /* Two points? Get outa here... */
277  if (isPoly1 && isPt2)
278  {
279  itree = itree_from_lwgeom(lwg1);
280  lwpt = lwgeom_as_lwpoint(lwg2);
281  }
282  else if (isPoly2 && isPt1)
283  {
284  itree = itree_from_lwgeom(lwg2);
285  lwpt = lwgeom_as_lwpoint(lwg1);
286  }
287 
288  if (!itree)
289  elog(ERROR, "arguments to %s must a point and a polygon", __func__);
290 
291  PG_RETURN_BOOL(itree_point_in_multipolygon(itree, lwpt) != ITREE_OUTSIDE);
292 }
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:268
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
Definition: gserialized.c:181
IntervalTree * itree_from_lwgeom(const LWGEOM *geom)
Definition: intervaltree.c:310
void itree_free(IntervalTree *itree)
Definition: intervaltree.c:28
IntervalTreeResult itree_point_in_multipolygon(const IntervalTree *itree, const LWPOINT *point)
Definition: intervaltree.c:461
IntervalTreeResult
Definition: intervaltree.h:32
@ ITREE_INSIDE
Definition: intervaltree.h:33
@ ITREE_OUTSIDE
Definition: intervaltree.h:35
LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:242
#define LW_FAILURE
Definition: liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#define LW_SUCCESS
Definition: liblwgeom.h:97
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
#define POLYGONTYPE
Definition: liblwgeom.h:104
This library is the generic geometry handling section of PostGIS.
int lwpoint_is_empty(const LWPOINT *point)
static GeomCacheMethods IntervalTreeCacheMethods
Definition: lwgeom_itree.c:100
bool itree_pip_intersects(const IntervalTree *itree, const LWGEOM *lwpoints)
Definition: lwgeom_itree.c:217
PG_FUNCTION_INFO_V1(ST_IntersectsIntervalTree)
static int IntervalTreeFreer(GeomCache *geomcache)
Definition: lwgeom_itree.c:80
IntervalTree * GetIntervalTree(FunctionCallInfo fcinfo, SHARED_GSERIALIZED *g1)
Checks for a cache hit against the provided geometry and returns a pre-built index structure (RTREE_P...
Definition: lwgeom_itree.c:114
static GeomCache * IntervalTreeAllocator(void)
Definition: lwgeom_itree.c:93
bool itree_pip_covers(const IntervalTree *itree, const LWGEOM *lwpoints)
Definition: lwgeom_itree.c:185
static int IntervalTreeBuilder(const LWGEOM *lwgeom, GeomCache *geomcache)
Builder, freeer and public accessor for cached IntervalTrees.
Definition: lwgeom_itree.c:62
Datum ST_IntersectsIntervalTree(PG_FUNCTION_ARGS)
Definition: lwgeom_itree.c:251
bool itree_pip_contains(const IntervalTree *itree, const LWGEOM *lwpoints)
Definition: lwgeom_itree.c:140
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwinline.h:141
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:127
IntervalTree * itree
Definition: lwgeom_itree.c:55
uint8_t type
Definition: liblwgeom.h:462
uint32_t ngeoms
Definition: liblwgeom.h:538
LWPOINT ** geoms
Definition: liblwgeom.h:533