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