1 /**********************************************************************
2 *
3 * rttopo - topology library
4 * http://git.osgeo.org/gitea/rttopo/librttopo
5 *
6 * rttopo 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 * rttopo 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 rttopo. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright (C) 2001-2006 Refractions Research Inc.
22 *
23 **********************************************************************/
24
25
26
27 #include "rttopo_config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include "librttopo_geom_internal.h"
32 #include "rtgeom_log.h"
33
34
35 #define CHECK_RTGEOM_ZM 1
36
37 void
rtcollection_release(const RTCTX * ctx,RTCOLLECTION * rtcollection)38 rtcollection_release(const RTCTX *ctx, RTCOLLECTION *rtcollection)
39 {
40 rtgeom_release(ctx, rtcollection_as_rtgeom(ctx, rtcollection));
41 }
42
43
44 RTCOLLECTION *
rtcollection_construct(const RTCTX * ctx,uint8_t type,int srid,RTGBOX * bbox,uint32_t ngeoms,RTGEOM ** geoms)45 rtcollection_construct(const RTCTX *ctx, uint8_t type, int srid, RTGBOX *bbox,
46 uint32_t ngeoms, RTGEOM **geoms)
47 {
48 RTCOLLECTION *ret;
49 int hasz, hasm;
50 #ifdef CHECK_RTGEOM_ZM
51 char zm;
52 uint32_t i;
53 #endif
54
55 RTDEBUGF(ctx, 2, "rtcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms);
56
57 if( ! rttype_is_collection(ctx, type) )
58 rterror(ctx, "Non-collection type specified in collection constructor!");
59
60 hasz = 0;
61 hasm = 0;
62 if ( ngeoms > 0 )
63 {
64 hasz = RTFLAGS_GET_Z(geoms[0]->flags);
65 hasm = RTFLAGS_GET_M(geoms[0]->flags);
66 #ifdef CHECK_RTGEOM_ZM
67 zm = RTFLAGS_GET_ZM(geoms[0]->flags);
68
69 RTDEBUGF(ctx, 3, "rtcollection_construct type[0]=%d", geoms[0]->type);
70
71 for (i=1; i<ngeoms; i++)
72 {
73 RTDEBUGF(ctx, 3, "rtcollection_construct type=[%d]=%d", i, geoms[i]->type);
74
75 if ( zm != RTFLAGS_GET_ZM(geoms[i]->flags) )
76 rterror(ctx, "rtcollection_construct: mixed dimension geometries: %d/%d", zm, RTFLAGS_GET_ZM(geoms[i]->flags));
77 }
78 #endif
79 }
80
81
82 ret = rtalloc(ctx, sizeof(RTCOLLECTION));
83 ret->type = type;
84 ret->flags = gflags(ctx, hasz,hasm,0);
85 RTFLAGS_SET_BBOX(ret->flags, bbox?1:0);
86 ret->srid = srid;
87 ret->ngeoms = ngeoms;
88 ret->maxgeoms = ngeoms;
89 ret->geoms = geoms;
90 ret->bbox = bbox;
91
92 return ret;
93 }
94
95 RTCOLLECTION *
rtcollection_construct_empty(const RTCTX * ctx,uint8_t type,int srid,char hasz,char hasm)96 rtcollection_construct_empty(const RTCTX *ctx, uint8_t type, int srid, char hasz, char hasm)
97 {
98 RTCOLLECTION *ret;
99 if( ! rttype_is_collection(ctx, type) )
100 rterror(ctx, "Non-collection type specified in collection constructor!");
101
102 ret = rtalloc(ctx, sizeof(RTCOLLECTION));
103 ret->type = type;
104 ret->flags = gflags(ctx, hasz,hasm,0);
105 ret->srid = srid;
106 ret->ngeoms = 0;
107 ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */
108 ret->geoms = rtalloc(ctx, ret->maxgeoms * sizeof(RTGEOM*));
109 ret->bbox = NULL;
110
111 return ret;
112 }
113
114 RTGEOM *
rtcollection_getsubgeom(const RTCTX * ctx,RTCOLLECTION * col,int gnum)115 rtcollection_getsubgeom(const RTCTX *ctx, RTCOLLECTION *col, int gnum)
116 {
117 return (RTGEOM *)col->geoms[gnum];
118 }
119
120 /**
121 * @brief Clone #RTCOLLECTION object. #RTPOINTARRAY are not copied.
122 * Bbox is cloned if present in input.
123 */
124 RTCOLLECTION *
rtcollection_clone(const RTCTX * ctx,const RTCOLLECTION * g)125 rtcollection_clone(const RTCTX *ctx, const RTCOLLECTION *g)
126 {
127 uint32_t i;
128 RTCOLLECTION *ret = rtalloc(ctx, sizeof(RTCOLLECTION));
129 memcpy(ret, g, sizeof(RTCOLLECTION));
130 if ( g->ngeoms > 0 )
131 {
132 ret->geoms = rtalloc(ctx, sizeof(RTGEOM *)*g->ngeoms);
133 for (i=0; i<g->ngeoms; i++)
134 {
135 ret->geoms[i] = rtgeom_clone(ctx, g->geoms[i]);
136 }
137 if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox);
138 }
139 else
140 {
141 ret->bbox = NULL; /* empty collection */
142 ret->geoms = NULL;
143 }
144 return ret;
145 }
146
147 /**
148 * @brief Deep clone #RTCOLLECTION object. #RTPOINTARRAY are copied.
149 */
150 RTCOLLECTION *
rtcollection_clone_deep(const RTCTX * ctx,const RTCOLLECTION * g)151 rtcollection_clone_deep(const RTCTX *ctx, const RTCOLLECTION *g)
152 {
153 uint32_t i;
154 RTCOLLECTION *ret = rtalloc(ctx, sizeof(RTCOLLECTION));
155 memcpy(ret, g, sizeof(RTCOLLECTION));
156 if ( g->ngeoms > 0 )
157 {
158 ret->geoms = rtalloc(ctx, sizeof(RTGEOM *)*g->ngeoms);
159 for (i=0; i<g->ngeoms; i++)
160 {
161 ret->geoms[i] = rtgeom_clone_deep(ctx, g->geoms[i]);
162 }
163 if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox);
164 }
165 else
166 {
167 ret->bbox = NULL; /* empty collection */
168 ret->geoms = NULL;
169 }
170 return ret;
171 }
172
173 /**
174 * Ensure the collection can hold up at least ngeoms
175 */
rtcollection_reserve(const RTCTX * ctx,RTCOLLECTION * col,int ngeoms)176 void rtcollection_reserve(const RTCTX *ctx, RTCOLLECTION *col, int ngeoms)
177 {
178 if ( ngeoms <= col->maxgeoms ) return;
179
180 /* Allocate more space if we need it */
181 do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms );
182 col->geoms = rtrealloc(ctx, col->geoms, sizeof(RTGEOM*) * col->maxgeoms);
183 }
184
185 /**
186 * Appends geom to the collection managed by col. Does not copy or
187 * clone, simply takes a reference on the passed geom.
188 */
rtcollection_add_rtgeom(const RTCTX * ctx,RTCOLLECTION * col,const RTGEOM * geom)189 RTCOLLECTION* rtcollection_add_rtgeom(const RTCTX *ctx, RTCOLLECTION *col, const RTGEOM *geom)
190 {
191 if ( col == NULL || geom == NULL ) return NULL;
192
193 if ( col->geoms == NULL && (col->ngeoms || col->maxgeoms) ) {
194 rterror(ctx, "Collection is in inconsistent state. Null memory but non-zero collection counts.");
195 return NULL;
196 }
197
198 /* Check type compatibility */
199 if ( ! rtcollection_allows_subtype(ctx, col->type, geom->type) ) {
200 rterror(ctx, "%s cannot contain %s element", rttype_name(ctx, col->type), rttype_name(ctx, geom->type));
201 return NULL;
202 }
203
204 /* In case this is a truly empty, make some initial space */
205 if ( col->geoms == NULL )
206 {
207 col->maxgeoms = 2;
208 col->ngeoms = 0;
209 col->geoms = rtalloc(ctx, col->maxgeoms * sizeof(RTGEOM*));
210 }
211
212 /* Allocate more space if we need it */
213 rtcollection_reserve(ctx, col, col->ngeoms + 1);
214
215 #if PARANOIA_LEVEL > 1
216 /* See http://trac.osgeo.org/postgis/ticket/2933 */
217 /* Make sure we don't already have a reference to this geom */
218 {
219 int i = 0;
220 for ( i = 0; i < col->ngeoms; i++ )
221 {
222 if ( col->geoms[i] == geom )
223 {
224 RTDEBUGF(ctx, 4, "Found duplicate geometry in collection %p == %p", col->geoms[i], geom);
225 return col;
226 }
227 }
228 }
229 #endif
230
231 col->geoms[col->ngeoms] = (RTGEOM*)geom;
232 col->ngeoms++;
233 return col;
234 }
235
236
237 RTCOLLECTION *
rtcollection_segmentize2d(const RTCTX * ctx,RTCOLLECTION * col,double dist)238 rtcollection_segmentize2d(const RTCTX *ctx, RTCOLLECTION *col, double dist)
239 {
240 uint32_t i;
241 RTGEOM **newgeoms;
242
243 if ( ! col->ngeoms ) return rtcollection_clone(ctx, col);
244
245 newgeoms = rtalloc(ctx, sizeof(RTGEOM *)*col->ngeoms);
246 for (i=0; i<col->ngeoms; i++)
247 {
248 newgeoms[i] = rtgeom_segmentize2d(ctx, col->geoms[i], dist);
249 if ( ! newgeoms[i] ) {
250 while (i--) rtgeom_free(ctx, newgeoms[i]);
251 rtfree(ctx, newgeoms);
252 return NULL;
253 }
254 }
255
256 return rtcollection_construct(ctx, col->type, col->srid, NULL, col->ngeoms, newgeoms);
257 }
258
259 /** @brief check for same geometry composition
260 *
261 */
262 char
rtcollection_same(const RTCTX * ctx,const RTCOLLECTION * c1,const RTCOLLECTION * c2)263 rtcollection_same(const RTCTX *ctx, const RTCOLLECTION *c1, const RTCOLLECTION *c2)
264 {
265 uint32_t i;
266
267 RTDEBUG(ctx, 2, "rtcollection_same called");
268
269 if ( c1->type != c2->type ) return RT_FALSE;
270 if ( c1->ngeoms != c2->ngeoms ) return RT_FALSE;
271
272 for ( i = 0; i < c1->ngeoms; i++ )
273 {
274 if ( ! rtgeom_same(ctx, c1->geoms[i], c2->geoms[i]) )
275 return RT_FALSE;
276 }
277
278 /* Former method allowed out-of-order equality between collections
279
280 hit = rtalloc(ctx, sizeof(uint32_t)*c1->ngeoms);
281 memset(hit, 0, sizeof(uint32_t)*c1->ngeoms);
282
283 for (i=0; i<c1->ngeoms; i++)
284 {
285 char found=0;
286 for (j=0; j<c2->ngeoms; j++)
287 {
288 if ( hit[j] ) continue;
289 if ( rtgeom_same(ctx, c1->geoms[i], c2->geoms[j]) )
290 {
291 hit[j] = 1;
292 found=1;
293 break;
294 }
295 }
296 if ( ! found ) return RT_FALSE;
297 }
298 */
299
300 return RT_TRUE;
301 }
302
rtcollection_ngeoms(const RTCTX * ctx,const RTCOLLECTION * col)303 int rtcollection_ngeoms(const RTCTX *ctx, const RTCOLLECTION *col)
304 {
305 int i;
306 int ngeoms = 0;
307
308 if ( ! col )
309 {
310 rterror(ctx, "Null input geometry.");
311 return 0;
312 }
313
314 for ( i = 0; i < col->ngeoms; i++ )
315 {
316 if ( col->geoms[i])
317 {
318 switch (col->geoms[i]->type)
319 {
320 case RTPOINTTYPE:
321 case RTLINETYPE:
322 case RTCIRCSTRINGTYPE:
323 case RTPOLYGONTYPE:
324 ngeoms += 1;
325 break;
326 case RTMULTIPOINTTYPE:
327 case RTMULTILINETYPE:
328 case RTMULTICURVETYPE:
329 case RTMULTIPOLYGONTYPE:
330 ngeoms += col->ngeoms;
331 break;
332 case RTCOLLECTIONTYPE:
333 ngeoms += rtcollection_ngeoms(ctx, (RTCOLLECTION*)col->geoms[i]);
334 break;
335 }
336 }
337 }
338 return ngeoms;
339 }
340
rtcollection_free(const RTCTX * ctx,RTCOLLECTION * col)341 void rtcollection_free(const RTCTX *ctx, RTCOLLECTION *col)
342 {
343 int i;
344 if ( ! col ) return;
345
346 if ( col->bbox )
347 {
348 rtfree(ctx, col->bbox);
349 }
350 for ( i = 0; i < col->ngeoms; i++ )
351 {
352 RTDEBUGF(ctx, 4,"freeing geom[%d]", i);
353 if ( col->geoms && col->geoms[i] )
354 rtgeom_free(ctx, col->geoms[i]);
355 }
356 if ( col->geoms )
357 {
358 rtfree(ctx, col->geoms);
359 }
360 rtfree(ctx, col);
361 }
362
363
364 /**
365 * Takes a potentially heterogeneous collection and returns a homogeneous
366 * collection consisting only of the specified type.
367 */
rtcollection_extract(const RTCTX * ctx,RTCOLLECTION * col,int type)368 RTCOLLECTION* rtcollection_extract(const RTCTX *ctx, RTCOLLECTION *col, int type)
369 {
370 int i = 0;
371 RTGEOM **geomlist;
372 RTCOLLECTION *outcol;
373 int geomlistsize = 16;
374 int geomlistlen = 0;
375 uint8_t outtype;
376
377 if ( ! col ) return NULL;
378
379 switch (type)
380 {
381 case RTPOINTTYPE:
382 outtype = RTMULTIPOINTTYPE;
383 break;
384 case RTLINETYPE:
385 outtype = RTMULTILINETYPE;
386 break;
387 case RTPOLYGONTYPE:
388 outtype = RTMULTIPOLYGONTYPE;
389 break;
390 default:
391 rterror(ctx, "Only POLYGON, LINESTRING and POINT are supported by rtcollection_extract. %s requested.", rttype_name(ctx, type));
392 return NULL;
393 }
394
395 geomlist = rtalloc(ctx, sizeof(RTGEOM*) * geomlistsize);
396
397 /* Process each sub-geometry */
398 for ( i = 0; i < col->ngeoms; i++ )
399 {
400 int subtype = col->geoms[i]->type;
401 /* Don't bother adding empty sub-geometries */
402 if ( rtgeom_is_empty(ctx, col->geoms[i]) )
403 {
404 continue;
405 }
406 /* Copy our sub-types into the output list */
407 if ( subtype == type )
408 {
409 /* We've over-run our buffer, double the memory segment */
410 if ( geomlistlen == geomlistsize )
411 {
412 geomlistsize *= 2;
413 geomlist = rtrealloc(ctx, geomlist, sizeof(RTGEOM*) * geomlistsize);
414 }
415 geomlist[geomlistlen] = rtgeom_clone(ctx, col->geoms[i]);
416 geomlistlen++;
417 }
418 /* Recurse into sub-collections */
419 if ( rttype_is_collection(ctx, subtype ) )
420 {
421 int j = 0;
422 RTCOLLECTION *tmpcol = rtcollection_extract(ctx, (RTCOLLECTION*)col->geoms[i], type);
423 for ( j = 0; j < tmpcol->ngeoms; j++ )
424 {
425 /* We've over-run our buffer, double the memory segment */
426 if ( geomlistlen == geomlistsize )
427 {
428 geomlistsize *= 2;
429 geomlist = rtrealloc(ctx, geomlist, sizeof(RTGEOM*) * geomlistsize);
430 }
431 geomlist[geomlistlen] = tmpcol->geoms[j];
432 geomlistlen++;
433 }
434 rtfree(ctx, tmpcol);
435 }
436 }
437
438 if ( geomlistlen > 0 )
439 {
440 RTGBOX gbox;
441 outcol = rtcollection_construct(ctx, outtype, col->srid, NULL, geomlistlen, geomlist);
442 rtgeom_calculate_gbox(ctx, (RTGEOM *) outcol, &gbox);
443 outcol->bbox = gbox_copy(ctx, &gbox);
444 }
445 else
446 {
447 rtfree(ctx, geomlist);
448 outcol = rtcollection_construct_empty(ctx, outtype, col->srid, RTFLAGS_GET_Z(col->flags), RTFLAGS_GET_M(col->flags));
449 }
450
451 return outcol;
452 }
453
454 RTGEOM*
rtcollection_remove_repeated_points(const RTCTX * ctx,const RTCOLLECTION * coll,double tolerance)455 rtcollection_remove_repeated_points(const RTCTX *ctx, const RTCOLLECTION *coll, double tolerance)
456 {
457 uint32_t i;
458 RTGEOM **newgeoms;
459
460 newgeoms = rtalloc(ctx, sizeof(RTGEOM *)*coll->ngeoms);
461 for (i=0; i<coll->ngeoms; i++)
462 {
463 newgeoms[i] = rtgeom_remove_repeated_points(ctx, coll->geoms[i], tolerance);
464 }
465
466 return (RTGEOM*)rtcollection_construct(ctx, coll->type,
467 coll->srid, coll->bbox ? gbox_copy(ctx, coll->bbox) : NULL,
468 coll->ngeoms, newgeoms);
469 }
470
471
472 RTCOLLECTION*
rtcollection_force_dims(const RTCTX * ctx,const RTCOLLECTION * col,int hasz,int hasm)473 rtcollection_force_dims(const RTCTX *ctx, const RTCOLLECTION *col, int hasz, int hasm)
474 {
475 RTCOLLECTION *colout;
476
477 /* Return 2D empty */
478 if( rtcollection_is_empty(ctx, col) )
479 {
480 colout = rtcollection_construct_empty(ctx, col->type, col->srid, hasz, hasm);
481 }
482 else
483 {
484 int i;
485 RTGEOM **geoms = NULL;
486 geoms = rtalloc(ctx, sizeof(RTGEOM*) * col->ngeoms);
487 for( i = 0; i < col->ngeoms; i++ )
488 {
489 geoms[i] = rtgeom_force_dims(ctx, col->geoms[i], hasz, hasm);
490 }
491 colout = rtcollection_construct(ctx, col->type, col->srid, NULL, col->ngeoms, geoms);
492 }
493 return colout;
494 }
495
rtcollection_is_empty(const RTCTX * ctx,const RTCOLLECTION * col)496 int rtcollection_is_empty(const RTCTX *ctx, const RTCOLLECTION *col)
497 {
498 int i;
499 if ( (col->ngeoms == 0) || (!col->geoms) )
500 return RT_TRUE;
501 for( i = 0; i < col->ngeoms; i++ )
502 {
503 if ( ! rtgeom_is_empty(ctx, col->geoms[i]) ) return RT_FALSE;
504 }
505 return RT_TRUE;
506 }
507
508
rtcollection_count_vertices(const RTCTX * ctx,RTCOLLECTION * col)509 int rtcollection_count_vertices(const RTCTX *ctx, RTCOLLECTION *col)
510 {
511 int i = 0;
512 int v = 0; /* vertices */
513 assert(col);
514 for ( i = 0; i < col->ngeoms; i++ )
515 {
516 v += rtgeom_count_vertices(ctx, col->geoms[i]);
517 }
518 return v;
519 }
520
rtcollection_simplify(const RTCTX * ctx,const RTCOLLECTION * igeom,double dist,int preserve_collapsed)521 RTCOLLECTION* rtcollection_simplify(const RTCTX *ctx, const RTCOLLECTION *igeom, double dist, int preserve_collapsed)
522 {
523 int i;
524 RTCOLLECTION *out = rtcollection_construct_empty(ctx, igeom->type, igeom->srid, RTFLAGS_GET_Z(igeom->flags), RTFLAGS_GET_M(igeom->flags));
525
526 if( rtcollection_is_empty(ctx, igeom) )
527 return out; /* should we return NULL instead ? */
528
529 for( i = 0; i < igeom->ngeoms; i++ )
530 {
531 RTGEOM *ngeom = rtgeom_simplify(ctx, igeom->geoms[i], dist, preserve_collapsed);
532 if ( ngeom ) out = rtcollection_add_rtgeom(ctx, out, ngeom);
533 }
534
535 return out;
536 }
537
rtcollection_allows_subtype(const RTCTX * ctx,int collectiontype,int subtype)538 int rtcollection_allows_subtype(const RTCTX *ctx, int collectiontype, int subtype)
539 {
540 if ( collectiontype == RTCOLLECTIONTYPE )
541 return RT_TRUE;
542 if ( collectiontype == RTMULTIPOINTTYPE &&
543 subtype == RTPOINTTYPE )
544 return RT_TRUE;
545 if ( collectiontype == RTMULTILINETYPE &&
546 subtype == RTLINETYPE )
547 return RT_TRUE;
548 if ( collectiontype == RTMULTIPOLYGONTYPE &&
549 subtype == RTPOLYGONTYPE )
550 return RT_TRUE;
551 if ( collectiontype == RTCOMPOUNDTYPE &&
552 (subtype == RTLINETYPE || subtype == RTCIRCSTRINGTYPE) )
553 return RT_TRUE;
554 if ( collectiontype == RTCURVEPOLYTYPE &&
555 (subtype == RTCIRCSTRINGTYPE || subtype == RTLINETYPE || subtype == RTCOMPOUNDTYPE) )
556 return RT_TRUE;
557 if ( collectiontype == RTMULTICURVETYPE &&
558 (subtype == RTCIRCSTRINGTYPE || subtype == RTLINETYPE || subtype == RTCOMPOUNDTYPE) )
559 return RT_TRUE;
560 if ( collectiontype == RTMULTISURFACETYPE &&
561 (subtype == RTPOLYGONTYPE || subtype == RTCURVEPOLYTYPE) )
562 return RT_TRUE;
563 if ( collectiontype == RTPOLYHEDRALSURFACETYPE &&
564 subtype == RTPOLYGONTYPE )
565 return RT_TRUE;
566 if ( collectiontype == RTTINTYPE &&
567 subtype == RTTRIANGLETYPE )
568 return RT_TRUE;
569
570 /* Must be a bad combination! */
571 return RT_FALSE;
572 }
573
574 int
rtcollection_startpoint(const RTCTX * ctx,const RTCOLLECTION * col,RTPOINT4D * pt)575 rtcollection_startpoint(const RTCTX *ctx, const RTCOLLECTION* col, RTPOINT4D* pt)
576 {
577 if ( col->ngeoms < 1 )
578 return RT_FAILURE;
579
580 return rtgeom_startpoint(ctx, col->geoms[0], pt);
581 }
582
583
rtcollection_grid(const RTCTX * ctx,const RTCOLLECTION * coll,const gridspec * grid)584 RTCOLLECTION* rtcollection_grid(const RTCTX *ctx, const RTCOLLECTION *coll, const gridspec *grid)
585 {
586 uint32_t i;
587 RTCOLLECTION *newcoll;
588
589 newcoll = rtcollection_construct_empty(ctx, coll->type, coll->srid, rtgeom_has_z(ctx, (RTGEOM*)coll), rtgeom_has_m(ctx, (RTGEOM*)coll));
590
591 for (i=0; i<coll->ngeoms; i++)
592 {
593 RTGEOM *g = rtgeom_grid(ctx, coll->geoms[i], grid);
594 if ( g )
595 rtcollection_add_rtgeom(ctx, newcoll, g);
596 }
597
598 return newcoll;
599 }
600