PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
lwgeom_geos_node.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) 2011-2024 Sandro Santilli <strk@kbt.io>
22 *
23 **********************************************************************/
24
25#include "../postgis_config.h"
26
27/*#define POSTGIS_DEBUG_LEVEL 1*/
28#include "lwgeom_log.h"
29
30#include "lwgeom_geos.h"
31#include "liblwgeom_internal.h"
32
33#include <string.h>
34#include <assert.h>
35
36static int
38{
40 if ( c ) return c->ngeoms;
41 else return 1;
42}
43
44static const LWGEOM*
45lwgeom_subgeom(const LWGEOM* g, int n)
46{
48 if ( c ) return lwcollection_getsubgeom((LWCOLLECTION*)c, n);
49 else return g;
50}
51
52
53static void
55{
56 int i, n;
57 LWLINE* l;
58
59 switch (lwg->type)
60 {
61 case MULTILINETYPE:
62 for ( i = 0,
63 n = lwgeom_ngeoms(lwg);
64 i < n; ++i )
65 {
67 lwgeom_subgeom(lwg, i),
68 col);
69 }
70 break;
71 case LINETYPE:
72 l = (LWLINE*)lwg;
73 col = lwmpoint_add_lwpoint(col,
74 lwline_get_lwpoint(l, 0));
75 col = lwmpoint_add_lwpoint(col,
77 break;
78 default:
79 lwerror("lwgeom_collect_endpoints: invalid type %s",
80 lwtype_name(lwg->type));
81 break;
82 }
83}
84
85static LWMPOINT*
87{
89 FLAGS_GET_Z(lwg->flags),
90 FLAGS_GET_M(lwg->flags));
92
93 return col;
94}
95
96/* Assumes initGEOS was called already */
97/* May return LWPOINT or LWMPOINT */
98static LWGEOM*
100{
101 LWGEOM* ret;
102 GEOSGeometry *gepu;
104 GEOSGeometry *gepall = LWGEOM2GEOS((LWGEOM*)epall, 1);
105 lwmpoint_free(epall);
106 if ( ! gepall ) {
107 lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
108 return NULL;
109 }
110
111 /* UnaryUnion to remove duplicates */
112 /* TODO: do it all within pgis using indices */
113 gepu = GEOSUnaryUnion(gepall);
114 if ( ! gepu ) {
115 GEOSGeom_destroy(gepall);
116 lwerror("GEOSUnaryUnion: %s", lwgeom_geos_errmsg);
117 return NULL;
118 }
119 GEOSGeom_destroy(gepall);
120
121 ret = GEOS2LWGEOM(gepu, FLAGS_GET_Z(lwg->flags));
122 GEOSGeom_destroy(gepu);
123 if ( ! ret ) {
124 lwerror("Error during GEOS2LWGEOM");
125 return NULL;
126 }
127
128 return ret;
129}
130
131/* exported */
132extern LWGEOM* lwgeom_node(const LWGEOM* lwgeom_in);
133LWGEOM*
134lwgeom_node(const LWGEOM* lwgeom_in)
135{
136 GEOSGeometry *g1, *gn, *gm;
137 LWGEOM *ep, *lines;
138 LWCOLLECTION *col, *tc;
139 int pn, ln, np, nl;
140
141 if ( lwgeom_dimension(lwgeom_in) != 1 ) {
142 lwerror("Noding geometries of dimension != 1 is unsupported");
143 return NULL;
144 }
145
147 g1 = LWGEOM2GEOS(lwgeom_in, 1);
148 if ( ! g1 ) {
149 lwerror("LWGEOM2GEOS: %s", lwgeom_geos_errmsg);
150 return NULL;
151 }
152
153 ep = lwgeom_extract_unique_endpoints(lwgeom_in);
154 if ( ! ep ) {
155 GEOSGeom_destroy(g1);
156 lwerror("Error extracting unique endpoints from input");
157 return NULL;
158 }
159
160 gn = GEOSNode(g1);
161 GEOSGeom_destroy(g1);
162 if ( ! gn ) {
163 lwgeom_free(ep);
164 lwerror("GEOSNode: %s", lwgeom_geos_errmsg);
165 return NULL;
166 }
167 LWDEBUGGEOS(1, gn, "Noded");
168
169 nl = GEOSGetNumGeometries(gn);
170 if ( nl > 1 )
171 {
172 gm = GEOSLineMerge(gn);
173 GEOSGeom_destroy(gn);
174 if ( ! gm ) {
175 lwgeom_free(ep);
176 lwerror("GEOSLineMerge: %s", lwgeom_geos_errmsg);
177 return NULL;
178 }
179 LWDEBUGGEOS(1, gm, "LineMerged");
180 gn = gm;
181
182 lines = GEOS2LWGEOM(gn, FLAGS_GET_Z(lwgeom_in->flags));
183 GEOSGeom_destroy(gn);
184 if ( ! lines ) {
185 lwgeom_free(ep);
186 lwerror("Error during GEOS2LWGEOM");
187 return NULL;
188 }
189 }
190 else if ( nl == 1 )
191 {
192 const GEOSGeometry *gc = GEOSGetGeometryN(gn, 0);
193 lines = GEOS2LWGEOM(gc, FLAGS_GET_Z(lwgeom_in->flags));
194 GEOSGeom_destroy(gn);
195 if ( ! lines ) {
196 lwgeom_free(ep);
197 lwerror("Error during GEOS2LWGEOM");
198 return NULL;
199 }
200 }
201 else
202 {
203 /* No geometries, don't bother with re-adding endpoints */
204 lines = GEOS2LWGEOM(gn, FLAGS_GET_Z(lwgeom_in->flags));
205 GEOSGeom_destroy(gn);
206 if ( ! lines ) {
207 lwgeom_free(ep);
208 lwerror("Error during GEOS2LWGEOM");
209 return NULL;
210 }
211 lwgeom_set_srid(lines, lwgeom_in->srid);
212 return (LWGEOM*)lines;
213 }
214
215
216 /*
217 * Reintroduce endpoints from input, using split-line-by-point.
218 * Note that by now we can be sure that each point splits at
219 * most _one_ segment as any point shared by multiple segments
220 * would already be a node. Also we can be sure that any of
221 * the segments endpoints won't split any other segment.
222 * We can use the above 2 assertions to early exit the loop.
223 */
224
226 FLAGS_GET_Z(lwgeom_in->flags),
227 FLAGS_GET_M(lwgeom_in->flags));
228
229 np = lwgeom_ngeoms(ep);
230 for (pn=0; pn<np; ++pn) { /* for each point */
231
232 const LWPOINT* p = (LWPOINT*)lwgeom_subgeom(ep, pn);
233
234 nl = lwgeom_ngeoms(lines);
235 for (ln=0; ln<nl; ++ln) { /* for each line */
236
237 const LWLINE* l = (LWLINE*)lwgeom_subgeom(lines, ln);
238
239 int s = lwline_split_by_point_to(l, p, (LWMLINE*)col);
240
241 if ( ! s ) continue; /* not on this line */
242
243 if ( s == 1 ) {
244 /* found on this line, but not splitting it */
245 break;
246 }
247
248 /* splits this line */
249
250 /* replace this line with the two splits */
251 if ( lwgeom_is_collection(lines) ) {
252 tc = (LWCOLLECTION*)lines;
253 lwcollection_reserve(tc, nl + 1);
254 while (nl > ln+1) {
255 tc->geoms[nl] = tc->geoms[nl-1];
256 --nl;
257 }
258 lwgeom_free(tc->geoms[ln]);
259 tc->geoms[ln] = col->geoms[0];
260 tc->geoms[ln+1] = col->geoms[1];
261 tc->ngeoms++;
262 } else {
263 lwgeom_free(lines);
264 /* transfer ownership rather than cloning */
265 lines = (LWGEOM*)lwcollection_clone_deep(col);
266 assert(col->ngeoms == 2);
267 lwgeom_free(col->geoms[0]);
268 lwgeom_free(col->geoms[1]);
269 }
270
271 /* reset the vector */
272 assert(col->ngeoms == 2);
273 col->ngeoms = 0;
274
275 break;
276 }
277
278 }
279
280 lwgeom_free(ep);
282
283 lwgeom_set_srid(lines, lwgeom_in->srid);
284 return (LWGEOM*)lines;
285}
286
char * s
Definition cu_in_wkt.c:23
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
void lwgeom_geos_error(const char *fmt,...)
void(*) LWGEOM GEOS2LWGEOM)(const GEOSGeometry *geom, uint8_t want3d)
#define LWDEBUGGEOS(level, geom, msg)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
void lwmpoint_free(LWMPOINT *mpt)
Definition lwmpoint.c:72
void lwgeom_set_srid(LWGEOM *geom, int32_t srid)
Set the SRID on an LWGEOM For collections, only the parent gets an SRID, all the children get SRID_UN...
Definition lwgeom.c:1638
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition lwline.c:319
#define MULTILINETYPE
Definition liblwgeom.h:106
#define LINETYPE
Definition liblwgeom.h:103
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition lwgeom.c:261
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition lwmpoint.c:45
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:165
int lwgeom_dimension(const LWGEOM *geom)
For an LWGEOM, returns 0 for points, 1 for lines, 2 for polygons, 3 for volume, and the max dimension...
Definition lwgeom.c:1389
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM contains sub-geometries or not This basically just checks that the struct ...
Definition lwgeom.c:1125
const LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, uint32_t gnum)
#define FLAGS_GET_M(flags)
Definition liblwgeom.h:166
void lwcollection_free(LWCOLLECTION *col)
LWMPOINT * lwmpoint_construct_empty(int32_t srid, char hasz, char hasm)
Definition lwmpoint.c:39
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
#define SRID_UNKNOWN
Unknown SRID value.
Definition liblwgeom.h:215
LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *lwgeom)
Deep clone LWCOLLECTION object.
int lwline_split_by_point_to(const LWLINE *ln, const LWPOINT *pt, LWMLINE *to)
Split a line by a point and push components to the provided multiline.
void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
Ensure the collection can hold at least up to ngeoms geometries.
static LWGEOM * lwgeom_extract_unique_endpoints(const LWGEOM *lwg)
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
static LWMPOINT * lwgeom_extract_endpoints(const LWGEOM *lwg)
static const LWGEOM * lwgeom_subgeom(const LWGEOM *g, int n)
static int lwgeom_ngeoms(const LWGEOM *n)
static void lwgeom_collect_endpoints(const LWGEOM *lwg, LWMPOINT *col)
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
uint32_t ngeoms
Definition liblwgeom.h:580
LWGEOM ** geoms
Definition liblwgeom.h:575
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
lwflags_t flags
Definition liblwgeom.h:461
POINTARRAY * points
Definition liblwgeom.h:483
LWLINE ** geoms
Definition liblwgeom.h:547
uint32_t ngeoms
Definition liblwgeom.h:552
uint32_t npoints
Definition liblwgeom.h:427