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 4166 : OGRGeometryCollection::OGRGeometryCollection()
44 :
45 : {
46 4166 : nGeomCount = 0;
47 4166 : papoGeoms = NULL;
48 4166 : }
49 :
50 : /************************************************************************/
51 : /* ~OGRGeometryCollection() */
52 : /************************************************************************/
53 :
54 4134 : OGRGeometryCollection::~OGRGeometryCollection()
55 :
56 : {
57 4134 : empty();
58 4134 : }
59 :
60 : /************************************************************************/
61 : /* empty() */
62 : /************************************************************************/
63 :
64 5316 : void OGRGeometryCollection::empty()
65 :
66 : {
67 5316 : if( papoGeoms != NULL )
68 : {
69 9889 : for( int i = 0; i < nGeomCount; i++ )
70 : {
71 6227 : delete papoGeoms[i];
72 : }
73 3662 : OGRFree( papoGeoms );
74 : }
75 :
76 5316 : nGeomCount = 0;
77 5316 : papoGeoms = NULL;
78 5316 : nCoordDimension = 2;
79 5316 : }
80 :
81 :
82 : /************************************************************************/
83 : /* clone() */
84 : /************************************************************************/
85 :
86 143 : OGRGeometry *OGRGeometryCollection::clone() const
87 :
88 : {
89 : OGRGeometryCollection *poNewGC;
90 :
91 143 : poNewGC = new OGRGeometryCollection;
92 143 : poNewGC->assignSpatialReference( getSpatialReference() );
93 :
94 374 : for( int i = 0; i < nGeomCount; i++ )
95 : {
96 231 : poNewGC->addGeometry( papoGeoms[i] );
97 : }
98 :
99 143 : return poNewGC;
100 : }
101 :
102 : /************************************************************************/
103 : /* getGeometryType() */
104 : /************************************************************************/
105 :
106 863 : OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const
107 :
108 : {
109 863 : if( getCoordinateDimension() == 3 )
110 226 : return wkbGeometryCollection25D;
111 : else
112 637 : 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 330 : const char * OGRGeometryCollection::getGeometryName() const
143 :
144 : {
145 330 : 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 4803 : int OGRGeometryCollection::getNumGeometries() const
162 :
163 : {
164 4803 : 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 2483 : OGRGeometry * OGRGeometryCollection::getGeometryRef( int i )
187 :
188 : {
189 2483 : if( i < 0 || i >= nGeomCount )
190 0 : return NULL;
191 : else
192 2483 : return papoGeoms[i];
193 : }
194 :
195 1285 : const OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) const
196 :
197 : {
198 1285 : if( i < 0 || i >= nGeomCount )
199 0 : return NULL;
200 : else
201 1285 : 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 1340 : OGRErr OGRGeometryCollection::addGeometry( const OGRGeometry * poNewGeom )
230 :
231 : {
232 1340 : OGRGeometry *poClone = poNewGeom->clone();
233 : OGRErr eErr;
234 :
235 1340 : eErr = addGeometryDirectly( poClone );
236 1340 : if( eErr != OGRERR_NONE )
237 0 : delete poClone;
238 :
239 1340 : 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 5363 : OGRErr OGRGeometryCollection::addGeometryDirectly( OGRGeometry * poNewGeom )
269 :
270 : {
271 : papoGeoms = (OGRGeometry **) OGRRealloc( papoGeoms,
272 5363 : sizeof(void*) * (nGeomCount+1) );
273 :
274 5363 : papoGeoms[nGeomCount] = poNewGeom;
275 :
276 5363 : nGeomCount++;
277 :
278 5363 : if( poNewGeom->getCoordinateDimension() == 3 )
279 894 : nCoordDimension = 3;
280 :
281 5363 : 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 31 : OGRErr OGRGeometryCollection::removeGeometry( int iGeom, int bDelete )
310 :
311 : {
312 31 : if( iGeom < -1 || iGeom >= nGeomCount )
313 0 : return OGRERR_FAILURE;
314 :
315 : // Special case.
316 31 : if( iGeom == -1 )
317 : {
318 0 : while( nGeomCount > 0 )
319 0 : removeGeometry( nGeomCount-1, bDelete );
320 0 : return OGRERR_NONE;
321 : }
322 :
323 31 : if( bDelete )
324 4 : delete papoGeoms[iGeom];
325 :
326 : memmove( papoGeoms + iGeom, papoGeoms + iGeom + 1,
327 31 : sizeof(void*) * (nGeomCount-iGeom-1) );
328 :
329 31 : nGeomCount--;
330 :
331 31 : 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 635 : int OGRGeometryCollection::WkbSize() const
342 :
343 : {
344 635 : int nSize = 9;
345 :
346 1343 : for( int i = 0; i < nGeomCount; i++ )
347 : {
348 708 : nSize += papoGeoms[i]->WkbSize();
349 : }
350 :
351 635 : return nSize;
352 : }
353 :
354 : /************************************************************************/
355 : /* importFromWkbInternal() */
356 : /************************************************************************/
357 :
358 443 : 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 443 : 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 442 : if( nSize < 9 && nSize != -1 )
375 0 : return OGRERR_NOT_ENOUGH_DATA;
376 :
377 : /* -------------------------------------------------------------------- */
378 : /* Get the byte order byte. */
379 : /* -------------------------------------------------------------------- */
380 442 : eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData);
381 442 : 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 442 : if( eByteOrder == wkbNDR )
392 : {
393 349 : eGeometryType = (OGRwkbGeometryType) pabyData[1];
394 : }
395 : else
396 : {
397 93 : eGeometryType = (OGRwkbGeometryType) pabyData[4];
398 : }
399 :
400 442 : if ( eGeometryType != wkbFlatten(getGeometryType()) )
401 0 : return OGRERR_CORRUPT_DATA;
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Clear existing Geoms. */
405 : /* -------------------------------------------------------------------- */
406 442 : empty();
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Get the geometry count. */
410 : /* -------------------------------------------------------------------- */
411 442 : memcpy( &nGeomCount, pabyData + 5, 4 );
412 :
413 442 : if( OGR_SWAP( eByteOrder ) )
414 93 : nGeomCount = CPL_SWAP32(nGeomCount);
415 :
416 442 : 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 442 : 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 442 : papoGeoms = (OGRGeometry **) VSIMalloc2(sizeof(void*), nGeomCount);
432 442 : if (nGeomCount != 0 && papoGeoms == NULL)
433 : {
434 0 : nGeomCount = 0;
435 0 : return OGRERR_NOT_ENOUGH_MEMORY;
436 : }
437 :
438 442 : nDataOffset = 9;
439 442 : if( nSize != -1 )
440 442 : nSize -= nDataOffset;
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Get the Geoms. */
444 : /* -------------------------------------------------------------------- */
445 1337 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
446 : {
447 : OGRErr eErr;
448 928 : OGRGeometry* poSubGeom = NULL;
449 :
450 : /* Parses sub-geometry */
451 928 : unsigned char* pabySubData = pabyData + nDataOffset;
452 928 : if( nSize < 9 && nSize != -1 )
453 0 : return OGRERR_NOT_ENOUGH_DATA;
454 : OGRwkbByteOrder eSubGeomByteOrder =
455 928 : DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabySubData);
456 :
457 928 : if( eSubGeomByteOrder != wkbXDR && eSubGeomByteOrder != wkbNDR )
458 0 : return OGRERR_CORRUPT_DATA;
459 :
460 : OGRwkbGeometryType eSubGeomType;
461 928 : if( eSubGeomByteOrder == wkbNDR )
462 825 : eSubGeomType = (OGRwkbGeometryType) pabySubData[1];
463 : else
464 103 : eSubGeomType = (OGRwkbGeometryType) pabySubData[4];
465 :
466 1789 : if( eSubGeomType == wkbPoint ||
467 : eSubGeomType == wkbLineString ||
468 : eSubGeomType == wkbPolygon)
469 : {
470 : eErr = OGRGeometryFactory::
471 : createFromWkb( pabySubData, NULL,
472 861 : &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 928 : if( eErr != OGRERR_NONE )
487 : {
488 32 : nGeomCount = iGeom;
489 32 : delete poSubGeom;
490 32 : return eErr;
491 : }
492 :
493 896 : if( (eGeometryType == wkbMultiPoint && eSubGeomType != wkbPoint) ||
494 : (eGeometryType == wkbMultiLineString && eSubGeomType != wkbLineString) ||
495 : (eGeometryType == wkbMultiPolygon && eSubGeomType != wkbPolygon) )
496 : {
497 1 : nGeomCount = iGeom;
498 : CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)",
499 1 : eSubGeomType, eGeometryType);
500 1 : delete poSubGeom;
501 1 : return OGRERR_CORRUPT_DATA;
502 : }
503 :
504 895 : papoGeoms[iGeom] = poSubGeom;
505 :
506 895 : if (papoGeoms[iGeom]->getCoordinateDimension() == 3)
507 321 : nCoordDimension = 3;
508 :
509 895 : int nSubGeomWkbSize = papoGeoms[iGeom]->WkbSize();
510 895 : if( nSize != -1 )
511 895 : nSize -= nSubGeomWkbSize;
512 :
513 895 : nDataOffset += nSubGeomWkbSize;
514 : }
515 :
516 409 : return OGRERR_NONE;
517 : }
518 :
519 : /************************************************************************/
520 : /* importFromWkb() */
521 : /* */
522 : /* Initialize from serialized stream in well known binary */
523 : /* format. */
524 : /************************************************************************/
525 :
526 376 : OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData,
527 : int nSize )
528 :
529 : {
530 376 : return importFromWkbInternal(pabyData, nSize, 0);
531 : }
532 :
533 : /************************************************************************/
534 : /* exportToWkb() */
535 : /* */
536 : /* Build a well known binary representation of this object. */
537 : /************************************************************************/
538 :
539 131 : OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder,
540 : unsigned char * pabyData ) const
541 :
542 : {
543 : int nOffset;
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Set the byte order. */
547 : /* -------------------------------------------------------------------- */
548 131 : 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 131 : GUInt32 nGType = getGeometryType();
555 :
556 131 : if( eByteOrder == wkbNDR )
557 104 : nGType = CPL_LSBWORD32( nGType );
558 : else
559 27 : nGType = CPL_MSBWORD32( nGType );
560 :
561 131 : memcpy( pabyData + 1, &nGType, 4 );
562 :
563 : /* -------------------------------------------------------------------- */
564 : /* Copy in the raw data. */
565 : /* -------------------------------------------------------------------- */
566 131 : if( OGR_SWAP( eByteOrder ) )
567 : {
568 : int nCount;
569 :
570 27 : nCount = CPL_SWAP32( nGeomCount );
571 27 : memcpy( pabyData+5, &nCount, 4 );
572 : }
573 : else
574 : {
575 104 : memcpy( pabyData+5, &nGeomCount, 4 );
576 : }
577 :
578 131 : nOffset = 9;
579 :
580 : /* ==================================================================== */
581 : /* Serialize each of the Geoms. */
582 : /* ==================================================================== */
583 366 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
584 : {
585 235 : papoGeoms[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset );
586 :
587 235 : nOffset += papoGeoms[iGeom]->WkbSize();
588 : }
589 :
590 131 : return OGRERR_NONE;
591 : }
592 :
593 : /************************************************************************/
594 : /* importFromWktInternal() */
595 : /************************************************************************/
596 :
597 200 : OGRErr OGRGeometryCollection::importFromWktInternal( char ** ppszInput, int nRecLevel )
598 :
599 : {
600 :
601 : char szToken[OGR_WKT_TOKEN_MAX];
602 200 : const char *pszInput = *ppszInput;
603 :
604 :
605 : /* Arbitrary value, but certainly large enough for reasonable usages ! */
606 200 : if( nRecLevel == 32 )
607 : {
608 : CPLError( CE_Failure, CPLE_AppDefined,
609 : "Too many recursiong level (%d) while parsing WKT geometry.",
610 1 : nRecLevel );
611 1 : return OGRERR_CORRUPT_DATA;
612 : }
613 :
614 : /* -------------------------------------------------------------------- */
615 : /* Clear existing Geoms. */
616 : /* -------------------------------------------------------------------- */
617 199 : empty();
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Read and verify the type keyword, and ensure it matches the */
621 : /* actual type of this container. */
622 : /* -------------------------------------------------------------------- */
623 199 : pszInput = OGRWktReadToken( pszInput, szToken );
624 :
625 199 : if( !EQUAL(szToken,getGeometryName()) )
626 0 : return OGRERR_CORRUPT_DATA;
627 :
628 : /* -------------------------------------------------------------------- */
629 : /* Check for EMPTY ... */
630 : /* -------------------------------------------------------------------- */
631 : const char *pszPreScan;
632 199 : int bHasZ = FALSE, bHasM = FALSE;
633 :
634 199 : pszPreScan = OGRWktReadToken( pszInput, szToken );
635 199 : if( EQUAL(szToken,"EMPTY") )
636 : {
637 14 : *ppszInput = (char *) pszPreScan;
638 14 : empty();
639 14 : return OGRERR_NONE;
640 : }
641 :
642 : /* -------------------------------------------------------------------- */
643 : /* Check for Z, M or ZM. Will ignore the Measure */
644 : /* -------------------------------------------------------------------- */
645 185 : else if( EQUAL(szToken,"Z") )
646 : {
647 8 : bHasZ = TRUE;
648 : }
649 177 : else if( EQUAL(szToken,"M") )
650 : {
651 2 : bHasM = TRUE;
652 : }
653 175 : else if( EQUAL(szToken,"ZM") )
654 : {
655 2 : bHasZ = TRUE;
656 2 : bHasM = TRUE;
657 : }
658 :
659 185 : if (bHasZ || bHasM)
660 : {
661 12 : pszInput = pszPreScan;
662 12 : pszPreScan = OGRWktReadToken( pszInput, szToken );
663 12 : if( EQUAL(szToken,"EMPTY") )
664 : {
665 4 : *ppszInput = (char *) pszPreScan;
666 4 : 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 4 : return OGRERR_NONE;
670 : }
671 : }
672 :
673 181 : if( !EQUAL(szToken,"(") )
674 3 : return OGRERR_CORRUPT_DATA;
675 :
676 178 : if ( !bHasZ && !bHasM )
677 : {
678 : /* Test for old-style GEOMETRYCOLLECTION(EMPTY) */
679 171 : pszPreScan = OGRWktReadToken( pszPreScan, szToken );
680 171 : if( EQUAL(szToken,"EMPTY") )
681 : {
682 4 : pszInput = OGRWktReadToken( pszPreScan, szToken );
683 :
684 4 : if( !EQUAL(szToken,")") )
685 2 : return OGRERR_CORRUPT_DATA;
686 : else
687 : {
688 2 : *ppszInput = (char *) pszInput;
689 2 : empty();
690 2 : return OGRERR_NONE;
691 : }
692 : }
693 : }
694 :
695 : /* Skip first '(' */
696 174 : pszInput = OGRWktReadToken( pszInput, szToken );
697 :
698 : /* ==================================================================== */
699 : /* Read each subgeometry in turn. */
700 : /* ==================================================================== */
701 252 : do
702 : {
703 295 : OGRGeometry *poGeom = NULL;
704 : OGRErr eErr;
705 :
706 : /* -------------------------------------------------------------------- */
707 : /* Get the first token, which should be the geometry type. */
708 : /* -------------------------------------------------------------------- */
709 295 : if( OGRWktReadToken( pszInput, szToken ) == NULL )
710 0 : return OGRERR_CORRUPT_DATA;
711 :
712 : /* -------------------------------------------------------------------- */
713 : /* Do the import. */
714 : /* -------------------------------------------------------------------- */
715 295 : if (EQUAL(szToken,"GEOMETRYCOLLECTION"))
716 : {
717 70 : poGeom = new OGRGeometryCollection();
718 : eErr = ((OGRGeometryCollection*)poGeom)->
719 70 : importFromWktInternal( (char **) &pszInput, nRecLevel + 1 );
720 : }
721 : else
722 : eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput,
723 225 : NULL, &poGeom );
724 :
725 295 : if( eErr != OGRERR_NONE )
726 43 : return eErr;
727 :
728 252 : addGeometryDirectly( poGeom );
729 :
730 : /* -------------------------------------------------------------------- */
731 : /* Read the delimeter following the ring. */
732 : /* -------------------------------------------------------------------- */
733 :
734 252 : pszInput = OGRWktReadToken( pszInput, szToken );
735 :
736 252 : } while( szToken[0] == ',' );
737 :
738 : /* -------------------------------------------------------------------- */
739 : /* freak if we don't get a closing bracket. */
740 : /* -------------------------------------------------------------------- */
741 131 : if( szToken[0] != ')' )
742 2 : return OGRERR_CORRUPT_DATA;
743 :
744 129 : *ppszInput = (char *) pszInput;
745 :
746 129 : return OGRERR_NONE;
747 : }
748 :
749 : /************************************************************************/
750 : /* importFromWkt() */
751 : /************************************************************************/
752 :
753 130 : OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput )
754 :
755 : {
756 130 : 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 102 : OGRErr OGRGeometryCollection::exportToWkt( char ** ppszDstText ) const
767 :
768 : {
769 : char **papszGeoms;
770 102 : int iGeom, nCumulativeLength = 0;
771 : OGRErr eErr;
772 :
773 102 : if( getNumGeometries() == 0 )
774 : {
775 18 : *ppszDstText = CPLStrdup("GEOMETRYCOLLECTION EMPTY");
776 18 : return OGRERR_NONE;
777 : }
778 :
779 : /* -------------------------------------------------------------------- */
780 : /* Build a list of strings containing the stuff for each Geom. */
781 : /* -------------------------------------------------------------------- */
782 84 : papszGeoms = (char **) CPLCalloc(sizeof(char *),nGeomCount);
783 :
784 240 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
785 : {
786 156 : eErr = papoGeoms[iGeom]->exportToWkt( &(papszGeoms[iGeom]) );
787 156 : if( eErr != OGRERR_NONE )
788 0 : goto error;
789 :
790 156 : nCumulativeLength += strlen(papszGeoms[iGeom]);
791 : }
792 :
793 : /* -------------------------------------------------------------------- */
794 : /* Allocate the right amount of space for the aggregated string */
795 : /* -------------------------------------------------------------------- */
796 84 : *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nGeomCount + 23);
797 :
798 84 : 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 84 : strcpy( *ppszDstText, getGeometryName() );
808 84 : strcat( *ppszDstText, " (" );
809 84 : nCumulativeLength = strlen(*ppszDstText);
810 :
811 240 : for( iGeom = 0; iGeom < nGeomCount; iGeom++ )
812 : {
813 156 : if( iGeom > 0 )
814 72 : (*ppszDstText)[nCumulativeLength++] = ',';
815 :
816 156 : int nGeomLength = strlen(papszGeoms[iGeom]);
817 156 : memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom], nGeomLength );
818 156 : nCumulativeLength += nGeomLength;
819 156 : VSIFree( papszGeoms[iGeom] );
820 : }
821 :
822 84 : (*ppszDstText)[nCumulativeLength++] = ')';
823 84 : (*ppszDstText)[nCumulativeLength] = '\0';
824 :
825 84 : CPLFree( papszGeoms );
826 :
827 84 : 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 501 : void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const
841 :
842 : {
843 501 : OGREnvelope oGeomEnv;
844 501 : int bExtentSet = FALSE;
845 :
846 1338 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
847 : {
848 837 : if (!papoGeoms[iGeom]->IsEmpty())
849 : {
850 837 : if (!bExtentSet)
851 : {
852 485 : papoGeoms[iGeom]->getEnvelope( psEnvelope );
853 485 : bExtentSet = TRUE;
854 : }
855 : else
856 : {
857 352 : papoGeoms[iGeom]->getEnvelope( &oGeomEnv );
858 :
859 352 : if( psEnvelope->MinX > oGeomEnv.MinX )
860 170 : psEnvelope->MinX = oGeomEnv.MinX;
861 352 : if( psEnvelope->MinY > oGeomEnv.MinY )
862 145 : psEnvelope->MinY = oGeomEnv.MinY;
863 352 : if( psEnvelope->MaxX < oGeomEnv.MaxX )
864 146 : psEnvelope->MaxX = oGeomEnv.MaxX;
865 352 : if( psEnvelope->MaxY < oGeomEnv.MaxY )
866 129 : psEnvelope->MaxY = oGeomEnv.MaxY;
867 : }
868 : }
869 : }
870 :
871 501 : if (!bExtentSet)
872 : {
873 16 : psEnvelope->MinX = psEnvelope->MinY = 0;
874 16 : psEnvelope->MaxX = psEnvelope->MaxY = 0;
875 : }
876 501 : }
877 :
878 : /************************************************************************/
879 : /* getEnvelope() */
880 : /************************************************************************/
881 :
882 117 : void OGRGeometryCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const
883 :
884 : {
885 117 : OGREnvelope3D oGeomEnv;
886 117 : int bExtentSet = FALSE;
887 :
888 256 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
889 : {
890 139 : if (!papoGeoms[iGeom]->IsEmpty())
891 : {
892 139 : if (!bExtentSet)
893 : {
894 116 : papoGeoms[iGeom]->getEnvelope( psEnvelope );
895 116 : bExtentSet = TRUE;
896 : }
897 : else
898 : {
899 23 : papoGeoms[iGeom]->getEnvelope( &oGeomEnv );
900 :
901 23 : if( psEnvelope->MinX > oGeomEnv.MinX )
902 1 : psEnvelope->MinX = oGeomEnv.MinX;
903 23 : if( psEnvelope->MinY > oGeomEnv.MinY )
904 1 : psEnvelope->MinY = oGeomEnv.MinY;
905 23 : if( psEnvelope->MaxX < oGeomEnv.MaxX )
906 23 : psEnvelope->MaxX = oGeomEnv.MaxX;
907 23 : if( psEnvelope->MaxY < oGeomEnv.MaxY )
908 21 : psEnvelope->MaxY = oGeomEnv.MaxY;
909 :
910 23 : if( psEnvelope->MinZ > oGeomEnv.MinZ )
911 11 : psEnvelope->MinZ = oGeomEnv.MinZ;
912 23 : if( psEnvelope->MaxZ < oGeomEnv.MaxZ )
913 0 : psEnvelope->MaxZ = oGeomEnv.MaxZ;
914 : }
915 : }
916 : }
917 :
918 117 : if (!bExtentSet)
919 : {
920 1 : psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0;
921 1 : psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0;
922 : }
923 117 : }
924 :
925 : /************************************************************************/
926 : /* Equals() */
927 : /************************************************************************/
928 :
929 56 : OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const
930 :
931 : {
932 56 : OGRGeometryCollection *poOGC = (OGRGeometryCollection *) poOther;
933 :
934 56 : if( poOGC == this )
935 0 : return TRUE;
936 :
937 56 : if( poOther->getGeometryType() != getGeometryType() )
938 0 : return FALSE;
939 :
940 56 : if( getNumGeometries() != poOGC->getNumGeometries() )
941 0 : return FALSE;
942 :
943 : // we should eventually test the SRS.
944 :
945 123 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
946 : {
947 67 : if( !getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)) )
948 0 : return FALSE;
949 : }
950 :
951 56 : return TRUE;
952 : }
953 :
954 : /************************************************************************/
955 : /* transform() */
956 : /************************************************************************/
957 :
958 9 : OGRErr OGRGeometryCollection::transform( OGRCoordinateTransformation *poCT )
959 :
960 : {
961 : #ifdef DISABLE_OGRGEOM_TRANSFORM
962 : return OGRERR_FAILURE;
963 : #else
964 36 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
965 : {
966 : OGRErr eErr;
967 :
968 27 : eErr = papoGeoms[iGeom]->transform( poCT );
969 27 : 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 9 : assignSpatialReference( poCT->GetTargetCS() );
986 :
987 9 : return OGRERR_NONE;
988 : #endif
989 : }
990 :
991 : /************************************************************************/
992 : /* closeRings() */
993 : /************************************************************************/
994 :
995 53 : void OGRGeometryCollection::closeRings()
996 :
997 : {
998 122 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
999 : {
1000 69 : if( wkbFlatten(papoGeoms[iGeom]->getGeometryType()) == wkbPolygon )
1001 52 : ((OGRPolygon *) papoGeoms[iGeom])->closeRings();
1002 : }
1003 53 : }
1004 :
1005 : /************************************************************************/
1006 : /* setCoordinateDimension() */
1007 : /************************************************************************/
1008 :
1009 138 : void OGRGeometryCollection::setCoordinateDimension( int nNewDimension )
1010 :
1011 : {
1012 385 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1013 : {
1014 247 : papoGeoms[iGeom]->setCoordinateDimension( nNewDimension );
1015 : }
1016 :
1017 138 : OGRGeometry::setCoordinateDimension( nNewDimension );
1018 138 : }
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 17 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1041 : {
1042 11 : OGRGeometry* geom = papoGeoms[iGeom];
1043 11 : switch( wkbFlatten(geom->getGeometryType()) )
1044 : {
1045 : case wkbLinearRing:
1046 : case wkbLineString:
1047 7 : dfLength += ((OGRCurve *) geom)->get_Length();
1048 7 : break;
1049 :
1050 : case wkbGeometryCollection:
1051 1 : 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 2 : double OGRGeometryCollection::get_Area() const
1079 : {
1080 2 : double dfArea = 0.0;
1081 7 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1082 : {
1083 5 : OGRGeometry* geom = papoGeoms[iGeom];
1084 5 : switch( wkbFlatten(geom->getGeometryType()) )
1085 : {
1086 : case wkbPolygon:
1087 1 : dfArea += ((OGRPolygon *) geom)->get_Area();
1088 1 : break;
1089 :
1090 : case wkbMultiPolygon:
1091 1 : dfArea += ((OGRMultiPolygon *) geom)->get_Area();
1092 1 : 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 1 : if( EQUAL( ((OGRGeometry*) geom)->getGeometryName(), "LINEARRING" ) )
1100 : {
1101 0 : dfArea += ((OGRLinearRing *) geom)->get_Area();
1102 : }
1103 1 : break;
1104 :
1105 : case wkbGeometryCollection:
1106 1 : dfArea +=((OGRGeometryCollection *) geom)->get_Area();
1107 : break;
1108 :
1109 : default:
1110 : break;
1111 : }
1112 : }
1113 :
1114 2 : return dfArea;
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* IsEmpty() */
1119 : /************************************************************************/
1120 :
1121 786 : OGRBoolean OGRGeometryCollection::IsEmpty( ) const
1122 : {
1123 790 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1124 692 : if (papoGeoms[iGeom]->IsEmpty() == FALSE)
1125 688 : return FALSE;
1126 98 : 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 27 : void OGRGeometryCollection::swapXY()
1144 : {
1145 54 : for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
1146 27 : papoGeoms[iGeom]->swapXY();
1147 27 : }
|