1 : /******************************************************************************
2 : * $Id: ogrgeometrycollection.cpp 23589 2011-12-17 14:21:01Z 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 23589 2011-12-17 14:21:01Z rouault $");
34 :
35 : /************************************************************************/
36 : /* OGRGeometryCollection() */
37 : /************************************************************************/
38 :
39 : /**
40 : * \brief Create an empty geometry collection.
41 : */
42 :
43 2170 : OGRGeometryCollection::OGRGeometryCollection()
44 :
45 : {
46 2170 : nGeomCount = 0;
47 2170 : papoGeoms = NULL;
48 2170 : }
49 :
50 : /************************************************************************/
51 : /* ~OGRGeometryCollection() */
52 : /************************************************************************/
53 :
54 2106 : OGRGeometryCollection::~OGRGeometryCollection()
55 :
56 : {
57 2106 : empty();
58 2106 : }
59 :
60 : /************************************************************************/
61 : /* empty() */
62 : /************************************************************************/
63 :
64 3092 : void OGRGeometryCollection::empty()
65 :
66 : {
67 3092 : if( papoGeoms != NULL )
68 : {
69 5241 : for( int i = 0; i < nGeomCount; i++ )
70 : {
71 3510 : delete papoGeoms[i];
72 : }
73 1731 : OGRFree( papoGeoms );
74 : }
75 :
76 3092 : nGeomCount = 0;
77 3092 : papoGeoms = NULL;
78 3092 : nCoordDimension = 2;
79 3092 : }
80 :
81 :
82 : /************************************************************************/
83 : /* clone() */
84 : /************************************************************************/
85 :
86 115 : OGRGeometry *OGRGeometryCollection::clone() const
87 :
88 : {
89 : OGRGeometryCollection *poNewGC;
90 :
91 115 : poNewGC = new OGRGeometryCollection;
92 115 : poNewGC->assignSpatialReference( getSpatialReference() );
93 :
94 304 : for( int i = 0; i < nGeomCount; i++ )
95 : {
96 189 : poNewGC->addGeometry( papoGeoms[i] );
97 : }
98 :
99 115 : return poNewGC;
100 : }
101 :
102 : /************************************************************************/
103 : /* getGeometryType() */
104 : /************************************************************************/
105 :
106 645 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
107 :
108 : {
109 645 : if( getCoordinateDimension() == 3 )
110 209 : return wkbGeometryCollection25D;
111 : else
112 436 : 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 302 : const char * OGRGeometryCollection::getGeometryName() const
143 :
144 : {
145 302 : 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 3489 : int OGRGeometryCollection::getNumGeometries() const
162 :
163 : {
164 3489 : 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 1406 : OGRGeometry * OGRGeometryCollection::getGeometryRef( int i )
187 :
188 : {
189 1406 : if( i < 0 || i >= nGeomCount )
190 0 : return NULL;
191 : else
192 1406 : return papoGeoms[i];
193 : }
194 :
195 1023 : const OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) const
196 :
197 : {
198 1023 : if( i < 0 || i >= nGeomCount )
199 0 : return NULL;
200 : else
201 1023 : 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 1088 : OGRErr OGRGeometryCollection::addGeometry( const OGRGeometry * poNewGeom )
230 :
231 : {
232 1088 : OGRGeometry *poClone = poNewGeom->clone();
233 : OGRErr eErr;
234 :
235 1088 : eErr = addGeometryDirectly( poClone );
236 1088 : if( eErr != OGRERR_NONE )
237 0 : delete poClone;
238 :
239 1088 : 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 2858 : OGRErr OGRGeometryCollection::addGeometryDirectly( OGRGeometry * poNewGeom )
269 :
270 : {
271 : papoGeoms = (OGRGeometry **) OGRRealloc( papoGeoms,
272 2858 : sizeof(void*) * (nGeomCount+1) );
273 :
274 2858 : papoGeoms[nGeomCount] = poNewGeom;
275 :
276 2858 : nGeomCount++;
277 :
278 2858 : if( poNewGeom->getCoordinateDimension() == 3 )
279 538 : nCoordDimension = 3;
280 :
281 2858 : 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 15 : OGRErr OGRGeometryCollection::removeGeometry( int iGeom, int bDelete )
310 :
311 : {
312 15 : if( iGeom < -1 || iGeom >= nGeomCount )
313 0 : return OGRERR_FAILURE;
314 :
315 : // Special case.
316 15 : if( iGeom == -1 )
317 : {
318 0 : while( nGeomCount > 0 )
319 0 : removeGeometry( nGeomCount-1, bDelete );
320 0 : return OGRERR_NONE;
321 : }
322 :
323 15 : if( bDelete )
324 0 : delete papoGeoms[iGeom];
325 :
326 : memmove( papoGeoms + iGeom, papoGeoms + iGeom + 1,
327 15 : sizeof(void*) * (nGeomCount-iGeom-1) );
328 :
329 15 : nGeomCount--;
330 :
331 15 : 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 603 : int OGRGeometryCollection::WkbSize() const
342 :
343 : {
344 603 : int nSize = 9;
345 :
346 1248 : for( int i = 0; i < nGeomCount; i++ )
347 : {
348 645 : nSize += papoGeoms[i]->WkbSize();
349 : }
350 :
351 603 : return nSize;
352 : }
353 :
354 : /************************************************************************/
355 : /* importFromWkbInternal() */
356 : /************************************************************************/
357 :
358 330 : OGRErr OGRGeometryCollection::importFromWkbInternal( unsigned char * pabyData,
359 : int nSize, int nRecLevel )
360 :
361 : {
362 : OGRwkbByteOrder eByteOrder;
363 : int nDataOffset;
364 :
365 : /* Arbitrary value, but certainly large enough for reasonable usages ! */
366 330 : if( nRecLevel == 32 )
367 : {
368 : CPLError( CE_Failure, CPLE_AppDefined,
369 : "Too many recursiong level (%d) while parsing WKB geometry.",
370 1 : nRecLevel );
371 1 : return OGRERR_CORRUPT_DATA;
372 : }
373 :
374 329 : if( nSize < 9 && nSize != -1 )
375 0 : return OGRERR_NOT_ENOUGH_DATA;
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* Get the byte order byte. */
379 : /* -------------------------------------------------------------------- */
380 329 : eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData);
381 329 : if (!( eByteOrder == wkbXDR || eByteOrder == wkbNDR ))
382 0 : return OGRERR_CORRUPT_DATA;
383 :
384 : /* -------------------------------------------------------------------- */
385 : /* Get the geometry feature type. For now we assume that */
386 : /* geometry type is between 0 and 255 so we only have to fetch */
387 : /* one byte. */
388 : /* -------------------------------------------------------------------- */
389 : OGRwkbGeometryType eGeometryType;
390 :
391 329 : if( eByteOrder == wkbNDR )
392 : {
393 236 : eGeometryType = (OGRwkbGeometryType) pabyData[1];
394 : }
395 : else
396 : {
397 93 : eGeometryType = (OGRwkbGeometryType) pabyData[4];
398 : }
399 :
400 329 : if ( eGeometryType != wkbFlatten(getGeometryType()) )
401 0 : return OGRERR_CORRUPT_DATA;
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Clear existing Geoms. */
405 : /* -------------------------------------------------------------------- */
406 329 : empty();
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Get the geometry count. */
410 : /* -------------------------------------------------------------------- */
411 329 : memcpy( &nGeomCount, pabyData + 5, 4 );
412 :
413 329 : if( OGR_SWAP( eByteOrder ) )
414 93 : nGeomCount = CPL_SWAP32(nGeomCount);
415 :
416 329 : if (nGeomCount < 0 || nGeomCount > INT_MAX / 9)
417 : {
418 0 : nGeomCount = 0;
419 0 : return OGRERR_CORRUPT_DATA;
420 : }
421 :
422 : /* Each geometry has a minimum of 9 bytes */
423 329 : if (nSize != -1 && nSize - 9 < nGeomCount * 9)
424 : {
425 : CPLError( CE_Failure, CPLE_AppDefined,
426 0 : "Length of input WKB is too small" );
427 0 : nGeomCount = 0;
428 0 : return OGRERR_NOT_ENOUGH_DATA;
429 : }
430 :
431 329 : papoGeoms = (OGRGeometry **) VSIMalloc2(sizeof(void*), nGeomCount);
432 329 : if (nGeomCount != 0 && papoGeoms == NULL)
433 : {
434 0 : nGeomCount = 0;
435 0 : return OGRERR_NOT_ENOUGH_MEMORY;
436 : }
437 :
438 329 : nDataOffset = 9;
439 329 : if( nSize != -1 )
440 329 : nSize -= nDataOffset;
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Get the Geoms. */
444 : /* -------------------------------------------------------------------- */
445 996 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
446 : {
447 : OGRErr eErr;
448 700 : OGRGeometry* poSubGeom = NULL;
449 :
450 : /* Parses sub-geometry */
451 700 : unsigned char* pabySubData = pabyData + nDataOffset;
452 700 : if( nSize < 9 && nSize != -1 )
453 0 : return OGRERR_NOT_ENOUGH_DATA;
454 : OGRwkbByteOrder eSubGeomByteOrder =
455 700 : DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabySubData);
456 :
457 700 : if( eSubGeomByteOrder != wkbXDR && eSubGeomByteOrder != wkbNDR )
458 0 : return OGRERR_CORRUPT_DATA;
459 :
460 : OGRwkbGeometryType eSubGeomType;
461 700 : if( eSubGeomByteOrder == wkbNDR )
462 597 : eSubGeomType = (OGRwkbGeometryType) pabySubData[1];
463 : else
464 103 : eSubGeomType = (OGRwkbGeometryType) pabySubData[4];
465 :
466 1333 : if( eSubGeomType == wkbPoint ||
467 : eSubGeomType == wkbLineString ||
468 : eSubGeomType == wkbPolygon)
469 : {
470 : eErr = OGRGeometryFactory::
471 : createFromWkb( pabySubData, NULL,
472 633 : &poSubGeom, nSize );
473 : }
474 134 : else if (eSubGeomType == wkbGeometryCollection ||
475 : eSubGeomType == wkbMultiPolygon ||
476 : eSubGeomType == wkbMultiPoint ||
477 : eSubGeomType == wkbMultiLineString)
478 : {
479 67 : poSubGeom = OGRGeometryFactory::createGeometry( eSubGeomType );
480 : eErr = ((OGRGeometryCollection*)poSubGeom)->
481 67 : importFromWkbInternal( pabySubData, nSize, nRecLevel + 1 );
482 : }
483 : else
484 0 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
485 :
486 700 : if( eErr != OGRERR_NONE )
487 : {
488 32 : nGeomCount = iGeom;
489 32 : return eErr;
490 : }
491 :
492 668 : if( (eGeometryType == wkbMultiPoint && eSubGeomType != wkbPoint) ||
493 : (eGeometryType == wkbMultiLineString && eSubGeomType != wkbLineString) ||
494 : (eGeometryType == wkbMultiPolygon && eSubGeomType != wkbPolygon) )
495 : {
496 1 : nGeomCount = iGeom;
497 : CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)",
498 1 : eSubGeomType, eGeometryType);
499 1 : delete poSubGeom;
500 1 : return OGRERR_CORRUPT_DATA;
501 : }
502 :
503 667 : papoGeoms[iGeom] = poSubGeom;
504 :
505 667 : if (papoGeoms[iGeom]->getCoordinateDimension() == 3)
506 271 : nCoordDimension = 3;
507 :
508 667 : int nSubGeomWkbSize = papoGeoms[iGeom]->WkbSize();
509 667 : if( nSize != -1 )
510 667 : nSize -= nSubGeomWkbSize;
511 :
512 667 : nDataOffset += nSubGeomWkbSize;
513 : }
514 :
515 296 : return OGRERR_NONE;
516 : }
517 :
518 : /************************************************************************/
519 : /* importFromWkb() */
520 : /* */
521 : /* Initialize from serialized stream in well known binary */
522 : /* format. */
523 : /************************************************************************/
524 :
525 263 : OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData,
526 : int nSize )
527 :
528 : {
529 263 : return importFromWkbInternal(pabyData, nSize, 0);
530 : }
531 :
532 : /************************************************************************/
533 : /* exportToWkb() */
534 : /* */
535 : /* Build a well known binary representation of this object. */
536 : /************************************************************************/
537 :
538 99 : OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder,
539 : unsigned char * pabyData ) const
540 :
541 : {
542 : int nOffset;
543 :
544 : /* -------------------------------------------------------------------- */
545 : /* Set the byte order. */
546 : /* -------------------------------------------------------------------- */
547 99 : pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder);
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Set the geometry feature type, ensuring that 3D flag is */
551 : /* preserved. */
552 : /* -------------------------------------------------------------------- */
553 99 : GUInt32 nGType = getGeometryType();
554 :
555 99 : if( eByteOrder == wkbNDR )
556 72 : nGType = CPL_LSBWORD32( nGType );
557 : else
558 27 : nGType = CPL_MSBWORD32( nGType );
559 :
560 99 : memcpy( pabyData + 1, &nGType, 4 );
561 :
562 : /* -------------------------------------------------------------------- */
563 : /* Copy in the raw data. */
564 : /* -------------------------------------------------------------------- */
565 99 : if( OGR_SWAP( eByteOrder ) )
566 : {
567 : int nCount;
568 :
569 27 : nCount = CPL_SWAP32( nGeomCount );
570 27 : memcpy( pabyData+5, &nCount, 4 );
571 : }
572 : else
573 : {
574 72 : memcpy( pabyData+5, &nGeomCount, 4 );
575 : }
576 :
577 99 : nOffset = 9;
578 :
579 : /* ==================================================================== */
580 : /* Serialize each of the Geoms. */
581 : /* ==================================================================== */
582 271 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
583 : {
584 172 : papoGeoms[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset );
585 :
586 172 : nOffset += papoGeoms[iGeom]->WkbSize();
587 : }
588 :
589 99 : return OGRERR_NONE;
590 : }
591 :
592 : /************************************************************************/
593 : /* importFromWktInternal() */
594 : /************************************************************************/
595 :
596 189 : OGRErr OGRGeometryCollection::importFromWktInternal( char ** ppszInput, int nRecLevel )
597 :
598 : {
599 :
600 : char szToken[OGR_WKT_TOKEN_MAX];
601 189 : const char *pszInput = *ppszInput;
602 :
603 :
604 : /* Arbitrary value, but certainly large enough for reasonable usages ! */
605 189 : if( nRecLevel == 32 )
606 : {
607 : CPLError( CE_Failure, CPLE_AppDefined,
608 : "Too many recursiong level (%d) while parsing WKT geometry.",
609 1 : nRecLevel );
610 1 : return OGRERR_CORRUPT_DATA;
611 : }
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Clear existing Geoms. */
615 : /* -------------------------------------------------------------------- */
616 188 : empty();
617 :
618 : /* -------------------------------------------------------------------- */
619 : /* Read and verify the type keyword, and ensure it matches the */
620 : /* actual type of this container. */
621 : /* -------------------------------------------------------------------- */
622 188 : pszInput = OGRWktReadToken( pszInput, szToken );
623 :
624 188 : if( !EQUAL(szToken,getGeometryName()) )
625 0 : return OGRERR_CORRUPT_DATA;
626 :
627 : /* -------------------------------------------------------------------- */
628 : /* Check for EMPTY ... */
629 : /* -------------------------------------------------------------------- */
630 : const char *pszPreScan;
631 188 : int bHasZ = FALSE, bHasM = FALSE;
632 :
633 188 : pszPreScan = OGRWktReadToken( pszInput, szToken );
634 188 : if( EQUAL(szToken,"EMPTY") )
635 : {
636 13 : *ppszInput = (char *) pszPreScan;
637 13 : empty();
638 13 : return OGRERR_NONE;
639 : }
640 :
641 : /* -------------------------------------------------------------------- */
642 : /* Check for Z, M or ZM. Will ignore the Measure */
643 : /* -------------------------------------------------------------------- */
644 175 : else if( EQUAL(szToken,"Z") )
645 : {
646 8 : bHasZ = TRUE;
647 : }
648 167 : else if( EQUAL(szToken,"M") )
649 : {
650 2 : bHasM = TRUE;
651 : }
652 165 : else if( EQUAL(szToken,"ZM") )
653 : {
654 2 : bHasZ = TRUE;
655 2 : bHasM = TRUE;
656 : }
657 :
658 175 : if (bHasZ || bHasM)
659 : {
660 12 : pszInput = pszPreScan;
661 12 : pszPreScan = OGRWktReadToken( pszInput, szToken );
662 12 : if( EQUAL(szToken,"EMPTY") )
663 : {
664 4 : *ppszInput = (char *) pszPreScan;
665 4 : empty();
666 : /* FIXME?: In theory we should store the dimension and M presence */
667 : /* if we want to allow round-trip with ExportToWKT v1.2 */
668 4 : return OGRERR_NONE;
669 : }
670 : }
671 :
672 171 : if( !EQUAL(szToken,"(") )
673 3 : return OGRERR_CORRUPT_DATA;
674 :
675 168 : if ( !bHasZ && !bHasM )
676 : {
677 : /* Test for old-style GEOMETRYCOLLECTION(EMPTY) */
678 161 : pszPreScan = OGRWktReadToken( pszPreScan, szToken );
679 161 : if( EQUAL(szToken,"EMPTY") )
680 : {
681 4 : pszInput = OGRWktReadToken( pszPreScan, szToken );
682 :
683 4 : if( !EQUAL(szToken,")") )
684 2 : return OGRERR_CORRUPT_DATA;
685 : else
686 : {
687 2 : *ppszInput = (char *) pszInput;
688 2 : empty();
689 2 : return OGRERR_NONE;
690 : }
691 : }
692 : }
693 :
694 : /* Skip first '(' */
695 164 : pszInput = OGRWktReadToken( pszInput, szToken );
696 :
697 : /* ==================================================================== */
698 : /* Read each subgeometry in turn. */
699 : /* ==================================================================== */
700 234 : do
701 : {
702 277 : OGRGeometry *poGeom = NULL;
703 : OGRErr eErr;
704 :
705 : /* -------------------------------------------------------------------- */
706 : /* Get the first token, which should be the geometry type. */
707 : /* -------------------------------------------------------------------- */
708 277 : if( OGRWktReadToken( pszInput, szToken ) == NULL )
709 0 : return OGRERR_CORRUPT_DATA;
710 :
711 : /* -------------------------------------------------------------------- */
712 : /* Do the import. */
713 : /* -------------------------------------------------------------------- */
714 277 : if (EQUAL(szToken,"GEOMETRYCOLLECTION"))
715 : {
716 70 : poGeom = new OGRGeometryCollection();
717 : eErr = ((OGRGeometryCollection*)poGeom)->
718 70 : importFromWktInternal( (char **) &pszInput, nRecLevel + 1 );
719 : }
720 : else
721 : eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput,
722 207 : NULL, &poGeom );
723 :
724 277 : if( eErr != OGRERR_NONE )
725 43 : return eErr;
726 :
727 234 : addGeometryDirectly( poGeom );
728 :
729 : /* -------------------------------------------------------------------- */
730 : /* Read the delimeter following the ring. */
731 : /* -------------------------------------------------------------------- */
732 :
733 234 : pszInput = OGRWktReadToken( pszInput, szToken );
734 :
735 234 : } while( szToken[0] == ',' );
736 :
737 : /* -------------------------------------------------------------------- */
738 : /* freak if we don't get a closing bracket. */
739 : /* -------------------------------------------------------------------- */
740 121 : if( szToken[0] != ')' )
741 2 : return OGRERR_CORRUPT_DATA;
742 :
743 119 : *ppszInput = (char *) pszInput;
744 :
745 119 : return OGRERR_NONE;
746 : }
747 :
748 : /************************************************************************/
749 : /* importFromWkt() */
750 : /************************************************************************/
751 :
752 119 : OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput )
753 :
754 : {
755 119 : return importFromWktInternal(ppszInput, 0);
756 : }
757 :
758 : /************************************************************************/
759 : /* exportToWkt() */
760 : /* */
761 : /* Translate this structure into it's well known text format */
762 : /* equivelent. This could be made alot more CPU efficient! */
763 : /************************************************************************/
764 :
765 96 : OGRErr OGRGeometryCollection::exportToWkt( char ** ppszDstText ) const
766 :
767 : {
768 : char **papszGeoms;
769 96 : int iGeom, nCumulativeLength = 0;
770 : OGRErr eErr;
771 :
772 96 : if( getNumGeometries() == 0 )
773 : {
774 17 : *ppszDstText = CPLStrdup("GEOMETRYCOLLECTION EMPTY");
775 17 : return OGRERR_NONE;
776 : }
777 :
778 : /* -------------------------------------------------------------------- */
779 : /* Build a list of strings containing the stuff for each Geom. */
780 : /* -------------------------------------------------------------------- */
781 79 : papszGeoms = (char **) CPLCalloc(sizeof(char *),nGeomCount);
782 :
783 229 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
784 : {
785 150 : eErr = papoGeoms[iGeom]->exportToWkt( &(papszGeoms[iGeom]) );
786 150 : if( eErr != OGRERR_NONE )
787 0 : goto error;
788 :
789 150 : nCumulativeLength += strlen(papszGeoms[iGeom]);
790 : }
791 :
792 : /* -------------------------------------------------------------------- */
793 : /* Allocate the right amount of space for the aggregated string */
794 : /* -------------------------------------------------------------------- */
795 79 : *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nGeomCount + 23);
796 :
797 79 : if( *ppszDstText == NULL )
798 : {
799 0 : eErr = OGRERR_NOT_ENOUGH_MEMORY;
800 0 : goto error;
801 : }
802 :
803 : /* -------------------------------------------------------------------- */
804 : /* Build up the string, freeing temporary strings as we go. */
805 : /* -------------------------------------------------------------------- */
806 79 : strcpy( *ppszDstText, getGeometryName() );
807 79 : strcat( *ppszDstText, " (" );
808 79 : nCumulativeLength = strlen(*ppszDstText);
809 :
810 229 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
811 : {
812 150 : if( iGeom > 0 )
813 71 : (*ppszDstText)[nCumulativeLength++] = ',';
814 :
815 150 : int nGeomLength = strlen(papszGeoms[iGeom]);
816 150 : memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom], nGeomLength );
817 150 : nCumulativeLength += nGeomLength;
818 150 : VSIFree( papszGeoms[iGeom] );
819 : }
820 :
821 79 : (*ppszDstText)[nCumulativeLength++] = ')';
822 79 : (*ppszDstText)[nCumulativeLength] = '\0';
823 :
824 79 : CPLFree( papszGeoms );
825 :
826 79 : return OGRERR_NONE;
827 :
828 : error:
829 0 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
830 0 : CPLFree( papszGeoms[iGeom] );
831 0 : CPLFree( papszGeoms );
832 0 : return eErr;
833 : }
834 :
835 : /************************************************************************/
836 : /* getEnvelope() */
837 : /************************************************************************/
838 :
839 242 : void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const
840 :
841 : {
842 242 : OGREnvelope oGeomEnv;
843 242 : int bExtentSet = FALSE;
844 :
845 689 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
846 : {
847 447 : if (!papoGeoms[iGeom]->IsEmpty())
848 : {
849 447 : if (!bExtentSet)
850 : {
851 230 : papoGeoms[iGeom]->getEnvelope( psEnvelope );
852 230 : bExtentSet = TRUE;
853 : }
854 : else
855 : {
856 217 : papoGeoms[iGeom]->getEnvelope( &oGeomEnv );
857 :
858 217 : if( psEnvelope->MinX > oGeomEnv.MinX )
859 119 : psEnvelope->MinX = oGeomEnv.MinX;
860 217 : if( psEnvelope->MinY > oGeomEnv.MinY )
861 101 : psEnvelope->MinY = oGeomEnv.MinY;
862 217 : if( psEnvelope->MaxX < oGeomEnv.MaxX )
863 72 : psEnvelope->MaxX = oGeomEnv.MaxX;
864 217 : if( psEnvelope->MaxY < oGeomEnv.MaxY )
865 62 : psEnvelope->MaxY = oGeomEnv.MaxY;
866 : }
867 : }
868 : }
869 :
870 242 : if (!bExtentSet)
871 : {
872 12 : psEnvelope->MinX = psEnvelope->MinY = 0;
873 12 : psEnvelope->MaxX = psEnvelope->MaxY = 0;
874 : }
875 242 : }
876 :
877 : /************************************************************************/
878 : /* getEnvelope() */
879 : /************************************************************************/
880 :
881 18 : void OGRGeometryCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const
882 :
883 : {
884 18 : OGREnvelope3D oGeomEnv;
885 18 : int bExtentSet = FALSE;
886 :
887 38 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
888 : {
889 20 : if (!papoGeoms[iGeom]->IsEmpty())
890 : {
891 20 : if (!bExtentSet)
892 : {
893 17 : papoGeoms[iGeom]->getEnvelope( psEnvelope );
894 17 : bExtentSet = TRUE;
895 : }
896 : else
897 : {
898 3 : papoGeoms[iGeom]->getEnvelope( &oGeomEnv );
899 :
900 3 : if( psEnvelope->MinX > oGeomEnv.MinX )
901 1 : psEnvelope->MinX = oGeomEnv.MinX;
902 3 : if( psEnvelope->MinY > oGeomEnv.MinY )
903 1 : psEnvelope->MinY = oGeomEnv.MinY;
904 3 : if( psEnvelope->MaxX < oGeomEnv.MaxX )
905 3 : psEnvelope->MaxX = oGeomEnv.MaxX;
906 3 : if( psEnvelope->MaxY < oGeomEnv.MaxY )
907 3 : psEnvelope->MaxY = oGeomEnv.MaxY;
908 :
909 3 : if( psEnvelope->MinZ > oGeomEnv.MinZ )
910 2 : psEnvelope->MinZ = oGeomEnv.MinZ;
911 3 : if( psEnvelope->MaxZ < oGeomEnv.MaxZ )
912 0 : psEnvelope->MaxZ = oGeomEnv.MaxZ;
913 : }
914 : }
915 : }
916 :
917 18 : if (!bExtentSet)
918 : {
919 1 : psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0;
920 1 : psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0;
921 : }
922 18 : }
923 :
924 : /************************************************************************/
925 : /* Equals() */
926 : /************************************************************************/
927 :
928 25 : OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const
929 :
930 : {
931 25 : OGRGeometryCollection *poOGC = (OGRGeometryCollection *) poOther;
932 :
933 25 : if( poOGC == this )
934 0 : return TRUE;
935 :
936 25 : if( poOther->getGeometryType() != getGeometryType() )
937 0 : return FALSE;
938 :
939 25 : if( getNumGeometries() != poOGC->getNumGeometries() )
940 0 : return FALSE;
941 :
942 : // we should eventually test the SRS.
943 :
944 55 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
945 : {
946 30 : if( !getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)) )
947 0 : return FALSE;
948 : }
949 :
950 25 : return TRUE;
951 : }
952 :
953 : /************************************************************************/
954 : /* transform() */
955 : /************************************************************************/
956 :
957 9 : OGRErr OGRGeometryCollection::transform( OGRCoordinateTransformation *poCT )
958 :
959 : {
960 : #ifdef DISABLE_OGRGEOM_TRANSFORM
961 : return OGRERR_FAILURE;
962 : #else
963 36 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
964 : {
965 : OGRErr eErr;
966 :
967 27 : eErr = papoGeoms[iGeom]->transform( poCT );
968 27 : if( eErr != OGRERR_NONE )
969 : {
970 0 : if( iGeom != 0 )
971 : {
972 : CPLDebug("OGR",
973 : "OGRGeometryCollection::transform() failed for a geometry other\n"
974 : "than the first, meaning some geometries are transformed\n"
975 0 : "and some are not!\n" );
976 :
977 0 : return OGRERR_FAILURE;
978 : }
979 :
980 0 : return eErr;
981 : }
982 : }
983 :
984 9 : assignSpatialReference( poCT->GetTargetCS() );
985 :
986 9 : return OGRERR_NONE;
987 : #endif
988 : }
989 :
990 : /************************************************************************/
991 : /* closeRings() */
992 : /************************************************************************/
993 :
994 20 : void OGRGeometryCollection::closeRings()
995 :
996 : {
997 56 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
998 : {
999 36 : if( wkbFlatten(papoGeoms[iGeom]->getGeometryType()) == wkbPolygon )
1000 19 : ((OGRPolygon *) papoGeoms[iGeom])->closeRings();
1001 : }
1002 20 : }
1003 :
1004 : /************************************************************************/
1005 : /* setCoordinateDimension() */
1006 : /************************************************************************/
1007 :
1008 42 : void OGRGeometryCollection::setCoordinateDimension( int nNewDimension )
1009 :
1010 : {
1011 97 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1012 : {
1013 55 : papoGeoms[iGeom]->setCoordinateDimension( nNewDimension );
1014 : }
1015 :
1016 42 : OGRGeometry::setCoordinateDimension( nNewDimension );
1017 42 : }
1018 :
1019 :
1020 : /************************************************************************/
1021 : /* get_Length() */
1022 : /************************************************************************/
1023 :
1024 : /**
1025 : * \brief Compute the length of a multicurve.
1026 : *
1027 : * The length is computed as the sum of the length of all members
1028 : * in this collection.
1029 : *
1030 : * @note No warning will be issued if a member of the collection does not
1031 : * support the get_Length method.
1032 : *
1033 : * @return computed area.
1034 : */
1035 :
1036 3 : double OGRGeometryCollection::get_Length() const
1037 : {
1038 3 : double dfLength = 0.0;
1039 11 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1040 : {
1041 8 : OGRGeometry* geom = papoGeoms[iGeom];
1042 8 : switch( wkbFlatten(geom->getGeometryType()) )
1043 : {
1044 : case wkbLinearRing:
1045 : case wkbLineString:
1046 4 : dfLength += ((OGRCurve *) geom)->get_Length();
1047 4 : break;
1048 :
1049 : case wkbGeometryCollection:
1050 1 : dfLength +=((OGRGeometryCollection *) geom)->get_Length();
1051 : break;
1052 :
1053 : default:
1054 : break;
1055 : }
1056 : }
1057 :
1058 3 : return dfLength;
1059 : }
1060 :
1061 : /************************************************************************/
1062 : /* get_Area() */
1063 : /************************************************************************/
1064 :
1065 : /**
1066 : * \brief Compute area of geometry collection.
1067 : *
1068 : * The area is computed as the sum of the areas of all members
1069 : * in this collection.
1070 : *
1071 : * @note No warning will be issued if a member of the collection does not
1072 : * support the get_Area method.
1073 : *
1074 : * @return computed area.
1075 : */
1076 :
1077 2 : double OGRGeometryCollection::get_Area() const
1078 : {
1079 2 : double dfArea = 0.0;
1080 7 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1081 : {
1082 5 : OGRGeometry* geom = papoGeoms[iGeom];
1083 5 : switch( wkbFlatten(geom->getGeometryType()) )
1084 : {
1085 : case wkbPolygon:
1086 1 : dfArea += ((OGRPolygon *) geom)->get_Area();
1087 1 : break;
1088 :
1089 : case wkbMultiPolygon:
1090 1 : dfArea += ((OGRMultiPolygon *) geom)->get_Area();
1091 1 : break;
1092 :
1093 : case wkbLinearRing:
1094 : case wkbLineString:
1095 : /* This test below is required to filter out wkbLineString geometries
1096 : * not being of type of wkbLinearRing.
1097 : */
1098 1 : if( EQUAL( ((OGRGeometry*) geom)->getGeometryName(), "LINEARRING" ) )
1099 : {
1100 0 : dfArea += ((OGRLinearRing *) geom)->get_Area();
1101 : }
1102 1 : break;
1103 :
1104 : case wkbGeometryCollection:
1105 1 : dfArea +=((OGRGeometryCollection *) geom)->get_Area();
1106 : break;
1107 :
1108 : default:
1109 : break;
1110 : }
1111 : }
1112 :
1113 2 : return dfArea;
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /* IsEmpty() */
1118 : /************************************************************************/
1119 :
1120 301 : OGRBoolean OGRGeometryCollection::IsEmpty( ) const
1121 : {
1122 305 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1123 224 : if (papoGeoms[iGeom]->IsEmpty() == FALSE)
1124 220 : return FALSE;
1125 81 : return TRUE;
1126 : }
1127 :
1128 : /************************************************************************/
1129 : /* OGRGeometryCollection::segmentize() */
1130 : /************************************************************************/
1131 :
1132 0 : void OGRGeometryCollection::segmentize( double dfMaxLength )
1133 : {
1134 0 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1135 0 : papoGeoms[iGeom]->segmentize(dfMaxLength);
1136 0 : }
1137 :
1138 : /************************************************************************/
1139 : /* swapXY() */
1140 : /************************************************************************/
1141 :
1142 15 : void OGRGeometryCollection::swapXY()
1143 : {
1144 30 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1145 15 : papoGeoms[iGeom]->swapXY();
1146 15 : }
|