/src/postgis/liblwgeom/lwcompound.c
Line | Count | Source |
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) 2001-2006 Refractions Research Inc. |
22 | | * |
23 | | **********************************************************************/ |
24 | | |
25 | | |
26 | | #include <stdio.h> |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | #include "liblwgeom_internal.h" |
30 | | #include "lwgeom_log.h" |
31 | | |
32 | | |
33 | | uint32_t |
34 | | lwcompound_num_curves(const LWCOMPOUND *compound) |
35 | 0 | { |
36 | 0 | if ( compound->type != COMPOUNDTYPE ) |
37 | 0 | lwerror("%s only supports compound curves", __func__); |
38 | 0 | return compound->ngeoms; |
39 | 0 | } |
40 | | |
41 | | const LWGEOM * |
42 | | lwcollection_getsubcurve(const LWCOMPOUND *compound, uint32_t curvenum) |
43 | 0 | { |
44 | 0 | return (const LWGEOM *)compound->geoms[curvenum]; |
45 | 0 | } |
46 | | |
47 | | static int |
48 | | lwcompound_curve_endpoint(const LWGEOM *geom, POINT4D *pt) |
49 | 0 | { |
50 | 0 | if (!geom || lwgeom_is_empty(geom)) |
51 | 0 | return LW_FAILURE; |
52 | | |
53 | 0 | if (geom->type == NURBSCURVETYPE) |
54 | 0 | { |
55 | 0 | LWPOINT *endpoint = lwnurbscurve_evaluate((const LWNURBSCURVE*)geom, 1.0); |
56 | 0 | int rv = LW_FAILURE; |
57 | 0 | if (endpoint && endpoint->point && endpoint->point->npoints > 0) |
58 | 0 | rv = getPoint4d_p(endpoint->point, 0, pt) ? LW_SUCCESS : LW_FAILURE; |
59 | 0 | lwpoint_free(endpoint); |
60 | 0 | return rv; |
61 | 0 | } |
62 | | |
63 | 0 | if (geom->type == LINETYPE || geom->type == CIRCSTRINGTYPE) |
64 | 0 | { |
65 | 0 | const LWLINE *line = (const LWLINE *)geom; |
66 | 0 | if (!line->points || line->points->npoints < 1) |
67 | 0 | return LW_FAILURE; |
68 | 0 | return getPoint4d_p(line->points, line->points->npoints - 1, pt) ? LW_SUCCESS : LW_FAILURE; |
69 | 0 | } |
70 | | |
71 | 0 | return LW_FAILURE; |
72 | 0 | } |
73 | | |
74 | | static int |
75 | | lwcompound_points_equal(const POINT4D *a, const POINT4D *b, int hasz) |
76 | 0 | { |
77 | 0 | if (!FP_EQUALS(a->x, b->x) || !FP_EQUALS(a->y, b->y)) |
78 | 0 | return LW_FALSE; |
79 | | |
80 | 0 | if (hasz && !FP_EQUALS(a->z, b->z)) |
81 | 0 | return LW_FALSE; |
82 | | |
83 | 0 | return LW_TRUE; |
84 | 0 | } |
85 | | |
86 | | int |
87 | | lwcompound_is_closed(const LWCOMPOUND *compound) |
88 | 0 | { |
89 | 0 | POINT4D pt_start, pt_end; |
90 | |
|
91 | 0 | int hasz = lwgeom_has_z(lwcompound_as_lwgeom(compound)); |
92 | 0 | if (lwgeom_is_empty(lwcompound_as_lwgeom(compound))) |
93 | 0 | return LW_FALSE; |
94 | | |
95 | | /* Single entry, closes on itself */ |
96 | 0 | if (compound->ngeoms == 1 && lwgeom_is_closed(compound->geoms[0])) |
97 | 0 | return LW_TRUE; |
98 | | |
99 | | /* If internal connectivity is lacking, so is closure */ |
100 | 0 | if (!lwcompound_is_valid(compound)) |
101 | 0 | return LW_FALSE; |
102 | | |
103 | 0 | if (lwgeom_startpoint(compound->geoms[0], &pt_start) == LW_FAILURE || |
104 | 0 | lwcompound_curve_endpoint(compound->geoms[compound->ngeoms-1], &pt_end) == LW_FAILURE) |
105 | 0 | return LW_FALSE; |
106 | | |
107 | 0 | return lwcompound_points_equal(&pt_start, &pt_end, hasz); |
108 | 0 | } |
109 | | |
110 | | int |
111 | | lwcompound_is_valid(const LWCOMPOUND *compound) |
112 | 0 | { |
113 | 0 | int hasz = lwgeom_has_z(lwcompound_as_lwgeom(compound)); |
114 | 0 | if (lwgeom_is_empty(lwcompound_as_lwgeom(compound))) |
115 | 0 | return LW_TRUE; |
116 | | |
117 | | /* Only one component, do not need to test connectivity */ |
118 | 0 | if (compound->ngeoms == 1) |
119 | 0 | return LW_TRUE; |
120 | | |
121 | | /* Check internal connectivity between components */ |
122 | 0 | for (uint32_t i = 1; i < compound->ngeoms; i++) |
123 | 0 | { |
124 | 0 | POINT4D pt_start, pt_end; |
125 | 0 | const LWGEOM *geom_start = compound->geoms[i]; |
126 | 0 | const LWGEOM *geom_end = compound->geoms[i-1]; |
127 | | |
128 | | /* Empty cannot be a compound component, because it joins nothing */ |
129 | 0 | if (lwgeom_is_empty(geom_start) || lwgeom_is_empty(geom_end)) |
130 | 0 | return LW_FALSE; |
131 | | |
132 | 0 | if (lwgeom_startpoint(geom_start, &pt_start) == LW_FAILURE || |
133 | 0 | lwcompound_curve_endpoint(geom_end, &pt_end) == LW_FAILURE) |
134 | 0 | return LW_FALSE; |
135 | | |
136 | 0 | if (!lwcompound_points_equal(&pt_start, &pt_end, hasz)) |
137 | 0 | return LW_FALSE; |
138 | 0 | } |
139 | | |
140 | 0 | return LW_TRUE; |
141 | 0 | } |
142 | | |
143 | | double lwcompound_length(const LWCOMPOUND *comp) |
144 | 0 | { |
145 | 0 | return lwcompound_length_2d(comp); |
146 | 0 | } |
147 | | |
148 | | double lwcompound_length_2d(const LWCOMPOUND *comp) |
149 | 0 | { |
150 | 0 | uint32_t i; |
151 | 0 | double length = 0.0; |
152 | 0 | if ( lwgeom_is_empty((LWGEOM*)comp) ) |
153 | 0 | return 0.0; |
154 | | |
155 | 0 | for (i = 0; i < comp->ngeoms; i++) |
156 | 0 | { |
157 | 0 | length += lwgeom_length_2d(comp->geoms[i]); |
158 | 0 | } |
159 | 0 | return length; |
160 | 0 | } |
161 | | |
162 | | int lwcompound_add_lwgeom(LWCOMPOUND *comp, LWGEOM *geom) |
163 | 0 | { |
164 | 0 | LWCOLLECTION *col = (LWCOLLECTION*)comp; |
165 | | |
166 | | /* Empty things can't continuously join up with other things */ |
167 | 0 | if ( lwgeom_is_empty(geom) ) |
168 | 0 | { |
169 | 0 | LWDEBUG(4, "Got an empty component for a compound curve!"); |
170 | 0 | return LW_FAILURE; |
171 | 0 | } |
172 | | |
173 | 0 | if( col->ngeoms > 0 ) |
174 | 0 | { |
175 | 0 | POINT4D last, first; |
176 | 0 | if (lwgeom_startpoint(geom, &first) == LW_FAILURE || |
177 | 0 | lwcompound_curve_endpoint(col->geoms[col->ngeoms - 1], &last) == LW_FAILURE) |
178 | 0 | return LW_FAILURE; |
179 | | |
180 | 0 | if ( !(FP_EQUALS(first.x,last.x) && FP_EQUALS(first.y,last.y)) ) |
181 | 0 | { |
182 | 0 | LWDEBUG(4, "Components don't join up end-to-end!"); |
183 | 0 | LWDEBUGF(4, "first pt (%g %g %g %g) last pt (%g %g %g %g)", first.x, first.y, first.z, first.m, last.x, last.y, last.z, last.m); |
184 | 0 | return LW_FAILURE; |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | 0 | col = lwcollection_add_lwgeom(col, geom); |
189 | 0 | return LW_SUCCESS; |
190 | 0 | } |
191 | | |
192 | | LWCOMPOUND * |
193 | | lwcompound_construct_empty(int32_t srid, char hasz, char hasm) |
194 | 0 | { |
195 | 0 | LWCOMPOUND *ret = (LWCOMPOUND*)lwcollection_construct_empty(COMPOUNDTYPE, srid, hasz, hasm); |
196 | 0 | return ret; |
197 | 0 | } |
198 | | |
199 | | int lwgeom_contains_point(const LWGEOM *geom, const POINT2D *pt) |
200 | 0 | { |
201 | 0 | switch( geom->type ) |
202 | 0 | { |
203 | 0 | case LINETYPE: |
204 | 0 | return ptarray_contains_point(((LWLINE*)geom)->points, pt); |
205 | 0 | case CIRCSTRINGTYPE: |
206 | 0 | return ptarrayarc_contains_point(((LWCIRCSTRING*)geom)->points, pt); |
207 | 0 | case COMPOUNDTYPE: |
208 | 0 | return lwcompound_contains_point((LWCOMPOUND*)geom, pt); |
209 | 0 | case NURBSCURVETYPE: |
210 | 0 | { |
211 | 0 | int result; |
212 | 0 | LWLINE *lwline = lwnurbscurve_to_linestring((LWNURBSCURVE*)geom, 32); |
213 | 0 | result = ptarray_contains_point(lwline->points, pt); |
214 | 0 | lwline_free(lwline); |
215 | 0 | return result; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | lwerror("lwgeom_contains_point failed"); |
219 | 0 | return LW_FAILURE; |
220 | 0 | } |
221 | | |
222 | | /* |
223 | | * Use a ray-casting count to determine if the point |
224 | | * is inside or outside of the compound curve. Ray-casting |
225 | | * is run against each component of the overall arc, and |
226 | | * the even/odd test run against the total of all components. |
227 | | * Returns LW_INSIDE / LW_BOUNDARY / LW_OUTSIDE |
228 | | */ |
229 | | int |
230 | | lwcompound_contains_point(const LWCOMPOUND *comp, const POINT2D *pt) |
231 | 0 | { |
232 | 0 | int intersections = 0; |
233 | |
|
234 | 0 | if (lwgeom_is_empty(lwcompound_as_lwgeom(comp))) |
235 | 0 | return LW_OUTSIDE; |
236 | | |
237 | 0 | for (uint32_t j = 0; j < comp->ngeoms; j++) |
238 | 0 | { |
239 | 0 | int on_boundary = LW_FALSE; |
240 | 0 | const LWGEOM *sub = comp->geoms[j]; |
241 | 0 | if (sub->type == LINETYPE) |
242 | 0 | { |
243 | 0 | LWLINE *lwline = lwgeom_as_lwline(sub); |
244 | 0 | intersections += ptarray_raycast_intersections(lwline->points, pt, &on_boundary); |
245 | 0 | } |
246 | 0 | else if (sub->type == CIRCSTRINGTYPE) |
247 | 0 | { |
248 | 0 | LWCIRCSTRING *lwcirc = lwgeom_as_lwcircstring(sub); |
249 | 0 | intersections += ptarrayarc_raycast_intersections(lwcirc->points, pt, &on_boundary); |
250 | 0 | } |
251 | 0 | else if (sub->type == NURBSCURVETYPE) |
252 | 0 | { |
253 | 0 | LWLINE *lwline = lwnurbscurve_to_linestring((LWNURBSCURVE*)sub, 32); |
254 | 0 | intersections += ptarray_raycast_intersections(lwline->points, pt, &on_boundary); |
255 | 0 | lwline_free(lwline); |
256 | 0 | } |
257 | 0 | else |
258 | 0 | { |
259 | 0 | lwerror("%s: unsupported type %s", __func__, lwtype_name(sub->type)); |
260 | 0 | } |
261 | 0 | if (on_boundary) |
262 | 0 | return LW_BOUNDARY; |
263 | 0 | } |
264 | | |
265 | | /* |
266 | | * Odd number of intersections means inside. |
267 | | * Even means outside. |
268 | | */ |
269 | 0 | return (intersections % 2) ? LW_INSIDE : LW_OUTSIDE; |
270 | 0 | } |
271 | | |
272 | | LWCOMPOUND * |
273 | | lwcompound_construct_from_lwline(const LWLINE *lwline) |
274 | 0 | { |
275 | 0 | LWCOMPOUND* ogeom = lwcompound_construct_empty(lwline->srid, FLAGS_GET_Z(lwline->flags), FLAGS_GET_M(lwline->flags)); |
276 | 0 | lwcompound_add_lwgeom(ogeom, lwgeom_clone((LWGEOM*)lwline)); |
277 | | /* ogeom->bbox = lwline->bbox; */ |
278 | 0 | return ogeom; |
279 | 0 | } |
280 | | |
281 | | LWPOINT* |
282 | | lwcompound_get_lwpoint(const LWCOMPOUND *lwcmp, uint32_t where) |
283 | 0 | { |
284 | 0 | uint32_t i; |
285 | 0 | uint32_t count = 0; |
286 | 0 | uint32_t npoints = 0; |
287 | 0 | if ( lwgeom_is_empty((LWGEOM*)lwcmp) ) |
288 | 0 | return NULL; |
289 | | |
290 | 0 | npoints = lwgeom_count_vertices((LWGEOM*)lwcmp); |
291 | 0 | if ( where >= npoints ) |
292 | 0 | { |
293 | 0 | lwerror("%s: index %d is not in range of number of vertices (%d) in input", __func__, where, npoints); |
294 | 0 | return NULL; |
295 | 0 | } |
296 | | |
297 | 0 | for ( i = 0; i < lwcmp->ngeoms; i++ ) |
298 | 0 | { |
299 | 0 | LWGEOM* part = lwcmp->geoms[i]; |
300 | 0 | uint32_t npoints_part = lwgeom_count_vertices(part); |
301 | 0 | if ( where >= count && where < count + npoints_part ) |
302 | 0 | { |
303 | 0 | if (part->type == NURBSCURVETYPE) |
304 | 0 | { |
305 | 0 | const LWNURBSCURVE *curve = (const LWNURBSCURVE*)part; |
306 | 0 | POINTARRAY *pa; |
307 | 0 | POINT4D pt; |
308 | |
|
309 | 0 | if (!curve->points || where - count >= curve->points->npoints) |
310 | 0 | return NULL; |
311 | | |
312 | 0 | pa = ptarray_construct_empty(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 1); |
313 | 0 | pt = getPoint4d(curve->points, where - count); |
314 | 0 | ptarray_append_point(pa, &pt, LW_TRUE); |
315 | 0 | return lwpoint_construct(curve->srid, NULL, pa); |
316 | 0 | } |
317 | 0 | return lwline_get_lwpoint((LWLINE*)part, where - count); |
318 | 0 | } |
319 | 0 | else |
320 | 0 | { |
321 | 0 | count += npoints_part; |
322 | 0 | } |
323 | 0 | } |
324 | | |
325 | 0 | return NULL; |
326 | 0 | } |
327 | | |
328 | | |
329 | | |
330 | | LWPOINT * |
331 | | lwcompound_get_startpoint(const LWCOMPOUND *lwcmp) |
332 | 0 | { |
333 | 0 | if ( lwcmp->ngeoms < 1 ) |
334 | 0 | return NULL; |
335 | | |
336 | 0 | if ( lwcmp->geoms[0]->type == NURBSCURVETYPE ) |
337 | 0 | return lwnurbscurve_evaluate((const LWNURBSCURVE*)lwcmp->geoms[0], 0.0); |
338 | | |
339 | 0 | return lwcompound_get_lwpoint(lwcmp, 0); |
340 | 0 | } |
341 | | |
342 | | LWPOINT * |
343 | | lwcompound_get_endpoint(const LWCOMPOUND *lwcmp) |
344 | 0 | { |
345 | 0 | LWGEOM *last; |
346 | 0 | LWLINE *lwline; |
347 | 0 | if ( lwcmp->ngeoms < 1 ) |
348 | 0 | { |
349 | 0 | return NULL; |
350 | 0 | } |
351 | | |
352 | 0 | last = lwcmp->geoms[lwcmp->ngeoms-1]; |
353 | 0 | if ( last->type == NURBSCURVETYPE ) |
354 | 0 | return lwnurbscurve_evaluate((const LWNURBSCURVE*)last, 1.0); |
355 | | |
356 | 0 | lwline = (LWLINE*)last; |
357 | |
|
358 | 0 | if ( (!lwline) || (!lwline->points) || (lwline->points->npoints < 1) ) |
359 | 0 | { |
360 | 0 | return NULL; |
361 | 0 | } |
362 | | |
363 | 0 | return lwline_get_lwpoint(lwline, lwline->points->npoints-1); |
364 | 0 | } |