PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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 */
42Datum 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 */
53typedef struct {
54 GeomCache gcache;
57
61static int
62IntervalTreeBuilder(const LWGEOM *lwgeom, GeomCache *geomcache)
63{
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
79static int
80IntervalTreeFreer(GeomCache *geomcache)
81{
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
92static GeomCache *
94{
95 IntervalTreeGeomCache *cache = palloc(sizeof(IntervalTreeGeomCache));
96 memset(cache, 0, sizeof(IntervalTreeGeomCache));
97 return (GeomCache*)cache;
98}
99
100static 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 */
114GetIntervalTree(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 */
140bool itree_pip_contains(const IntervalTree *itree, const LWGEOM *lwpoints)
141{
142 if (lwgeom_get_type(lwpoints) == POINTTYPE)
143 {
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 */
185bool itree_pip_covers(const IntervalTree *itree, const LWGEOM *lwpoints)
186{
187 if (lwgeom_get_type(lwpoints) == POINTTYPE)
188 {
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
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 */
217bool itree_pip_intersects(const IntervalTree *itree, const LWGEOM *lwpoints)
218{
219 if (lwgeom_get_type(lwpoints) == POINTTYPE)
220 {
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
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
251Datum 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.
int gserialized_is_empty(const GSERIALIZED *g)
Check if a GSERIALIZED is empty without deserializing first.
void itree_free(IntervalTree *itree)
IntervalTreeResult itree_point_in_multipolygon(const IntervalTree *itree, const LWPOINT *point)
IntervalTree * itree_from_lwgeom(const LWGEOM *geom)
IntervalTreeResult
@ ITREE_INSIDE
@ ITREE_OUTSIDE
#define LW_FAILURE
Definition liblwgeom.h:96
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#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
LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
Definition lwgeom.c:270
This library is the generic geometry handling section of PostGIS.
int lwpoint_is_empty(const LWPOINT *point)
static GeomCacheMethods IntervalTreeCacheMethods
bool itree_pip_intersects(const IntervalTree *itree, const LWGEOM *lwpoints)
PG_FUNCTION_INFO_V1(ST_IntersectsIntervalTree)
static int IntervalTreeFreer(GeomCache *geomcache)
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...
static GeomCache * IntervalTreeAllocator(void)
bool itree_pip_covers(const IntervalTree *itree, const LWGEOM *lwpoints)
static int IntervalTreeBuilder(const LWGEOM *lwgeom, GeomCache *geomcache)
Builder, freeer and public accessor for cached IntervalTrees.
Datum ST_IntersectsIntervalTree(PG_FUNCTION_ARGS)
bool itree_pip_contains(const IntervalTree *itree, const LWGEOM *lwpoints)
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
uint8_t type
Definition liblwgeom.h:462
uint32_t ngeoms
Definition liblwgeom.h:538
LWPOINT ** geoms
Definition liblwgeom.h:533