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