1 : /******************************************************************************
2 : * $Id: ogrgeometrycollection.cpp 19999 2010-07-10 09:59:58Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: The OGRGeometryCollection class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_geometry.h"
31 : #include "ogr_p.h"
32 :
33 : CPL_CVSID("$Id: ogrgeometrycollection.cpp 19999 2010-07-10 09:59:58Z rouault $");
34 :
35 : /************************************************************************/
36 : /* OGRGeometryCollection() */
37 : /************************************************************************/
38 :
39 : /**
40 : * \brief Create an empty geometry collection.
41 : */
42 :
43 1169 : OGRGeometryCollection::OGRGeometryCollection()
44 :
45 : {
46 1169 : nGeomCount = 0;
47 1169 : papoGeoms = NULL;
48 1169 : }
49 :
50 : /************************************************************************/
51 : /* ~OGRGeometryCollection() */
52 : /************************************************************************/
53 :
54 1169 : OGRGeometryCollection::~OGRGeometryCollection()
55 :
56 : {
57 1169 : empty();
58 1169 : }
59 :
60 : /************************************************************************/
61 : /* empty() */
62 : /************************************************************************/
63 :
64 1744 : void OGRGeometryCollection::empty()
65 :
66 : {
67 1744 : if( papoGeoms != NULL )
68 : {
69 2899 : for( int i = 0; i < nGeomCount; i++ )
70 : {
71 1995 : delete papoGeoms[i];
72 : }
73 904 : OGRFree( papoGeoms );
74 : }
75 :
76 1744 : nGeomCount = 0;
77 1744 : papoGeoms = NULL;
78 1744 : nCoordDimension = 2;
79 1744 : }
80 :
81 :
82 : /************************************************************************/
83 : /* clone() */
84 : /************************************************************************/
85 :
86 83 : OGRGeometry *OGRGeometryCollection::clone() const
87 :
88 : {
89 : OGRGeometryCollection *poNewGC;
90 :
91 83 : poNewGC = new OGRGeometryCollection;
92 83 : poNewGC->assignSpatialReference( getSpatialReference() );
93 :
94 208 : for( int i = 0; i < nGeomCount; i++ )
95 : {
96 125 : poNewGC->addGeometry( papoGeoms[i] );
97 : }
98 :
99 83 : return poNewGC;
100 : }
101 :
102 : /************************************************************************/
103 : /* getGeometryType() */
104 : /************************************************************************/
105 :
106 260 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
107 :
108 : {
109 260 : if( getCoordinateDimension() == 3 )
110 104 : return wkbGeometryCollection25D;
111 : else
112 156 : return wkbGeometryCollection;
113 : }
114 :
115 : /************************************************************************/
116 : /* getDimension() */
117 : /************************************************************************/
118 :
119 0 : int OGRGeometryCollection::getDimension() const
120 :
121 : {
122 0 : return 2; // This isn't strictly correct. It should be based on members.
123 : }
124 :
125 : /************************************************************************/
126 : /* flattenTo2D() */
127 : /************************************************************************/
128 :
129 3 : void OGRGeometryCollection::flattenTo2D()
130 :
131 : {
132 12 : for( int i = 0; i < nGeomCount; i++ )
133 9 : papoGeoms[i]->flattenTo2D();
134 :
135 3 : nCoordDimension = 2;
136 3 : }
137 :
138 : /************************************************************************/
139 : /* getGeometryName() */
140 : /************************************************************************/
141 :
142 114 : const char * OGRGeometryCollection::getGeometryName() const
143 :
144 : {
145 114 : return "GEOMETRYCOLLECTION";
146 : }
147 :
148 : /************************************************************************/
149 : /* getNumGeometries() */
150 : /************************************************************************/
151 :
152 : /**
153 : * \brief Fetch number of geometries in container.
154 : *
155 : * This method relates to the SFCOM IGeometryCollect::get_NumGeometries()
156 : * method.
157 : *
158 : * @return count of children geometries. May be zero.
159 : */
160 :
161 2463 : int OGRGeometryCollection::getNumGeometries() const
162 :
163 : {
164 2463 : return nGeomCount;
165 : }
166 :
167 : /************************************************************************/
168 : /* getGeometryRef() */
169 : /************************************************************************/
170 :
171 : /**
172 : * \brief Fetch geometry from container.
173 : *
174 : * This method returns a pointer to an geometry within the container. The
175 : * returned geometry remains owned by the container, and should not be
176 : * modified. The pointer is only valid untill the next change to the
177 : * geometry container. Use IGeometry::clone() to make a copy.
178 : *
179 : * This method relates to the SFCOM IGeometryCollection::get_Geometry() method.
180 : *
181 : * @param i the index of the geometry to fetch, between 0 and
182 : * getNumGeometries() - 1.
183 : * @return pointer to requested geometry.
184 : */
185 :
186 971 : OGRGeometry * OGRGeometryCollection::getGeometryRef( int i )
187 :
188 : {
189 971 : if( i < 0 || i >= nGeomCount )
190 0 : return NULL;
191 : else
192 971 : return papoGeoms[i];
193 : }
194 :
195 770 : const OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) const
196 :
197 : {
198 770 : if( i < 0 || i >= nGeomCount )
199 0 : return NULL;
200 : else
201 770 : return papoGeoms[i];
202 : }
203 :
204 : /************************************************************************/
205 : /* addGeometry() */
206 : /* */
207 : /* Add a new geometry to a collection. Subclasses should */
208 : /* override this to verify the type of the new geometry, and */
209 : /* then call this method to actually add it. */
210 : /************************************************************************/
211 :
212 : /**
213 : * \brief Add a geometry to the container.
214 : *
215 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
216 : * that can be added, and may return an error. The passed geometry is cloned
217 : * to make an internal copy.
218 : *
219 : * There is no SFCOM analog to this method.
220 : *
221 : * This method is the same as the C function OGR_G_AddGeometry().
222 : *
223 : * @param poNewGeom geometry to add to the container.
224 : *
225 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
226 : * the geometry type is illegal for the type of geometry container.
227 : */
228 :
229 825 : OGRErr OGRGeometryCollection::addGeometry( const OGRGeometry * poNewGeom )
230 :
231 : {
232 825 : OGRGeometry *poClone = poNewGeom->clone();
233 : OGRErr eErr;
234 :
235 825 : eErr = addGeometryDirectly( poClone );
236 825 : if( eErr != OGRERR_NONE )
237 0 : delete poClone;
238 :
239 825 : return eErr;
240 : }
241 :
242 : /************************************************************************/
243 : /* addGeometryDirectly() */
244 : /* */
245 : /* Add a new geometry to a collection. Subclasses should */
246 : /* override this to verify the type of the new geometry, and */
247 : /* then call this method to actually add it. */
248 : /************************************************************************/
249 :
250 : /**
251 : * \brief Add a geometry directly to the container.
252 : *
253 : * Some subclasses of OGRGeometryCollection restrict the types of geometry
254 : * that can be added, and may return an error. Ownership of the passed
255 : * geometry is taken by the container rather than cloning as addGeometry()
256 : * does.
257 : *
258 : * This method is the same as the C function OGR_G_AddGeometryDirectly().
259 : *
260 : * There is no SFCOM analog to this method.
261 : *
262 : * @param poNewGeom geometry to add to the container.
263 : *
264 : * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if
265 : * the geometry type is illegal for the type of geometry container.
266 : */
267 :
268 1749 : OGRErr OGRGeometryCollection::addGeometryDirectly( OGRGeometry * poNewGeom )
269 :
270 : {
271 : papoGeoms = (OGRGeometry **) OGRRealloc( papoGeoms,
272 1749 : sizeof(void*) * (nGeomCount+1) );
273 :
274 1749 : papoGeoms[nGeomCount] = poNewGeom;
275 :
276 1749 : nGeomCount++;
277 :
278 1749 : if( poNewGeom->getCoordinateDimension() == 3 )
279 314 : nCoordDimension = 3;
280 :
281 1749 : return OGRERR_NONE;
282 : }
283 :
284 : /************************************************************************/
285 : /* removeGeometry() */
286 : /************************************************************************/
287 :
288 : /**
289 : * \brief Remove a geometry from the container.
290 : *
291 : * Removing a geometry will cause the geometry count to drop by one, and all
292 : * "higher" geometries will shuffle down one in index.
293 : *
294 : * There is no SFCOM analog to this method.
295 : *
296 : * This method is the same as the C function OGR_G_RemoveGeometry().
297 : *
298 : * @param iGeom the index of the geometry to delete. A value of -1 is a
299 : * special flag meaning that all geometries should be removed.
300 : *
301 : * @param bDelete if TRUE the geometry will be deallocated, otherwise it will
302 : * not. The default is TRUE as the container is considered to own the
303 : * geometries in it.
304 : *
305 : * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is
306 : * out of range.
307 : */
308 :
309 13 : OGRErr OGRGeometryCollection::removeGeometry( int iGeom, int bDelete )
310 :
311 : {
312 13 : if( iGeom < -1 || iGeom >= nGeomCount )
313 0 : return OGRERR_FAILURE;
314 :
315 : // Special case.
316 13 : if( iGeom == -1 )
317 : {
318 0 : while( nGeomCount > 0 )
319 0 : removeGeometry( nGeomCount-1, bDelete );
320 0 : return OGRERR_NONE;
321 : }
322 :
323 13 : if( bDelete )
324 0 : delete papoGeoms[iGeom];
325 :
326 : memmove( papoGeoms + iGeom, papoGeoms + iGeom + 1,
327 13 : sizeof(void*) * (nGeomCount-iGeom-1) );
328 :
329 13 : nGeomCount--;
330 :
331 13 : return OGRERR_NONE;
332 : }
333 :
334 : /************************************************************************/
335 : /* WkbSize() */
336 : /* */
337 : /* Return the size of this object in well known binary */
338 : /* representation including the byte order, and type information. */
339 : /************************************************************************/
340 :
341 82 : int OGRGeometryCollection::WkbSize() const
342 :
343 : {
344 82 : int nSize = 9;
345 :
346 224 : for( int i = 0; i < nGeomCount; i++ )
347 : {
348 142 : nSize += papoGeoms[i]->WkbSize();
349 : }
350 :
351 82 : return nSize;
352 : }
353 :
354 : /************************************************************************/
355 : /* importFromWkb() */
356 : /* */
357 : /* Initialize from serialized stream in well known binary */
358 : /* format. */
359 : /************************************************************************/
360 :
361 : OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData,
362 139 : int nSize )
363 :
364 : {
365 : OGRwkbByteOrder eByteOrder;
366 : int nDataOffset;
367 :
368 139 : if( nSize < 9 && nSize != -1 )
369 0 : return OGRERR_NOT_ENOUGH_DATA;
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Get the byte order byte. */
373 : /* -------------------------------------------------------------------- */
374 139 : eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData);
375 139 : if (!( eByteOrder == wkbXDR || eByteOrder == wkbNDR ))
376 0 : return OGRERR_CORRUPT_DATA;
377 :
378 : /* -------------------------------------------------------------------- */
379 : /* Get the geometry feature type. For now we assume that */
380 : /* geometry type is between 0 and 255 so we only have to fetch */
381 : /* one byte. */
382 : /* -------------------------------------------------------------------- */
383 : #ifdef DEBUG
384 : OGRwkbGeometryType eGeometryType;
385 :
386 139 : if( eByteOrder == wkbNDR )
387 : {
388 122 : eGeometryType = (OGRwkbGeometryType) pabyData[1];
389 : }
390 : else
391 : {
392 17 : eGeometryType = (OGRwkbGeometryType) pabyData[4];
393 : }
394 :
395 139 : if (! ( eGeometryType == wkbGeometryCollection
396 : || eGeometryType == wkbMultiPolygon
397 : || eGeometryType == wkbMultiLineString
398 : || eGeometryType == wkbMultiPoint ))
399 0 : return OGRERR_CORRUPT_DATA;
400 : #endif
401 :
402 : /* -------------------------------------------------------------------- */
403 : /* Clear existing Geoms. */
404 : /* -------------------------------------------------------------------- */
405 139 : empty();
406 :
407 : /* -------------------------------------------------------------------- */
408 : /* Get the geometry count. */
409 : /* -------------------------------------------------------------------- */
410 139 : memcpy( &nGeomCount, pabyData + 5, 4 );
411 :
412 139 : if( OGR_SWAP( eByteOrder ) )
413 17 : nGeomCount = CPL_SWAP32(nGeomCount);
414 :
415 139 : if (nGeomCount < 0 || nGeomCount > INT_MAX / 9)
416 : {
417 0 : nGeomCount = 0;
418 0 : return OGRERR_CORRUPT_DATA;
419 : }
420 :
421 : /* Each geometry has a minimum of 9 bytes */
422 139 : if (nSize != -1 && nSize - 9 < nGeomCount * 9)
423 : {
424 : CPLError( CE_Failure, CPLE_AppDefined,
425 0 : "Length of input WKB is too small" );
426 0 : nGeomCount = 0;
427 0 : return OGRERR_NOT_ENOUGH_DATA;
428 : }
429 :
430 139 : papoGeoms = (OGRGeometry **) VSIMalloc2(sizeof(void*), nGeomCount);
431 139 : if (nGeomCount != 0 && papoGeoms == NULL)
432 : {
433 0 : nGeomCount = 0;
434 0 : return OGRERR_NOT_ENOUGH_MEMORY;
435 : }
436 :
437 139 : nDataOffset = 9;
438 139 : if( nSize != -1 )
439 139 : nSize -= nDataOffset;
440 :
441 : /* -------------------------------------------------------------------- */
442 : /* Get the Geoms. */
443 : /* -------------------------------------------------------------------- */
444 398 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
445 : {
446 : OGRErr eErr;
447 :
448 : eErr = OGRGeometryFactory::
449 : createFromWkb( pabyData + nDataOffset, NULL,
450 259 : papoGeoms + iGeom, nSize );
451 :
452 259 : if( eErr != OGRERR_NONE )
453 : {
454 0 : nGeomCount = iGeom;
455 0 : return eErr;
456 : }
457 :
458 259 : if (papoGeoms[iGeom]->getCoordinateDimension() == 3)
459 60 : nCoordDimension = 3;
460 :
461 259 : if( nSize != -1 )
462 259 : nSize -= papoGeoms[iGeom]->WkbSize();
463 :
464 259 : nDataOffset += papoGeoms[iGeom]->WkbSize();
465 : }
466 :
467 139 : return OGRERR_NONE;
468 : }
469 :
470 : /************************************************************************/
471 : /* exportToWkb() */
472 : /* */
473 : /* Build a well known binary representation of this object. */
474 : /************************************************************************/
475 :
476 : OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder,
477 82 : unsigned char * pabyData ) const
478 :
479 : {
480 : int nOffset;
481 :
482 : /* -------------------------------------------------------------------- */
483 : /* Set the byte order. */
484 : /* -------------------------------------------------------------------- */
485 82 : pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder);
486 :
487 : /* -------------------------------------------------------------------- */
488 : /* Set the geometry feature type, ensuring that 3D flag is */
489 : /* preserved. */
490 : /* -------------------------------------------------------------------- */
491 82 : GUInt32 nGType = getGeometryType();
492 :
493 82 : if( eByteOrder == wkbNDR )
494 66 : nGType = CPL_LSBWORD32( nGType );
495 : else
496 16 : nGType = CPL_MSBWORD32( nGType );
497 :
498 82 : memcpy( pabyData + 1, &nGType, 4 );
499 :
500 : /* -------------------------------------------------------------------- */
501 : /* Copy in the raw data. */
502 : /* -------------------------------------------------------------------- */
503 82 : if( OGR_SWAP( eByteOrder ) )
504 : {
505 : int nCount;
506 :
507 16 : nCount = CPL_SWAP32( nGeomCount );
508 16 : memcpy( pabyData+5, &nCount, 4 );
509 : }
510 : else
511 : {
512 66 : memcpy( pabyData+5, &nGeomCount, 4 );
513 : }
514 :
515 82 : nOffset = 9;
516 :
517 : /* ==================================================================== */
518 : /* Serialize each of the Geoms. */
519 : /* ==================================================================== */
520 224 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
521 : {
522 142 : papoGeoms[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset );
523 :
524 142 : nOffset += papoGeoms[iGeom]->WkbSize();
525 : }
526 :
527 82 : return OGRERR_NONE;
528 : }
529 :
530 : /************************************************************************/
531 : /* importFromWkt() */
532 : /************************************************************************/
533 :
534 68 : OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput )
535 :
536 : {
537 :
538 : char szToken[OGR_WKT_TOKEN_MAX];
539 68 : const char *pszInput = *ppszInput;
540 :
541 : /* -------------------------------------------------------------------- */
542 : /* Clear existing Geoms. */
543 : /* -------------------------------------------------------------------- */
544 68 : empty();
545 :
546 : /* -------------------------------------------------------------------- */
547 : /* Read and verify the type keyword, and ensure it matches the */
548 : /* actual type of this container. */
549 : /* -------------------------------------------------------------------- */
550 68 : pszInput = OGRWktReadToken( pszInput, szToken );
551 :
552 68 : if( !EQUAL(szToken,getGeometryName()) )
553 0 : return OGRERR_CORRUPT_DATA;
554 :
555 : /* -------------------------------------------------------------------- */
556 : /* Check for EMPTY ... */
557 : /* -------------------------------------------------------------------- */
558 : const char *pszPreScan;
559 68 : int bHasZ = FALSE, bHasM = FALSE;
560 :
561 68 : pszPreScan = OGRWktReadToken( pszInput, szToken );
562 68 : if( EQUAL(szToken,"EMPTY") )
563 : {
564 6 : *ppszInput = (char *) pszPreScan;
565 6 : empty();
566 6 : return OGRERR_NONE;
567 : }
568 :
569 : /* -------------------------------------------------------------------- */
570 : /* Check for Z, M or ZM. Will ignore the Measure */
571 : /* -------------------------------------------------------------------- */
572 62 : else if( EQUAL(szToken,"Z") )
573 : {
574 8 : bHasZ = TRUE;
575 : }
576 54 : else if( EQUAL(szToken,"M") )
577 : {
578 2 : bHasM = TRUE;
579 : }
580 52 : else if( EQUAL(szToken,"ZM") )
581 : {
582 2 : bHasZ = TRUE;
583 2 : bHasM = TRUE;
584 : }
585 :
586 62 : if (bHasZ || bHasM)
587 : {
588 12 : pszInput = pszPreScan;
589 12 : pszPreScan = OGRWktReadToken( pszInput, szToken );
590 12 : if( EQUAL(szToken,"EMPTY") )
591 : {
592 4 : *ppszInput = (char *) pszPreScan;
593 4 : empty();
594 : /* FIXME?: In theory we should store the dimension and M presence */
595 : /* if we want to allow round-trip with ExportToWKT v1.2 */
596 4 : return OGRERR_NONE;
597 : }
598 : }
599 :
600 58 : if( !EQUAL(szToken,"(") )
601 3 : return OGRERR_CORRUPT_DATA;
602 :
603 55 : if ( !bHasZ && !bHasM )
604 : {
605 : /* Test for old-style GEOMETRYCOLLECTION(EMPTY) */
606 48 : pszPreScan = OGRWktReadToken( pszPreScan, szToken );
607 48 : if( EQUAL(szToken,"EMPTY") )
608 : {
609 4 : pszInput = OGRWktReadToken( pszPreScan, szToken );
610 :
611 4 : if( !EQUAL(szToken,")") )
612 2 : return OGRERR_CORRUPT_DATA;
613 : else
614 : {
615 2 : *ppszInput = (char *) pszInput;
616 2 : empty();
617 2 : return OGRERR_NONE;
618 : }
619 : }
620 : }
621 :
622 : /* Skip first '(' */
623 51 : pszInput = OGRWktReadToken( pszInput, szToken );
624 :
625 : /* ==================================================================== */
626 : /* Read each subgeometry in turn. */
627 : /* ==================================================================== */
628 110 : do
629 : {
630 121 : OGRGeometry *poGeom = NULL;
631 : OGRErr eErr;
632 :
633 : eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput,
634 121 : NULL, &poGeom );
635 121 : if( eErr != OGRERR_NONE )
636 11 : return eErr;
637 :
638 110 : addGeometryDirectly( poGeom );
639 :
640 : /* -------------------------------------------------------------------- */
641 : /* Read the delimeter following the ring. */
642 : /* -------------------------------------------------------------------- */
643 :
644 110 : pszInput = OGRWktReadToken( pszInput, szToken );
645 :
646 : } while( szToken[0] == ',' );
647 :
648 : /* -------------------------------------------------------------------- */
649 : /* freak if we don't get a closing bracket. */
650 : /* -------------------------------------------------------------------- */
651 40 : if( szToken[0] != ')' )
652 2 : return OGRERR_CORRUPT_DATA;
653 :
654 38 : *ppszInput = (char *) pszInput;
655 :
656 38 : return OGRERR_NONE;
657 : }
658 :
659 : /************************************************************************/
660 : /* exportToWkt() */
661 : /* */
662 : /* Translate this structure into it's well known text format */
663 : /* equivelent. This could be made alot more CPU efficient! */
664 : /************************************************************************/
665 :
666 35 : OGRErr OGRGeometryCollection::exportToWkt( char ** ppszDstText ) const
667 :
668 : {
669 : char **papszGeoms;
670 35 : int iGeom, nCumulativeLength = 0;
671 : OGRErr eErr;
672 :
673 35 : if( getNumGeometries() == 0 )
674 : {
675 12 : *ppszDstText = CPLStrdup("GEOMETRYCOLLECTION EMPTY");
676 12 : return OGRERR_NONE;
677 : }
678 :
679 : /* -------------------------------------------------------------------- */
680 : /* Build a list of strings containing the stuff for each Geom. */
681 : /* -------------------------------------------------------------------- */
682 23 : papszGeoms = (char **) CPLCalloc(sizeof(char *),nGeomCount);
683 :
684 110 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
685 : {
686 87 : eErr = papoGeoms[iGeom]->exportToWkt( &(papszGeoms[iGeom]) );
687 87 : if( eErr != OGRERR_NONE )
688 0 : goto error;
689 :
690 87 : nCumulativeLength += strlen(papszGeoms[iGeom]);
691 : }
692 :
693 : /* -------------------------------------------------------------------- */
694 : /* Allocate the right amount of space for the aggregated string */
695 : /* -------------------------------------------------------------------- */
696 23 : *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nGeomCount + 23);
697 :
698 23 : if( *ppszDstText == NULL )
699 : {
700 0 : eErr = OGRERR_NOT_ENOUGH_MEMORY;
701 0 : goto error;
702 : }
703 :
704 : /* -------------------------------------------------------------------- */
705 : /* Build up the string, freeing temporary strings as we go. */
706 : /* -------------------------------------------------------------------- */
707 23 : strcpy( *ppszDstText, getGeometryName() );
708 23 : strcat( *ppszDstText, " (" );
709 23 : nCumulativeLength = strlen(*ppszDstText);
710 :
711 110 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
712 : {
713 87 : if( iGeom > 0 )
714 64 : (*ppszDstText)[nCumulativeLength++] = ',';
715 :
716 87 : int nGeomLength = strlen(papszGeoms[iGeom]);
717 87 : memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom], nGeomLength );
718 87 : nCumulativeLength += nGeomLength;
719 87 : VSIFree( papszGeoms[iGeom] );
720 : }
721 :
722 23 : (*ppszDstText)[nCumulativeLength++] = ')';
723 23 : (*ppszDstText)[nCumulativeLength] = '\0';
724 :
725 23 : CPLFree( papszGeoms );
726 :
727 23 : return OGRERR_NONE;
728 :
729 0 : error:
730 0 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
731 0 : CPLFree( papszGeoms[iGeom] );
732 0 : CPLFree( papszGeoms );
733 0 : return eErr;
734 : }
735 :
736 : /************************************************************************/
737 : /* getEnvelope() */
738 : /************************************************************************/
739 :
740 62 : void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const
741 :
742 : {
743 62 : OGREnvelope oGeomEnv;
744 62 : int bExtentSet = FALSE;
745 :
746 194 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
747 : {
748 132 : if (!papoGeoms[iGeom]->IsEmpty())
749 : {
750 132 : if (!bExtentSet)
751 : {
752 58 : papoGeoms[iGeom]->getEnvelope( psEnvelope );
753 58 : bExtentSet = TRUE;
754 : }
755 : else
756 : {
757 74 : papoGeoms[iGeom]->getEnvelope( &oGeomEnv );
758 :
759 74 : if( psEnvelope->MinX > oGeomEnv.MinX )
760 41 : psEnvelope->MinX = oGeomEnv.MinX;
761 74 : if( psEnvelope->MinY > oGeomEnv.MinY )
762 33 : psEnvelope->MinY = oGeomEnv.MinY;
763 74 : if( psEnvelope->MaxX < oGeomEnv.MaxX )
764 23 : psEnvelope->MaxX = oGeomEnv.MaxX;
765 74 : if( psEnvelope->MaxY < oGeomEnv.MaxY )
766 19 : psEnvelope->MaxY = oGeomEnv.MaxY;
767 : }
768 : }
769 : }
770 :
771 62 : if (!bExtentSet)
772 : {
773 4 : psEnvelope->MinX = psEnvelope->MinY = 0;
774 4 : psEnvelope->MaxX = psEnvelope->MaxY = 0;
775 : }
776 62 : }
777 :
778 : /************************************************************************/
779 : /* Equals() */
780 : /************************************************************************/
781 :
782 1 : OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const
783 :
784 : {
785 1 : OGRGeometryCollection *poOGC = (OGRGeometryCollection *) poOther;
786 :
787 1 : if( poOGC == this )
788 0 : return TRUE;
789 :
790 1 : if( poOther->getGeometryType() != getGeometryType() )
791 0 : return FALSE;
792 :
793 1 : if( getNumGeometries() != poOGC->getNumGeometries() )
794 0 : return FALSE;
795 :
796 : // we should eventually test the SRS.
797 :
798 3 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
799 : {
800 2 : if( !getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)) )
801 0 : return FALSE;
802 : }
803 :
804 1 : return TRUE;
805 : }
806 :
807 : /************************************************************************/
808 : /* transform() */
809 : /************************************************************************/
810 :
811 3 : OGRErr OGRGeometryCollection::transform( OGRCoordinateTransformation *poCT )
812 :
813 : {
814 : #ifdef DISABLE_OGRGEOM_TRANSFORM
815 : return OGRERR_FAILURE;
816 : #else
817 6 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
818 : {
819 : OGRErr eErr;
820 :
821 3 : eErr = papoGeoms[iGeom]->transform( poCT );
822 3 : if( eErr != OGRERR_NONE )
823 : {
824 0 : if( iGeom != 0 )
825 : {
826 : CPLDebug("OGR",
827 : "OGRGeometryCollection::transform() failed for a geometry other\n"
828 : "than the first, meaning some geometries are transformed\n"
829 0 : "and some are not!\n" );
830 :
831 0 : return OGRERR_FAILURE;
832 : }
833 :
834 0 : return eErr;
835 : }
836 : }
837 :
838 3 : assignSpatialReference( poCT->GetTargetCS() );
839 :
840 3 : return OGRERR_NONE;
841 : #endif
842 : }
843 :
844 : /************************************************************************/
845 : /* closeRings() */
846 : /************************************************************************/
847 :
848 8 : void OGRGeometryCollection::closeRings()
849 :
850 : {
851 32 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
852 : {
853 24 : if( wkbFlatten(papoGeoms[iGeom]->getGeometryType()) == wkbPolygon )
854 7 : ((OGRPolygon *) papoGeoms[iGeom])->closeRings();
855 : }
856 8 : }
857 :
858 : /************************************************************************/
859 : /* setCoordinateDimension() */
860 : /************************************************************************/
861 :
862 49 : void OGRGeometryCollection::setCoordinateDimension( int nNewDimension )
863 :
864 : {
865 126 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
866 : {
867 77 : papoGeoms[iGeom]->setCoordinateDimension( nNewDimension );
868 : }
869 :
870 49 : OGRGeometry::setCoordinateDimension( nNewDimension );
871 49 : }
872 :
873 :
874 : /************************************************************************/
875 : /* get_Area() */
876 : /************************************************************************/
877 :
878 : /**
879 : * \brief Compute area of geometry collection.
880 : *
881 : * The area is computed as the sum of the areas of all members
882 : * in this collection.
883 : *
884 : * @note No warning will be issued if a member of the collection does not
885 : * support the get_Area method.
886 : *
887 : * @return computed area.
888 : */
889 :
890 0 : double OGRGeometryCollection::get_Area() const
891 : {
892 0 : double dfArea = 0.0;
893 0 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
894 : {
895 0 : OGRGeometry* geom = papoGeoms[iGeom];
896 0 : switch( wkbFlatten(geom->getGeometryType()) )
897 : {
898 : case wkbPolygon:
899 0 : dfArea += ((OGRPolygon *) geom)->get_Area();
900 0 : break;
901 :
902 : case wkbMultiPolygon:
903 0 : dfArea += ((OGRMultiPolygon *) geom)->get_Area();
904 0 : break;
905 :
906 : case wkbLinearRing:
907 : case wkbLineString:
908 : /* This test below is required to filter out wkbLineString geometries
909 : * not being of type of wkbLinearRing.
910 : */
911 0 : if( EQUAL( ((OGRGeometry*) geom)->getGeometryName(), "LINEARRING" ) )
912 : {
913 0 : dfArea += ((OGRLinearRing *) geom)->get_Area();
914 : }
915 0 : break;
916 :
917 : case wkbGeometryCollection:
918 0 : dfArea +=((OGRGeometryCollection *) geom)->get_Area();
919 : break;
920 :
921 : default:
922 : break;
923 : }
924 : }
925 :
926 0 : return dfArea;
927 : }
928 :
929 : /************************************************************************/
930 : /* IsEmpty() */
931 : /************************************************************************/
932 :
933 150 : OGRBoolean OGRGeometryCollection::IsEmpty( ) const
934 : {
935 154 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
936 79 : if (papoGeoms[iGeom]->IsEmpty() == FALSE)
937 75 : return FALSE;
938 75 : return TRUE;
939 : }
940 :
941 : /************************************************************************/
942 : /* OGRGeometryCollection::segmentize() */
943 : /************************************************************************/
944 :
945 0 : void OGRGeometryCollection::segmentize( double dfMaxLength )
946 : {
947 0 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
948 0 : papoGeoms[iGeom]->segmentize(dfMaxLength);
949 0 : }
|