1 /******************************************************************************
2  * $Id: ogrcurvecollection.cpp 29330 2015-06-14 12:11:11Z rouault $
3  *
4  * Project:  OpenGIS Simple Features Reference Implementation
5  * Purpose:  The OGRCurveCollection class.
6  * Author:   Even Rouault, even dot rouault at spatialys dot com
7  *
8  ******************************************************************************
9  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
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 #include <assert.h>
33 
34 CPL_CVSID("$Id");
35 
36 /************************************************************************/
37 /*                         OGRCurveCollection()                         */
38 /************************************************************************/
39 
OGRCurveCollection()40 OGRCurveCollection::OGRCurveCollection()
41 
42 {
43     nCurveCount = 0;
44     papoCurves = NULL;
45 }
46 
47 /************************************************************************/
48 /*                         ~OGRCurveCollection()                        */
49 /************************************************************************/
50 
~OGRCurveCollection()51 OGRCurveCollection::~OGRCurveCollection()
52 
53 {
54     empty(NULL);
55 }
56 
57 /************************************************************************/
58 /*                              WkbSize()                               */
59 /************************************************************************/
60 
WkbSize() const61 int OGRCurveCollection::WkbSize() const
62 {
63     int         nSize = 9;
64 
65     for( int i = 0; i < nCurveCount; i++ )
66     {
67         nSize += papoCurves[i]->WkbSize();
68     }
69 
70     return nSize;
71 }
72 
73 /************************************************************************/
74 /*                          addCurveDirectly()                          */
75 /************************************************************************/
76 
addCurveDirectly(OGRGeometry * poGeom,OGRCurve * poCurve,int bNeedRealloc)77 OGRErr OGRCurveCollection::addCurveDirectly( OGRGeometry* poGeom,
78                                              OGRCurve* poCurve,
79                                              int bNeedRealloc )
80 {
81     if( poCurve->getCoordinateDimension() == 3 && poGeom->getCoordinateDimension() != 3 )
82         poGeom->setCoordinateDimension(3);
83     else if( poCurve->getCoordinateDimension() != 3 && poGeom->getCoordinateDimension() == 3 )
84         poCurve->setCoordinateDimension(3);
85 
86     if( bNeedRealloc )
87     {
88         papoCurves = (OGRCurve **) OGRRealloc( papoCurves,
89                                              sizeof(OGRCurve*) * (nCurveCount+1) );
90     }
91 
92     papoCurves[nCurveCount] = poCurve;
93 
94     nCurveCount++;
95 
96     return OGRERR_NONE;
97 }
98 
99 /************************************************************************/
100 /*                        importPreambuleFromWkb()                      */
101 /************************************************************************/
102 
importPreambuleFromWkb(OGRGeometry * poGeom,unsigned char * pabyData,int & nSize,int & nDataOffset,OGRwkbByteOrder & eByteOrder,int nMinSubGeomSize,OGRwkbVariant eWkbVariant)103 OGRErr OGRCurveCollection::importPreambuleFromWkb( OGRGeometry* poGeom,
104                                                    unsigned char * pabyData,
105                                                    int& nSize,
106                                                    int& nDataOffset,
107                                                    OGRwkbByteOrder& eByteOrder,
108                                                    int nMinSubGeomSize,
109                                                    OGRwkbVariant eWkbVariant )
110 {
111     OGRErr eErr = poGeom->importPreambuleOfCollectionFromWkb(
112                                                         pabyData,
113                                                         nSize,
114                                                         nDataOffset,
115                                                         eByteOrder,
116                                                         nMinSubGeomSize,
117                                                         nCurveCount,
118                                                         eWkbVariant );
119     if( eErr >= 0 )
120         return eErr;
121 
122     papoCurves = (OGRCurve **) VSIMalloc2(sizeof(void*), nCurveCount);
123     if (nCurveCount != 0 && papoCurves == NULL)
124     {
125         nCurveCount = 0;
126         return OGRERR_NOT_ENOUGH_MEMORY;
127     }
128 
129     return -1;
130 }
131 
132 /************************************************************************/
133 /*                       importBodyFromWkb()                            */
134 /************************************************************************/
135 
importBodyFromWkb(OGRGeometry * poGeom,unsigned char * pabyData,int nSize,int nDataOffset,int bAcceptCompoundCurve,OGRErr (* pfnAddCurveDirectlyFromWkb)(OGRGeometry * poGeom,OGRCurve * poCurve),OGRwkbVariant eWkbVariant)136 OGRErr OGRCurveCollection::importBodyFromWkb( OGRGeometry* poGeom,
137                                        unsigned char * pabyData,
138                                        int nSize,
139                                        int nDataOffset,
140                                        int bAcceptCompoundCurve,
141                                        OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry* poGeom, OGRCurve* poCurve),
142                                        OGRwkbVariant eWkbVariant )
143 {
144 
145 /* -------------------------------------------------------------------- */
146 /*      Get the Geoms.                                                  */
147 /* -------------------------------------------------------------------- */
148     int nIter = nCurveCount;
149     nCurveCount = 0;
150     for( int iGeom = 0; iGeom < nIter; iGeom++ )
151     {
152         OGRErr  eErr;
153         OGRGeometry* poSubGeom = NULL;
154 
155         /* Parses sub-geometry */
156         unsigned char* pabySubData = pabyData + nDataOffset;
157         if( nSize < 9 && nSize != -1 )
158             return OGRERR_NOT_ENOUGH_DATA;
159 
160         OGRwkbGeometryType eSubGeomType;
161         OGRBoolean bIs3D;
162         if ( OGRReadWKBGeometryType( pabySubData, eWkbVariant, &eSubGeomType, &bIs3D ) != OGRERR_NONE )
163             return OGRERR_FAILURE;
164 
165         if( (eSubGeomType != wkbCompoundCurve && OGR_GT_IsCurve(eSubGeomType)) ||
166             (bAcceptCompoundCurve && eSubGeomType == wkbCompoundCurve) )
167         {
168             eErr = OGRGeometryFactory::
169                 createFromWkb( pabySubData, NULL,
170                                &poSubGeom, nSize, eWkbVariant );
171         }
172         else
173         {
174             CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)",
175                      eSubGeomType, poGeom->getGeometryType());
176             return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
177         }
178 
179         if( eErr == OGRERR_NONE )
180             eErr = pfnAddCurveDirectlyFromWkb(poGeom, (OGRCurve*)poSubGeom);
181         if( eErr != OGRERR_NONE )
182         {
183             delete poSubGeom;
184             return eErr;
185         }
186 
187         int nSubGeomWkbSize = poSubGeom->WkbSize();
188         if( nSize != -1 )
189             nSize -= nSubGeomWkbSize;
190 
191         nDataOffset += nSubGeomWkbSize;
192     }
193 
194     return OGRERR_NONE;
195 }
196 
197 /************************************************************************/
198 /*                            exportToWkt()                             */
199 /************************************************************************/
200 
exportToWkt(const OGRGeometry * poGeom,char ** ppszDstText) const201 OGRErr OGRCurveCollection::exportToWkt( const OGRGeometry* poGeom,
202                                         char ** ppszDstText ) const
203 
204 {
205     char        **papszGeoms;
206     int         iGeom, nCumulativeLength = 0;
207     OGRErr      eErr;
208 
209     if( nCurveCount == 0 )
210     {
211         CPLString osEmpty;
212         if( poGeom->getCoordinateDimension()  == 3 )
213             osEmpty.Printf("%s Z EMPTY",poGeom->getGeometryName());
214         else
215             osEmpty.Printf("%s EMPTY",poGeom->getGeometryName());
216         *ppszDstText = CPLStrdup(osEmpty);
217         return OGRERR_NONE;
218     }
219 
220 /* -------------------------------------------------------------------- */
221 /*      Build a list of strings containing the stuff for each Geom.     */
222 /* -------------------------------------------------------------------- */
223     papszGeoms = (char **) CPLCalloc(sizeof(char *),nCurveCount);
224 
225     for( iGeom = 0; iGeom < nCurveCount; iGeom++ )
226     {
227         eErr = papoCurves[iGeom]->exportToWkt( &(papszGeoms[iGeom]), wkbVariantIso );
228         if( eErr != OGRERR_NONE )
229             goto error;
230 
231         nCumulativeLength += strlen(papszGeoms[iGeom]);
232     }
233 
234 /* -------------------------------------------------------------------- */
235 /*      Allocate the right amount of space for the aggregated string    */
236 /* -------------------------------------------------------------------- */
237     *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nCurveCount +
238                                     strlen(poGeom->getGeometryName()) + 10);
239 
240     if( *ppszDstText == NULL )
241     {
242         eErr = OGRERR_NOT_ENOUGH_MEMORY;
243         goto error;
244     }
245 
246 /* -------------------------------------------------------------------- */
247 /*      Build up the string, freeing temporary strings as we go.        */
248 /* -------------------------------------------------------------------- */
249     strcpy( *ppszDstText, poGeom->getGeometryName() );
250     if( poGeom->getCoordinateDimension() == 3 )
251         strcat( *ppszDstText, " Z" );
252     strcat( *ppszDstText, " (" );
253     nCumulativeLength = strlen(*ppszDstText);
254 
255     for( iGeom = 0; iGeom < nCurveCount; iGeom++ )
256     {
257         if( iGeom > 0 )
258             (*ppszDstText)[nCumulativeLength++] = ',';
259 
260         /* We must strip the explicit "LINESTRING " prefix */
261         int nSkip = 0;
262         if( !papoCurves[iGeom]->IsEmpty() &&
263             EQUALN(papszGeoms[iGeom], "LINESTRING ", strlen("LINESTRING ")) )
264         {
265             nSkip = strlen("LINESTRING ");
266             if( EQUALN(papszGeoms[iGeom] + nSkip, "Z ", 2) )
267                 nSkip += 2;
268         }
269 
270         int nGeomLength = strlen(papszGeoms[iGeom] + nSkip);
271         memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom] + nSkip, nGeomLength );
272         nCumulativeLength += nGeomLength;
273         VSIFree( papszGeoms[iGeom] );
274     }
275 
276     (*ppszDstText)[nCumulativeLength++] = ')';
277     (*ppszDstText)[nCumulativeLength] = '\0';
278 
279     CPLFree( papszGeoms );
280 
281     return OGRERR_NONE;
282 
283 error:
284     for( iGeom = 0; iGeom < nCurveCount; iGeom++ )
285         CPLFree( papszGeoms[iGeom] );
286     CPLFree( papszGeoms );
287     return eErr;
288 }
289 
290 /************************************************************************/
291 /*                            exportToWkb()                             */
292 /************************************************************************/
293 
exportToWkb(const OGRGeometry * poGeom,OGRwkbByteOrder eByteOrder,unsigned char * pabyData,OGRwkbVariant eWkbVariant) const294 OGRErr OGRCurveCollection::exportToWkb( const OGRGeometry* poGeom,
295                                         OGRwkbByteOrder eByteOrder,
296                                         unsigned char * pabyData,
297                                         OGRwkbVariant eWkbVariant ) const
298 {
299     int         nOffset;
300 
301 /* -------------------------------------------------------------------- */
302 /*      Set the byte order.                                             */
303 /* -------------------------------------------------------------------- */
304     pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder);
305 
306 /* -------------------------------------------------------------------- */
307 /*      Set the geometry feature type, ensuring that 3D flag is         */
308 /*      preserved.                                                      */
309 /* -------------------------------------------------------------------- */
310     GUInt32 nGType = poGeom->getIsoGeometryType();
311     if( eWkbVariant == wkbVariantPostGIS1 )
312     {
313         int bIs3D = wkbHasZ((OGRwkbGeometryType)nGType);
314         nGType = wkbFlatten(nGType);
315         if( nGType == wkbCurvePolygon )
316             nGType = POSTGIS15_CURVEPOLYGON;
317         if( bIs3D )
318             nGType = (OGRwkbGeometryType)(nGType | wkb25DBitInternalUse); /* yes we explicitly set wkb25DBit */
319     }
320 
321     if( eByteOrder == wkbNDR )
322         nGType = CPL_LSBWORD32( nGType );
323     else
324         nGType = CPL_MSBWORD32( nGType );
325 
326     memcpy( pabyData + 1, &nGType, 4 );
327 
328 /* -------------------------------------------------------------------- */
329 /*      Copy in the raw data.                                           */
330 /* -------------------------------------------------------------------- */
331     if( OGR_SWAP( eByteOrder ) )
332     {
333         int     nCount;
334 
335         nCount = CPL_SWAP32( nCurveCount );
336         memcpy( pabyData+5, &nCount, 4 );
337     }
338     else
339     {
340         memcpy( pabyData+5, &nCurveCount, 4 );
341     }
342 
343     nOffset = 9;
344 
345 /* ==================================================================== */
346 /*      Serialize each of the Geoms.                                    */
347 /* ==================================================================== */
348     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
349     {
350         papoCurves[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset, eWkbVariant );
351 
352         nOffset += papoCurves[iGeom]->WkbSize();
353     }
354 
355     return OGRERR_NONE;
356 }
357 
358 /************************************************************************/
359 /*                               empty()                                */
360 /************************************************************************/
361 
empty(OGRGeometry * poGeom)362 void OGRCurveCollection::empty(OGRGeometry* poGeom)
363 {
364     if( papoCurves != NULL )
365     {
366         for( int i = 0; i < nCurveCount; i++ )
367         {
368             delete papoCurves[i];
369         }
370         OGRFree( papoCurves );
371     }
372 
373     nCurveCount = 0;
374     papoCurves = NULL;
375     if( poGeom )
376         poGeom->setCoordinateDimension(2);
377 }
378 
379 /************************************************************************/
380 /*                            getEnvelope()                             */
381 /************************************************************************/
382 
getEnvelope(OGREnvelope * psEnvelope) const383 void OGRCurveCollection::getEnvelope( OGREnvelope * psEnvelope ) const
384 {
385     OGREnvelope3D         oEnv3D;
386     getEnvelope(&oEnv3D);
387     psEnvelope->MinX = oEnv3D.MinX;
388     psEnvelope->MinY = oEnv3D.MinY;
389     psEnvelope->MaxX = oEnv3D.MaxX;
390     psEnvelope->MaxY = oEnv3D.MaxY;
391 }
392 
393 /************************************************************************/
394 /*                            getEnvelope()                             */
395 /************************************************************************/
396 
getEnvelope(OGREnvelope3D * psEnvelope) const397 void OGRCurveCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const
398 {
399     OGREnvelope3D       oGeomEnv;
400     int                 bExtentSet = FALSE;
401 
402     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
403     {
404         if (!papoCurves[iGeom]->IsEmpty())
405         {
406             if (!bExtentSet)
407             {
408                 papoCurves[iGeom]->getEnvelope( psEnvelope );
409                 bExtentSet = TRUE;
410             }
411             else
412             {
413                 papoCurves[iGeom]->getEnvelope( &oGeomEnv );
414                 psEnvelope->Merge( oGeomEnv );
415             }
416         }
417     }
418 
419     if (!bExtentSet)
420     {
421         psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0;
422         psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0;
423     }
424 }
425 
426 /************************************************************************/
427 /*                               IsEmpty()                              */
428 /************************************************************************/
429 
IsEmpty() const430 OGRBoolean OGRCurveCollection::IsEmpty() const
431 {
432     return nCurveCount == 0;
433 }
434 
435 /************************************************************************/
436 /*                               Equals()                                */
437 /************************************************************************/
438 
Equals(OGRCurveCollection * poOCC) const439 OGRBoolean  OGRCurveCollection::Equals( OGRCurveCollection *poOCC ) const
440 {
441     if( getNumCurves() != poOCC->getNumCurves() )
442         return FALSE;
443 
444     // we should eventually test the SRS.
445 
446     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
447     {
448         if( !getCurve(iGeom)->Equals(poOCC->getCurve(iGeom)) )
449             return FALSE;
450     }
451 
452     return TRUE;
453 }
454 
455 /************************************************************************/
456 /*                       setCoordinateDimension()                       */
457 /************************************************************************/
458 
setCoordinateDimension(OGRGeometry * poGeom,int nNewDimension)459 void OGRCurveCollection::setCoordinateDimension( OGRGeometry* poGeom,
460                                                  int nNewDimension )
461 {
462     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
463     {
464         papoCurves[iGeom]->setCoordinateDimension( nNewDimension );
465     }
466 
467     poGeom->OGRGeometry::setCoordinateDimension( nNewDimension );
468 }
469 
470 /************************************************************************/
471 /*                          getNumCurves()                              */
472 /************************************************************************/
473 
getNumCurves() const474 int          OGRCurveCollection::getNumCurves() const
475 {
476     return nCurveCount;
477 }
478 
479 /************************************************************************/
480 /*                           getCurve()                                 */
481 /************************************************************************/
482 
getCurve(int i)483 OGRCurve    *OGRCurveCollection::getCurve( int i )
484 {
485     if( i < 0 || i >= nCurveCount )
486         return NULL;
487     return papoCurves[i];
488 }
489 
490 /************************************************************************/
491 /*                           getCurve()                                 */
492 /************************************************************************/
493 
getCurve(int i) const494 const OGRCurve *OGRCurveCollection::getCurve( int i ) const
495 {
496     if( i < 0 || i >= nCurveCount )
497         return NULL;
498     return papoCurves[i];
499 }
500 
501 /************************************************************************/
502 /*                           stealCurve()                               */
503 /************************************************************************/
504 
stealCurve(int i)505 OGRCurve* OGRCurveCollection::stealCurve( int i )
506 {
507     if( i < 0 || i >= nCurveCount )
508         return NULL;
509     OGRCurve* poRet = papoCurves[i];
510     if( i < nCurveCount - 1 )
511     {
512         memmove(papoCurves + i, papoCurves + i + 1, (nCurveCount - i - 1) * sizeof(OGRCurve*));
513     }
514     nCurveCount --;
515     return poRet;
516 }
517 
518 /************************************************************************/
519 /*                             transform()                              */
520 /************************************************************************/
521 
transform(OGRGeometry * poGeom,OGRCoordinateTransformation * poCT)522 OGRErr  OGRCurveCollection::transform( OGRGeometry* poGeom,
523                                        OGRCoordinateTransformation *poCT )
524 {
525 #ifdef DISABLE_OGRGEOM_TRANSFORM
526     return OGRERR_FAILURE;
527 #else
528     for( int iGeom = 0; iGeom < nCurveCount; iGeom++ )
529     {
530         OGRErr  eErr;
531 
532         eErr = papoCurves[iGeom]->transform( poCT );
533         if( eErr != OGRERR_NONE )
534         {
535             if( iGeom != 0 )
536             {
537                 CPLDebug("OGR",
538                          "OGRCurveCollection::transform() failed for a geometry other\n"
539                          "than the first, meaning some geometries are transformed\n"
540                          "and some are not!\n" );
541 
542                 return OGRERR_FAILURE;
543             }
544 
545             return eErr;
546         }
547     }
548 
549     poGeom->assignSpatialReference( poCT->GetTargetCS() );
550 
551     return OGRERR_NONE;
552 #endif
553 }
554 
555 /************************************************************************/
556 /*                            flattenTo2D()                             */
557 /************************************************************************/
558 
flattenTo2D(OGRGeometry * poGeom)559 void OGRCurveCollection::flattenTo2D(OGRGeometry* poGeom)
560 {
561     for( int i = 0; i < nCurveCount; i++ )
562         papoCurves[i]->flattenTo2D();
563 
564     poGeom->setCoordinateDimension(2);
565 }
566 
567 /************************************************************************/
568 /*                              segmentize()                            */
569 /************************************************************************/
570 
segmentize(double dfMaxLength)571 void OGRCurveCollection::segmentize(double dfMaxLength)
572 {
573     for( int i = 0; i < nCurveCount; i++ )
574         papoCurves[i]->segmentize(dfMaxLength);
575 }
576 
577 /************************************************************************/
578 /*                               swapXY()                               */
579 /************************************************************************/
580 
swapXY()581 void OGRCurveCollection::swapXY()
582 {
583     for( int i = 0; i < nCurveCount; i++ )
584         papoCurves[i]->swapXY();
585 }
586 
587 /************************************************************************/
588 /*                         hasCurveGeometry()                           */
589 /************************************************************************/
590 
hasCurveGeometry(int bLookForNonLinear) const591 OGRBoolean OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const
592 {
593     for( int i = 0; i < nCurveCount; i++ )
594     {
595         if( papoCurves[i]->hasCurveGeometry(bLookForNonLinear) )
596             return TRUE;
597     }
598     return FALSE;
599 }
600