PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 
36 static int
38 {
40  if ( c ) return c->ngeoms;
41  else return 1;
42 }
43 
44 static const LWGEOM*
45 lwgeom_subgeom(const LWGEOM* g, int n)
46 {
48  if ( c ) return lwcollection_getsubgeom((LWCOLLECTION*)c, n);
49  else return g;
50 }
51 
52 
53 static 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 
85 static LWMPOINT*
87 {
89  FLAGS_GET_Z(lwg->flags),
90  FLAGS_GET_M(lwg->flags));
91  lwgeom_collect_endpoints(lwg, col);
92 
93  return col;
94 }
95 
96 /* Assumes initGEOS was called already */
97 /* May return LWPOINT or LWMPOINT */
98 static LWGEOM*
100 {
101  LWGEOM* ret;
102  GEOSGeometry *gepu;
103  LWMPOINT *epall = lwgeom_extract_endpoints(lwg);
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 */
132 extern LWGEOM* lwgeom_node(const LWGEOM* lwgeom_in);
133 LWGEOM*
134 lwgeom_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);
281  lwcollection_free(col);
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)
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:1610
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:165
const LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, uint32_t gnum)
Definition: lwcollection.c:115
LWMPOINT * lwmpoint_add_lwpoint(LWMPOINT *mobj, const LWPOINT *obj)
Definition: lwmpoint.c:45
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:1361
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:1097
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)
Definition: lwcollection.c:92
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:166
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:357
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:309
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.
LWCOLLECTION * lwcollection_clone_deep(const LWCOLLECTION *lwgeom)
Deep clone LWCOLLECTION object.
Definition: lwcollection.c:151
void lwcollection_reserve(LWCOLLECTION *col, uint32_t ngeoms)
Ensure the collection can hold at least up to ngeoms geometries.
Definition: lwcollection.c:176
static LWGEOM * lwgeom_extract_unique_endpoints(const LWGEOM *lwg)
static LWMPOINT * lwgeom_extract_endpoints(const LWGEOM *lwg)
static const LWGEOM * lwgeom_subgeom(const LWGEOM *g, int n)
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
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