1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Factory for converting geometry to and from well known binary
5  *           format.
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 1999, Frank Warmerdam
10  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys dot com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "cpl_port.h"
32 
33 #include "cpl_conv.h"
34 #include "cpl_error.h"
35 #include "cpl_string.h"
36 #include "ogr_geometry.h"
37 #include "ogr_api.h"
38 #include "ogr_core.h"
39 #include "ogr_geos.h"
40 #include "ogr_sfcgal.h"
41 #include "ogr_p.h"
42 #include "ogr_spatialref.h"
43 #include "ogr_srs_api.h"
44 #ifdef HAVE_GEOS
45 #include "geos_c.h"
46 #endif
47 #include "ogrgeojsonreader.h"
48 
49 #include <cassert>
50 #include <climits>
51 #include <cmath>
52 #include <cstdlib>
53 #include <cstring>
54 #include <cstddef>
55 
56 #include <algorithm>
57 #include <limits>
58 #include <new>
59 #include <utility>
60 #include <vector>
61 
62 #ifndef HAVE_GEOS
63 #define UNUSED_IF_NO_GEOS CPL_UNUSED
64 #else
65 #define UNUSED_IF_NO_GEOS
66 #endif
67 
68 CPL_CVSID("$Id: ogrgeometryfactory.cpp 3798cbe48457b7127606931896549f26507469db 2021-04-09 15:04:16 +0200 Even Rouault $")
69 
70 /************************************************************************/
71 /*                           createFromWkb()                            */
72 /************************************************************************/
73 
74 /**
75  * \brief Create a geometry object of the appropriate type from its
76  * well known binary representation.
77  *
78  * Note that if nBytes is passed as zero, no checking can be done on whether
79  * the pabyData is sufficient.  This can result in a crash if the input
80  * data is corrupt.  This function returns no indication of the number of
81  * bytes from the data source actually used to represent the returned
82  * geometry object.  Use OGRGeometry::WkbSize() on the returned geometry to
83  * establish the number of bytes it required in WKB format.
84  *
85  * Also note that this is a static method, and that there
86  * is no need to instantiate an OGRGeometryFactory object.
87  *
88  * The C function OGR_G_CreateFromWkb() is the same as this method.
89  *
90  * @param pabyData pointer to the input BLOB data.
91  * @param poSR pointer to the spatial reference to be assigned to the
92  *             created geometry object.  This may be NULL.
93  * @param ppoReturn the newly created geometry object will be assigned to the
94  *                  indicated pointer on return.  This will be NULL in case
95  *                  of failure. If not NULL, *ppoReturn should be freed with
96  *                  OGRGeometryFactory::destroyGeometry() after use.
97  * @param nBytes the number of bytes available in pabyData, or -1 if it isn't
98  *               known
99  * @param eWkbVariant WKB variant.
100  *
101  * @return OGRERR_NONE if all goes well, otherwise any of
102  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
103  * OGRERR_CORRUPT_DATA may be returned.
104  */
105 
createFromWkb(const void * pabyData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn,size_t nBytes,OGRwkbVariant eWkbVariant)106 OGRErr OGRGeometryFactory::createFromWkb( const void *pabyData,
107                                           OGRSpatialReference * poSR,
108                                           OGRGeometry **ppoReturn,
109                                           size_t nBytes,
110                                           OGRwkbVariant eWkbVariant )
111 
112 {
113     size_t nBytesConsumedOutIgnored = 0;
114     return createFromWkb( pabyData,
115                           poSR,
116                           ppoReturn,
117                           nBytes,
118                           eWkbVariant,
119                           nBytesConsumedOutIgnored);
120 }
121 
122 /**
123  * \brief Create a geometry object of the appropriate type from its
124  * well known binary representation.
125  *
126  * Note that if nBytes is passed as zero, no checking can be done on whether
127  * the pabyData is sufficient.  This can result in a crash if the input
128  * data is corrupt.  This function returns no indication of the number of
129  * bytes from the data source actually used to represent the returned
130  * geometry object.  Use OGRGeometry::WkbSize() on the returned geometry to
131  * establish the number of bytes it required in WKB format.
132  *
133  * Also note that this is a static method, and that there
134  * is no need to instantiate an OGRGeometryFactory object.
135  *
136  * The C function OGR_G_CreateFromWkb() is the same as this method.
137  *
138  * @param pabyData pointer to the input BLOB data.
139  * @param poSR pointer to the spatial reference to be assigned to the
140  *             created geometry object.  This may be NULL.
141  * @param ppoReturn the newly created geometry object will be assigned to the
142  *                  indicated pointer on return.  This will be NULL in case
143  *                  of failure. If not NULL, *ppoReturn should be freed with
144  *                  OGRGeometryFactory::destroyGeometry() after use.
145  * @param nBytes the number of bytes available in pabyData, or -1 if it isn't
146  *               known
147  * @param eWkbVariant WKB variant.
148  * @param nBytesConsumedOut output parameter. Number of bytes consumed.
149  *
150  * @return OGRERR_NONE if all goes well, otherwise any of
151  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
152  * OGRERR_CORRUPT_DATA may be returned.
153  * @since GDAL 2.3
154  */
155 
createFromWkb(const void * pabyData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn,size_t nBytes,OGRwkbVariant eWkbVariant,size_t & nBytesConsumedOut)156 OGRErr OGRGeometryFactory::createFromWkb( const void *pabyData,
157                                           OGRSpatialReference * poSR,
158                                           OGRGeometry **ppoReturn,
159                                           size_t nBytes,
160                                           OGRwkbVariant eWkbVariant,
161                                           size_t& nBytesConsumedOut )
162 
163 {
164     const GByte* l_pabyData = static_cast<const GByte*>(pabyData);
165     nBytesConsumedOut = 0;
166     *ppoReturn = nullptr;
167 
168     if( nBytes < 9 && nBytes != static_cast<size_t>(-1) )
169         return OGRERR_NOT_ENOUGH_DATA;
170 
171 /* -------------------------------------------------------------------- */
172 /*      Get the byte order byte.  The extra tests are to work around    */
173 /*      bug sin the WKB of DB2 v7.2 as identified by Safe Software.     */
174 /* -------------------------------------------------------------------- */
175     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*l_pabyData);
176     if( nByteOrder != wkbXDR && nByteOrder != wkbNDR )
177     {
178         CPLDebug( "OGR",
179                   "OGRGeometryFactory::createFromWkb() - got corrupt data.\n"
180                   "%02X%02X%02X%02X%02X%02X%02X%02X%02X",
181                   l_pabyData[0],
182                   l_pabyData[1],
183                   l_pabyData[2],
184                   l_pabyData[3],
185                   l_pabyData[4],
186                   l_pabyData[5],
187                   l_pabyData[6],
188                   l_pabyData[7],
189                   l_pabyData[8]);
190         return OGRERR_CORRUPT_DATA;
191     }
192 
193 /* -------------------------------------------------------------------- */
194 /*      Get the geometry feature type.  For now we assume that          */
195 /*      geometry type is between 0 and 255 so we only have to fetch     */
196 /*      one byte.                                                       */
197 /* -------------------------------------------------------------------- */
198 
199     OGRwkbGeometryType eGeometryType = wkbUnknown;
200     const OGRErr err =
201         OGRReadWKBGeometryType( l_pabyData, eWkbVariant, &eGeometryType );
202 
203     if( err != OGRERR_NONE )
204         return err;
205 
206 /* -------------------------------------------------------------------- */
207 /*      Instantiate a geometry of the appropriate type, and             */
208 /*      initialize from the input stream.                               */
209 /* -------------------------------------------------------------------- */
210     OGRGeometry *poGeom = createGeometry( eGeometryType );
211 
212     if( poGeom == nullptr )
213         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
214 
215 /* -------------------------------------------------------------------- */
216 /*      Import from binary.                                             */
217 /* -------------------------------------------------------------------- */
218     const OGRErr eErr = poGeom->importFromWkb( l_pabyData, nBytes, eWkbVariant,
219                                                nBytesConsumedOut );
220     if( eErr != OGRERR_NONE )
221     {
222         delete poGeom;
223         return eErr;
224     }
225 
226 /* -------------------------------------------------------------------- */
227 /*      Assign spatial reference system.                                */
228 /* -------------------------------------------------------------------- */
229 
230     if( poGeom->hasCurveGeometry() &&
231         CPLTestBool(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")) )
232     {
233         OGRGeometry* poNewGeom = poGeom->getLinearGeometry();
234         delete poGeom;
235         poGeom = poNewGeom;
236     }
237     poGeom->assignSpatialReference( poSR );
238     *ppoReturn = poGeom;
239 
240     return OGRERR_NONE;
241 }
242 
243 /************************************************************************/
244 /*                        OGR_G_CreateFromWkb()                         */
245 /************************************************************************/
246 /**
247  * \brief Create a geometry object of the appropriate type from its
248  * well known binary representation.
249  *
250  * Note that if nBytes is passed as zero, no checking can be done on whether
251  * the pabyData is sufficient.  This can result in a crash if the input
252  * data is corrupt.  This function returns no indication of the number of
253  * bytes from the data source actually used to represent the returned
254  * geometry object.  Use OGR_G_WkbSize() on the returned geometry to
255  * establish the number of bytes it required in WKB format.
256  *
257  * The OGRGeometryFactory::createFromWkb() CPP method is the same as this
258  * function.
259  *
260  * @param pabyData pointer to the input BLOB data.
261  * @param hSRS handle to the spatial reference to be assigned to the
262  *             created geometry object.  This may be NULL.
263  * @param phGeometry the newly created geometry object will
264  * be assigned to the indicated handle on return.  This will be NULL in case
265  * of failure. If not NULL, *phGeometry should be freed with
266  * OGR_G_DestroyGeometry() after use.
267  * @param nBytes the number of bytes of data available in pabyData, or -1
268  * if it is not known, but assumed to be sufficient.
269  *
270  * @return OGRERR_NONE if all goes well, otherwise any of
271  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
272  * OGRERR_CORRUPT_DATA may be returned.
273  */
274 
OGR_G_CreateFromWkb(const void * pabyData,OGRSpatialReferenceH hSRS,OGRGeometryH * phGeometry,int nBytes)275 OGRErr CPL_DLL OGR_G_CreateFromWkb( const void *pabyData,
276                                     OGRSpatialReferenceH hSRS,
277                                     OGRGeometryH *phGeometry,
278                                     int nBytes )
279 
280 {
281     return OGRGeometryFactory::createFromWkb(
282         pabyData,
283         OGRSpatialReference::FromHandle(hSRS),
284         reinterpret_cast<OGRGeometry **>(phGeometry),
285         nBytes );
286 }
287 
288 /************************************************************************/
289 /*                      OGR_G_CreateFromWkbEx()                         */
290 /************************************************************************/
291 /**
292  * \brief Create a geometry object of the appropriate type from its
293  * well known binary representation.
294  *
295  * Note that if nBytes is passed as zero, no checking can be done on whether
296  * the pabyData is sufficient.  This can result in a crash if the input
297  * data is corrupt.  This function returns no indication of the number of
298  * bytes from the data source actually used to represent the returned
299  * geometry object.  Use OGR_G_WkbSizeEx() on the returned geometry to
300  * establish the number of bytes it required in WKB format.
301  *
302  * The OGRGeometryFactory::createFromWkb() CPP method is the same as this
303  * function.
304  *
305  * @param pabyData pointer to the input BLOB data.
306  * @param hSRS handle to the spatial reference to be assigned to the
307  *             created geometry object.  This may be NULL.
308  * @param phGeometry the newly created geometry object will
309  * be assigned to the indicated handle on return.  This will be NULL in case
310  * of failure. If not NULL, *phGeometry should be freed with
311  * OGR_G_DestroyGeometry() after use.
312  * @param nBytes the number of bytes of data available in pabyData, or -1
313  * if it is not known, but assumed to be sufficient.
314  *
315  * @return OGRERR_NONE if all goes well, otherwise any of
316  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
317  * OGRERR_CORRUPT_DATA may be returned.
318  * @since GDAL 3.3
319  */
320 
OGR_G_CreateFromWkbEx(const void * pabyData,OGRSpatialReferenceH hSRS,OGRGeometryH * phGeometry,size_t nBytes)321 OGRErr CPL_DLL OGR_G_CreateFromWkbEx( const void *pabyData,
322                                       OGRSpatialReferenceH hSRS,
323                                       OGRGeometryH *phGeometry,
324                                       size_t nBytes )
325 
326 {
327     return OGRGeometryFactory::createFromWkb(
328         pabyData,
329         OGRSpatialReference::FromHandle(hSRS),
330         reinterpret_cast<OGRGeometry **>(phGeometry),
331         nBytes );
332 }
333 
334 /************************************************************************/
335 /*                           createFromWkt()                            */
336 /************************************************************************/
337 
338 /**
339  * \brief Create a geometry object of the appropriate type from its
340  * well known text representation.
341  *
342  * The C function OGR_G_CreateFromWkt() is the same as this method.
343  *
344  * @param ppszData input zero terminated string containing well known text
345  *                representation of the geometry to be created.  The pointer
346  *                is updated to point just beyond that last character consumed.
347  * @param poSR pointer to the spatial reference to be assigned to the
348  *             created geometry object.  This may be NULL.
349  * @param ppoReturn the newly created geometry object will be assigned to the
350  *                  indicated pointer on return.  This will be NULL if the
351  *                  method fails. If not NULL, *ppoReturn should be freed with
352  *                  OGRGeometryFactory::destroyGeometry() after use.
353  *
354  *  <b>Example:</b>
355  *
356  * \code{.cpp}
357  *    const char* wkt= "POINT(0 0)";
358  *
359  *    // cast because OGR_G_CreateFromWkt will move the pointer
360  *    char* pszWkt = (char*) wkt;
361  *    OGRSpatialReferenceH ref = OSRNewSpatialReference(NULL);
362  *    OGRGeometryH new_geom;
363  *    OSRSetAxisMappingStrategy(poSR, OAMS_TRADITIONAL_GIS_ORDER);
364  *    OGRErr err = OGR_G_CreateFromWkt(&pszWkt, ref, &new_geom);
365  * \endcode
366  *
367  *
368  *
369  * @return OGRERR_NONE if all goes well, otherwise any of
370  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
371  * OGRERR_CORRUPT_DATA may be returned.
372  */
373 
createFromWkt(const char ** ppszData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn)374 OGRErr OGRGeometryFactory::createFromWkt(const char **ppszData,
375                                          OGRSpatialReference * poSR,
376                                          OGRGeometry **ppoReturn )
377 
378 {
379     const char *pszInput = *ppszData;
380     *ppoReturn = nullptr;
381 
382 /* -------------------------------------------------------------------- */
383 /*      Get the first token, which should be the geometry type.         */
384 /* -------------------------------------------------------------------- */
385     char szToken[OGR_WKT_TOKEN_MAX] = {};
386     if( OGRWktReadToken( pszInput, szToken ) == nullptr )
387         return OGRERR_CORRUPT_DATA;
388 
389 /* -------------------------------------------------------------------- */
390 /*      Instantiate a geometry of the appropriate type.                 */
391 /* -------------------------------------------------------------------- */
392     OGRGeometry *poGeom = nullptr;
393     if( STARTS_WITH_CI(szToken, "POINT") )
394     {
395         poGeom = new OGRPoint();
396     }
397     else if( STARTS_WITH_CI(szToken, "LINESTRING") )
398     {
399         poGeom = new OGRLineString();
400     }
401     else if( STARTS_WITH_CI(szToken, "POLYGON") )
402     {
403         poGeom = new OGRPolygon();
404     }
405     else if( STARTS_WITH_CI(szToken,"TRIANGLE") )
406     {
407         poGeom = new OGRTriangle();
408     }
409     else if( STARTS_WITH_CI(szToken, "GEOMETRYCOLLECTION") )
410     {
411         poGeom = new OGRGeometryCollection();
412     }
413     else if( STARTS_WITH_CI(szToken, "MULTIPOLYGON") )
414     {
415         poGeom = new OGRMultiPolygon();
416     }
417     else if( STARTS_WITH_CI(szToken, "MULTIPOINT") )
418     {
419         poGeom = new OGRMultiPoint();
420     }
421     else if( STARTS_WITH_CI(szToken, "MULTILINESTRING") )
422     {
423         poGeom = new OGRMultiLineString();
424     }
425     else if( STARTS_WITH_CI(szToken, "CIRCULARSTRING") )
426     {
427         poGeom = new OGRCircularString();
428     }
429     else if( STARTS_WITH_CI(szToken, "COMPOUNDCURVE") )
430     {
431         poGeom = new OGRCompoundCurve();
432     }
433     else if( STARTS_WITH_CI(szToken, "CURVEPOLYGON") )
434     {
435         poGeom = new OGRCurvePolygon();
436     }
437     else if( STARTS_WITH_CI(szToken, "MULTICURVE") )
438     {
439         poGeom = new OGRMultiCurve();
440     }
441     else if( STARTS_WITH_CI(szToken, "MULTISURFACE") )
442     {
443         poGeom = new OGRMultiSurface();
444     }
445 
446     else if( STARTS_WITH_CI(szToken,"POLYHEDRALSURFACE") )
447     {
448         poGeom = new OGRPolyhedralSurface();
449     }
450 
451     else if( STARTS_WITH_CI(szToken,"TIN") )
452     {
453         poGeom = new OGRTriangulatedSurface();
454     }
455 
456     else
457     {
458         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
459     }
460 
461 /* -------------------------------------------------------------------- */
462 /*      Do the import.                                                  */
463 /* -------------------------------------------------------------------- */
464     const OGRErr eErr = poGeom->importFromWkt( &pszInput );
465 
466 /* -------------------------------------------------------------------- */
467 /*      Assign spatial reference system.                                */
468 /* -------------------------------------------------------------------- */
469     if( eErr == OGRERR_NONE )
470     {
471         if( poGeom->hasCurveGeometry() &&
472             CPLTestBool(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")) )
473         {
474             OGRGeometry* poNewGeom = poGeom->getLinearGeometry();
475             delete poGeom;
476             poGeom = poNewGeom;
477         }
478         poGeom->assignSpatialReference( poSR );
479         *ppoReturn = poGeom;
480         *ppszData = pszInput;
481     }
482     else
483     {
484         delete poGeom;
485     }
486 
487     return eErr;
488 }
489 
490 /**
491  * \brief Create a geometry object of the appropriate type from its
492  * well known text representation.
493  *
494  * The C function OGR_G_CreateFromWkt() is the same as this method.
495  *
496  * @param pszData input zero terminated string containing well known text
497  *                representation of the geometry to be created.
498  * @param poSR pointer to the spatial reference to be assigned to the
499  *             created geometry object.  This may be NULL.
500  * @param ppoReturn the newly created geometry object will be assigned to the
501  *                  indicated pointer on return.  This will be NULL if the
502  *                  method fails. If not NULL, *ppoReturn should be freed with
503  *                  OGRGeometryFactory::destroyGeometry() after use.
504 
505  * @return OGRERR_NONE if all goes well, otherwise any of
506  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
507  * OGRERR_CORRUPT_DATA may be returned.
508  * @since GDAL 2.3
509  */
510 
createFromWkt(const char * pszData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn)511 OGRErr OGRGeometryFactory::createFromWkt(const char* pszData,
512                                          OGRSpatialReference * poSR,
513                                          OGRGeometry **ppoReturn )
514 
515 {
516     return createFromWkt(&pszData, poSR, ppoReturn);
517 }
518 
519 /************************************************************************/
520 /*                        OGR_G_CreateFromWkt()                         */
521 /************************************************************************/
522 /**
523  * \brief Create a geometry object of the appropriate type from its well known
524  * text representation.
525  *
526  * The OGRGeometryFactory::createFromWkt CPP method is the same as this
527  * function.
528  *
529  * @param ppszData input zero terminated string containing well known text
530  *                representation of the geometry to be created.  The pointer
531  *                is updated to point just beyond that last character consumed.
532  * @param hSRS handle to the spatial reference to be assigned to the
533  *             created geometry object.  This may be NULL.
534  * @param phGeometry the newly created geometry object will be assigned to the
535  *                  indicated handle on return.  This will be NULL if the
536  *                  method fails. If not NULL, *phGeometry should be freed with
537  *                  OGR_G_DestroyGeometry() after use.
538  *
539  * @return OGRERR_NONE if all goes well, otherwise any of
540  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
541  * OGRERR_CORRUPT_DATA may be returned.
542  */
543 
OGR_G_CreateFromWkt(char ** ppszData,OGRSpatialReferenceH hSRS,OGRGeometryH * phGeometry)544 OGRErr CPL_DLL OGR_G_CreateFromWkt( char **ppszData,
545                                     OGRSpatialReferenceH hSRS,
546                                     OGRGeometryH *phGeometry )
547 
548 {
549     return OGRGeometryFactory::createFromWkt(
550         const_cast<const char**>(ppszData),
551         reinterpret_cast<OGRSpatialReference *>(hSRS),
552         reinterpret_cast<OGRGeometry **>(phGeometry));
553 }
554 
555 /************************************************************************/
556 /*                           createGeometry()                           */
557 /************************************************************************/
558 
559 /**
560  * \brief Create an empty geometry of desired type.
561  *
562  * This is equivalent to allocating the desired geometry with new, but
563  * the allocation is guaranteed to take place in the context of the
564  * GDAL/OGR heap.
565  *
566  * This method is the same as the C function OGR_G_CreateGeometry().
567  *
568  * @param eGeometryType the type code of the geometry class to be instantiated.
569  *
570  * @return the newly create geometry or NULL on failure. Should be freed with
571  *          OGRGeometryFactory::destroyGeometry() after use.
572  */
573 
574 OGRGeometry *
createGeometry(OGRwkbGeometryType eGeometryType)575 OGRGeometryFactory::createGeometry( OGRwkbGeometryType eGeometryType )
576 
577 {
578     switch( wkbFlatten(eGeometryType) )
579     {
580       case wkbPoint:
581           return new (std::nothrow) OGRPoint();
582 
583       case wkbLineString:
584           return new (std::nothrow) OGRLineString();
585 
586       case wkbPolygon:
587           return new (std::nothrow) OGRPolygon();
588 
589       case wkbGeometryCollection:
590           return new (std::nothrow) OGRGeometryCollection();
591 
592       case wkbMultiPolygon:
593           return new (std::nothrow) OGRMultiPolygon();
594 
595       case wkbMultiPoint:
596           return new (std::nothrow) OGRMultiPoint();
597 
598       case wkbMultiLineString:
599           return new (std::nothrow) OGRMultiLineString();
600 
601       case wkbLinearRing:
602           return new (std::nothrow) OGRLinearRing();
603 
604       case wkbCircularString:
605           return new (std::nothrow) OGRCircularString();
606 
607       case wkbCompoundCurve:
608           return new (std::nothrow) OGRCompoundCurve();
609 
610       case wkbCurvePolygon:
611           return new (std::nothrow) OGRCurvePolygon();
612 
613       case wkbMultiCurve:
614           return new (std::nothrow) OGRMultiCurve();
615 
616       case wkbMultiSurface:
617           return new (std::nothrow) OGRMultiSurface();
618 
619       case wkbTriangle:
620           return new (std::nothrow) OGRTriangle();
621 
622       case wkbPolyhedralSurface:
623           return new (std::nothrow) OGRPolyhedralSurface();
624 
625       case wkbTIN:
626           return new (std::nothrow) OGRTriangulatedSurface();
627 
628       default:
629           return nullptr;
630     }
631 }
632 
633 /************************************************************************/
634 /*                        OGR_G_CreateGeometry()                        */
635 /************************************************************************/
636 /**
637  * \brief Create an empty geometry of desired type.
638  *
639  * This is equivalent to allocating the desired geometry with new, but
640  * the allocation is guaranteed to take place in the context of the
641  * GDAL/OGR heap.
642  *
643  * This function is the same as the CPP method
644  * OGRGeometryFactory::createGeometry.
645  *
646  * @param eGeometryType the type code of the geometry to be created.
647  *
648  * @return handle to the newly create geometry or NULL on failure. Should be
649  *         freed with OGR_G_DestroyGeometry() after use.
650  */
651 
OGR_G_CreateGeometry(OGRwkbGeometryType eGeometryType)652 OGRGeometryH OGR_G_CreateGeometry( OGRwkbGeometryType eGeometryType )
653 
654 {
655     return reinterpret_cast<OGRGeometryH>(
656         OGRGeometryFactory::createGeometry(eGeometryType));
657 }
658 
659 /************************************************************************/
660 /*                          destroyGeometry()                           */
661 /************************************************************************/
662 
663 /**
664  * \brief Destroy geometry object.
665  *
666  * Equivalent to invoking delete on a geometry, but it guaranteed to take
667  * place within the context of the GDAL/OGR heap.
668  *
669  * This method is the same as the C function OGR_G_DestroyGeometry().
670  *
671  * @param poGeom the geometry to deallocate.
672  */
673 
destroyGeometry(OGRGeometry * poGeom)674 void OGRGeometryFactory::destroyGeometry( OGRGeometry *poGeom )
675 
676 {
677     delete poGeom;
678 }
679 
680 /************************************************************************/
681 /*                        OGR_G_DestroyGeometry()                       */
682 /************************************************************************/
683 /**
684  * \brief Destroy geometry object.
685  *
686  * Equivalent to invoking delete on a geometry, but it guaranteed to take
687  * place within the context of the GDAL/OGR heap.
688  *
689  * This function is the same as the CPP method
690  * OGRGeometryFactory::destroyGeometry.
691  *
692  * @param hGeom handle to the geometry to delete.
693  */
694 
OGR_G_DestroyGeometry(OGRGeometryH hGeom)695 void OGR_G_DestroyGeometry( OGRGeometryH hGeom )
696 
697 {
698     OGRGeometryFactory::destroyGeometry(reinterpret_cast<OGRGeometry *>(hGeom));
699 }
700 
701 /************************************************************************/
702 /*                           forceToPolygon()                           */
703 /************************************************************************/
704 
705 /**
706  * \brief Convert to polygon.
707  *
708  * Tries to force the provided geometry to be a polygon. This effects a change
709  * on multipolygons.
710  * Starting with GDAL 2.0, curve polygons or closed curves will be changed to
711  * polygons.  The passed in geometry is consumed and a new one returned (or
712  * potentially the same one).
713  *
714  * Note: the resulting polygon may break the Simple Features rules for polygons,
715  * for example when converting from a multi-part multipolygon.
716  *
717  * @param poGeom the input geometry - ownership is passed to the method.
718  * @return new geometry.
719  */
720 
forceToPolygon(OGRGeometry * poGeom)721 OGRGeometry *OGRGeometryFactory::forceToPolygon( OGRGeometry *poGeom )
722 
723 {
724     if( poGeom == nullptr )
725         return nullptr;
726 
727     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
728 
729     if( eGeomType == wkbCurvePolygon )
730     {
731         OGRCurvePolygon *poCurve = poGeom->toCurvePolygon();
732 
733         if( !poGeom->hasCurveGeometry(TRUE) )
734             return OGRSurface::CastToPolygon(poCurve);
735 
736         OGRPolygon* poPoly = poCurve->CurvePolyToPoly();
737         delete poGeom;
738         return poPoly;
739     }
740 
741     // base polygon or triangle
742     if( OGR_GT_IsSubClassOf( eGeomType, wkbPolygon ) )
743     {
744         return OGRSurface::CastToPolygon(poGeom->toSurface());
745     }
746 
747     if( OGR_GT_IsCurve(eGeomType) )
748     {
749         OGRCurve* poCurve = poGeom->toCurve();
750         if( poCurve->getNumPoints() >= 3 &&
751             poCurve->get_IsClosed() )
752         {
753             OGRPolygon *poPolygon = new OGRPolygon();
754             poPolygon->assignSpatialReference(poGeom->getSpatialReference());
755 
756             if( !poGeom->hasCurveGeometry(TRUE) )
757             {
758                 poPolygon->addRingDirectly(
759                     OGRCurve::CastToLinearRing(poCurve ));
760             }
761             else
762             {
763                 OGRLineString* poLS = poCurve->CurveToLine();
764                 poPolygon->addRingDirectly( OGRCurve::CastToLinearRing(poLS) );
765                 delete poGeom;
766             }
767             return poPolygon;
768         }
769     }
770 
771     if( OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface) )
772     {
773         OGRPolyhedralSurface* poPS = poGeom->toPolyhedralSurface();
774         if( poPS->getNumGeometries() == 1 )
775         {
776             poGeom = OGRSurface::CastToPolygon(
777               poPS->getGeometryRef(0)->clone()->toSurface());
778             delete poPS;
779             return poGeom;
780         }
781     }
782 
783     if( eGeomType != wkbGeometryCollection
784         && eGeomType != wkbMultiPolygon
785         && eGeomType != wkbMultiSurface )
786         return poGeom;
787 
788     // Build an aggregated polygon from all the polygon rings in the container.
789     OGRPolygon *poPolygon = new OGRPolygon();
790     OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
791     if( poGeom->hasCurveGeometry() )
792     {
793         OGRGeometryCollection *poNewGC =
794             poGC->getLinearGeometry()->toGeometryCollection();
795         delete poGC;
796         poGeom = poNewGC;
797         poGC = poNewGC;
798     }
799 
800     poPolygon->assignSpatialReference(poGeom->getSpatialReference());
801 
802     for( int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
803     {
804         if( wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType())
805             != wkbPolygon )
806             continue;
807 
808         OGRPolygon *poOldPoly = poGC->getGeometryRef(iGeom)->toPolygon();
809 
810         if( poOldPoly->getExteriorRing() == nullptr )
811             continue;
812 
813         poPolygon->addRingDirectly( poOldPoly->stealExteriorRing() );
814 
815         for( int iRing = 0; iRing < poOldPoly->getNumInteriorRings(); iRing++ )
816             poPolygon->addRingDirectly( poOldPoly->stealInteriorRing( iRing ) );
817     }
818 
819     delete poGC;
820 
821     return poPolygon;
822 }
823 
824 /************************************************************************/
825 /*                        OGR_G_ForceToPolygon()                        */
826 /************************************************************************/
827 
828 /**
829  * \brief Convert to polygon.
830  *
831  * This function is the same as the C++ method
832  * OGRGeometryFactory::forceToPolygon().
833  *
834  * @param hGeom handle to the geometry to convert (ownership surrendered).
835  * @return the converted geometry (ownership to caller).
836  *
837  * @since GDAL/OGR 1.8.0
838  */
839 
OGR_G_ForceToPolygon(OGRGeometryH hGeom)840 OGRGeometryH OGR_G_ForceToPolygon( OGRGeometryH hGeom )
841 
842 {
843     return reinterpret_cast<OGRGeometryH>(
844         OGRGeometryFactory::forceToPolygon(
845             reinterpret_cast<OGRGeometry *>(hGeom)));
846 }
847 
848 /************************************************************************/
849 /*                        forceToMultiPolygon()                         */
850 /************************************************************************/
851 
852 /**
853  * \brief Convert to multipolygon.
854  *
855  * Tries to force the provided geometry to be a multipolygon.  Currently
856  * this just effects a change on polygons.  The passed in geometry is
857  * consumed and a new one returned (or potentially the same one).
858  *
859  * @return new geometry.
860  */
861 
forceToMultiPolygon(OGRGeometry * poGeom)862 OGRGeometry *OGRGeometryFactory::forceToMultiPolygon( OGRGeometry *poGeom )
863 
864 {
865     if( poGeom == nullptr )
866         return nullptr;
867 
868     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
869 
870 /* -------------------------------------------------------------------- */
871 /*      If this is already a MultiPolygon, nothing to do                */
872 /* -------------------------------------------------------------------- */
873     if( eGeomType == wkbMultiPolygon )
874     {
875         return poGeom;
876     }
877 
878 /* -------------------------------------------------------------------- */
879 /*      If this is already a MultiSurface with compatible content,      */
880 /*      just cast                                                       */
881 /* -------------------------------------------------------------------- */
882     if( eGeomType == wkbMultiSurface )
883     {
884         OGRMultiSurface* poMS = poGeom->toMultiSurface();
885         if( !poMS->hasCurveGeometry(TRUE) )
886         {
887             return OGRMultiSurface::CastToMultiPolygon(poMS);
888         }
889     }
890 
891 /* -------------------------------------------------------------------- */
892 /*      Check for the case of a geometrycollection that can be          */
893 /*      promoted to MultiPolygon.                                       */
894 /* -------------------------------------------------------------------- */
895     if( eGeomType == wkbGeometryCollection ||
896         eGeomType == wkbMultiSurface )
897     {
898         bool bAllPoly = true;
899         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
900         if( poGeom->hasCurveGeometry() )
901         {
902             OGRGeometryCollection *poNewGC =
903                 poGC->getLinearGeometry()->toGeometryCollection();
904             delete poGC;
905             poGeom = poNewGC;
906             poGC = poNewGC;
907         }
908 
909         bool bCanConvertToMultiPoly = true;
910         for( int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ )
911         {
912             OGRwkbGeometryType eSubGeomType =
913                 wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
914             if( eSubGeomType != wkbPolygon )
915                 bAllPoly = false;
916             if( eSubGeomType != wkbMultiPolygon && eSubGeomType != wkbPolygon &&
917                 eSubGeomType != wkbPolyhedralSurface && eSubGeomType != wkbTIN )
918             {
919                 bCanConvertToMultiPoly = false;
920             }
921         }
922 
923         if( !bCanConvertToMultiPoly )
924             return poGeom;
925 
926         OGRMultiPolygon *poMP = new OGRMultiPolygon();
927         poMP->assignSpatialReference(poGeom->getSpatialReference());
928 
929         while( poGC->getNumGeometries() > 0 )
930         {
931             OGRGeometry* poSubGeom = poGC->getGeometryRef(0);
932             poGC->removeGeometry( 0, FALSE );
933             if( bAllPoly )
934             {
935                 poMP->addGeometryDirectly( poSubGeom );
936             }
937             else
938             {
939                 poSubGeom = forceToMultiPolygon( poSubGeom );
940                 OGRMultiPolygon* poSubMP = poSubGeom->toMultiPolygon();
941                 while( poSubMP != nullptr && poSubMP->getNumGeometries() > 0 )
942                 {
943                     poMP->addGeometryDirectly( poSubMP->getGeometryRef(0) );
944                     poSubMP->removeGeometry( 0, FALSE );
945                 }
946                 delete poSubMP;
947             }
948         }
949 
950         delete poGC;
951 
952         return poMP;
953     }
954 
955     if( eGeomType == wkbCurvePolygon )
956     {
957         OGRPolygon* poPoly = poGeom->toCurvePolygon()->CurvePolyToPoly();
958         OGRMultiPolygon *poMP = new OGRMultiPolygon();
959         poMP->assignSpatialReference(poGeom->getSpatialReference());
960         poMP->addGeometryDirectly( poPoly );
961         delete poGeom;
962         return poMP;
963     }
964 
965 /* -------------------------------------------------------------------- */
966 /*      If it is PolyhedralSurface or TIN, then pretend it is a         */
967 /*      multipolygon.                                                   */
968 /* -------------------------------------------------------------------- */
969     if( OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface) )
970     {
971         return OGRPolyhedralSurface::CastToMultiPolygon(
972                                             poGeom->toPolyhedralSurface());
973     }
974 
975     if( eGeomType == wkbTriangle )
976     {
977         return forceToMultiPolygon( forceToPolygon( poGeom ) );
978     }
979 
980 /* -------------------------------------------------------------------- */
981 /*      Eventually we should try to split the polygon into component    */
982 /*      island polygons.  But that is a lot of work and can be put off. */
983 /* -------------------------------------------------------------------- */
984     if( eGeomType != wkbPolygon )
985         return poGeom;
986 
987     OGRMultiPolygon *poMP = new OGRMultiPolygon();
988     poMP->assignSpatialReference(poGeom->getSpatialReference());
989     poMP->addGeometryDirectly( poGeom );
990 
991     return poMP;
992 }
993 
994 /************************************************************************/
995 /*                     OGR_G_ForceToMultiPolygon()                      */
996 /************************************************************************/
997 
998 /**
999  * \brief Convert to multipolygon.
1000  *
1001  * This function is the same as the C++ method
1002  * OGRGeometryFactory::forceToMultiPolygon().
1003  *
1004  * @param hGeom handle to the geometry to convert (ownership surrendered).
1005  * @return the converted geometry (ownership to caller).
1006  *
1007  * @since GDAL/OGR 1.8.0
1008  */
1009 
OGR_G_ForceToMultiPolygon(OGRGeometryH hGeom)1010 OGRGeometryH OGR_G_ForceToMultiPolygon( OGRGeometryH hGeom )
1011 
1012 {
1013     return reinterpret_cast<OGRGeometryH>(
1014         OGRGeometryFactory::forceToMultiPolygon(
1015             reinterpret_cast<OGRGeometry *>(hGeom)));
1016 }
1017 
1018 /************************************************************************/
1019 /*                        forceToMultiPoint()                           */
1020 /************************************************************************/
1021 
1022 /**
1023  * \brief Convert to multipoint.
1024  *
1025  * Tries to force the provided geometry to be a multipoint.  Currently
1026  * this just effects a change on points or collection of points.
1027  * The passed in geometry is
1028  * consumed and a new one returned (or potentially the same one).
1029  *
1030  * @return new geometry.
1031  */
1032 
forceToMultiPoint(OGRGeometry * poGeom)1033 OGRGeometry *OGRGeometryFactory::forceToMultiPoint( OGRGeometry *poGeom )
1034 
1035 {
1036     if( poGeom == nullptr )
1037         return nullptr;
1038 
1039     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
1040 
1041 /* -------------------------------------------------------------------- */
1042 /*      If this is already a MultiPoint, nothing to do                  */
1043 /* -------------------------------------------------------------------- */
1044     if( eGeomType == wkbMultiPoint )
1045     {
1046         return poGeom;
1047     }
1048 
1049 /* -------------------------------------------------------------------- */
1050 /*      Check for the case of a geometrycollection that can be          */
1051 /*      promoted to MultiPoint.                                         */
1052 /* -------------------------------------------------------------------- */
1053     if( eGeomType == wkbGeometryCollection )
1054     {
1055         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1056         for( auto& poMember: poGC )
1057         {
1058             if( wkbFlatten(poMember->getGeometryType()) != wkbPoint )
1059                 return poGeom;
1060         }
1061 
1062         OGRMultiPoint *poMP = new OGRMultiPoint();
1063         poMP->assignSpatialReference(poGeom->getSpatialReference());
1064 
1065         while( poGC->getNumGeometries() > 0 )
1066         {
1067             poMP->addGeometryDirectly( poGC->getGeometryRef(0) );
1068             poGC->removeGeometry( 0, FALSE );
1069         }
1070 
1071         delete poGC;
1072 
1073         return poMP;
1074     }
1075 
1076     if( eGeomType != wkbPoint )
1077         return poGeom;
1078 
1079     OGRMultiPoint *poMP = new OGRMultiPoint();
1080     poMP->assignSpatialReference(poGeom->getSpatialReference());
1081     poMP->addGeometryDirectly( poGeom );
1082 
1083     return poMP;
1084 }
1085 
1086 /************************************************************************/
1087 /*                      OGR_G_ForceToMultiPoint()                       */
1088 /************************************************************************/
1089 
1090 /**
1091  * \brief Convert to multipoint.
1092  *
1093  * This function is the same as the C++ method
1094  * OGRGeometryFactory::forceToMultiPoint().
1095  *
1096  * @param hGeom handle to the geometry to convert (ownership surrendered).
1097  * @return the converted geometry (ownership to caller).
1098  *
1099  * @since GDAL/OGR 1.8.0
1100  */
1101 
OGR_G_ForceToMultiPoint(OGRGeometryH hGeom)1102 OGRGeometryH OGR_G_ForceToMultiPoint( OGRGeometryH hGeom )
1103 
1104 {
1105     return reinterpret_cast<OGRGeometryH>(
1106         OGRGeometryFactory::forceToMultiPoint(
1107             reinterpret_cast<OGRGeometry *>(hGeom)));
1108 }
1109 
1110 /************************************************************************/
1111 /*                        forceToMultiLinestring()                      */
1112 /************************************************************************/
1113 
1114 /**
1115  * \brief Convert to multilinestring.
1116  *
1117  * Tries to force the provided geometry to be a multilinestring.
1118  *
1119  * - linestrings are placed in a multilinestring.
1120  * - circularstrings and compoundcurves will be approximated and placed in a
1121  * multilinestring.
1122  * - geometry collections will be converted to multilinestring if they only
1123  * contain linestrings.
1124  * - polygons will be changed to a collection of linestrings (one per ring).
1125  * - curvepolygons will be approximated and changed to a collection of
1126  ( linestrings (one per ring).
1127  *
1128  * The passed in geometry is
1129  * consumed and a new one returned (or potentially the same one).
1130  *
1131  * @return new geometry.
1132  */
1133 
forceToMultiLineString(OGRGeometry * poGeom)1134 OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom )
1135 
1136 {
1137     if( poGeom == nullptr )
1138         return nullptr;
1139 
1140     OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
1141 
1142 /* -------------------------------------------------------------------- */
1143 /*      If this is already a MultiLineString, nothing to do             */
1144 /* -------------------------------------------------------------------- */
1145     if( eGeomType == wkbMultiLineString )
1146     {
1147         return poGeom;
1148     }
1149 
1150 /* -------------------------------------------------------------------- */
1151 /*      Check for the case of a geometrycollection that can be          */
1152 /*      promoted to MultiLineString.                                    */
1153 /* -------------------------------------------------------------------- */
1154     if( eGeomType == wkbGeometryCollection )
1155     {
1156         OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1157         if( poGeom->hasCurveGeometry() )
1158         {
1159             OGRGeometryCollection *poNewGC = poGC->getLinearGeometry()->
1160                 toGeometryCollection();
1161             delete poGC;
1162             poGeom = poNewGC;
1163             poGC = poNewGC;
1164         }
1165 
1166         for( auto&& poMember: poGC )
1167         {
1168             if( wkbFlatten(poMember->getGeometryType()) != wkbLineString )
1169             {
1170                 return poGeom;
1171             }
1172         }
1173 
1174         OGRMultiLineString *poMP = new OGRMultiLineString();
1175         poMP->assignSpatialReference(poGeom->getSpatialReference());
1176 
1177         while( poGC->getNumGeometries() > 0 )
1178         {
1179             poMP->addGeometryDirectly( poGC->getGeometryRef(0) );
1180             poGC->removeGeometry( 0, FALSE );
1181         }
1182 
1183         delete poGC;
1184 
1185         return poMP;
1186     }
1187 
1188 /* -------------------------------------------------------------------- */
1189 /*      Turn a linestring into a multilinestring.                       */
1190 /* -------------------------------------------------------------------- */
1191     if( eGeomType == wkbLineString )
1192     {
1193         OGRMultiLineString *poMP = new OGRMultiLineString();
1194         poMP->assignSpatialReference(poGeom->getSpatialReference());
1195         poMP->addGeometryDirectly( poGeom );
1196         return poMP;
1197     }
1198 
1199 /* -------------------------------------------------------------------- */
1200 /*      Convert polygons into a multilinestring.                        */
1201 /* -------------------------------------------------------------------- */
1202     if( OGR_GT_IsSubClassOf(eGeomType, wkbCurvePolygon ) )
1203     {
1204         OGRMultiLineString *poMP = new OGRMultiLineString();
1205         OGRPolygon *poPoly = nullptr;
1206         if( OGR_GT_IsSubClassOf(eGeomType, wkbPolygon) )
1207             poPoly = poGeom->toPolygon();
1208         else
1209         {
1210             poPoly = poGeom->toCurvePolygon()->CurvePolyToPoly();
1211             delete poGeom;
1212             poGeom = poPoly;
1213         }
1214 
1215         poMP->assignSpatialReference(poGeom->getSpatialReference());
1216 
1217         for( int iRing = 0; iRing < poPoly->getNumInteriorRings()+1; iRing++ )
1218         {
1219             OGRLineString *poNewLS, *poLR;
1220 
1221             if( iRing == 0 )
1222             {
1223                 poLR = poPoly->getExteriorRing();
1224                 if( poLR == nullptr )
1225                     break;
1226             }
1227             else
1228                 poLR = poPoly->getInteriorRing(iRing-1);
1229 
1230             if( poLR == nullptr || poLR->getNumPoints() == 0 )
1231                 continue;
1232 
1233             poNewLS = new OGRLineString();
1234             poNewLS->addSubLineString( poLR );
1235             poMP->addGeometryDirectly( poNewLS );
1236         }
1237 
1238         delete poPoly;
1239 
1240         return poMP;
1241     }
1242 
1243 /* -------------------------------------------------------------------- */
1244 /*      If it is PolyhedralSurface or TIN, then pretend it is a         */
1245 /*      multipolygon.                                                   */
1246 /* -------------------------------------------------------------------- */
1247     if( OGR_GT_IsSubClassOf(eGeomType, wkbPolyhedralSurface) )
1248     {
1249         poGeom = forceToMultiPolygon(poGeom);
1250         assert(poGeom);
1251         eGeomType = wkbMultiPolygon;
1252     }
1253 
1254 /* -------------------------------------------------------------------- */
1255 /*      Convert multi-polygons into a multilinestring.                  */
1256 /* -------------------------------------------------------------------- */
1257     if( eGeomType == wkbMultiPolygon || eGeomType == wkbMultiSurface )
1258     {
1259         OGRMultiLineString *poMP = new OGRMultiLineString();
1260         OGRMultiPolygon *poMPoly = nullptr;
1261         if( eGeomType == wkbMultiPolygon )
1262             poMPoly = poGeom->toMultiPolygon();
1263         else
1264         {
1265             poMPoly = poGeom->getLinearGeometry()->toMultiPolygon();
1266             delete poGeom;
1267             poGeom = poMPoly;
1268         }
1269 
1270         assert(poGeom);
1271         poMP->assignSpatialReference(poGeom->getSpatialReference());
1272 
1273         for( auto&& poPoly: poMPoly )
1274         {
1275             for( auto&& poLR: poPoly )
1276             {
1277                 if( poLR->IsEmpty() )
1278                     continue;
1279 
1280                 OGRLineString* poNewLS = new OGRLineString();
1281                 poNewLS->addSubLineString( poLR );
1282                 poMP->addGeometryDirectly( poNewLS );
1283             }
1284         }
1285         delete poMPoly;
1286 
1287         return poMP;
1288     }
1289 
1290 /* -------------------------------------------------------------------- */
1291 /*      If it is a curve line, approximate it and wrap in a multilinestring */
1292 /* -------------------------------------------------------------------- */
1293     if( eGeomType == wkbCircularString ||
1294         eGeomType == wkbCompoundCurve )
1295     {
1296         OGRMultiLineString *poMP = new OGRMultiLineString();
1297         poMP->assignSpatialReference(poGeom->getSpatialReference());
1298         poMP->addGeometryDirectly( poGeom->toCurve()->CurveToLine() );
1299         delete poGeom;
1300         return poMP;
1301     }
1302 
1303 /* -------------------------------------------------------------------- */
1304 /*      If this is already a MultiCurve with compatible content,        */
1305 /*      just cast                                                       */
1306 /* -------------------------------------------------------------------- */
1307     if( eGeomType == wkbMultiCurve &&
1308         !poGeom->toMultiCurve()->hasCurveGeometry(TRUE) )
1309     {
1310         return OGRMultiCurve::CastToMultiLineString(poGeom->toMultiCurve());
1311     }
1312 
1313 /* -------------------------------------------------------------------- */
1314 /*      If it is a multicurve, call getLinearGeometry()                */
1315 /* -------------------------------------------------------------------- */
1316     if( eGeomType == wkbMultiCurve )
1317     {
1318         OGRGeometry* poNewGeom = poGeom->getLinearGeometry();
1319         CPLAssert( wkbFlatten(poNewGeom->getGeometryType()) ==
1320                    wkbMultiLineString );
1321         delete poGeom;
1322         return poNewGeom->toMultiLineString();
1323     }
1324 
1325     return poGeom;
1326 }
1327 
1328 /************************************************************************/
1329 /*                    OGR_G_ForceToMultiLineString()                    */
1330 /************************************************************************/
1331 
1332 /**
1333  * \brief Convert to multilinestring.
1334  *
1335  * This function is the same as the C++ method
1336  * OGRGeometryFactory::forceToMultiLineString().
1337  *
1338  * @param hGeom handle to the geometry to convert (ownership surrendered).
1339  * @return the converted geometry (ownership to caller).
1340  *
1341  * @since GDAL/OGR 1.8.0
1342  */
1343 
OGR_G_ForceToMultiLineString(OGRGeometryH hGeom)1344 OGRGeometryH OGR_G_ForceToMultiLineString( OGRGeometryH hGeom )
1345 
1346 {
1347     return reinterpret_cast<OGRGeometryH>(
1348         OGRGeometryFactory::forceToMultiLineString(
1349             reinterpret_cast<OGRGeometry *>(hGeom)));
1350 }
1351 
1352 /************************************************************************/
1353 /*                      removeLowerDimensionSubGeoms()                  */
1354 /************************************************************************/
1355 
1356 /** \brief Remove sub-geometries from a geometry collection that do not have
1357  *         the maximum topological dimensionality of the collection.
1358  *
1359  * This is typically to be used as a cleanup phase after running OGRGeometry::MakeValid()
1360  *
1361  * For example, MakeValid() on a polygon can return a geometry collection of
1362  * polygons and linestrings. Calling this method will return either a polygon
1363  * or multipolygon by dropping those linestrings.
1364  *
1365  * On a non-geometry collection, this will return a clone of the passed geometry.
1366  *
1367  * @param poGeom input geometry
1368  * @return a new geometry.
1369  *
1370  * @since GDAL 3.1.0
1371  */
removeLowerDimensionSubGeoms(const OGRGeometry * poGeom)1372 OGRGeometry* OGRGeometryFactory::removeLowerDimensionSubGeoms( const OGRGeometry* poGeom )
1373 {
1374     if( poGeom == nullptr )
1375         return nullptr;
1376     if( wkbFlatten(poGeom->getGeometryType()) != wkbGeometryCollection ||
1377         poGeom->IsEmpty() )
1378     {
1379         return poGeom->clone();
1380     }
1381     const OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
1382     int nMaxDim = 0;
1383     OGRBoolean bHasCurve = FALSE;
1384     for( const auto poSubGeom: *poGC )
1385     {
1386         nMaxDim = std::max(nMaxDim, poSubGeom->getDimension());
1387         bHasCurve |= poSubGeom->hasCurveGeometry();
1388     }
1389     int nCountAtMaxDim = 0;
1390     const OGRGeometry* poGeomAtMaxDim = nullptr;
1391     for( const auto poSubGeom: *poGC )
1392     {
1393         if( poSubGeom->getDimension() == nMaxDim )
1394         {
1395             poGeomAtMaxDim = poSubGeom;
1396             nCountAtMaxDim ++;
1397         }
1398     }
1399     if( nCountAtMaxDim == 1 && poGeomAtMaxDim != nullptr )
1400     {
1401         return poGeomAtMaxDim->clone();
1402     }
1403     OGRGeometryCollection* poRet =
1404         (nMaxDim == 0) ?               static_cast<OGRGeometryCollection*>(new OGRMultiPoint()) :
1405         (nMaxDim == 1 && !bHasCurve) ? static_cast<OGRGeometryCollection*>(new OGRMultiLineString()) :
1406         (nMaxDim == 1 && bHasCurve) ?  static_cast<OGRGeometryCollection*>(new OGRMultiCurve()) :
1407         (nMaxDim == 2 && !bHasCurve) ? static_cast<OGRGeometryCollection*>(new OGRMultiPolygon()) :
1408                                        static_cast<OGRGeometryCollection*>(new OGRMultiSurface());
1409     for( const auto poSubGeom: *poGC )
1410     {
1411         if( poSubGeom->getDimension() == nMaxDim )
1412         {
1413             if( OGR_GT_IsSubClassOf(poSubGeom->getGeometryType(), wkbGeometryCollection) )
1414             {
1415                 const OGRGeometryCollection* poSubGeomAsGC = poSubGeom->toGeometryCollection();
1416                 for( const auto poSubSubGeom: *poSubGeomAsGC )
1417                 {
1418                     if( poSubSubGeom->getDimension() == nMaxDim )
1419                     {
1420                         poRet->addGeometryDirectly(poSubSubGeom->clone());
1421                     }
1422                 }
1423             }
1424             else
1425             {
1426                 poRet->addGeometryDirectly(poSubGeom->clone());
1427             }
1428         }
1429     }
1430     return poRet;
1431 }
1432 
1433 /************************************************************************/
1434 /*                  OGR_G_RemoveLowerDimensionSubGeoms()                */
1435 /************************************************************************/
1436 
1437 /** \brief Remove sub-geometries from a geometry collection that do not have
1438  *         the maximum topological dimensionality of the collection.
1439  *
1440  * This function is the same as the C++ method
1441  * OGRGeometryFactory::removeLowerDimensionSubGeoms().
1442  *
1443  * @param hGeom handle to the geometry to convert
1444  * @return a new geometry.
1445  *
1446  * @since GDAL 3.1.0
1447  */
1448 
OGR_G_RemoveLowerDimensionSubGeoms(const OGRGeometryH hGeom)1449 OGRGeometryH OGR_G_RemoveLowerDimensionSubGeoms( const OGRGeometryH hGeom )
1450 
1451 {
1452     return OGRGeometry::ToHandle(
1453         OGRGeometryFactory::removeLowerDimensionSubGeoms(
1454             OGRGeometry::FromHandle(hGeom)));
1455 }
1456 
1457 /************************************************************************/
1458 /*                          organizePolygons()                          */
1459 /************************************************************************/
1460 
1461 struct sPolyExtended
1462 {
1463     CPL_DISALLOW_COPY_ASSIGN(sPolyExtended)
1464     sPolyExtended() = default;
1465     sPolyExtended(sPolyExtended&&) = default;
1466     sPolyExtended& operator= (sPolyExtended&&) = default;
1467 
1468     OGRGeometry* poGeometry = nullptr;
1469     OGRCurvePolygon* poPolygon = nullptr;
1470     OGREnvelope     sEnvelope{};
1471     OGRCurve*  poExteriorRing = nullptr;
1472     OGRPoint        poAPoint{};
1473     int             nInitialIndex = 0;
1474     OGRCurvePolygon*     poEnclosingPolygon = nullptr;
1475     double          dfArea = 0.0;
1476     bool            bIsTopLevel = false;
1477     bool            bIsCW = false;
1478     bool            bIsPolygon = false;
1479 };
1480 
OGRGeometryFactoryCompareArea(const sPolyExtended & sPoly1,const sPolyExtended & sPoly2)1481 static bool OGRGeometryFactoryCompareArea(const sPolyExtended& sPoly1, const sPolyExtended& sPoly2)
1482 {
1483     return sPoly2.dfArea < sPoly1.dfArea;
1484 }
1485 
OGRGeometryFactoryCompareByIndex(const sPolyExtended & sPoly1,const sPolyExtended & sPoly2)1486 static bool OGRGeometryFactoryCompareByIndex(const sPolyExtended& sPoly1, const sPolyExtended& sPoly2)
1487 {
1488     return sPoly1.nInitialIndex < sPoly2.nInitialIndex;
1489 }
1490 
1491 constexpr int N_CRITICAL_PART_NUMBER = 100;
1492 
1493 enum OrganizePolygonMethod
1494 {
1495    METHOD_NORMAL,
1496    METHOD_SKIP,
1497    METHOD_ONLY_CCW,
1498    METHOD_CCW_INNER_JUST_AFTER_CW_OUTER
1499 };
1500 
1501 /**
1502  * \brief Organize polygons based on geometries.
1503  *
1504  * Analyse a set of rings (passed as simple polygons), and based on a
1505  * geometric analysis convert them into a polygon with inner rings,
1506  * (or a MultiPolygon if dealing with more than one polygon) that follow the
1507  * OGC Simple Feature specification.
1508  *
1509  * All the input geometries must be OGRPolygon/OGRCurvePolygon with only a valid exterior
1510  * ring (at least 4 points) and no interior rings.
1511  *
1512  * The passed in geometries become the responsibility of the method, but the
1513  * papoPolygons "pointer array" remains owned by the caller.
1514  *
1515  * For faster computation, a polygon is considered to be inside
1516  * another one if a single point of its external ring is included into the other one.
1517  * (unless 'OGR_DEBUG_ORGANIZE_POLYGONS' configuration option is set to TRUE.
1518  * In that case, a slower algorithm that tests exact topological relationships
1519  * is used if GEOS is available.)
1520  *
1521  * In cases where a big number of polygons is passed to this function, the default processing
1522  * may be really slow. You can skip the processing by adding METHOD=SKIP
1523  * to the option list (the result of the function will be a multi-polygon with all polygons
1524  * as toplevel polygons) or only make it analyze counterclockwise polygons by adding
1525  * METHOD=ONLY_CCW to the option list if you can assume that the outline
1526  * of holes is counterclockwise defined (this is the convention for example in shapefiles,
1527  * Personal Geodatabases or File Geodatabases).
1528  *
1529  * For FileGDB, in most cases, but not always, a faster method than ONLY_CCW can be used. It is
1530  * CCW_INNER_JUST_AFTER_CW_OUTER. When using it, inner rings are assumed to be
1531  * counterclockwise oriented, and following immediately the outer ring (clockwise
1532  * oriented) that they belong to. If that assumption is not met, an inner ring
1533  * could be attached to the wrong outer ring, so this method must be used
1534  * with care.
1535  *
1536  * If the OGR_ORGANIZE_POLYGONS configuration option is defined, its value will override
1537  * the value of the METHOD option of papszOptions (useful to modify the behavior of the
1538  * shapefile driver)
1539  *
1540  * @param papoPolygons array of geometry pointers - should all be OGRPolygons.
1541  * Ownership of the geometries is passed, but not of the array itself.
1542  * @param nPolygonCount number of items in papoPolygons
1543  * @param pbIsValidGeometry value will be set TRUE if result is valid or
1544  * FALSE otherwise.
1545  * @param papszOptions a list of strings for passing options
1546  *
1547  * @return a single resulting geometry (either OGRPolygon, OGRCurvePolygon,
1548  * OGRMultiPolygon, OGRMultiSurface or OGRGeometryCollection). Returns a
1549  * POLYGON EMPTY in the case of nPolygonCount being 0.
1550  */
1551 
organizePolygons(OGRGeometry ** papoPolygons,int nPolygonCount,int * pbIsValidGeometry,const char ** papszOptions)1552 OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons,
1553                                                    int nPolygonCount,
1554                                                    int *pbIsValidGeometry,
1555                                                    const char** papszOptions )
1556 {
1557     if( nPolygonCount == 0 )
1558     {
1559         if( pbIsValidGeometry )
1560             *pbIsValidGeometry = TRUE;
1561 
1562         return new OGRPolygon();
1563     }
1564 
1565     OGRGeometry* geom = nullptr;
1566     OrganizePolygonMethod method = METHOD_NORMAL;
1567     bool bHasCurves = false;
1568 
1569 /* -------------------------------------------------------------------- */
1570 /*      Trivial case of a single polygon.                               */
1571 /* -------------------------------------------------------------------- */
1572     if( nPolygonCount == 1 )
1573     {
1574         geom = papoPolygons[0];
1575         papoPolygons[0] = nullptr;
1576 
1577         if( pbIsValidGeometry )
1578             *pbIsValidGeometry = TRUE;
1579 
1580         return geom;
1581     }
1582 
1583     bool bUseFastVersion = true;
1584     if( CPLTestBool(CPLGetConfigOption("OGR_DEBUG_ORGANIZE_POLYGONS",
1585                                        "NO")) )
1586     {
1587         /* ------------------------------------------------------------------ */
1588         /*      A wee bit of a warning.                                       */
1589         /* ------------------------------------------------------------------ */
1590         static int firstTime = 1;
1591         // cppcheck-suppress knownConditionTrueFalse
1592         if( !haveGEOS() && firstTime )
1593         {
1594             CPLDebug(
1595                 "OGR",
1596                 "In OGR_DEBUG_ORGANIZE_POLYGONS mode, GDAL should be built "
1597                 "with GEOS support enabled in order "
1598                 "OGRGeometryFactory::organizePolygons to provide reliable "
1599                 "results on complex polygons.");
1600             firstTime = 0;
1601         }
1602         // cppcheck-suppress knownConditionTrueFalse
1603         bUseFastVersion = !haveGEOS();
1604     }
1605 
1606 /* -------------------------------------------------------------------- */
1607 /*      Setup per polygon envelope and area information.                */
1608 /* -------------------------------------------------------------------- */
1609     std::vector<sPolyExtended> asPolyEx(nPolygonCount);
1610 
1611     bool bValidTopology = true;
1612     bool bMixedUpGeometries = false;
1613     bool bNonPolygon = false;
1614     bool bFoundCCW = false;
1615 
1616     const char* pszMethodValue =
1617         CSLFetchNameValue( papszOptions, "METHOD" );
1618     const char* pszMethodValueOption =
1619         CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", nullptr);
1620     if( pszMethodValueOption != nullptr && pszMethodValueOption[0] != '\0' )
1621         pszMethodValue = pszMethodValueOption;
1622 
1623     if( pszMethodValue != nullptr )
1624     {
1625         if( EQUAL(pszMethodValue, "SKIP") )
1626         {
1627             method = METHOD_SKIP;
1628             bMixedUpGeometries = true;
1629         }
1630         else if( EQUAL(pszMethodValue, "ONLY_CCW") )
1631         {
1632             method = METHOD_ONLY_CCW;
1633         }
1634         else if( EQUAL(pszMethodValue, "CCW_INNER_JUST_AFTER_CW_OUTER") )
1635         {
1636             method = METHOD_CCW_INNER_JUST_AFTER_CW_OUTER;
1637         }
1638         else if( !EQUAL(pszMethodValue, "DEFAULT") )
1639         {
1640             CPLError(CE_Warning, CPLE_AppDefined,
1641                      "Unrecognized value for METHOD option : %s",
1642                      pszMethodValue);
1643         }
1644     }
1645 
1646     int nCountCWPolygon = 0;
1647     int indexOfCWPolygon = -1;
1648 
1649     for( int i = 0; i < nPolygonCount; i++ )
1650     {
1651         asPolyEx[i].nInitialIndex = i;
1652         asPolyEx[i].poGeometry = papoPolygons[i];
1653         asPolyEx[i].poPolygon = papoPolygons[i]->toCurvePolygon();
1654         papoPolygons[i]->getEnvelope(&asPolyEx[i].sEnvelope);
1655 
1656         OGRwkbGeometryType eType =
1657             wkbFlatten(papoPolygons[i]->getGeometryType());
1658         if( eType == wkbCurvePolygon )
1659             bHasCurves = true;
1660         if( asPolyEx[i].poPolygon != nullptr
1661             && !asPolyEx[i].poPolygon->IsEmpty()
1662             && asPolyEx[i].poPolygon->getNumInteriorRings() == 0
1663             && asPolyEx[i].poPolygon->
1664                 getExteriorRingCurve()->getNumPoints() >= 4)
1665         {
1666             if( method != METHOD_CCW_INNER_JUST_AFTER_CW_OUTER )
1667                 asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area();
1668             asPolyEx[i].poExteriorRing =
1669                 asPolyEx[i].poPolygon->getExteriorRingCurve();
1670             asPolyEx[i].poExteriorRing->StartPoint(&asPolyEx[i].poAPoint);
1671             if( eType == wkbPolygon )
1672             {
1673                 asPolyEx[i].bIsCW =
1674                     CPL_TO_BOOL(asPolyEx[i].poExteriorRing->
1675                                         toLinearRing()->isClockwise());
1676                 asPolyEx[i].bIsPolygon = true;
1677             }
1678             else
1679             {
1680                 OGRLineString* poLS = asPolyEx[i].poExteriorRing->CurveToLine();
1681                 OGRLinearRing oLR;
1682                 oLR.addSubLineString(poLS);
1683                 asPolyEx[i].bIsCW = CPL_TO_BOOL(oLR.isClockwise());
1684                 asPolyEx[i].bIsPolygon = false;
1685                 delete poLS;
1686             }
1687             if( asPolyEx[i].bIsCW )
1688             {
1689                 indexOfCWPolygon = i;
1690                 nCountCWPolygon++;
1691             }
1692             if( !bFoundCCW )
1693                 bFoundCCW = !(asPolyEx[i].bIsCW);
1694         }
1695         else
1696         {
1697             if( !bMixedUpGeometries )
1698             {
1699                 CPLError(
1700                     CE_Warning, CPLE_AppDefined,
1701                     "organizePolygons() received an unexpected geometry.  "
1702                     "Either a polygon with interior rings, or a polygon "
1703                     "with less than 4 points, or a non-Polygon geometry.  "
1704                     "Return arguments as a collection." );
1705                 bMixedUpGeometries = true;
1706             }
1707             if( eType != wkbPolygon && eType != wkbCurvePolygon )
1708                 bNonPolygon = true;
1709         }
1710     }
1711 
1712     // If we are in ONLY_CCW mode and that we have found that there is only one
1713     // outer ring, then it is pretty easy : we can assume that all other rings
1714     // are inside.
1715     if( (method == METHOD_ONLY_CCW ||
1716          method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER) &&
1717         nCountCWPolygon == 1 && bUseFastVersion && !bNonPolygon )
1718     {
1719         OGRCurvePolygon* poCP = asPolyEx[indexOfCWPolygon].poPolygon;
1720         for( int i = 0; i < nPolygonCount; i++ )
1721         {
1722             if( i != indexOfCWPolygon )
1723             {
1724                 poCP->addRingDirectly(
1725                     asPolyEx[i].poPolygon->stealExteriorRingCurve());
1726                 delete asPolyEx[i].poPolygon;
1727             }
1728         }
1729 
1730         if( pbIsValidGeometry )
1731             *pbIsValidGeometry = TRUE;
1732         return poCP;
1733     }
1734 
1735     if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER &&
1736         !bNonPolygon && asPolyEx[0].bIsCW )
1737     {
1738         // Inner rings are CCW oriented and follow immediately the outer
1739         // ring (that is CW oriented) in which they are included.
1740         OGRMultiSurface* poMulti = nullptr;
1741         OGRCurvePolygon* poCur = asPolyEx[0].poPolygon;
1742         OGRGeometry* poRet = poCur;
1743         // We have already checked that the first ring is CW.
1744         OGREnvelope* psEnvelope = &(asPolyEx[0].sEnvelope);
1745         for( int i = 1; i < nPolygonCount; i++ )
1746         {
1747             if( asPolyEx[i].bIsCW )
1748             {
1749                 if( poMulti == nullptr )
1750                 {
1751                     if( bHasCurves )
1752                         poMulti = new OGRMultiSurface();
1753                     else
1754                         poMulti = new OGRMultiPolygon();
1755                     poRet = poMulti;
1756                     poMulti->addGeometryDirectly(poCur);
1757                 }
1758                 poCur = asPolyEx[i].poPolygon;
1759                 poMulti->addGeometryDirectly(poCur);
1760                 psEnvelope = &(asPolyEx[i].sEnvelope);
1761             }
1762             else
1763             {
1764                 poCur->addRingDirectly(
1765                     asPolyEx[i].poPolygon->stealExteriorRingCurve());
1766                 if( !(asPolyEx[i].poAPoint.getX() >= psEnvelope->MinX &&
1767                       asPolyEx[i].poAPoint.getX() <= psEnvelope->MaxX &&
1768                       asPolyEx[i].poAPoint.getY() >= psEnvelope->MinY &&
1769                       asPolyEx[i].poAPoint.getY() <= psEnvelope->MaxY) )
1770                 {
1771                     CPLError(CE_Warning, CPLE_AppDefined,
1772                              "Part %d does not respect "
1773                              "CCW_INNER_JUST_AFTER_CW_OUTER rule",
1774                              i);
1775                 }
1776                 delete asPolyEx[i].poPolygon;
1777             }
1778         }
1779 
1780         if( pbIsValidGeometry )
1781             *pbIsValidGeometry = TRUE;
1782         return poRet;
1783     }
1784     else if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && !bNonPolygon )
1785     {
1786         method = METHOD_ONLY_CCW;
1787         for( int i = 0; i < nPolygonCount; i++ )
1788             asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area();
1789     }
1790 
1791     // Emits a warning if the number of parts is sufficiently big to anticipate
1792     // for very long computation time, and the user didn't specify an explicit
1793     // method.
1794     if( nPolygonCount > N_CRITICAL_PART_NUMBER &&
1795         method == METHOD_NORMAL && pszMethodValue == nullptr )
1796     {
1797         static int firstTime = 1;
1798         if( firstTime )
1799         {
1800             if( bFoundCCW )
1801             {
1802                 CPLError(
1803                     CE_Warning, CPLE_AppDefined,
1804                     "organizePolygons() received a polygon with more than %d "
1805                     "parts. The processing may be really slow.  "
1806                     "You can skip the processing by setting METHOD=SKIP, "
1807                     "or only make it analyze counter-clock wise parts by "
1808                     "setting METHOD=ONLY_CCW if you can assume that the "
1809                     "outline of holes is counter-clock wise defined",
1810                     N_CRITICAL_PART_NUMBER);
1811             }
1812             else
1813             {
1814                 CPLError(
1815                     CE_Warning, CPLE_AppDefined,
1816                     "organizePolygons() received a polygon with more than %d "
1817                     "parts.  The processing may be really slow.  "
1818                     "You can skip the processing by setting METHOD=SKIP.",
1819                     N_CRITICAL_PART_NUMBER);
1820             }
1821             firstTime = 0;
1822         }
1823     }
1824 
1825     /* This a nulti-step algorithm :
1826        1) Sort polygons by descending areas
1827        2) For each polygon of rank i, find its smallest enclosing polygon
1828           among the polygons of rank [i-1 ... 0]. If there are no such polygon,
1829           this is a top-level polygon. Otherwise, depending on if the enclosing
1830           polygon is top-level or not, we can decide if we are top-level or not
1831        3) Re-sort the polygons to retrieve their initial order (nicer for
1832           some applications)
1833        4) For each non top-level polygon (= inner ring), add it to its
1834           outer ring
1835        5) Add the top-level polygons to the multipolygon
1836 
1837        Complexity : O(nPolygonCount^2)
1838     */
1839 
1840     /* Compute how each polygon relate to the other ones
1841        To save a bit of computation we always begin the computation by a test
1842        on the envelope. We also take into account the areas to avoid some
1843        useless tests.  (A contains B implies envelop(A) contains envelop(B)
1844        and area(A) > area(B)) In practice, we can hope that few full geometry
1845        intersection of inclusion test is done:
1846        * if the polygons are well separated geographically (a set of islands
1847        for example), no full geometry intersection or inclusion test is done.
1848        (the envelopes don't intersect each other)
1849 
1850        * if the polygons are 'lake inside an island inside a lake inside an
1851        area' and that each polygon is much smaller than its enclosing one,
1852        their bounding boxes are strictly contained into each other, and thus,
1853        no full geometry intersection or inclusion test is done
1854     */
1855 
1856     if( !bMixedUpGeometries )
1857     {
1858         // STEP 1: Sort polygons by descending area.
1859         std::sort(asPolyEx.begin(), asPolyEx.end(),
1860                   OGRGeometryFactoryCompareArea);
1861     }
1862     papoPolygons = nullptr;  // Just to use to avoid it afterwards.
1863 
1864 /* -------------------------------------------------------------------- */
1865 /*      Compute relationships, if things seem well structured.          */
1866 /* -------------------------------------------------------------------- */
1867 
1868     // The first (largest) polygon is necessarily top-level.
1869     asPolyEx[0].bIsTopLevel = true;
1870     asPolyEx[0].poEnclosingPolygon = nullptr;
1871 
1872     int nCountTopLevel = 1;
1873 
1874     // STEP 2.
1875     for( int i = 1;
1876          !bMixedUpGeometries && bValidTopology && i<nPolygonCount;
1877          i++ )
1878     {
1879         if( method == METHOD_ONLY_CCW && asPolyEx[i].bIsCW )
1880         {
1881             nCountTopLevel++;
1882             asPolyEx[i].bIsTopLevel = true;
1883             asPolyEx[i].poEnclosingPolygon = nullptr;
1884             continue;
1885         }
1886 
1887         int j = i - 1;  // Used after for.
1888         for( ; bValidTopology && j >= 0; j-- )
1889         {
1890             bool b_i_inside_j = false;
1891 
1892             if( method == METHOD_ONLY_CCW && asPolyEx[j].bIsCW == false )
1893             {
1894                 // In that mode, i which is CCW if we reach here can only be
1895                 // included in a CW polygon.
1896                 continue;
1897             }
1898 
1899             if( asPolyEx[j].sEnvelope.Contains(asPolyEx[i].sEnvelope) )
1900             {
1901                 if( bUseFastVersion )
1902                 {
1903                     if( method == METHOD_ONLY_CCW && j == 0 )
1904                     {
1905                         // We are testing if a CCW ring is in the biggest CW
1906                         // ring It *must* be inside as this is the last
1907                         // candidate, otherwise the winding order rules is
1908                         // broken.
1909                         b_i_inside_j = true;
1910                     }
1911                     else if( asPolyEx[i].bIsPolygon &&
1912                              asPolyEx[j].bIsPolygon &&
1913                              asPolyEx[j].poExteriorRing->toLinearRing()->
1914                                      isPointOnRingBoundary(
1915                                          &asPolyEx[i].poAPoint, FALSE) )
1916                     {
1917                         OGRLinearRing* poLR_i =
1918                             asPolyEx[i].poExteriorRing->toLinearRing();
1919                         OGRLinearRing* poLR_j =
1920                             asPolyEx[j].poExteriorRing->toLinearRing();
1921 
1922                         // If the point of i is on the boundary of j, we will
1923                         // iterate over the other points of i.
1924                         const int nPoints = poLR_i->getNumPoints();
1925                         int k = 1;  // Used after for.
1926                         OGRPoint previousPoint = asPolyEx[i].poAPoint;
1927                         for( ; k < nPoints; k++ )
1928                         {
1929                             OGRPoint point;
1930                             poLR_i->getPoint(k, &point);
1931                             if( point.getX() == previousPoint.getX() &&
1932                                 point.getY() == previousPoint.getY() )
1933                             {
1934                                 continue;
1935                             }
1936                             if( poLR_j->isPointOnRingBoundary(&point, FALSE) )
1937                             {
1938                                 // If it is on the boundary of j, iterate again.
1939                             }
1940                             else if( poLR_j->isPointInRing(&point, FALSE) )
1941                             {
1942                                 // If then point is strictly included in j, then
1943                                 // i is considered inside j.
1944                                 b_i_inside_j = true;
1945                                 break;
1946                             }
1947                             else
1948                             {
1949                                 // If it is outside, then i cannot be inside j.
1950                                 break;
1951                             }
1952                             previousPoint = point;
1953                         }
1954                         if( !b_i_inside_j && k == nPoints && nPoints > 2 )
1955                         {
1956                             // All points of i are on the boundary of j.
1957                             // Take a point in the middle of a segment of i and
1958                             // test it against j.
1959                             poLR_i->getPoint(0, &previousPoint);
1960                             for( k = 1; k < nPoints; k++ )
1961                             {
1962                                 OGRPoint point;
1963                                 poLR_i->getPoint(k, &point);
1964                                 if( point.getX() == previousPoint.getX() &&
1965                                     point.getY() == previousPoint.getY() )
1966                                 {
1967                                     continue;
1968                                 }
1969                                 OGRPoint pointMiddle;
1970                                 pointMiddle.setX((point.getX() +
1971                                                   previousPoint.getX()) / 2);
1972                                 pointMiddle.setY((point.getY() +
1973                                                   previousPoint.getY()) / 2);
1974                                 if( poLR_j->isPointOnRingBoundary(&pointMiddle,
1975                                                                   FALSE) )
1976                                 {
1977                                     // If it is on the boundary of j, iterate
1978                                     // again.
1979                                 }
1980                                 else if( poLR_j->isPointInRing(&pointMiddle,
1981                                                                FALSE) )
1982                                 {
1983                                     // If then point is strictly included in j,
1984                                     // then i is considered inside j.
1985                                     b_i_inside_j = true;
1986                                     break;
1987                                 }
1988                                 else
1989                                 {
1990                                     // If it is outside, then i cannot be inside
1991                                     // j.
1992                                     break;
1993                                 }
1994                                 previousPoint = point;
1995                             }
1996                         }
1997                     }
1998                     // Note that isPointInRing only test strict inclusion in the
1999                     // ring.
2000                     else if( asPolyEx[i].bIsPolygon &&
2001                              asPolyEx[j].bIsPolygon &&
2002                              asPolyEx[j].poExteriorRing->toLinearRing()->
2003                                      isPointInRing(&asPolyEx[i].poAPoint,
2004                                                    FALSE) )
2005                     {
2006                         b_i_inside_j = true;
2007                     }
2008                 }
2009                 else if( asPolyEx[j].poPolygon->
2010                              Contains(asPolyEx[i].poPolygon) )
2011                 {
2012                     b_i_inside_j = true;
2013                 }
2014             }
2015 
2016             if( b_i_inside_j )
2017             {
2018                 if( asPolyEx[j].bIsTopLevel )
2019                 {
2020                     // We are a lake.
2021                     asPolyEx[i].bIsTopLevel = false;
2022                     asPolyEx[i].poEnclosingPolygon = asPolyEx[j].poPolygon;
2023                 }
2024                 else
2025                 {
2026                     // We are included in a something not toplevel (a lake),
2027                     // so in OGCSF we are considered as toplevel too.
2028                     nCountTopLevel++;
2029                     asPolyEx[i].bIsTopLevel = true;
2030                     asPolyEx[i].poEnclosingPolygon = nullptr;
2031                 }
2032                 break;
2033             }
2034             // Use Overlaps instead of Intersects to be more
2035             // tolerant about touching polygons.
2036             else if( bUseFastVersion ||
2037                      !asPolyEx[i].sEnvelope.Intersects(asPolyEx[j].sEnvelope) ||
2038                      !asPolyEx[i].poPolygon->Overlaps(asPolyEx[j].poPolygon) )
2039             {
2040 
2041             }
2042             else
2043             {
2044                 // Bad... The polygons are intersecting but no one is
2045                 // contained inside the other one. This is a really broken
2046                 // case. We just make a multipolygon with the whole set of
2047                 // polygons.
2048                 bValidTopology = false;
2049 #ifdef DEBUG
2050                 char* wkt1 = nullptr;
2051                 char* wkt2 = nullptr;
2052                 asPolyEx[i].poPolygon->exportToWkt(&wkt1);
2053                 asPolyEx[j].poPolygon->exportToWkt(&wkt2);
2054                 CPLDebug( "OGR",
2055                           "Bad intersection for polygons %d and %d\n"
2056                           "geom %d: %s\n"
2057                           "geom %d: %s",
2058                           i, j, i, wkt1, j, wkt2 );
2059                 CPLFree(wkt1);
2060                 CPLFree(wkt2);
2061 #endif
2062             }
2063         }
2064 
2065         if( j < 0 )
2066         {
2067             // We come here because we are not included in anything.
2068             // We are toplevel.
2069             nCountTopLevel++;
2070             asPolyEx[i].bIsTopLevel = true;
2071             asPolyEx[i].poEnclosingPolygon = nullptr;
2072         }
2073     }
2074 
2075     if( pbIsValidGeometry )
2076         *pbIsValidGeometry = bValidTopology && !bMixedUpGeometries;
2077 
2078 /* -------------------------------------------------------------------- */
2079 /*      Things broke down - just turn everything into a multipolygon.   */
2080 /* -------------------------------------------------------------------- */
2081     if( !bValidTopology || bMixedUpGeometries )
2082     {
2083         OGRGeometryCollection* poGC = nullptr;
2084         if( bNonPolygon )
2085             poGC = new OGRGeometryCollection();
2086         else if( bHasCurves )
2087             poGC = new OGRMultiSurface();
2088         else
2089             poGC = new OGRMultiPolygon();
2090         geom = poGC;
2091 
2092         for( int i = 0; i < nPolygonCount; i++ )
2093         {
2094             poGC->addGeometryDirectly( asPolyEx[i].poGeometry );
2095         }
2096     }
2097 
2098 /* -------------------------------------------------------------------- */
2099 /*      Try to turn into one or more polygons based on the ring         */
2100 /*      relationships.                                                  */
2101 /* -------------------------------------------------------------------- */
2102     else
2103     {
2104         // STEP 3: Sort again in initial order.
2105         std::sort(asPolyEx.begin(), asPolyEx.end(),
2106                   OGRGeometryFactoryCompareByIndex);
2107 
2108         // STEP 4: Add holes as rings of their enclosing polygon.
2109         for( int i = 0; i < nPolygonCount; i++ )
2110         {
2111             if( asPolyEx[i].bIsTopLevel == false )
2112             {
2113                 asPolyEx[i].poEnclosingPolygon->addRingDirectly(
2114                     asPolyEx[i].poPolygon->stealExteriorRingCurve());
2115                 delete asPolyEx[i].poPolygon;
2116             }
2117             else if( nCountTopLevel == 1 )
2118             {
2119                 geom = asPolyEx[i].poPolygon;
2120             }
2121         }
2122 
2123         // STEP 5: Add toplevel polygons.
2124         if( nCountTopLevel > 1 )
2125         {
2126             OGRGeometryCollection* poGC = nullptr;
2127             for( int i = 0; i < nPolygonCount; i++ )
2128             {
2129                 if( asPolyEx[i].bIsTopLevel )
2130                 {
2131                     if( poGC == nullptr )
2132                     {
2133                         if( bHasCurves )
2134                             poGC = new OGRMultiSurface();
2135                         else
2136                             poGC = new OGRMultiPolygon();
2137                     }
2138                     poGC->addGeometryDirectly(asPolyEx[i].poPolygon);
2139                 }
2140             }
2141             geom = poGC;
2142         }
2143     }
2144 
2145     return geom;
2146 }
2147 
2148 /************************************************************************/
2149 /*                           createFromGML()                            */
2150 /************************************************************************/
2151 
2152 /**
2153  * \brief Create geometry from GML.
2154  *
2155  * This method translates a fragment of GML containing only the geometry
2156  * portion into a corresponding OGRGeometry.  There are many limitations
2157  * on the forms of GML geometries supported by this parser, but they are
2158  * too numerous to list here.
2159  *
2160  * The following GML2 elements are parsed : Point, LineString, Polygon,
2161  * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry.
2162  *
2163  * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface,
2164  * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, LineStringSegment, Arc,
2165  * Circle, CompositeSurface, OrientableSurface, Solid, Tin, TriangulatedSurface.
2166  *
2167  * Arc and Circle elements are stroked to linestring, by using a
2168  * 4 degrees step, unless the user has overridden the value with the
2169  * OGR_ARC_STEPSIZE configuration variable.
2170  *
2171  * The C function OGR_G_CreateFromGML() is the same as this method.
2172  *
2173  * @param pszData The GML fragment for the geometry.
2174  *
2175  * @return a geometry on success, or NULL on error.
2176  */
2177 
createFromGML(const char * pszData)2178 OGRGeometry *OGRGeometryFactory::createFromGML( const char *pszData )
2179 
2180 {
2181     OGRGeometryH hGeom;
2182 
2183     hGeom = OGR_G_CreateFromGML( pszData );
2184 
2185     return OGRGeometry::FromHandle(hGeom);
2186 }
2187 
2188 /************************************************************************/
2189 /*                           createFromGEOS()                           */
2190 /************************************************************************/
2191 
2192 /** Builds a OGRGeometry* from a GEOSGeom.
2193  * @param hGEOSCtxt GEOS context
2194  * @param geosGeom GEOS geometry
2195  * @return a OGRGeometry*
2196  */
2197 OGRGeometry *
createFromGEOS(UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,UNUSED_IF_NO_GEOS GEOSGeom geosGeom)2198 OGRGeometryFactory::createFromGEOS(
2199     UNUSED_IF_NO_GEOS GEOSContextHandle_t hGEOSCtxt,
2200     UNUSED_IF_NO_GEOS GEOSGeom geosGeom )
2201 
2202 {
2203 #ifndef HAVE_GEOS
2204 
2205     CPLError( CE_Failure, CPLE_NotSupported,
2206               "GEOS support not enabled." );
2207     return nullptr;
2208 
2209 #else
2210 
2211     size_t nSize = 0;
2212     unsigned char *pabyBuf = nullptr;
2213     OGRGeometry *poGeometry = nullptr;
2214 
2215     // Special case as POINT EMPTY cannot be translated to WKB.
2216     if( GEOSGeomTypeId_r(hGEOSCtxt, geosGeom) == GEOS_POINT &&
2217         GEOSisEmpty_r(hGEOSCtxt, geosGeom) )
2218         return new OGRPoint();
2219 
2220 #if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3)
2221     // GEOSGeom_getCoordinateDimension only available in GEOS 3.3.0.
2222     const int nCoordDim =
2223         GEOSGeom_getCoordinateDimension_r(hGEOSCtxt, geosGeom);
2224     GEOSWKBWriter* wkbwriter = GEOSWKBWriter_create_r(hGEOSCtxt);
2225     GEOSWKBWriter_setOutputDimension_r(hGEOSCtxt, wkbwriter, nCoordDim);
2226     pabyBuf = GEOSWKBWriter_write_r(hGEOSCtxt, wkbwriter, geosGeom, &nSize );
2227     GEOSWKBWriter_destroy_r(hGEOSCtxt, wkbwriter);
2228 #else
2229     pabyBuf = GEOSGeomToWKB_buf_r( hGEOSCtxt, geosGeom, &nSize );
2230 #endif
2231     if( pabyBuf == nullptr || nSize == 0 )
2232     {
2233         return nullptr;
2234     }
2235 
2236     if( OGRGeometryFactory::createFromWkb( pabyBuf,
2237                                            nullptr, &poGeometry,
2238                                            static_cast<int>(nSize) )
2239         != OGRERR_NONE )
2240     {
2241         poGeometry = nullptr;
2242     }
2243     // Since GEOS 3.1.1, so we test 3.2.0.
2244 #if GEOS_CAPI_VERSION_MAJOR >= 2 || \
2245     (GEOS_CAPI_VERSION_MAJOR == 1 && GEOS_CAPI_VERSION_MINOR >= 6)
2246     GEOSFree_r( hGEOSCtxt, pabyBuf );
2247 #else
2248     free( pabyBuf );
2249 #endif
2250 
2251     return poGeometry;
2252 
2253 #endif  // HAVE_GEOS
2254 }
2255 
2256 /************************************************************************/
2257 /*                              haveGEOS()                              */
2258 /************************************************************************/
2259 
2260 /**
2261  * \brief Test if GEOS enabled.
2262  *
2263  * This static method returns TRUE if GEOS support is built into OGR,
2264  * otherwise it returns FALSE.
2265  *
2266  * @return TRUE if available, otherwise FALSE.
2267  */
2268 
haveGEOS()2269 bool OGRGeometryFactory::haveGEOS()
2270 
2271 {
2272 #ifndef HAVE_GEOS
2273     return false;
2274 #else
2275     return true;
2276 #endif
2277 }
2278 
2279 /************************************************************************/
2280 /*                           createFromFgf()                            */
2281 /************************************************************************/
2282 
2283 /**
2284  * \brief Create a geometry object of the appropriate type from its FGF (FDO Geometry Format) binary representation.
2285  *
2286  * Also note that this is a static method, and that there
2287  * is no need to instantiate an OGRGeometryFactory object.
2288  *
2289  * The C function OGR_G_CreateFromFgf() is the same as this method.
2290  *
2291  * @param pabyData pointer to the input BLOB data.
2292  * @param poSR pointer to the spatial reference to be assigned to the
2293  *             created geometry object.  This may be NULL.
2294  * @param ppoReturn the newly created geometry object will be assigned to the
2295  *                  indicated pointer on return.  This will be NULL in case
2296  *                  of failure, but NULL might be a valid return for a NULL shape.
2297  * @param nBytes the number of bytes available in pabyData.
2298  * @param pnBytesConsumed if not NULL, it will be set to the number of bytes
2299  * consumed (at most nBytes).
2300  *
2301  * @return OGRERR_NONE if all goes well, otherwise any of
2302  * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or
2303  * OGRERR_CORRUPT_DATA may be returned.
2304  */
2305 
createFromFgf(const void * pabyData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn,int nBytes,int * pnBytesConsumed)2306 OGRErr OGRGeometryFactory::createFromFgf( const void* pabyData,
2307                                           OGRSpatialReference * poSR,
2308                                           OGRGeometry **ppoReturn,
2309                                           int nBytes,
2310                                           int *pnBytesConsumed )
2311 
2312 {
2313     return createFromFgfInternal(static_cast<const GByte*>(pabyData),
2314                                  poSR, ppoReturn, nBytes,
2315                                  pnBytesConsumed, 0);
2316 }
2317 
2318 /************************************************************************/
2319 /*                       createFromFgfInternal()                        */
2320 /************************************************************************/
2321 
createFromFgfInternal(const unsigned char * pabyData,OGRSpatialReference * poSR,OGRGeometry ** ppoReturn,int nBytes,int * pnBytesConsumed,int nRecLevel)2322 OGRErr OGRGeometryFactory::createFromFgfInternal( const unsigned char *pabyData,
2323                                                   OGRSpatialReference * poSR,
2324                                                   OGRGeometry **ppoReturn,
2325                                                   int nBytes,
2326                                                   int *pnBytesConsumed,
2327                                                   int nRecLevel )
2328 {
2329     // Arbitrary value, but certainly large enough for reasonable usages.
2330     if( nRecLevel == 32 )
2331     {
2332         CPLError( CE_Failure, CPLE_AppDefined,
2333                   "Too many recursion levels (%d) while parsing FGF geometry.",
2334                   nRecLevel );
2335         return OGRERR_CORRUPT_DATA;
2336     }
2337 
2338     *ppoReturn = nullptr;
2339 
2340     if( nBytes < 4 )
2341         return OGRERR_NOT_ENOUGH_DATA;
2342 
2343 /* -------------------------------------------------------------------- */
2344 /*      Decode the geometry type.                                       */
2345 /* -------------------------------------------------------------------- */
2346     GInt32 nGType = 0;
2347     memcpy( &nGType, pabyData + 0, 4 );
2348     CPL_LSBPTR32( &nGType );
2349 
2350     if( nGType < 0 || nGType > 13 )
2351         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
2352 
2353 /* -------------------------------------------------------------------- */
2354 /*      Decode the dimensionality if appropriate.                       */
2355 /* -------------------------------------------------------------------- */
2356     int          nTupleSize = 0;
2357     GInt32       nGDim = 0;
2358 
2359     // TODO: Why is this a switch?
2360     switch( nGType )
2361     {
2362       case 1: // Point
2363       case 2: // LineString
2364       case 3: // Polygon
2365         if( nBytes < 8 )
2366             return OGRERR_NOT_ENOUGH_DATA;
2367 
2368         memcpy( &nGDim, pabyData + 4, 4 );
2369         CPL_LSBPTR32( &nGDim );
2370 
2371         if( nGDim < 0 || nGDim > 3 )
2372             return OGRERR_CORRUPT_DATA;
2373 
2374         nTupleSize = 2;
2375         if( nGDim & 0x01 ) // Z
2376             nTupleSize++;
2377         if( nGDim & 0x02 ) // M
2378             nTupleSize++;
2379 
2380         break;
2381 
2382       default:
2383         break;
2384     }
2385 
2386     OGRGeometry *poGeom = nullptr;
2387 
2388 /* -------------------------------------------------------------------- */
2389 /*      None                                                            */
2390 /* -------------------------------------------------------------------- */
2391     if( nGType == 0 )
2392     {
2393         if( pnBytesConsumed )
2394             *pnBytesConsumed = 4;
2395     }
2396 
2397 /* -------------------------------------------------------------------- */
2398 /*      Point                                                           */
2399 /* -------------------------------------------------------------------- */
2400     else if( nGType == 1 )
2401     {
2402         if( nBytes < nTupleSize * 8 + 8 )
2403             return OGRERR_NOT_ENOUGH_DATA;
2404 
2405         double adfTuple[4] = { 0.0, 0.0, 0.0, 0.0 };
2406         memcpy( adfTuple, pabyData + 8, nTupleSize*8 );
2407 #ifdef CPL_MSB
2408         for( int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ )
2409             CPL_SWAP64PTR( adfTuple + iOrdinal );
2410 #endif
2411         if( nTupleSize > 2 )
2412             poGeom = new OGRPoint( adfTuple[0], adfTuple[1], adfTuple[2] );
2413         else
2414             poGeom = new OGRPoint( adfTuple[0], adfTuple[1] );
2415 
2416         if( pnBytesConsumed )
2417             *pnBytesConsumed = 8 + nTupleSize * 8;
2418     }
2419 
2420 /* -------------------------------------------------------------------- */
2421 /*      LineString                                                      */
2422 /* -------------------------------------------------------------------- */
2423     else if( nGType == 2 )
2424     {
2425         if( nBytes < 12 )
2426             return OGRERR_NOT_ENOUGH_DATA;
2427 
2428         GInt32 nPointCount = 0;
2429         memcpy( &nPointCount, pabyData + 8, 4 );
2430         CPL_LSBPTR32( &nPointCount );
2431 
2432         if( nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8) )
2433             return OGRERR_CORRUPT_DATA;
2434 
2435         if( nBytes - 12 < nTupleSize * 8 * nPointCount )
2436             return OGRERR_NOT_ENOUGH_DATA;
2437 
2438         OGRLineString *poLS = new OGRLineString();
2439         poGeom = poLS;
2440         poLS->setNumPoints( nPointCount );
2441 
2442         for( int iPoint = 0; iPoint < nPointCount; iPoint++ )
2443         {
2444             double adfTuple[4] = { 0.0, 0.0, 0.0, 0.0 };
2445             memcpy( adfTuple, pabyData + 12 + 8*nTupleSize*iPoint,
2446                     nTupleSize*8 );
2447 #ifdef CPL_MSB
2448             for( int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ )
2449                 CPL_SWAP64PTR( adfTuple + iOrdinal );
2450 #endif
2451             if( nTupleSize > 2 )
2452                 poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] );
2453             else
2454                 poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] );
2455         }
2456 
2457         if( pnBytesConsumed )
2458             *pnBytesConsumed = 12 + nTupleSize * 8 * nPointCount;
2459     }
2460 
2461 /* -------------------------------------------------------------------- */
2462 /*      Polygon                                                         */
2463 /* -------------------------------------------------------------------- */
2464     else if( nGType == 3 )
2465     {
2466         if( nBytes < 12 )
2467             return OGRERR_NOT_ENOUGH_DATA;
2468 
2469         GInt32 nRingCount = 0;
2470         memcpy( &nRingCount, pabyData + 8, 4 );
2471         CPL_LSBPTR32( &nRingCount );
2472 
2473         if( nRingCount < 0 || nRingCount > INT_MAX / 4 )
2474             return OGRERR_CORRUPT_DATA;
2475 
2476         // Each ring takes at least 4 bytes.
2477         if( nBytes - 12 < nRingCount * 4 )
2478             return OGRERR_NOT_ENOUGH_DATA;
2479 
2480         int nNextByte = 12;
2481 
2482         OGRPolygon * poPoly = new OGRPolygon();
2483         poGeom = poPoly;
2484 
2485         for( int iRing = 0; iRing < nRingCount; iRing++ )
2486         {
2487             if( nBytes - nNextByte < 4 )
2488             {
2489                 delete poGeom;
2490                 return OGRERR_NOT_ENOUGH_DATA;
2491             }
2492 
2493             GInt32 nPointCount = 0;
2494             memcpy( &nPointCount, pabyData + nNextByte, 4 );
2495             CPL_LSBPTR32( &nPointCount );
2496 
2497             if( nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8) )
2498             {
2499                 delete poGeom;
2500                 return OGRERR_CORRUPT_DATA;
2501             }
2502 
2503             nNextByte += 4;
2504 
2505             if( nBytes - nNextByte < nTupleSize * 8 * nPointCount )
2506             {
2507                 delete poGeom;
2508                 return OGRERR_NOT_ENOUGH_DATA;
2509             }
2510 
2511             OGRLinearRing *poLR = new OGRLinearRing();
2512             poLR->setNumPoints( nPointCount );
2513 
2514             for( int iPoint = 0; iPoint < nPointCount; iPoint++ )
2515             {
2516                 double adfTuple[4] = { 0.0, 0.0, 0.0, 0.0 };
2517                 memcpy( adfTuple, pabyData + nNextByte, nTupleSize*8 );
2518                 nNextByte += nTupleSize * 8;
2519 
2520 #ifdef CPL_MSB
2521                 for( int iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ )
2522                     CPL_SWAP64PTR( adfTuple + iOrdinal );
2523 #endif
2524                 if( nTupleSize > 2 )
2525                     poLR->setPoint( iPoint, adfTuple[0],
2526                                     adfTuple[1], adfTuple[2] );
2527                 else
2528                     poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] );
2529             }
2530 
2531             poPoly->addRingDirectly( poLR );
2532         }
2533 
2534         if( pnBytesConsumed )
2535             *pnBytesConsumed = nNextByte;
2536     }
2537 
2538 /* -------------------------------------------------------------------- */
2539 /*      GeometryCollections of various kinds.                           */
2540 /* -------------------------------------------------------------------- */
2541     else if( nGType == 4       // MultiPoint
2542              || nGType == 5    // MultiLineString
2543              || nGType == 6    // MultiPolygon
2544              || nGType == 7 )  // MultiGeometry
2545     {
2546         if( nBytes < 8 )
2547             return OGRERR_NOT_ENOUGH_DATA;
2548 
2549         GInt32 nGeomCount = 0;
2550         memcpy( &nGeomCount, pabyData + 4, 4 );
2551         CPL_LSBPTR32( &nGeomCount );
2552 
2553         if( nGeomCount < 0 || nGeomCount > INT_MAX / 4 )
2554             return OGRERR_CORRUPT_DATA;
2555 
2556         // Each geometry takes at least 4 bytes.
2557         if( nBytes - 8 < 4 * nGeomCount )
2558             return OGRERR_NOT_ENOUGH_DATA;
2559 
2560         OGRGeometryCollection *poGC = nullptr;
2561         if( nGType == 4 )
2562             poGC = new OGRMultiPoint();
2563         else if( nGType == 5 )
2564             poGC = new OGRMultiLineString();
2565         else if( nGType == 6 )
2566             poGC = new OGRMultiPolygon();
2567         else if( nGType == 7 )
2568             poGC = new OGRGeometryCollection();
2569 
2570         int nBytesUsed = 8;
2571 
2572         for( int iGeom = 0; iGeom < nGeomCount; iGeom++ )
2573         {
2574             int nThisGeomSize = 0;
2575             OGRGeometry *poThisGeom = nullptr;
2576 
2577             const OGRErr eErr =
2578                 createFromFgfInternal(pabyData + nBytesUsed, poSR, &poThisGeom,
2579                                       nBytes - nBytesUsed, &nThisGeomSize,
2580                                       nRecLevel + 1);
2581             if( eErr != OGRERR_NONE )
2582             {
2583                 delete poGC;
2584                 return eErr;
2585             }
2586 
2587             nBytesUsed += nThisGeomSize;
2588             if( poThisGeom != nullptr )
2589             {
2590                 const OGRErr eErr2 = poGC->addGeometryDirectly( poThisGeom );
2591                 if( eErr2 != OGRERR_NONE )
2592                 {
2593                     delete poGC;
2594                     delete poThisGeom;
2595                     return eErr2;
2596                 }
2597             }
2598         }
2599 
2600         poGeom = poGC;
2601         if( pnBytesConsumed )
2602             *pnBytesConsumed = nBytesUsed;
2603     }
2604 
2605 /* -------------------------------------------------------------------- */
2606 /*      Currently unsupported geometry.                                 */
2607 /*                                                                      */
2608 /*      We need to add 10/11/12/13 curve types in some fashion.         */
2609 /* -------------------------------------------------------------------- */
2610     else
2611     {
2612         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
2613     }
2614 
2615 /* -------------------------------------------------------------------- */
2616 /*      Assign spatial reference system.                                */
2617 /* -------------------------------------------------------------------- */
2618     if( poGeom != nullptr && poSR )
2619         poGeom->assignSpatialReference( poSR );
2620     *ppoReturn = poGeom;
2621 
2622     return OGRERR_NONE;
2623 }
2624 
2625 /************************************************************************/
2626 /*                        OGR_G_CreateFromFgf()                         */
2627 /************************************************************************/
2628 
2629 /**
2630  * \brief Create a geometry object of the appropriate type from its FGF
2631  * (FDO Geometry Format) binary representation.
2632  *
2633  * See OGRGeometryFactory::createFromFgf() */
OGR_G_CreateFromFgf(const void * pabyData,OGRSpatialReferenceH hSRS,OGRGeometryH * phGeometry,int nBytes,int * pnBytesConsumed)2634 OGRErr CPL_DLL OGR_G_CreateFromFgf( const void* pabyData,
2635                                     OGRSpatialReferenceH hSRS,
2636                                     OGRGeometryH *phGeometry,
2637                                     int nBytes, int *pnBytesConsumed )
2638 
2639 {
2640     return OGRGeometryFactory::createFromFgf( pabyData,
2641                                               OGRSpatialReference::FromHandle(hSRS),
2642                                               reinterpret_cast<OGRGeometry **>(phGeometry),
2643                                               nBytes, pnBytesConsumed );
2644 }
2645 
2646 /************************************************************************/
2647 /*                SplitLineStringAtDateline()                           */
2648 /************************************************************************/
2649 
SplitLineStringAtDateline(OGRGeometryCollection * poMulti,const OGRLineString * poLS,double dfDateLineOffset,double dfXOffset)2650 static void SplitLineStringAtDateline(OGRGeometryCollection* poMulti,
2651                                       const OGRLineString* poLS,
2652                                       double dfDateLineOffset,
2653                                       double dfXOffset)
2654 {
2655     const double dfLeftBorderX = 180 - dfDateLineOffset;
2656     const double dfRightBorderX = -180 + dfDateLineOffset;
2657     const double dfDiffSpace = 360 - dfDateLineOffset;
2658 
2659     const bool bIs3D = poLS->getCoordinateDimension() == 3;
2660     OGRLineString* poNewLS = new OGRLineString();
2661     poMulti->addGeometryDirectly(poNewLS);
2662     for( int i = 0; i < poLS->getNumPoints(); i++ )
2663     {
2664         const double dfX = poLS->getX(i) + dfXOffset;
2665         if( i > 0 && fabs(dfX - (poLS->getX(i-1) + dfXOffset)) > dfDiffSpace )
2666         {
2667             double dfX1 = poLS->getX(i-1) + dfXOffset;
2668             double dfY1 = poLS->getY(i-1);
2669             double dfZ1 = poLS->getY(i-1);
2670             double dfX2 = poLS->getX(i) + dfXOffset;
2671             double dfY2 = poLS->getY(i);
2672             double dfZ2 = poLS->getY(i);
2673 
2674             if( dfX1 > -180 && dfX1 < dfRightBorderX && dfX2 == 180 &&
2675                 i+1 < poLS->getNumPoints() &&
2676                 poLS->getX(i+1) + dfXOffset > -180 && poLS->getX(i+1) + dfXOffset < dfRightBorderX )
2677             {
2678                 if( bIs3D )
2679                     poNewLS->addPoint(-180, poLS->getY(i), poLS->getZ(i));
2680                 else
2681                     poNewLS->addPoint(-180, poLS->getY(i));
2682 
2683                 i++;
2684 
2685                 if( bIs3D )
2686                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i),
2687                                       poLS->getZ(i));
2688                 else
2689                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i));
2690                 continue;
2691             }
2692             else if( dfX1 > dfLeftBorderX && dfX1 < 180 && dfX2 == -180 &&
2693                      i+1 < poLS->getNumPoints() &&
2694                      poLS->getX(i+1) + dfXOffset > dfLeftBorderX && poLS->getX(i+1) + dfXOffset < 180 )
2695             {
2696                 if( bIs3D )
2697                     poNewLS->addPoint(180, poLS->getY(i), poLS->getZ(i));
2698                 else
2699                     poNewLS->addPoint(180, poLS->getY(i));
2700 
2701                 i++;
2702 
2703                 if( bIs3D )
2704                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i),
2705                                       poLS->getZ(i));
2706                 else
2707                     poNewLS->addPoint(poLS->getX(i) + dfXOffset, poLS->getY(i));
2708                 continue;
2709             }
2710 
2711             if( dfX1 < dfRightBorderX && dfX2 > dfLeftBorderX )
2712             {
2713                 std::swap(dfX1, dfX2);
2714                 std::swap(dfY1, dfY2);
2715                 std::swap(dfZ1, dfZ2);
2716             }
2717             if( dfX1 > dfLeftBorderX && dfX2 < dfRightBorderX )
2718                 dfX2 += 360;
2719 
2720             if( dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2 )
2721             {
2722                 const double dfRatio = (180 - dfX1) / (dfX2 - dfX1);
2723                 const double dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1;
2724                 const double dfZ = dfRatio * dfZ2 + (1 - dfRatio) * dfZ1;
2725                 if( bIs3D )
2726                     poNewLS->addPoint(
2727                         poLS->getX(i-1) + dfXOffset > dfLeftBorderX ? 180 : -180, dfY, dfZ);
2728                 else
2729                     poNewLS->addPoint(
2730                         poLS->getX(i-1) + dfXOffset > dfLeftBorderX ? 180 : -180, dfY);
2731                 poNewLS = new OGRLineString();
2732                 if( bIs3D )
2733                     poNewLS->addPoint(
2734                         poLS->getX(i-1) + dfXOffset > dfLeftBorderX ? -180 : 180, dfY, dfZ);
2735                 else
2736                     poNewLS->addPoint(
2737                         poLS->getX(i-1) + dfXOffset > dfLeftBorderX ? -180 : 180, dfY);
2738                 poMulti->addGeometryDirectly(poNewLS);
2739             }
2740             else
2741             {
2742                 poNewLS = new OGRLineString();
2743                 poMulti->addGeometryDirectly(poNewLS);
2744             }
2745         }
2746         if( bIs3D )
2747             poNewLS->addPoint(dfX, poLS->getY(i), poLS->getZ(i));
2748         else
2749             poNewLS->addPoint(dfX, poLS->getY(i));
2750     }
2751 }
2752 
2753 /************************************************************************/
2754 /*               FixPolygonCoordinatesAtDateLine()                      */
2755 /************************************************************************/
2756 
2757 #ifdef HAVE_GEOS
FixPolygonCoordinatesAtDateLine(OGRPolygon * poPoly,double dfDateLineOffset)2758 static void FixPolygonCoordinatesAtDateLine(OGRPolygon* poPoly,
2759                                             double dfDateLineOffset)
2760 {
2761     const double dfLeftBorderX = 180 - dfDateLineOffset;
2762     const double dfRightBorderX = -180 + dfDateLineOffset;
2763     const double dfDiffSpace = 360 - dfDateLineOffset;
2764 
2765     for( int iPart = 0; iPart < 1 + poPoly->getNumInteriorRings(); iPart++)
2766     {
2767         OGRLineString* poLS = (iPart == 0) ? poPoly->getExteriorRing() :
2768                                              poPoly->getInteriorRing(iPart-1);
2769         bool bGoEast = false;
2770         const bool bIs3D = poLS->getCoordinateDimension() == 3;
2771         for( int i = 1; i < poLS->getNumPoints(); i++ )
2772         {
2773             double dfX = poLS->getX(i);
2774             const double dfPrevX = poLS->getX(i-1);
2775             const double dfDiffLong = fabs(dfX - dfPrevX);
2776             if( dfDiffLong > dfDiffSpace )
2777             {
2778                 if( (dfPrevX > dfLeftBorderX && dfX < dfRightBorderX) ||
2779                     (dfX < 0 && bGoEast) )
2780                 {
2781                     dfX += 360;
2782                     bGoEast = true;
2783                     if( bIs3D )
2784                         poLS->setPoint(i, dfX, poLS->getY(i), poLS->getZ(i));
2785                     else
2786                         poLS->setPoint(i, dfX, poLS->getY(i));
2787                 }
2788                 else if( dfPrevX < dfRightBorderX && dfX > dfLeftBorderX )
2789                 {
2790                     for( int j = i - 1; j >= 0; j-- )
2791                     {
2792                         dfX = poLS->getX(j);
2793                         if( dfX < 0 )
2794                         {
2795                             if( bIs3D )
2796                                 poLS->setPoint(j, dfX + 360, poLS->getY(j),
2797                                                poLS->getZ(j));
2798                             else
2799                                 poLS->setPoint(j, dfX + 360, poLS->getY(j));
2800                         }
2801                     }
2802                     bGoEast = false;
2803                 }
2804                 else
2805                 {
2806                     bGoEast = false;
2807                 }
2808             }
2809         }
2810     }
2811 }
2812 #endif
2813 
2814 /************************************************************************/
2815 /*                            AddOffsetToLon()                          */
2816 /************************************************************************/
2817 
AddOffsetToLon(OGRGeometry * poGeom,double dfOffset)2818 static void AddOffsetToLon( OGRGeometry* poGeom, double dfOffset )
2819 {
2820     switch( wkbFlatten(poGeom->getGeometryType()) )
2821     {
2822         case wkbPolygon:
2823         case wkbMultiLineString:
2824         case wkbMultiPolygon:
2825         case wkbGeometryCollection:
2826         {
2827             const int nSubGeomCount =
2828                 OGR_G_GetGeometryCount(reinterpret_cast<OGRGeometryH>(poGeom));
2829             for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ )
2830             {
2831                 AddOffsetToLon(
2832                     reinterpret_cast<OGRGeometry*>(
2833                         OGR_G_GetGeometryRef(
2834                             reinterpret_cast<OGRGeometryH>(poGeom),
2835                             iGeom)),
2836                     dfOffset);
2837             }
2838 
2839             break;
2840         }
2841 
2842         case wkbLineString:
2843         {
2844             OGRLineString* poLineString = poGeom->toLineString();
2845             const int nPointCount = poLineString->getNumPoints();
2846             const int nCoordDim = poLineString->getCoordinateDimension();
2847             for( int iPoint = 0; iPoint < nPointCount; iPoint++)
2848             {
2849                 if( nCoordDim == 2 )
2850                     poLineString->setPoint(iPoint,
2851                                      poLineString->getX(iPoint) + dfOffset,
2852                                      poLineString->getY(iPoint));
2853                 else
2854                     poLineString->setPoint(iPoint,
2855                                      poLineString->getX(iPoint) + dfOffset,
2856                                      poLineString->getY(iPoint),
2857                                      poLineString->getZ(iPoint));
2858             }
2859             break;
2860         }
2861 
2862         default:
2863             break;
2864     }
2865 }
2866 
2867 /************************************************************************/
2868 /*                        AddSimpleGeomToMulti()                        */
2869 /************************************************************************/
2870 
2871 #ifdef HAVE_GEOS
AddSimpleGeomToMulti(OGRGeometryCollection * poMulti,const OGRGeometry * poGeom)2872 static void AddSimpleGeomToMulti( OGRGeometryCollection* poMulti,
2873                                   const OGRGeometry* poGeom )
2874 {
2875     switch( wkbFlatten(poGeom->getGeometryType()) )
2876     {
2877         case wkbPolygon:
2878         case wkbLineString:
2879             poMulti->addGeometry(poGeom);
2880             break;
2881 
2882         case wkbMultiLineString:
2883         case wkbMultiPolygon:
2884         case wkbGeometryCollection:
2885         {
2886             // TODO(schwehr): Can the const_casts be removed or improved?
2887             const int nSubGeomCount =
2888                 OGR_G_GetGeometryCount(reinterpret_cast<OGRGeometryH>(
2889                     const_cast<OGRGeometry *>(poGeom)));
2890             for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ )
2891             {
2892                 OGRGeometry* poSubGeom =
2893                     reinterpret_cast<OGRGeometry *>(
2894                         OGR_G_GetGeometryRef(
2895                             reinterpret_cast<OGRGeometryH>(
2896                                 const_cast<OGRGeometry *>(poGeom)),
2897                             iGeom));
2898                 AddSimpleGeomToMulti(poMulti, poSubGeom);
2899             }
2900             break;
2901         }
2902 
2903         default:
2904             break;
2905     }
2906 }
2907 #endif // #ifdef HAVE_GEOS
2908 
2909 /************************************************************************/
2910 /*                 CutGeometryOnDateLineAndAddToMulti()                 */
2911 /************************************************************************/
2912 
CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection * poMulti,const OGRGeometry * poGeom,double dfDateLineOffset)2913 static void CutGeometryOnDateLineAndAddToMulti( OGRGeometryCollection* poMulti,
2914                                                 const OGRGeometry* poGeom,
2915                                                 double dfDateLineOffset )
2916 {
2917     const OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
2918     switch( eGeomType )
2919     {
2920         case wkbPolygon:
2921         case wkbLineString:
2922         {
2923             bool bSplitLineStringAtDateline = false;
2924             OGREnvelope oEnvelope;
2925 
2926             poGeom->getEnvelope(&oEnvelope);
2927             const bool bAroundMinus180 = (oEnvelope.MinX < -180.0);
2928 
2929             // Naive heuristics... Place to improve.
2930 #ifdef HAVE_GEOS
2931             OGRGeometry* poDupGeom = nullptr;
2932             bool bWrapDateline = false;
2933 #endif
2934 
2935             const double dfLeftBorderX = 180 - dfDateLineOffset;
2936             const double dfRightBorderX = -180 + dfDateLineOffset;
2937             const double dfDiffSpace = 360 - dfDateLineOffset;
2938 
2939             const double dfXOffset = (bAroundMinus180) ? 360.0 : 0.0;
2940             if( oEnvelope.MinX < -180 ||
2941                 oEnvelope.MaxX > 180 ||
2942                 (oEnvelope.MinX + dfXOffset > dfLeftBorderX &&
2943                  oEnvelope.MaxX + dfXOffset > 180) )
2944             {
2945 #ifndef HAVE_GEOS
2946                 CPLError( CE_Failure, CPLE_NotSupported,
2947                         "GEOS support not enabled." );
2948 #else
2949                 bWrapDateline = true;
2950 #endif
2951             }
2952             else
2953             {
2954                 auto poLS = eGeomType == wkbPolygon
2955                     ? poGeom->toPolygon()->getExteriorRing()
2956                     : poGeom->toLineString();
2957                 if( poLS )
2958                 {
2959                     double dfMaxSmallDiffLong = 0;
2960                     bool bHasBigDiff = false;
2961                     bool bOnlyAtPlusMinus180 = poLS->getNumPoints() > 0 &&
2962                         ( fabs(fabs(poLS->getX(0)) - 180) < 1e-10 );
2963                     // Detect big gaps in longitude.
2964                     for( int i = 1; i < poLS->getNumPoints(); i++ )
2965                     {
2966                         const double dfPrevX = poLS->getX(i-1) + dfXOffset;
2967                         const double dfX = poLS->getX(i) + dfXOffset;
2968                         const double dfDiffLong = fabs(dfX - dfPrevX);
2969                         if( fabs(fabs(poLS->getX(i)) - 180) > 1e-10 )
2970                             bOnlyAtPlusMinus180 = false;
2971 
2972                         if( dfDiffLong > dfDiffSpace &&
2973                             ((dfX > dfLeftBorderX &&
2974                               dfPrevX < dfRightBorderX) ||
2975                              (dfPrevX > dfLeftBorderX &&
2976                               dfX < dfRightBorderX)) )
2977                             bHasBigDiff = true;
2978                         else if( dfDiffLong > dfMaxSmallDiffLong )
2979                             dfMaxSmallDiffLong = dfDiffLong;
2980                     }
2981                     if( bHasBigDiff && !bOnlyAtPlusMinus180 &&
2982                         dfMaxSmallDiffLong < dfDateLineOffset )
2983                     {
2984                         if( eGeomType == wkbLineString )
2985                             bSplitLineStringAtDateline = true;
2986                         else
2987                         {
2988 #ifndef HAVE_GEOS
2989                             CPLError( CE_Failure, CPLE_NotSupported,
2990                                     "GEOS support not enabled." );
2991 #else
2992                             bWrapDateline = true;
2993                             poDupGeom = poGeom->clone();
2994                             FixPolygonCoordinatesAtDateLine(
2995                                 poDupGeom->toPolygon(), dfDateLineOffset);
2996 #endif
2997                         }
2998                     }
2999                 }
3000             }
3001 
3002             if( bSplitLineStringAtDateline )
3003             {
3004                 SplitLineStringAtDateline(poMulti, poGeom->toLineString(),
3005                                           dfDateLineOffset,
3006                                           ( bAroundMinus180 ) ? 360.0 : 0.0 );
3007             }
3008 #ifdef HAVE_GEOS
3009             else if( bWrapDateline )
3010             {
3011                 const OGRGeometry* poWorkGeom =
3012                     poDupGeom ? poDupGeom : poGeom;
3013                 OGRGeometry* poRectangle1 = nullptr;
3014                 OGRGeometry* poRectangle2 = nullptr;
3015                 const char* pszWKT1 = !bAroundMinus180 ?
3016                     "POLYGON((-180 90,180 90,180 -90,-180 -90,-180 90))" :
3017                     "POLYGON((180 90,-180 90,-180 -90,180 -90,180 90))";
3018                 const char* pszWKT2 = !bAroundMinus180 ?
3019                     "POLYGON((180 90,360 90,360 -90,180 -90,180 90))" :
3020                     "POLYGON((-180 90,-360 90,-360 -90,-180 -90,-180 90))";
3021                 OGRGeometryFactory::createFromWkt(pszWKT1, nullptr,
3022                                                   &poRectangle1);
3023                 OGRGeometryFactory::createFromWkt(pszWKT2, nullptr,
3024                                                   &poRectangle2);
3025                 OGRGeometry* poGeom1 = poWorkGeom->Intersection(poRectangle1);
3026                 OGRGeometry* poGeom2 = poWorkGeom->Intersection(poRectangle2);
3027                 delete poRectangle1;
3028                 delete poRectangle2;
3029 
3030                 if( poGeom1 != nullptr && poGeom2 != nullptr )
3031                 {
3032                     AddSimpleGeomToMulti(poMulti, poGeom1);
3033                     AddOffsetToLon(poGeom2, !bAroundMinus180 ? -360.0 : 360.0);
3034                     AddSimpleGeomToMulti(poMulti, poGeom2);
3035                 }
3036                 else
3037                 {
3038                     AddSimpleGeomToMulti(poMulti, poGeom);
3039                 }
3040 
3041                 delete poGeom1;
3042                 delete poGeom2;
3043                 delete poDupGeom;
3044             }
3045 #endif
3046             else
3047             {
3048                 poMulti->addGeometry(poGeom);
3049             }
3050             break;
3051         }
3052 
3053         case wkbMultiLineString:
3054         case wkbMultiPolygon:
3055         case wkbGeometryCollection:
3056         {
3057             // TODO(schwehr): Fix the const_cast.
3058             int nSubGeomCount =
3059               OGR_G_GetGeometryCount(reinterpret_cast<OGRGeometryH>(
3060                   const_cast<OGRGeometry *>(poGeom)));
3061             for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ )
3062             {
3063                 OGRGeometry* poSubGeom =
3064                     reinterpret_cast<OGRGeometry *>(OGR_G_GetGeometryRef(
3065                         reinterpret_cast<OGRGeometryH>(
3066                             const_cast<OGRGeometry *>(poGeom)),
3067                         iGeom));
3068                 CutGeometryOnDateLineAndAddToMulti(poMulti, poSubGeom,
3069                                                    dfDateLineOffset);
3070             }
3071             break;
3072         }
3073 
3074         default:
3075             break;
3076     }
3077 }
3078 
3079 #ifdef HAVE_GEOS
3080 
3081 /************************************************************************/
3082 /*                             RemovePoint()                            */
3083 /************************************************************************/
3084 
RemovePoint(OGRGeometry * poGeom,OGRPoint * poPoint)3085 static void RemovePoint(OGRGeometry* poGeom, OGRPoint* poPoint)
3086 {
3087     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
3088     switch( eType )
3089     {
3090         case wkbLineString:
3091         {
3092             OGRLineString* poLS = poGeom->toLineString();
3093             const bool bIs3D = ( poLS->getCoordinateDimension() == 3 );
3094             int j = 0;
3095             for( int i = 0; i < poLS->getNumPoints(); i++ )
3096             {
3097                 if( poLS->getX(i) != poPoint->getX() ||
3098                     poLS->getY(i) != poPoint->getY() )
3099                 {
3100                     if( i > j )
3101                     {
3102                         if( bIs3D )
3103                         {
3104                             poLS->setPoint( j, poLS->getX(i), poLS->getY(i),
3105                                             poLS->getZ(i) );
3106                         }
3107                         else
3108                         {
3109                             poLS->setPoint( j, poLS->getX(i), poLS->getY(i) );
3110                         }
3111                     }
3112                     j++;
3113                 }
3114             }
3115             poLS->setNumPoints(j);
3116             break;
3117         }
3118 
3119         case wkbPolygon:
3120         {
3121             OGRPolygon* poPoly = poGeom->toPolygon();
3122             if( poPoly->getExteriorRing() != nullptr )
3123             {
3124                 RemovePoint(poPoly->getExteriorRing(), poPoint);
3125                 for( int i=0; i<poPoly->getNumInteriorRings(); ++i )
3126                 {
3127                     RemovePoint(poPoly->getInteriorRing(i), poPoint);
3128                 }
3129             }
3130             break;
3131         }
3132 
3133         case wkbMultiLineString:
3134         case wkbMultiPolygon:
3135         case wkbGeometryCollection:
3136         {
3137             OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
3138             for( int i=0; i<poGC->getNumGeometries(); ++i )
3139             {
3140                 RemovePoint(poGC->getGeometryRef(i), poPoint);
3141             }
3142             break;
3143         }
3144 
3145         default:
3146             break;
3147     }
3148 }
3149 
3150 /************************************************************************/
3151 /*                              GetDist()                               */
3152 /************************************************************************/
3153 
GetDist(double dfDeltaX,double dfDeltaY)3154 static double GetDist(double dfDeltaX, double dfDeltaY)
3155 {
3156     return sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
3157 }
3158 
3159 /************************************************************************/
3160 /*                             AlterPole()                              */
3161 /*                                                                      */
3162 /* Replace and point at the pole by points really close to the pole,    */
3163 /* but on the previous and later segments.                              */
3164 /************************************************************************/
3165 
AlterPole(OGRGeometry * poGeom,OGRPoint * poPole,bool bIsRing=false)3166 static void AlterPole(OGRGeometry* poGeom, OGRPoint* poPole,
3167                       bool bIsRing = false)
3168 {
3169     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
3170     switch( eType )
3171     {
3172         case wkbLineString:
3173         {
3174             if( !bIsRing )
3175                 return;
3176             OGRLineString* poLS = poGeom->toLineString();
3177             const int nNumPoints = poLS->getNumPoints();
3178             if( nNumPoints >= 4 )
3179             {
3180                 const bool bIs3D = ( poLS->getCoordinateDimension() == 3 );
3181                 std::vector<OGRRawPoint> aoPoints;
3182                 std::vector<double> adfZ;
3183                 bool bMustClose = false;
3184                 for( int i = 0; i < nNumPoints; i++ )
3185                 {
3186                     const double dfX = poLS->getX(i);
3187                     const double dfY = poLS->getY(i);
3188                     if( dfX == poPole->getX() && dfY == poPole->getY() )
3189                     {
3190                         // Replace the pole by points really close to it
3191                         if( i == 0 )
3192                             bMustClose = true;
3193                         if( i == nNumPoints - 1 )
3194                             continue;
3195                         const int iBefore = i > 0 ? i - 1: nNumPoints - 2;
3196                         double dfXBefore = poLS->getX(iBefore);
3197                         double dfYBefore = poLS->getY(iBefore);
3198                         double dfNorm = GetDist(dfXBefore - dfX,
3199                                                 dfYBefore - dfY);
3200                         double dfXInterp =
3201                             dfX + (dfXBefore - dfX) / dfNorm * 1.0e-7;
3202                         double dfYInterp =
3203                             dfY + (dfYBefore - dfY) / dfNorm * 1.0e-7;
3204                         OGRRawPoint oPoint;
3205                         oPoint.x = dfXInterp;
3206                         oPoint.y = dfYInterp;
3207                         aoPoints.push_back(oPoint);
3208                         adfZ.push_back(poLS->getZ(i));
3209 
3210                         const int iAfter = i+1;
3211                         double dfXAfter = poLS->getX(iAfter);
3212                         double dfYAfter = poLS->getY(iAfter);
3213                         dfNorm = GetDist(dfXAfter - dfX, dfYAfter - dfY);
3214                         dfXInterp = dfX + (dfXAfter - dfX) / dfNorm * 1e-7;
3215                         dfYInterp = dfY + (dfYAfter - dfY) / dfNorm * 1e-7;
3216                         oPoint.x = dfXInterp;
3217                         oPoint.y = dfYInterp;
3218                         aoPoints.push_back(oPoint);
3219                         adfZ.push_back(poLS->getZ(i));
3220                     }
3221                     else
3222                     {
3223                         OGRRawPoint oPoint;
3224                         oPoint.x = dfX;
3225                         oPoint.y = dfY;
3226                         aoPoints.push_back(oPoint);
3227                         adfZ.push_back(poLS->getZ(i));
3228                     }
3229                 }
3230                 if( bMustClose )
3231                 {
3232                     aoPoints.push_back(aoPoints[0]);
3233                     adfZ.push_back(adfZ[0]);
3234                 }
3235 
3236                 poLS->setPoints(static_cast<int>(aoPoints.size()),
3237                                 &(aoPoints[0]),
3238                                 bIs3D ? &adfZ[0] : nullptr);
3239             }
3240             break;
3241         }
3242 
3243         case wkbPolygon:
3244         {
3245             OGRPolygon* poPoly = poGeom->toPolygon();
3246             if( poPoly->getExteriorRing() != nullptr )
3247             {
3248                 AlterPole(poPoly->getExteriorRing(), poPole, true);
3249                 for( int i=0; i<poPoly->getNumInteriorRings(); ++i )
3250                 {
3251                     AlterPole(poPoly->getInteriorRing(i), poPole, true);
3252                 }
3253             }
3254             break;
3255         }
3256 
3257         case wkbMultiLineString:
3258         case wkbMultiPolygon:
3259         case wkbGeometryCollection:
3260         {
3261             OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
3262             for( int i=0; i<poGC->getNumGeometries(); ++i )
3263             {
3264                 AlterPole(poGC->getGeometryRef(i), poPole);
3265             }
3266             break;
3267         }
3268 
3269         default:
3270             break;
3271     }
3272 }
3273 
3274 /************************************************************************/
3275 /*                          IsPolarToWGS84()                            */
3276 /*                                                                      */
3277 /* Returns true if poCT transforms from a projection that includes one  */
3278 /* of the pole in a continuous way.                                     */
3279 /************************************************************************/
3280 
IsPolarToWGS84(OGRCoordinateTransformation * poCT,OGRCoordinateTransformation * poRevCT,bool & bIsNorthPolarOut)3281 static bool IsPolarToWGS84( OGRCoordinateTransformation* poCT,
3282                             OGRCoordinateTransformation* poRevCT,
3283                             bool& bIsNorthPolarOut )
3284 {
3285     bool bIsNorthPolar = false;
3286     bool bIsSouthPolar = false;
3287     double x = 0.0;
3288     double y = 90.0;
3289 
3290     const bool bBackupEmitErrors = poCT->GetEmitErrors();
3291     poRevCT->SetEmitErrors(false);
3292     poCT->SetEmitErrors(false);
3293 
3294     if( poRevCT->Transform( 1, &x, &y ) &&
3295         // Surprisingly, pole south projects correctly back &
3296         // forth for antarctic polar stereographic.  Therefore, check that
3297         // the projected value is not too big.
3298         fabs(x) < 1e10 && fabs(y) < 1e10 )
3299     {
3300         double x_tab[] = {x, x - 1e5, x + 1e5};
3301         double y_tab[] = {y, y - 1e5, y + 1e5};
3302         if( poCT->Transform(3, x_tab, y_tab) &&
3303             fabs(y_tab[0] - (90.0)) < 1e-10 &&
3304             fabs(x_tab[2] - x_tab[1]) > 170 &&
3305             fabs(y_tab[2] - y_tab[1]) < 1e-10 )
3306         {
3307             bIsNorthPolar = true;
3308         }
3309     }
3310 
3311     x = 0.0;
3312     y = -90.0;
3313     if( poRevCT->Transform( 1, &x, &y ) &&
3314         fabs(x) < 1e10 && fabs(y) < 1e10 )
3315     {
3316         double x_tab[] = {x, x - 1e5, x + 1e5};
3317         double y_tab[] = {y, y - 1e5, y + 1e5};
3318         if( poCT->Transform(3, x_tab, y_tab) &&
3319             fabs(y_tab[0] - (-90.0)) < 1e-10 &&
3320             fabs(x_tab[2] - x_tab[1]) > 170 &&
3321             fabs(y_tab[2] - y_tab[1]) < 1e-10 )
3322         {
3323             bIsSouthPolar = true;
3324         }
3325     }
3326 
3327     poCT->SetEmitErrors(bBackupEmitErrors);
3328 
3329     if( bIsNorthPolar && bIsSouthPolar )
3330     {
3331         bIsNorthPolar = false;
3332         bIsSouthPolar = false;
3333     }
3334 
3335     bIsNorthPolarOut = bIsNorthPolar;
3336     return bIsNorthPolar || bIsSouthPolar;
3337 }
3338 
3339 /************************************************************************/
3340 /*                     TransformBeforePolarToWGS84()                    */
3341 /*                                                                      */
3342 /* Transform the geometry (by intersection), so as to cut each geometry */
3343 /* that crosses the pole, in 2 parts. Do also tricks for geometries     */
3344 /* that just touch the pole.                                            */
3345 /************************************************************************/
3346 
TransformBeforePolarToWGS84(OGRCoordinateTransformation * poRevCT,bool bIsNorthPolar,OGRGeometry * poDstGeom,bool & bNeedPostCorrectionOut)3347 static OGRGeometry* TransformBeforePolarToWGS84(
3348                                         OGRCoordinateTransformation* poRevCT,
3349                                         bool bIsNorthPolar,
3350                                         OGRGeometry* poDstGeom,
3351                                         bool& bNeedPostCorrectionOut )
3352 {
3353     const int nSign = (bIsNorthPolar) ? 1 : -1;
3354 
3355     // Does the geometry fully contains the pole ? */
3356     double dfXPole = 0.0;
3357     double dfYPole = nSign * 90.0;
3358     poRevCT->Transform( 1, &dfXPole, &dfYPole );
3359     OGRPoint oPole(dfXPole, dfYPole);
3360     const bool bContainsPole =
3361                 CPL_TO_BOOL(poDstGeom->Contains(&oPole));
3362 
3363     const double EPS = 1e-9;
3364 
3365     // Does the geometry touches the pole and intersects the antimeridian ?
3366     double dfNearPoleAntiMeridianX = 180.0;
3367     double dfNearPoleAntiMeridianY = nSign*(90.0 - EPS);
3368     poRevCT->Transform( 1,
3369                         &dfNearPoleAntiMeridianX,
3370                         &dfNearPoleAntiMeridianY );
3371     OGRPoint oNearPoleAntimeridian(dfNearPoleAntiMeridianX,
3372                                     dfNearPoleAntiMeridianY);
3373     const bool bContainsNearPoleAntimeridian = CPL_TO_BOOL(
3374         poDstGeom->Contains(&oNearPoleAntimeridian));
3375 
3376     // Does the geometry touches the pole (but not intersect the antimeridian) ?
3377     const bool bRegularTouchesPole =
3378         !bContainsPole &&
3379         !bContainsNearPoleAntimeridian &&
3380         CPL_TO_BOOL(poDstGeom->Touches(&oPole));
3381 
3382     // Create a polygon of nearly a full hemisphere, but excluding the anti
3383     // meridian and the pole.
3384     OGRPolygon oCutter;
3385     OGRLinearRing* poRing = new OGRLinearRing();
3386     poRing->addPoint(180.0 - EPS, 0);
3387     poRing->addPoint(180.0 - EPS, nSign*(90.0 - EPS));
3388     // If the geometry doesn't contain the pole, then we add it to the cutter
3389     // geometry, but will later remove it completely (geometry touching the
3390     // pole but intersecting the antimeridian), or will replace it by 2
3391     // close points (geometry touching the pole without intersecting the
3392     // antimeridian)
3393     if( !bContainsPole )
3394         poRing->addPoint(180.0, nSign*90);
3395     poRing->addPoint(-180.0 + EPS, nSign*(90.0 - EPS));
3396     poRing->addPoint(-180.0 + EPS, 0);
3397     poRing->addPoint(180.0 - EPS, 0);
3398     oCutter.addRingDirectly(poRing);
3399 
3400     if( oCutter.transform(poRevCT) == OGRERR_NONE &&
3401         // Check that longitudes +/- 180 are continuous
3402         // in the polar projection
3403         fabs(poRing->getX(0) -
3404                 poRing->getX(poRing->getNumPoints()-2)) < 1 &&
3405         (bContainsPole || bContainsNearPoleAntimeridian ||
3406             bRegularTouchesPole) )
3407     {
3408         if( bContainsPole || bContainsNearPoleAntimeridian )
3409         {
3410             OGRGeometry* poNewGeom =
3411                             poDstGeom->Difference(&oCutter);
3412             if( poNewGeom )
3413             {
3414                 if( bContainsNearPoleAntimeridian )
3415                     RemovePoint(poNewGeom, &oPole);
3416                 delete poDstGeom;
3417                 poDstGeom = poNewGeom;
3418             }
3419         }
3420 
3421         if( bRegularTouchesPole )
3422         {
3423             AlterPole(poDstGeom, &oPole);
3424         }
3425 
3426         bNeedPostCorrectionOut = true;
3427     }
3428     return poDstGeom;
3429 }
3430 
3431 /************************************************************************/
3432 /*                        IsAntimeridianProjToWGS84()                   */
3433 /*                                                                      */
3434 /* Returns true if poCT transforms from a projection that includes the  */
3435 /* antimeridian in a continuous way.                                    */
3436 /************************************************************************/
3437 
IsAntimeridianProjToWGS84(OGRCoordinateTransformation * poCT,OGRCoordinateTransformation * poRevCT,OGRGeometry * poDstGeometry)3438 static bool IsAntimeridianProjToWGS84( OGRCoordinateTransformation* poCT,
3439                                        OGRCoordinateTransformation* poRevCT,
3440                                        OGRGeometry* poDstGeometry )
3441 {
3442     const bool bBackupEmitErrors = poCT->GetEmitErrors();
3443     poRevCT->SetEmitErrors(false);
3444     poCT->SetEmitErrors(false);
3445 
3446     // Find a reasonable latitude for the geometry
3447     OGREnvelope sEnvelope;
3448     poDstGeometry->getEnvelope(&sEnvelope);
3449     OGRPoint pMean( sEnvelope.MinX, (sEnvelope.MinY + sEnvelope.MaxY) / 2 );
3450     if( pMean.transform(poCT) != OGRERR_NONE )
3451     {
3452         poCT->SetEmitErrors(bBackupEmitErrors);
3453         return false;
3454     }
3455     const double dfMeanLat = pMean.getY();
3456 
3457     // Check that close points on each side of the antimeridian in (long, lat)
3458     // project to close points in the source projection, and check that they
3459     // roundtrip correctly.
3460     const double EPS = 1.0e-8;
3461     double x1 = 180 - EPS;
3462     double y1 = dfMeanLat;
3463     double x2 = -180 + EPS;
3464     double y2 = dfMeanLat;
3465     if( !poRevCT->Transform( 1, &x1, &y1 ) ||
3466         !poRevCT->Transform( 1, &x2, &y2 ) ||
3467         GetDist(x2-x1, y2-y1) > 1 ||
3468         !poCT->Transform( 1, &x1, &y1 ) ||
3469         !poCT->Transform( 1, &x2, &y2 ) ||
3470         GetDist(x1 - (180 - EPS), y1 - dfMeanLat) > 2 * EPS ||
3471         GetDist(x2 - (-180 + EPS), y2 - dfMeanLat) > 2 * EPS )
3472     {
3473         poCT->SetEmitErrors(bBackupEmitErrors);
3474         return false;
3475     }
3476 
3477     poCT->SetEmitErrors(bBackupEmitErrors);
3478 
3479     return true;
3480 }
3481 
3482 /************************************************************************/
3483 /*                      CollectPointsOnAntimeridian()                   */
3484 /*                                                                      */
3485 /* Collect points that are the intersection of the lines of the geometry*/
3486 /* with the antimeridian.                                               */
3487 /************************************************************************/
3488 
CollectPointsOnAntimeridian(OGRGeometry * poGeom,OGRCoordinateTransformation * poCT,OGRCoordinateTransformation * poRevCT,std::vector<OGRRawPoint> & aoPoints)3489 static void CollectPointsOnAntimeridian(OGRGeometry* poGeom,
3490                                         OGRCoordinateTransformation* poCT,
3491                                         OGRCoordinateTransformation* poRevCT,
3492                                         std::vector<OGRRawPoint>& aoPoints )
3493 {
3494     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
3495     switch( eType )
3496     {
3497         case wkbLineString:
3498         {
3499             OGRLineString* poLS = poGeom->toLineString();
3500             const int nNumPoints = poLS->getNumPoints();
3501             for( int i = 0; i < nNumPoints-1; i++ )
3502             {
3503                 const double dfX = poLS->getX(i);
3504                 const double dfY = poLS->getY(i);
3505                 const double dfX2 = poLS->getX(i+1);
3506                 const double dfY2 = poLS->getY(i+1);
3507                 double dfXTrans = dfX;
3508                 double dfYTrans = dfY;
3509                 double dfX2Trans = dfX2;
3510                 double dfY2Trans = dfY2;
3511                 poCT->Transform(1, &dfXTrans, &dfYTrans);
3512                 poCT->Transform(1, &dfX2Trans, &dfY2Trans);
3513                 // Are we crossing the antimeridian ? (detecting by inversion of
3514                 // sign of X)
3515                 if( (dfX2 - dfX) * (dfX2Trans - dfXTrans) < 0 )
3516                 {
3517                     double dfXStart = dfX;
3518                     double dfYStart = dfY;
3519                     double dfXEnd = dfX2;
3520                     double dfYEnd = dfY2;
3521                     double dfXStartTrans = dfXTrans;
3522                     double dfXEndTrans = dfX2Trans;
3523                     int iIter = 0;
3524                     const double EPS = 1e-8;
3525                     // Find point of the segment intersecting the antimeridian
3526                     // by dichotomy
3527                     for( ;
3528                          iIter < 50 &&
3529                            (fabs(fabs(dfXStartTrans) - 180) > EPS ||
3530                             fabs(fabs(dfXEndTrans) - 180) > EPS);
3531                          ++iIter )
3532                     {
3533                         double dfXMid = (dfXStart + dfXEnd) / 2;
3534                         double dfYMid = (dfYStart + dfYEnd) / 2;
3535                         double dfXMidTrans = dfXMid;
3536                         double dfYMidTrans = dfYMid;
3537                         poCT->Transform(1, &dfXMidTrans, &dfYMidTrans);
3538                         if( (dfXMid - dfXStart) *
3539                                         (dfXMidTrans - dfXStartTrans) < 0 )
3540                         {
3541                             dfXEnd = dfXMid;
3542                             dfYEnd = dfYMid;
3543                             dfXEndTrans = dfXMidTrans;
3544                         }
3545                         else
3546                         {
3547                             dfXStart = dfXMid;
3548                             dfYStart = dfYMid;
3549                             dfXStartTrans = dfXMidTrans;
3550                         }
3551                     }
3552                     if( iIter < 50 )
3553                     {
3554                         OGRRawPoint oPoint;
3555                         oPoint.x = (dfXStart + dfXEnd) / 2;
3556                         oPoint.y = (dfYStart + dfYEnd) / 2;
3557                         poCT->Transform(1, &(oPoint.x), &(oPoint.y));
3558                         oPoint.x = 180.0;
3559                         aoPoints.push_back(oPoint);
3560                     }
3561                 }
3562             }
3563             break;
3564         }
3565 
3566         case wkbPolygon:
3567         {
3568             OGRPolygon* poPoly = poGeom->toPolygon();
3569             if( poPoly->getExteriorRing() != nullptr )
3570             {
3571                 CollectPointsOnAntimeridian(poPoly->getExteriorRing(),
3572                                             poCT, poRevCT, aoPoints);
3573                 for( int i=0; i<poPoly->getNumInteriorRings(); ++i )
3574                 {
3575                     CollectPointsOnAntimeridian(poPoly->getInteriorRing(i),
3576                                                 poCT, poRevCT, aoPoints);
3577                 }
3578             }
3579             break;
3580         }
3581 
3582         case wkbMultiLineString:
3583         case wkbMultiPolygon:
3584         case wkbGeometryCollection:
3585         {
3586             OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
3587             for( int i=0; i<poGC->getNumGeometries(); ++i )
3588             {
3589                 CollectPointsOnAntimeridian(poGC->getGeometryRef(i),
3590                                             poCT, poRevCT, aoPoints);
3591             }
3592             break;
3593         }
3594 
3595         default:
3596             break;
3597     }
3598 }
3599 
3600 /************************************************************************/
3601 /*                         SortPointsByAscendingY()                     */
3602 /************************************************************************/
3603 
3604 struct SortPointsByAscendingY
3605 {
operator ()SortPointsByAscendingY3606     bool operator()(const OGRRawPoint& a, const OGRRawPoint& b)
3607     {
3608         return a.y < b.y;
3609     }
3610 };
3611 
3612 /************************************************************************/
3613 /*                  TransformBeforeAntimeridianToWGS84()                */
3614 /*                                                                      */
3615 /* Transform the geometry (by intersection), so as to cut each geometry */
3616 /* that crosses the antimeridian, in 2 parts.                           */
3617 /************************************************************************/
3618 
TransformBeforeAntimeridianToWGS84(OGRCoordinateTransformation * poCT,OGRCoordinateTransformation * poRevCT,OGRGeometry * poDstGeom,bool & bNeedPostCorrectionOut)3619 static OGRGeometry* TransformBeforeAntimeridianToWGS84(
3620                                         OGRCoordinateTransformation* poCT,
3621                                         OGRCoordinateTransformation* poRevCT,
3622                                         OGRGeometry* poDstGeom,
3623                                         bool& bNeedPostCorrectionOut )
3624 {
3625     OGREnvelope sEnvelope;
3626     poDstGeom->getEnvelope(&sEnvelope);
3627     OGRPoint pMean( sEnvelope.MinX, (sEnvelope.MinY + sEnvelope.MaxY) / 2 );
3628     pMean.transform(poCT);
3629     const double dfMeanLat = pMean.getY();
3630     pMean.setX( 180.0 );
3631     pMean.setY( dfMeanLat );
3632     pMean.transform(poRevCT);
3633     // Check if the antimeridian crosses the bbox of our geometry
3634     if( !(pMean.getX() >= sEnvelope.MinX && pMean.getY() >= sEnvelope.MinY &&
3635           pMean.getX() <= sEnvelope.MaxX && pMean.getY() <= sEnvelope.MaxY) )
3636     {
3637         return poDstGeom;
3638     }
3639 
3640     // Collect points that are the intersection of the lines of the geometry
3641     // with the antimeridian
3642     std::vector<OGRRawPoint> aoPoints;
3643     CollectPointsOnAntimeridian(poDstGeom, poCT, poRevCT, aoPoints);
3644     if( aoPoints.empty() )
3645         return poDstGeom;
3646 
3647     SortPointsByAscendingY sortFunc;
3648     std::sort( aoPoints.begin(), aoPoints.end(), sortFunc );
3649 
3650     const double EPS = 1e-9;
3651 
3652     // Build a very thin polygon cutting the antimeridian at our points
3653     OGRLinearRing* poLR = new OGRLinearRing;
3654     {
3655         double x = 180.0-EPS;
3656         double y = aoPoints[0].y-EPS;
3657         poRevCT->Transform(1, &x, &y);
3658         poLR->addPoint( x, y );
3659     }
3660     for( const auto& oPoint: aoPoints )
3661     {
3662         double x = 180.0-EPS;
3663         double y = oPoint.y;
3664         poRevCT->Transform(1, &x, &y);
3665         poLR->addPoint( x, y );
3666     }
3667     {
3668         double x = 180.0-EPS;
3669         double y = aoPoints.back().y+EPS;
3670         poRevCT->Transform(1, &x, &y);
3671         poLR->addPoint( x, y );
3672     }
3673     {
3674         double x = 180.0+EPS;
3675         double y = aoPoints.back().y+EPS;
3676         poRevCT->Transform(1, &x, &y);
3677         poLR->addPoint( x, y );
3678     }
3679     for( size_t i = aoPoints.size(); i > 0; )
3680     {
3681         --i;
3682         const OGRRawPoint& oPoint = aoPoints[i];
3683         double x = 180.0+EPS;
3684         double y = oPoint.y;
3685         poRevCT->Transform(1, &x, &y);
3686         poLR->addPoint( x, y );
3687     }
3688     {
3689         double x = 180.0+EPS;
3690         double y = aoPoints[0].y-EPS;
3691         poRevCT->Transform(1, &x, &y);
3692         poLR->addPoint( x, y );
3693     }
3694     poLR->closeRings();
3695 
3696     OGRPolygon oPolyToCut;
3697     oPolyToCut.addRingDirectly(poLR);
3698 
3699 #if DEBUG_VERBOSE
3700     char* pszWKT = NULL;
3701     oPolyToCut.exportToWkt(&pszWKT);
3702     CPLDebug("OGR", "Geometry to cut: %s", pszWKT);
3703     CPLFree(pszWKT);
3704 #endif
3705 
3706     // Get the geometry without the antimeridian
3707     OGRGeometry* poInter = poDstGeom->Difference(&oPolyToCut);
3708     if( poInter != nullptr )
3709     {
3710         delete poDstGeom;
3711         poDstGeom = poInter;
3712         bNeedPostCorrectionOut = true;
3713     }
3714 
3715     return poDstGeom;
3716 }
3717 
3718 /************************************************************************/
3719 /*                 SnapCoordsCloseToLatLongBounds()                     */
3720 /*                                                                      */
3721 /* This function snaps points really close to the antimerdian or poles  */
3722 /* to their exact longitudes/latitudes.                                 */
3723 /************************************************************************/
3724 
SnapCoordsCloseToLatLongBounds(OGRGeometry * poGeom)3725 static void SnapCoordsCloseToLatLongBounds(OGRGeometry* poGeom)
3726 {
3727     const OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
3728     switch( eType )
3729     {
3730         case wkbLineString:
3731         {
3732             OGRLineString* poLS = poGeom->toLineString();
3733             const double EPS = 1e-8;
3734             for( int i = 0; i < poLS->getNumPoints(); i++ )
3735             {
3736                 OGRPoint p;
3737                 poLS->getPoint(i, &p);
3738                 if( fabs( p.getX() - 180.0 ) < EPS )
3739                 {
3740                     p.setX(180.0);
3741                     poLS->setPoint(i, &p);
3742                 }
3743                 else if( fabs( p.getX() - -180.0 ) < EPS )
3744                 {
3745                     p.setX(-180.0);
3746                     poLS->setPoint(i, &p);
3747                 }
3748 
3749                 if( fabs( p.getY() - 90.0 ) < EPS )
3750                 {
3751                     p.setY(90.0);
3752                     poLS->setPoint(i, &p);
3753                 }
3754                 else if( fabs( p.getY() - -90.0 ) < EPS )
3755                 {
3756                     p.setY(-90.0);
3757                     poLS->setPoint(i, &p);
3758                 }
3759             }
3760             break;
3761         }
3762 
3763         case wkbPolygon:
3764         {
3765             OGRPolygon* poPoly = poGeom->toPolygon();
3766             if( poPoly->getExteriorRing() != nullptr )
3767             {
3768                 SnapCoordsCloseToLatLongBounds(poPoly->getExteriorRing());
3769                 for( int i=0; i<poPoly->getNumInteriorRings(); ++i )
3770                 {
3771                     SnapCoordsCloseToLatLongBounds(poPoly->getInteriorRing(i));
3772                 }
3773             }
3774             break;
3775         }
3776 
3777         case wkbMultiLineString:
3778         case wkbMultiPolygon:
3779         case wkbGeometryCollection:
3780         {
3781             OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
3782             for( int i=0; i<poGC->getNumGeometries(); ++i )
3783             {
3784                 SnapCoordsCloseToLatLongBounds(poGC->getGeometryRef(i));
3785             }
3786             break;
3787         }
3788 
3789         default:
3790             break;
3791     }
3792 }
3793 
3794 #endif
3795 
3796 /************************************************************************/
3797 /*                  TransformWithOptionsCache::Private                  */
3798 /************************************************************************/
3799 
3800 struct OGRGeometryFactory::TransformWithOptionsCache::Private
3801 {
3802     OGRCoordinateTransformation* poRevCT = nullptr;
3803     bool bIsPolar = false;
3804     bool bIsNorthPolar = false;
3805 
~PrivateOGRGeometryFactory::TransformWithOptionsCache::Private3806     ~Private()
3807     {
3808         delete poRevCT;
3809     }
3810 };
3811 
3812 /************************************************************************/
3813 /*                     TransformWithOptionsCache()                      */
3814 /************************************************************************/
3815 
TransformWithOptionsCache()3816 OGRGeometryFactory::TransformWithOptionsCache::TransformWithOptionsCache(): d(new Private())
3817 {
3818 }
3819 
3820 /************************************************************************/
3821 /*                     ~TransformWithOptionsCache()                      */
3822 /************************************************************************/
3823 
~TransformWithOptionsCache()3824 OGRGeometryFactory::TransformWithOptionsCache::~TransformWithOptionsCache()
3825 {
3826 }
3827 
3828 /************************************************************************/
3829 /*                       transformWithOptions()                         */
3830 /************************************************************************/
3831 
3832 /** Transform a geometry.
3833  * @param poSrcGeom source geometry
3834  * @param poCT coordinate transformation object, or NULL.
3835  * @param papszOptions options. Including WRAPDATELINE=YES and DATELINEOFFSET=.
3836  * @param cache Cache. May increase performance if persisted between invocations
3837  * @return (new) transformed geometry.
3838  */
transformWithOptions(const OGRGeometry * poSrcGeom,OGRCoordinateTransformation * poCT,char ** papszOptions,CPL_UNUSED const TransformWithOptionsCache & cache)3839 OGRGeometry* OGRGeometryFactory::transformWithOptions(
3840     const OGRGeometry* poSrcGeom,
3841     OGRCoordinateTransformation *poCT,
3842     char** papszOptions,
3843     CPL_UNUSED const TransformWithOptionsCache& cache )
3844 {
3845     OGRGeometry* poDstGeom = poSrcGeom->clone();
3846     if( poCT != nullptr )
3847     {
3848 #ifdef HAVE_GEOS
3849         bool bNeedPostCorrection = false;
3850 
3851         if( poCT->GetSourceCS() != nullptr &&
3852             poCT->GetTargetCS() != nullptr )
3853         {
3854             OGRSpatialReference oSRSWGS84;
3855             oSRSWGS84.SetWellKnownGeogCS( "WGS84" );
3856             oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3857             if( poCT->GetTargetCS()->IsSame(&oSRSWGS84) )
3858             {
3859                 if( cache.d->poRevCT == nullptr ||
3860                     !cache.d->poRevCT->GetTargetCS()->IsSame(poCT->GetSourceCS()) )
3861                 {
3862                     delete cache.d->poRevCT;
3863                     cache.d->poRevCT =
3864                         OGRCreateCoordinateTransformation( &oSRSWGS84,
3865                                                        poCT->GetSourceCS() );
3866                     cache.d->bIsNorthPolar = false;
3867                     cache.d->bIsPolar = false;
3868                     if( cache.d->poRevCT &&
3869                         IsPolarToWGS84(poCT, cache.d->poRevCT, cache.d->bIsNorthPolar) )
3870                     {
3871                         cache.d->bIsPolar = true;
3872                     }
3873                 }
3874                 auto poRevCT = cache.d->poRevCT;
3875                 if( poRevCT != nullptr )
3876                 {
3877                     if( cache.d->bIsPolar )
3878                     {
3879                         poDstGeom = TransformBeforePolarToWGS84(
3880                                         poRevCT, cache.d->bIsNorthPolar,
3881                                         poDstGeom,
3882                                         bNeedPostCorrection);
3883                     }
3884                     else if( IsAntimeridianProjToWGS84(poCT, poRevCT,
3885                                                        poDstGeom) )
3886                     {
3887                         poDstGeom = TransformBeforeAntimeridianToWGS84(
3888                                         poCT, poRevCT, poDstGeom,
3889                                         bNeedPostCorrection);
3890                     }
3891                 }
3892             }
3893         }
3894 #endif
3895         OGRErr eErr = poDstGeom->transform(poCT);
3896         if( eErr != OGRERR_NONE )
3897         {
3898             delete poDstGeom;
3899             return nullptr;
3900         }
3901 #ifdef HAVE_GEOS
3902         if( bNeedPostCorrection )
3903         {
3904             SnapCoordsCloseToLatLongBounds(poDstGeom);
3905         }
3906 #endif
3907     }
3908 
3909     if( CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "NO")) )
3910     {
3911         if( poDstGeom->getSpatialReference() &&
3912             !poDstGeom->getSpatialReference()->IsGeographic() )
3913         {
3914             static bool bHasWarned = false;
3915             if( !bHasWarned )
3916             {
3917                 CPLError(CE_Warning, CPLE_AppDefined,
3918                         "WRAPDATELINE is without effect when reprojecting to a "
3919                         "non-geographic CRS");
3920                 bHasWarned = true;
3921             }
3922             return poDstGeom;
3923         }
3924         // TODO and we should probably also test that the axis order + data axis mapping
3925         // is long-lat...
3926 
3927         const OGRwkbGeometryType eType =
3928             wkbFlatten(poDstGeom->getGeometryType());
3929         if( eType == wkbPoint )
3930         {
3931             OGRPoint* poDstPoint = poDstGeom->toPoint();
3932             if( poDstPoint->getX() > 180 )
3933             {
3934                 poDstPoint->setX(fmod(poDstPoint->getX() + 180, 360) - 180);
3935             }
3936             else if( poDstPoint->getX() < -180 )
3937             {
3938                 poDstPoint->setX(-(fmod(-poDstPoint->getX() + 180, 360) - 180));
3939             }
3940         }
3941         else
3942         {
3943             OGREnvelope sEnvelope;
3944             poDstGeom->getEnvelope(&sEnvelope);
3945             if( sEnvelope.MinX >= -360.0 && sEnvelope.MaxX <= -180.0 )
3946                 AddOffsetToLon( poDstGeom, 360.0 );
3947             else if( sEnvelope.MinX >= 180.0 && sEnvelope.MaxX <= 360.0 )
3948                 AddOffsetToLon( poDstGeom, -360.0 );
3949             else
3950             {
3951                 OGRwkbGeometryType eNewType;
3952                 if( eType == wkbPolygon || eType == wkbMultiPolygon )
3953                     eNewType = wkbMultiPolygon;
3954                 else if( eType == wkbLineString || eType == wkbMultiLineString )
3955                     eNewType = wkbMultiLineString;
3956                 else
3957                     eNewType = wkbGeometryCollection;
3958 
3959                 OGRGeometry* poMultiGeom = createGeometry(eNewType);
3960                 OGRGeometryCollection* poMulti = poMultiGeom->toGeometryCollection();
3961 
3962                 double dfDateLineOffset =
3963                     CPLAtofM(CSLFetchNameValueDef(papszOptions,
3964                                                 "DATELINEOFFSET", "10"));
3965                 if( dfDateLineOffset <= 0.0 || dfDateLineOffset >= 360.0 )
3966                     dfDateLineOffset = 10.0;
3967 
3968                 CutGeometryOnDateLineAndAddToMulti(poMulti, poDstGeom,
3969                                                 dfDateLineOffset);
3970 
3971                 if( poMulti->getNumGeometries() == 0 )
3972                 {
3973                     delete poMultiGeom;
3974                 }
3975                 else if( poMulti->getNumGeometries() == 1 )
3976                 {
3977                     delete poDstGeom;
3978                     poDstGeom = poMulti->getGeometryRef(0)->clone();
3979                     delete poMultiGeom;
3980                 }
3981                 else
3982                 {
3983                     delete poDstGeom;
3984                     poDstGeom = poMultiGeom;
3985                 }
3986             }
3987         }
3988     }
3989 
3990     return poDstGeom;
3991 }
3992 
3993 /************************************************************************/
3994 /*                         OGRGeomTransformer()                         */
3995 /************************************************************************/
3996 
3997 struct OGRGeomTransformer
3998 {
3999     std::unique_ptr<OGRCoordinateTransformation> poCT{};
4000     OGRGeometryFactory::TransformWithOptionsCache cache{};
4001     CPLStringList aosOptions{};
4002 
4003     OGRGeomTransformer() = default;
4004     OGRGeomTransformer(const OGRGeomTransformer&) = delete;
4005     OGRGeomTransformer& operator=(const OGRGeomTransformer&) = delete;
4006 };
4007 
4008 /************************************************************************/
4009 /*                     OGR_GeomTransformer_Create()                     */
4010 /************************************************************************/
4011 
4012 /** Create a geometry transformer.
4013  *
4014  * This is a enhanced version of OGR_G_Transform().
4015  *
4016  * When reprojecting geometries from a Polar Stereographic projection or a
4017  * projection naturally crossing the antimeridian (like UTM Zone 60) to a
4018  * geographic CRS, it will cut geometries along the antimeridian. So a
4019  * LineString might be returned as a MultiLineString.
4020  *
4021  * The WRAPDATELINE=YES option might be specified for circumstances to correct
4022  * geometries that incorrectly go from a longitude on a side of the antimeridian
4023  * to the other side, like a LINESTRING(-179 0,179 0) will be transformed to
4024  * a MULTILINESTRING ((-179 0,-180 0),(180 0,179 0)). For that use case, hCT
4025  * might be NULL.
4026  *
4027  * @param hCT Coordinate transformation object (will be cloned) or NULL.
4028  * @param papszOptions NULL terminated list of options, or NULL.
4029  *                     Supported options are:
4030  *                     <ul>
4031  *                         <li>WRAPDATELINE=YES</li>
4032  *                         <li>DATELINEOFFSET=longitude_gap_in_degree. Defaults to 10.</li>
4033  *                     </ul>
4034  * @return transformer object to free with OGR_GeomTransformer_Destroy()
4035  * @since GDAL 3.1
4036  */
OGR_GeomTransformer_Create(OGRCoordinateTransformationH hCT,CSLConstList papszOptions)4037 OGRGeomTransformerH OGR_GeomTransformer_Create( OGRCoordinateTransformationH hCT,
4038                                                 CSLConstList papszOptions )
4039 {
4040     OGRGeomTransformer* transformer = new OGRGeomTransformer;
4041     if( hCT )
4042     {
4043         transformer->poCT.reset(
4044             OGRCoordinateTransformation::FromHandle(hCT)->Clone());
4045     }
4046     transformer->aosOptions.Assign(CSLDuplicate(papszOptions));
4047     return transformer;
4048 }
4049 
4050 /************************************************************************/
4051 /*                     OGR_GeomTransformer_Transform()                  */
4052 /************************************************************************/
4053 
4054 /** Transforms a geometry.
4055  *
4056  * @param hTransformer transformer object.
4057  * @param hGeom Source geometry.
4058  * @return a new geometry (or NULL) to destroy with OGR_G_DestroyGeometry()
4059  * @since GDAL 3.1
4060  */
OGR_GeomTransformer_Transform(OGRGeomTransformerH hTransformer,OGRGeometryH hGeom)4061 OGRGeometryH OGR_GeomTransformer_Transform(OGRGeomTransformerH hTransformer,
4062                                            OGRGeometryH hGeom)
4063 {
4064     VALIDATE_POINTER1( hTransformer, "OGR_GeomTransformer_Transform", nullptr );
4065     VALIDATE_POINTER1( hGeom, "OGR_GeomTransformer_Transform", nullptr );
4066 
4067     return OGRGeometry::ToHandle(
4068         OGRGeometryFactory::transformWithOptions(
4069             OGRGeometry::FromHandle(hGeom),
4070             hTransformer->poCT.get(),
4071             hTransformer->aosOptions.List(),
4072             hTransformer->cache));
4073 }
4074 
4075 /************************************************************************/
4076 /*                      OGR_GeomTransformer_Destroy()                   */
4077 /************************************************************************/
4078 
4079 /** Destroy a geometry transformer allocated with OGR_GeomTransformer_Create()
4080  *
4081  * @param hTransformer transformer object.
4082  * @since GDAL 3.1
4083  */
OGR_GeomTransformer_Destroy(OGRGeomTransformerH hTransformer)4084 void OGR_GeomTransformer_Destroy(OGRGeomTransformerH hTransformer)
4085 {
4086     delete hTransformer;
4087 }
4088 
4089 /************************************************************************/
4090 /*                       OGRGF_GetDefaultStepSize()                     */
4091 /************************************************************************/
4092 
OGRGF_GetDefaultStepSize()4093 static double OGRGF_GetDefaultStepSize()
4094 {
4095     // coverity[tainted_data]
4096     return CPLAtofM(CPLGetConfigOption("OGR_ARC_STEPSIZE", "4"));
4097 }
4098 
4099 /************************************************************************/
4100 /*                              DISTANCE()                              */
4101 /************************************************************************/
4102 
DISTANCE(double x1,double y1,double x2,double y2)4103 static inline double DISTANCE(double x1, double y1, double x2, double y2)
4104 {
4105     return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
4106 }
4107 
4108 /************************************************************************/
4109 /*                        approximateArcAngles()                        */
4110 /************************************************************************/
4111 
4112 /**
4113  * Stroke arc to linestring.
4114  *
4115  * Stroke an arc of a circle to a linestring based on a center
4116  * point, radius, start angle and end angle, all angles in degrees.
4117  *
4118  * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
4119  * used.  This is currently 4 degrees unless the user has overridden the
4120  * value with the OGR_ARC_STEPSIZE configuration variable.
4121  *
4122  * If the OGR_ARC_MAX_GAP configuration variable is set, the straight-line
4123  * distance between adjacent pairs of interpolated points will be limited to
4124  * the specified distance. If the distance between a pair of points exceeds
4125  * this maximum, additional points are interpolated between the two points.
4126  *
4127  * @see CPLSetConfigOption()
4128  *
4129  * @param dfCenterX center X
4130  * @param dfCenterY center Y
4131  * @param dfZ center Z
4132  * @param dfPrimaryRadius X radius of ellipse.
4133  * @param dfSecondaryRadius Y radius of ellipse.
4134  * @param dfRotation rotation of the ellipse clockwise.
4135  * @param dfStartAngle angle to first point on arc (clockwise of X-positive)
4136  * @param dfEndAngle angle to last point on arc (clockwise of X-positive)
4137  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
4138  * arc, zero to use the default setting.
4139  * @param bUseMaxGap Optional: whether to honor OGR_ARC_MAX_GAP.
4140  *
4141  * @return OGRLineString geometry representing an approximation of the arc.
4142  *
4143  * @since OGR 1.8.0
4144  */
4145 
approximateArcAngles(double dfCenterX,double dfCenterY,double dfZ,double dfPrimaryRadius,double dfSecondaryRadius,double dfRotation,double dfStartAngle,double dfEndAngle,double dfMaxAngleStepSizeDegrees,const bool bUseMaxGap)4146 OGRGeometry* OGRGeometryFactory::approximateArcAngles(
4147     double dfCenterX, double dfCenterY, double dfZ,
4148     double dfPrimaryRadius, double dfSecondaryRadius, double dfRotation,
4149     double dfStartAngle, double dfEndAngle,
4150     double dfMaxAngleStepSizeDegrees, const bool bUseMaxGap /* = false */ )
4151 
4152 {
4153     OGRLineString *poLine = new OGRLineString();
4154     const double dfRotationRadians = dfRotation * M_PI / 180.0;
4155 
4156     // Support default arc step setting.
4157     if( dfMaxAngleStepSizeDegrees < 1e-6 )
4158     {
4159         dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize();
4160     }
4161 
4162     // Determine maximum interpolation gap. This is the largest straight-line
4163     // distance allowed between pairs of interpolated points. Default zero,
4164     // meaning no gap.
4165     // coverity[tainted_data]
4166     const double dfMaxInterpolationGap = bUseMaxGap ?
4167         CPLAtofM(CPLGetConfigOption("OGR_ARC_MAX_GAP", "0")) :
4168         0.0;
4169 
4170     // Is this a full circle?
4171     const bool bIsFullCircle = fabs( dfEndAngle - dfStartAngle ) == 360.0;
4172 
4173     // Switch direction.
4174     dfStartAngle *= -1;
4175     dfEndAngle *= -1;
4176 
4177     // Figure out the number of slices to make this into.
4178     int nVertexCount = std::max(2, static_cast<int>(
4179         ceil(fabs(dfEndAngle - dfStartAngle)/dfMaxAngleStepSizeDegrees) + 1));
4180     const double dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1);
4181 
4182     // If it is a full circle we will work out the last point separately.
4183     if( bIsFullCircle )
4184     {
4185         nVertexCount--;
4186     }
4187 
4188 /* -------------------------------------------------------------------- */
4189 /*      Compute the interpolated points.                                */
4190 /* -------------------------------------------------------------------- */
4191     double dfLastX = 0.0;
4192     double dfLastY = 0.0;
4193     int nTotalAddPoints = 0;
4194     for( int iPoint = 0; iPoint < nVertexCount; iPoint++ )
4195     {
4196         const double dfAngleOnEllipse =
4197             (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0;
4198 
4199         // Compute position on the unrotated ellipse.
4200         const double dfEllipseX = cos(dfAngleOnEllipse) * dfPrimaryRadius;
4201         const double dfEllipseY = sin(dfAngleOnEllipse) * dfSecondaryRadius;
4202 
4203         // Is this point too far from the previous point?
4204         if( iPoint && dfMaxInterpolationGap != 0.0 )
4205         {
4206             const double dfDistFromLast =
4207                 DISTANCE(dfLastX, dfLastY, dfEllipseX, dfEllipseY);
4208 
4209             if( dfDistFromLast > dfMaxInterpolationGap )
4210             {
4211                 const int nAddPoints =
4212                     static_cast<int>( dfDistFromLast / dfMaxInterpolationGap );
4213                 const double dfAddSlice = dfSlice / (nAddPoints + 1);
4214 
4215                 // Interpolate additional points
4216                 for( int iAddPoint = 0; iAddPoint < nAddPoints; iAddPoint++ )
4217                 {
4218                     const double dfAddAngleOnEllipse = (dfStartAngle +
4219                         (iPoint - 1) * dfSlice +
4220                         (iAddPoint + 1) * dfAddSlice) * (M_PI / 180.0);
4221 
4222                     poLine->setPoint( iPoint + nTotalAddPoints + iAddPoint,
4223                         cos(dfAddAngleOnEllipse) * dfPrimaryRadius,
4224                         sin(dfAddAngleOnEllipse) * dfSecondaryRadius,
4225                         dfZ );
4226                 }
4227 
4228                 nTotalAddPoints += nAddPoints;
4229             }
4230         }
4231 
4232         poLine->setPoint( iPoint + nTotalAddPoints,
4233             dfEllipseX, dfEllipseY, dfZ );
4234         dfLastX = dfEllipseX;
4235         dfLastY = dfEllipseY;
4236     }
4237 
4238 /* -------------------------------------------------------------------- */
4239 /*      Rotate and translate the ellipse.                               */
4240 /* -------------------------------------------------------------------- */
4241     nVertexCount = poLine->getNumPoints();
4242     for( int iPoint = 0; iPoint < nVertexCount; iPoint++ )
4243     {
4244         const double dfEllipseX = poLine->getX( iPoint );
4245         const double dfEllipseY = poLine->getY( iPoint );
4246 
4247         // Rotate this position around the center of the ellipse.
4248         const double dfArcX = dfCenterX
4249             + dfEllipseX * cos(dfRotationRadians)
4250             + dfEllipseY * sin(dfRotationRadians);
4251         const double dfArcY = dfCenterY
4252             - dfEllipseX * sin(dfRotationRadians)
4253             + dfEllipseY * cos(dfRotationRadians);
4254 
4255         poLine->setPoint( iPoint, dfArcX, dfArcY, dfZ );
4256     }
4257 
4258 /* -------------------------------------------------------------------- */
4259 /*      If we're asked to make a full circle, ensure the start and      */
4260 /*      end points coincide exactly, in spite of any rounding error.    */
4261 /* -------------------------------------------------------------------- */
4262     if( bIsFullCircle )
4263     {
4264         OGRPoint oPoint;
4265         poLine->getPoint( 0, &oPoint );
4266         poLine->setPoint( nVertexCount, &oPoint );
4267     }
4268 
4269     return poLine;
4270 }
4271 
4272 /************************************************************************/
4273 /*                     OGR_G_ApproximateArcAngles()                     */
4274 /************************************************************************/
4275 
4276 /**
4277  * Stroke arc to linestring.
4278  *
4279  * Stroke an arc of a circle to a linestring based on a center
4280  * point, radius, start angle and end angle, all angles in degrees.
4281  *
4282  * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be
4283  * used.  This is currently 4 degrees unless the user has overridden the
4284  * value with the OGR_ARC_STEPSIZE configuration variable.
4285  *
4286  * @see CPLSetConfigOption()
4287  *
4288  * @param dfCenterX center X
4289  * @param dfCenterY center Y
4290  * @param dfZ center Z
4291  * @param dfPrimaryRadius X radius of ellipse.
4292  * @param dfSecondaryRadius Y radius of ellipse.
4293  * @param dfRotation rotation of the ellipse clockwise.
4294  * @param dfStartAngle angle to first point on arc (clockwise of X-positive)
4295  * @param dfEndAngle angle to last point on arc (clockwise of X-positive)
4296  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
4297  * arc, zero to use the default setting.
4298  *
4299  * @return OGRLineString geometry representing an approximation of the arc.
4300  *
4301  * @since OGR 1.8.0
4302  */
4303 
4304 OGRGeometryH CPL_DLL
OGR_G_ApproximateArcAngles(double dfCenterX,double dfCenterY,double dfZ,double dfPrimaryRadius,double dfSecondaryRadius,double dfRotation,double dfStartAngle,double dfEndAngle,double dfMaxAngleStepSizeDegrees)4305 OGR_G_ApproximateArcAngles(
4306     double dfCenterX, double dfCenterY, double dfZ,
4307     double dfPrimaryRadius, double dfSecondaryRadius, double dfRotation,
4308     double dfStartAngle, double dfEndAngle,
4309     double dfMaxAngleStepSizeDegrees )
4310 
4311 {
4312     return reinterpret_cast<OGRGeometryH>(
4313         OGRGeometryFactory::approximateArcAngles(
4314             dfCenterX, dfCenterY, dfZ,
4315             dfPrimaryRadius, dfSecondaryRadius, dfRotation,
4316             dfStartAngle, dfEndAngle, dfMaxAngleStepSizeDegrees));
4317 }
4318 
4319 /************************************************************************/
4320 /*                           forceToLineString()                        */
4321 /************************************************************************/
4322 
4323 /**
4324  * \brief Convert to line string.
4325  *
4326  * Tries to force the provided geometry to be a line string.  This nominally
4327  * effects a change on multilinestrings.
4328  * In GDAL 2.0, for polygons or curvepolygons that have a single exterior ring,
4329  * it will return the ring. For circular strings or compound curves, it will
4330  * return an approximated line string.
4331  *
4332  * The passed in geometry is
4333  * consumed and a new one returned (or potentially the same one).
4334  *
4335  * @param poGeom the input geometry - ownership is passed to the method.
4336  * @param bOnlyInOrder flag that, if set to FALSE, indicate that the order of
4337  *                     points in a linestring might be reversed if it enables
4338  *                     to match the extremity of another linestring. If set
4339  *                     to TRUE, the start of a linestring must match the end
4340  *                     of another linestring.
4341  * @return new geometry.
4342  */
4343 
forceToLineString(OGRGeometry * poGeom,bool bOnlyInOrder)4344 OGRGeometry *OGRGeometryFactory::forceToLineString( OGRGeometry *poGeom,
4345                                                     bool bOnlyInOrder )
4346 
4347 {
4348     if( poGeom == nullptr )
4349         return nullptr;
4350 
4351     const OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType());
4352 
4353 /* -------------------------------------------------------------------- */
4354 /*      If this is already a LineString, nothing to do                  */
4355 /* -------------------------------------------------------------------- */
4356     if( eGeomType == wkbLineString )
4357     {
4358         // Except if it is a linearring.
4359         poGeom = OGRCurve::CastToLineString(poGeom->toCurve());
4360 
4361         return poGeom;
4362     }
4363 
4364 /* -------------------------------------------------------------------- */
4365 /*      If it is a polygon with a single ring, return it                 */
4366 /* -------------------------------------------------------------------- */
4367     if( eGeomType == wkbPolygon || eGeomType == wkbCurvePolygon )
4368     {
4369         OGRCurvePolygon* poCP = poGeom->toCurvePolygon();
4370         if( poCP->getNumInteriorRings() == 0 )
4371         {
4372             OGRCurve* poRing = poCP->stealExteriorRingCurve();
4373             delete poCP;
4374             return forceToLineString(poRing);
4375         }
4376         return poGeom;
4377     }
4378 
4379 /* -------------------------------------------------------------------- */
4380 /*      If it is a curve line, call CurveToLine()                        */
4381 /* -------------------------------------------------------------------- */
4382     if( eGeomType == wkbCircularString ||
4383         eGeomType == wkbCompoundCurve )
4384     {
4385         OGRGeometry* poNewGeom = poGeom->toCurve()->CurveToLine();
4386         delete poGeom;
4387         return poNewGeom;
4388     }
4389 
4390     if( eGeomType != wkbGeometryCollection
4391         && eGeomType != wkbMultiLineString
4392         && eGeomType != wkbMultiCurve )
4393         return poGeom;
4394 
4395     // Build an aggregated linestring from all the linestrings in the container.
4396     OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
4397     if( poGeom->hasCurveGeometry() )
4398     {
4399         OGRGeometryCollection *poNewGC =
4400             poGC->getLinearGeometry()->toGeometryCollection();
4401         delete poGC;
4402         poGC = poNewGC;
4403     }
4404 
4405     if( poGC->getNumGeometries() == 0 )
4406     {
4407         poGeom = new OGRLineString();
4408         poGeom->assignSpatialReference(poGC->getSpatialReference());
4409         delete poGC;
4410         return poGeom;
4411     }
4412 
4413     int iGeom0 = 0;
4414     while( iGeom0 < poGC->getNumGeometries() )
4415     {
4416         if( wkbFlatten(poGC->getGeometryRef(iGeom0)->getGeometryType())
4417             != wkbLineString )
4418         {
4419             iGeom0++;
4420             continue;
4421         }
4422 
4423         OGRLineString *poLineString0 =
4424             poGC->getGeometryRef(iGeom0)->toLineString();
4425         if( poLineString0->getNumPoints() < 2 )
4426         {
4427             iGeom0++;
4428             continue;
4429         }
4430 
4431         OGRPoint pointStart0;
4432         poLineString0->StartPoint( &pointStart0 );
4433         OGRPoint pointEnd0;
4434         poLineString0->EndPoint( &pointEnd0 );
4435 
4436         int iGeom1 = iGeom0 + 1;  // Used after for.
4437         for( ; iGeom1 < poGC->getNumGeometries(); iGeom1++ )
4438         {
4439             if( wkbFlatten(poGC->getGeometryRef(iGeom1)->getGeometryType())
4440                 != wkbLineString )
4441                 continue;
4442 
4443             OGRLineString *poLineString1 =
4444                 poGC->getGeometryRef(iGeom1)->toLineString();
4445             if( poLineString1->getNumPoints() < 2 )
4446                 continue;
4447 
4448             OGRPoint pointStart1;
4449             poLineString1->StartPoint( &pointStart1 );
4450             OGRPoint pointEnd1;
4451             poLineString1->EndPoint( &pointEnd1 );
4452 
4453             if( !bOnlyInOrder &&
4454                 (pointEnd0.Equals( &pointEnd1 ) ||
4455                  pointStart0.Equals( &pointStart1 )) )
4456             {
4457                 poLineString1->reversePoints();
4458                 poLineString1->StartPoint( &pointStart1 );
4459                 poLineString1->EndPoint( &pointEnd1 );
4460             }
4461 
4462             if( pointEnd0.Equals( &pointStart1 ) )
4463             {
4464                 poLineString0->addSubLineString( poLineString1, 1 );
4465                 poGC->removeGeometry( iGeom1 );
4466                 break;
4467             }
4468 
4469             if( pointEnd1.Equals( &pointStart0 ) )
4470             {
4471                 poLineString1->addSubLineString( poLineString0, 1 );
4472                 poGC->removeGeometry( iGeom0 );
4473                 break;
4474             }
4475         }
4476 
4477         if( iGeom1 == poGC->getNumGeometries() )
4478         {
4479             iGeom0++;
4480         }
4481     }
4482 
4483     if( poGC->getNumGeometries() == 1 )
4484     {
4485         OGRGeometry *poSingleGeom = poGC->getGeometryRef(0);
4486         poGC->removeGeometry( 0, FALSE );
4487         delete poGC;
4488 
4489         return poSingleGeom;
4490     }
4491 
4492     return poGC;
4493 }
4494 
4495 /************************************************************************/
4496 /*                      OGR_G_ForceToLineString()                       */
4497 /************************************************************************/
4498 
4499 /**
4500  * \brief Convert to line string.
4501  *
4502  * This function is the same as the C++ method
4503  * OGRGeometryFactory::forceToLineString().
4504  *
4505  * @param hGeom handle to the geometry to convert (ownership surrendered).
4506  * @return the converted geometry (ownership to caller).
4507  *
4508  * @since GDAL/OGR 1.10.0
4509  */
4510 
OGR_G_ForceToLineString(OGRGeometryH hGeom)4511 OGRGeometryH OGR_G_ForceToLineString( OGRGeometryH hGeom )
4512 
4513 {
4514     return reinterpret_cast<OGRGeometryH>(
4515         OGRGeometryFactory::forceToLineString(
4516             reinterpret_cast<OGRGeometry *>(hGeom)));
4517 }
4518 
4519 /************************************************************************/
4520 /*                           forceTo()                                  */
4521 /************************************************************************/
4522 
4523 /**
4524  * \brief Convert to another geometry type
4525  *
4526  * Tries to force the provided geometry to the specified geometry type.
4527  *
4528  * It can promote 'single' geometry type to their corresponding collection type
4529  * (see OGR_GT_GetCollection()) or the reverse. non-linear geometry type to
4530  * their corresponding linear geometry type (see OGR_GT_GetLinear()), by
4531  * possibly approximating circular arcs they may contain.  Regarding conversion
4532  * from linear geometry types to curve geometry types, only "wrapping" will be
4533  * done. No attempt to retrieve potential circular arcs by de-approximating
4534  * stroking will be done. For that, OGRGeometry::getCurveGeometry() can be used.
4535  *
4536  * The passed in geometry is consumed and a new one returned (or potentially the
4537  * same one).
4538  *
4539  * @param poGeom the input geometry - ownership is passed to the method.
4540  * @param eTargetType target output geometry type.
4541  * @param papszOptions options as a null-terminated list of strings or NULL.
4542  * @return new geometry.
4543  *
4544  * @since GDAL 2.0
4545  */
4546 
forceTo(OGRGeometry * poGeom,OGRwkbGeometryType eTargetType,const char * const * papszOptions)4547 OGRGeometry * OGRGeometryFactory::forceTo( OGRGeometry* poGeom,
4548                                            OGRwkbGeometryType eTargetType,
4549                                            const char*const* papszOptions )
4550 {
4551     if( poGeom == nullptr )
4552         return poGeom;
4553 
4554     eTargetType = wkbFlatten(eTargetType);
4555     OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType());
4556     if( eType == eTargetType || eTargetType == wkbUnknown )
4557         return poGeom;
4558 
4559     if( poGeom->IsEmpty() )
4560     {
4561         OGRGeometry* poRet = createGeometry(eTargetType);
4562         if( poRet )
4563             poRet->assignSpatialReference(poGeom->getSpatialReference());
4564         delete poGeom;
4565         return poRet;
4566     }
4567 
4568     if( OGR_GT_IsSubClassOf(eType, wkbPolyhedralSurface) &&
4569         (eTargetType == wkbMultiSurface ||
4570          eTargetType == wkbGeometryCollection) )
4571     {
4572         return forceTo( forceTo( poGeom, wkbMultiPolygon, papszOptions),
4573                         eTargetType, papszOptions );
4574     }
4575 
4576     if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) &&
4577         eTargetType == wkbGeometryCollection )
4578     {
4579         OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
4580         return OGRGeometryCollection::CastToGeometryCollection(poGC);
4581     }
4582 
4583     if( eType == wkbTriangle && eTargetType == wkbPolyhedralSurface )
4584     {
4585         OGRPolyhedralSurface* poPS = new OGRPolyhedralSurface();
4586         poPS->assignSpatialReference( poGeom->getSpatialReference() );
4587         poPS->addGeometryDirectly( OGRTriangle::CastToPolygon(poGeom) );
4588         return poPS;
4589     }
4590     else if( eType == wkbPolygon && eTargetType == wkbPolyhedralSurface )
4591     {
4592         OGRPolyhedralSurface* poPS = new OGRPolyhedralSurface();
4593         poPS->assignSpatialReference( poGeom->getSpatialReference() );
4594         poPS->addGeometryDirectly( poGeom );
4595         return poPS;
4596     }
4597     else if( eType == wkbMultiPolygon && eTargetType == wkbPolyhedralSurface )
4598     {
4599         OGRMultiPolygon* poMP = poGeom->toMultiPolygon();
4600         OGRPolyhedralSurface* poPS = new OGRPolyhedralSurface();
4601         for( int i = 0; i < poMP->getNumGeometries(); ++i )
4602         {
4603             poPS->addGeometry( poMP->getGeometryRef(i) );
4604         }
4605         delete poGeom;
4606         return poPS;
4607     }
4608     else if( eType == wkbTIN && eTargetType == wkbPolyhedralSurface )
4609     {
4610         poGeom = OGRTriangulatedSurface::CastToPolyhedralSurface(
4611                     poGeom->toTriangulatedSurface());
4612     }
4613     else if( eType == wkbCurvePolygon && eTargetType == wkbPolyhedralSurface )
4614     {
4615         return forceTo( forceTo( poGeom, wkbPolygon, papszOptions ),
4616                         eTargetType, papszOptions );
4617     }
4618     else if( eType == wkbMultiSurface && eTargetType == wkbPolyhedralSurface )
4619     {
4620         return forceTo( forceTo( poGeom, wkbMultiPolygon, papszOptions ),
4621                         eTargetType, papszOptions );
4622     }
4623 
4624     else if( eType == wkbTriangle && eTargetType == wkbTIN )
4625     {
4626         OGRTriangulatedSurface* poTS = new OGRTriangulatedSurface();
4627         poTS->assignSpatialReference( poGeom->getSpatialReference() );
4628         poTS->addGeometryDirectly( poGeom );
4629         return poTS;
4630     }
4631     else if( eType == wkbPolygon && eTargetType == wkbTIN )
4632     {
4633         OGRPolygon* poPoly = poGeom->toPolygon();
4634         OGRLinearRing* poLR = poPoly->getExteriorRing();
4635         if( !(poLR != nullptr && poLR->getNumPoints() == 4 &&
4636                 poPoly->getNumInteriorRings() == 0) )
4637         {
4638             return poGeom;
4639         }
4640         OGRErr eErr = OGRERR_NONE;
4641         OGRTriangle* poTriangle = new OGRTriangle(*poPoly, eErr);
4642         OGRTriangulatedSurface* poTS = new OGRTriangulatedSurface();
4643         poTS->assignSpatialReference( poGeom->getSpatialReference() );
4644         poTS->addGeometryDirectly( poTriangle );
4645         delete poGeom;
4646         return poTS;
4647     }
4648     else if( eType == wkbMultiPolygon && eTargetType == wkbTIN )
4649     {
4650         OGRMultiPolygon* poMP = poGeom->toMultiPolygon();
4651         for( const auto poPoly: *poMP )
4652         {
4653             const OGRLinearRing* poLR = poPoly->getExteriorRing();
4654             if( !(poLR != nullptr && poLR->getNumPoints() == 4 &&
4655                   poPoly->getNumInteriorRings() == 0) )
4656             {
4657                 return poGeom;
4658             }
4659         }
4660         OGRTriangulatedSurface* poTS = new OGRTriangulatedSurface();
4661         poTS->assignSpatialReference( poGeom->getSpatialReference() );
4662         for( const auto poPoly: *poMP )
4663         {
4664             OGRErr eErr = OGRERR_NONE;
4665             poTS->addGeometryDirectly( new OGRTriangle(*poPoly, eErr) );
4666         }
4667         delete poGeom;
4668         return poTS;
4669     }
4670     else if( eType == wkbPolyhedralSurface && eTargetType == wkbTIN )
4671     {
4672         OGRPolyhedralSurface* poPS = poGeom->toPolyhedralSurface();
4673         for( const auto poPoly: *poPS )
4674         {
4675             const OGRLinearRing* poLR = poPoly->getExteriorRing();
4676             if( !(poLR != nullptr && poLR->getNumPoints() == 4 &&
4677                   poPoly->getNumInteriorRings() == 0) )
4678             {
4679                 return poGeom;
4680             }
4681         }
4682         OGRTriangulatedSurface* poTS = new OGRTriangulatedSurface();
4683         poTS->assignSpatialReference( poGeom->getSpatialReference() );
4684         for( const auto poPoly: *poPS )
4685         {
4686             OGRErr eErr = OGRERR_NONE;
4687             poTS->addGeometryDirectly( new OGRTriangle(*poPoly, eErr) );
4688         }
4689         delete poGeom;
4690         return poTS;
4691     }
4692 
4693     else if( eType == wkbPolygon && eTargetType == wkbTriangle )
4694     {
4695         OGRPolygon* poPoly = poGeom->toPolygon();
4696         OGRLinearRing* poLR = poPoly->getExteriorRing();
4697         if( !(poLR != nullptr && poLR->getNumPoints() == 4 &&
4698                 poPoly->getNumInteriorRings() == 0) )
4699         {
4700             return poGeom;
4701         }
4702         OGRErr eErr = OGRERR_NONE;
4703         OGRTriangle* poTriangle = new OGRTriangle(*poPoly, eErr);
4704         delete poGeom;
4705         return poTriangle;
4706     }
4707 
4708     if( eTargetType == wkbTriangle || eTargetType == wkbTIN ||
4709         eTargetType == wkbPolyhedralSurface )
4710     {
4711         OGRGeometry* poPoly = forceTo( poGeom, wkbPolygon, papszOptions );
4712         if( poPoly == poGeom )
4713             return poGeom;
4714         return forceTo( poPoly, eTargetType, papszOptions );
4715     }
4716 
4717     if( eType == wkbTriangle && eTargetType == wkbGeometryCollection )
4718     {
4719         OGRGeometryCollection* poGC = new OGRGeometryCollection();
4720         poGC->assignSpatialReference(poGeom->getSpatialReference());
4721         poGC->addGeometryDirectly(poGeom);
4722         return poGC;
4723     }
4724 
4725     // Promote single to multi.
4726     if( !OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) &&
4727          OGR_GT_IsSubClassOf(OGR_GT_GetCollection(eType), eTargetType) )
4728     {
4729         OGRGeometry* poRet = createGeometry(eTargetType);
4730         if( poRet == nullptr)
4731         {
4732             delete poGeom;
4733             return nullptr;
4734         }
4735         poRet->assignSpatialReference(poGeom->getSpatialReference());
4736         if( eType == wkbLineString )
4737             poGeom = OGRCurve::CastToLineString( poGeom->toCurve() );
4738         poRet->toGeometryCollection()->addGeometryDirectly(poGeom);
4739         return poRet;
4740     }
4741 
4742     const bool bIsCurve = CPL_TO_BOOL(OGR_GT_IsCurve(eType));
4743     if( bIsCurve && eTargetType == wkbCompoundCurve )
4744     {
4745         return OGRCurve::CastToCompoundCurve(poGeom->toCurve());
4746     }
4747     else if( bIsCurve && eTargetType == wkbCurvePolygon )
4748     {
4749         OGRCurve* poCurve = poGeom->toCurve();
4750         if( poCurve->getNumPoints() >= 3 && poCurve->get_IsClosed() )
4751         {
4752             OGRCurvePolygon* poCP = new OGRCurvePolygon();
4753             if( poCP->addRingDirectly( poCurve ) == OGRERR_NONE )
4754             {
4755                 poCP->assignSpatialReference(poGeom->getSpatialReference());
4756                 return poCP;
4757             }
4758             else
4759             {
4760                 delete poCP;
4761             }
4762         }
4763     }
4764     else if( eType == wkbLineString &&
4765              OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface) )
4766     {
4767         OGRGeometry* poTmp = forceTo(poGeom, wkbPolygon, papszOptions);
4768         if( wkbFlatten(poTmp->getGeometryType()) != eType)
4769             return forceTo(poTmp, eTargetType, papszOptions);
4770     }
4771     else if( bIsCurve && eTargetType == wkbMultiSurface )
4772     {
4773         OGRGeometry* poTmp = forceTo(poGeom, wkbCurvePolygon, papszOptions);
4774         if( wkbFlatten(poTmp->getGeometryType()) != eType)
4775             return forceTo(poTmp, eTargetType, papszOptions);
4776     }
4777     else if( bIsCurve && eTargetType == wkbMultiPolygon )
4778     {
4779         OGRGeometry* poTmp = forceTo(poGeom, wkbPolygon, papszOptions);
4780         if( wkbFlatten(poTmp->getGeometryType()) != eType)
4781             return forceTo(poTmp, eTargetType, papszOptions);
4782     }
4783     else if( eType == wkbTriangle && eTargetType == wkbCurvePolygon )
4784     {
4785         return OGRSurface::CastToCurvePolygon(
4786             OGRTriangle::CastToPolygon(poGeom)->toSurface() );
4787     }
4788     else if( eType == wkbPolygon && eTargetType == wkbCurvePolygon )
4789     {
4790         return OGRSurface::CastToCurvePolygon(poGeom->toPolygon());
4791     }
4792     else if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) &&
4793              eTargetType == wkbCompoundCurve )
4794     {
4795         OGRCurvePolygon* poPoly = poGeom->toCurvePolygon();
4796         if( poPoly->getNumInteriorRings() == 0 )
4797         {
4798             OGRCurve* poRet = poPoly->stealExteriorRingCurve();
4799             if( poRet )
4800                 poRet->assignSpatialReference(poGeom->getSpatialReference());
4801             delete poPoly;
4802             return forceTo(poRet, eTargetType, papszOptions);
4803         }
4804     }
4805     else if( eType == wkbMultiPolygon && eTargetType == wkbMultiSurface )
4806     {
4807         return OGRMultiPolygon::CastToMultiSurface(poGeom->toMultiPolygon());
4808     }
4809     else if( eType == wkbMultiLineString && eTargetType == wkbMultiCurve )
4810     {
4811         return
4812             OGRMultiLineString::CastToMultiCurve(poGeom->toMultiLineString());
4813     }
4814     else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) )
4815     {
4816         OGRGeometryCollection* poGC = poGeom->toGeometryCollection();
4817         if( poGC->getNumGeometries() == 1 )
4818         {
4819             OGRGeometry* poSubGeom = poGC->getGeometryRef(0);
4820             if( poSubGeom )
4821                 poSubGeom->assignSpatialReference(
4822                     poGeom->getSpatialReference());
4823             poGC->removeGeometry(0, FALSE);
4824             OGRGeometry* poRet = forceTo(poSubGeom, eTargetType, papszOptions);
4825             if( OGR_GT_IsSubClassOf(wkbFlatten(poRet->getGeometryType()),
4826                                     eTargetType) )
4827             {
4828                 delete poGC;
4829                 return poRet;
4830             }
4831             poGC->addGeometryDirectly(poSubGeom);
4832         }
4833     }
4834     else if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) &&
4835              (OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface) ||
4836               OGR_GT_IsSubClassOf(eTargetType, wkbMultiCurve)) )
4837     {
4838         OGRCurvePolygon* poCP = poGeom->toCurvePolygon();
4839         if( poCP->getNumInteriorRings() == 0 )
4840         {
4841             OGRCurve* poRing = poCP->getExteriorRingCurve();
4842             poRing->assignSpatialReference(poGeom->getSpatialReference());
4843             OGRwkbGeometryType eRingType = poRing->getGeometryType();
4844             OGRGeometry* poRingDup = poRing->clone();
4845             OGRGeometry* poRet = forceTo(poRingDup, eTargetType, papszOptions);
4846             if( poRet->getGeometryType() != eRingType )
4847             {
4848                 delete poCP;
4849                 return poRet;
4850             }
4851             else
4852             {
4853                 delete poRet;
4854             }
4855         }
4856     }
4857 
4858     if( eTargetType == wkbLineString )
4859     {
4860         poGeom = forceToLineString(poGeom);
4861     }
4862     else if( eTargetType == wkbPolygon )
4863     {
4864         poGeom = forceToPolygon(poGeom);
4865     }
4866     else if( eTargetType == wkbMultiPolygon )
4867     {
4868         poGeom = forceToMultiPolygon(poGeom);
4869     }
4870     else if( eTargetType == wkbMultiLineString )
4871     {
4872         poGeom = forceToMultiLineString(poGeom);
4873     }
4874     else if( eTargetType == wkbMultiPoint )
4875     {
4876         poGeom = forceToMultiPoint(poGeom);
4877     }
4878 
4879     return poGeom;
4880 }
4881 
4882 /************************************************************************/
4883 /*                          OGR_G_ForceTo()                             */
4884 /************************************************************************/
4885 
4886 /**
4887  * \brief Convert to another geometry type
4888  *
4889  * This function is the same as the C++ method OGRGeometryFactory::forceTo().
4890  *
4891  * @param hGeom the input geometry - ownership is passed to the method.
4892  * @param eTargetType target output geometry type.
4893  * @param papszOptions options as a null-terminated list of strings or NULL.
4894  * @return new geometry.
4895  *
4896  * @since GDAL 2.0
4897  */
4898 
OGR_G_ForceTo(OGRGeometryH hGeom,OGRwkbGeometryType eTargetType,char ** papszOptions)4899 OGRGeometryH OGR_G_ForceTo( OGRGeometryH hGeom,
4900                             OGRwkbGeometryType eTargetType,
4901                             char** papszOptions )
4902 
4903 {
4904     return reinterpret_cast<OGRGeometryH>(
4905         OGRGeometryFactory::forceTo(
4906             reinterpret_cast<OGRGeometry *>(hGeom), eTargetType,
4907             papszOptions));
4908 }
4909 
4910 /************************************************************************/
4911 /*                         GetCurveParameters()                          */
4912 /************************************************************************/
4913 
4914 /**
4915  * \brief Returns the parameter of an arc circle.
4916  *
4917  * Angles are return in radians, with trigonometic convention (counter clock
4918  * wise)
4919  *
4920  * @param x0 x of first point
4921  * @param y0 y of first point
4922  * @param x1 x of intermediate point
4923  * @param y1 y of intermediate point
4924  * @param x2 x of final point
4925  * @param y2 y of final point
4926  * @param R radius (output)
4927  * @param cx x of arc center (output)
4928  * @param cy y of arc center (output)
4929  * @param alpha0 angle between center and first point, in radians (output)
4930  * @param alpha1 angle between center and intermediate point, in radians (output)
4931  * @param alpha2 angle between center and final point, in radians (output)
4932  * @return TRUE if the points are not aligned and define an arc circle.
4933  *
4934  * @since GDAL 2.0
4935  */
4936 
GetCurveParameters(double x0,double y0,double x1,double y1,double x2,double y2,double & R,double & cx,double & cy,double & alpha0,double & alpha1,double & alpha2)4937 int OGRGeometryFactory::GetCurveParameters(
4938     double x0, double y0, double x1, double y1, double x2, double y2,
4939     double& R, double& cx, double& cy,
4940     double& alpha0, double& alpha1, double& alpha2 )
4941 {
4942     if( CPLIsNan(x0) || CPLIsNan(y0) ||
4943         CPLIsNan(x1) || CPLIsNan(y1) ||
4944         CPLIsNan(x2) || CPLIsNan(y2) )
4945     {
4946         return FALSE;
4947     }
4948 
4949     // Circle.
4950     if( x0 == x2 && y0 == y2 )
4951     {
4952         if( x0 != x1 || y0 != y1 )
4953         {
4954             cx = (x0 + x1) / 2;
4955             cy = (y0 + y1) / 2;
4956             R = DISTANCE(cx, cy, x0, y0);
4957             // Arbitrarily pick counter-clock-wise order (like PostGIS does).
4958             alpha0 = atan2(y0 - cy, x0 - cx);
4959             alpha1 = alpha0 + M_PI;
4960             alpha2 = alpha0 + 2 * M_PI;
4961             return TRUE;
4962         }
4963         else
4964         {
4965             return FALSE;
4966         }
4967     }
4968 
4969     double dx01 = x1 - x0;
4970     double dy01 = y1 - y0;
4971     double dx12 = x2 - x1;
4972     double dy12 = y2 - y1;
4973 
4974     // Normalize above values so as to make sure we don't end up with
4975     // computing a difference of too big values.
4976     double dfScale = fabs(dx01);
4977     if( fabs(dy01) > dfScale ) dfScale = fabs(dy01);
4978     if( fabs(dx12) > dfScale ) dfScale = fabs(dx12);
4979     if( fabs(dy12) > dfScale ) dfScale = fabs(dy12);
4980     const double dfInvScale = 1.0 / dfScale;
4981     dx01 *= dfInvScale;
4982     dy01 *= dfInvScale;
4983     dx12 *= dfInvScale;
4984     dy12 *= dfInvScale;
4985 
4986     const double det = dx01 * dy12 - dx12 * dy01;
4987     if( fabs(det) < 1.0e-8 || CPLIsNan(det) )
4988     {
4989         return FALSE;
4990     }
4991     const double x01_mid = (x0 + x1) * dfInvScale;
4992     const double x12_mid = (x1 + x2) * dfInvScale;
4993     const double y01_mid = (y0 + y1) * dfInvScale;
4994     const double y12_mid = (y1 + y2) * dfInvScale;
4995     const double c01 = dx01 * x01_mid + dy01 * y01_mid;
4996     const double c12 = dx12 * x12_mid + dy12 * y12_mid;
4997     cx = 0.5 * dfScale * (c01 * dy12 - c12 * dy01) / det;
4998     cy = 0.5 * dfScale * (-c01 * dx12 + c12 * dx01) / det;
4999 
5000     alpha0 = atan2((y0 - cy) * dfInvScale, (x0 - cx) * dfInvScale);
5001     alpha1 = atan2((y1 - cy) * dfInvScale, (x1 - cx) * dfInvScale);
5002     alpha2 = atan2((y2 - cy) * dfInvScale, (x2 - cx) * dfInvScale);
5003     R = DISTANCE(cx, cy, x0, y0);
5004 
5005     // If det is negative, the orientation if clockwise.
5006     if( det < 0 )
5007     {
5008         if( alpha1 > alpha0 )
5009             alpha1 -= 2 * M_PI;
5010         if( alpha2 > alpha1 )
5011             alpha2 -= 2 * M_PI;
5012     }
5013     else
5014     {
5015         if( alpha1 < alpha0 )
5016             alpha1 += 2 * M_PI;
5017         if( alpha2 < alpha1 )
5018             alpha2 += 2 * M_PI;
5019     }
5020 
5021     CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2) ||
5022               (alpha0 >= alpha1 && alpha1 >= alpha2));
5023 
5024     return TRUE;
5025 }
5026 
5027 /************************************************************************/
5028 /*                      OGRGeometryFactoryStrokeArc()                   */
5029 /************************************************************************/
5030 
OGRGeometryFactoryStrokeArc(OGRLineString * poLine,double cx,double cy,double R,double z0,double z1,int bHasZ,double alpha0,double alpha1,double dfStep,int bStealthConstraints)5031 static void OGRGeometryFactoryStrokeArc( OGRLineString* poLine,
5032                                          double cx, double cy, double R,
5033                                          double z0, double z1, int bHasZ,
5034                                          double alpha0, double alpha1,
5035                                          double dfStep,
5036                                          int bStealthConstraints )
5037 {
5038     const int nSign = dfStep > 0 ? 1 : -1;
5039 
5040     // Constant angle between all points, so as to not depend on winding order.
5041     const double dfNumSteps = fabs((alpha1 - alpha0) / dfStep) + 0.5;
5042     if ( dfNumSteps >= std::numeric_limits<int>::max() ||
5043          dfNumSteps <= std::numeric_limits<int>::min() ||
5044          CPLIsNan(dfNumSteps) )
5045     {
5046         CPLError(CE_Warning, CPLE_AppDefined,
5047                  "OGRGeometryFactoryStrokeArc: bogus steps: "
5048                  "%lf %lf %lf %lf", alpha0, alpha1, dfStep, dfNumSteps);
5049         return;
5050     }
5051 
5052     int nSteps = static_cast<int>(dfNumSteps);
5053     if( bStealthConstraints )
5054     {
5055         // We need at least 6 intermediate vertex, and if more additional
5056         // multiples of 2.
5057         if( nSteps < 1+6 )
5058             nSteps = 1+6;
5059         else
5060             nSteps = 1+6 + 2 * ((nSteps - (1+6) + (2-1)) / 2);
5061     }
5062     else if( nSteps < 4 )
5063     {
5064         nSteps = 4;
5065     }
5066     dfStep = nSign * fabs((alpha1 - alpha0) / nSteps);
5067     double alpha = alpha0 + dfStep;
5068 
5069     for( ; (alpha - alpha1) * nSign < -1e-8; alpha += dfStep )
5070     {
5071         const double dfX = cx + R * cos(alpha);
5072         const double dfY = cy + R * sin(alpha);
5073         if( bHasZ )
5074         {
5075             const double z =
5076                 z0 + (z1 - z0) * (alpha - alpha0) / (alpha1 - alpha0);
5077             poLine->addPoint(dfX, dfY, z);
5078         }
5079         else
5080         {
5081             poLine->addPoint(dfX, dfY);
5082         }
5083     }
5084 }
5085 
5086 /************************************************************************/
5087 /*                         OGRGF_SetHiddenValue()                       */
5088 /************************************************************************/
5089 
5090 // TODO(schwehr): Cleanup these static constants.
5091 constexpr int HIDDEN_ALPHA_WIDTH = 32;
5092 constexpr GUInt32 HIDDEN_ALPHA_SCALE =
5093     static_cast<GUInt32>((static_cast<GUIntBig>(1) << HIDDEN_ALPHA_WIDTH) - 2);
5094 constexpr int HIDDEN_ALPHA_HALF_WIDTH = (HIDDEN_ALPHA_WIDTH / 2);
5095 constexpr int HIDDEN_ALPHA_HALF_MASK = (1 << HIDDEN_ALPHA_HALF_WIDTH) - 1;
5096 
5097 // Encode 16-bit nValue in the 8-lsb of dfX and dfY.
5098 
5099 #ifdef CPL_LSB
5100 constexpr int DOUBLE_LSB_OFFSET = 0;
5101 #else
5102 constexpr int DOUBLE_LSB_OFFSET = 7;
5103 #endif
5104 
OGRGF_SetHiddenValue(GUInt16 nValue,double & dfX,double & dfY)5105 static void OGRGF_SetHiddenValue( GUInt16 nValue, double& dfX, double &dfY )
5106 {
5107     GByte abyData[8] = {};
5108 
5109     memcpy(abyData, &dfX, sizeof(double));
5110     abyData[DOUBLE_LSB_OFFSET] = static_cast<GByte>(nValue & 0xFF);
5111     memcpy(&dfX, abyData, sizeof(double));
5112 
5113     memcpy(abyData, &dfY, sizeof(double));
5114     abyData[DOUBLE_LSB_OFFSET] = static_cast<GByte>(nValue >> 8);
5115     memcpy(&dfY, abyData, sizeof(double));
5116 }
5117 
5118 /************************************************************************/
5119 /*                         OGRGF_GetHiddenValue()                       */
5120 /************************************************************************/
5121 
5122 // Decode 16-bit nValue from the 8-lsb of dfX and dfY.
OGRGF_GetHiddenValue(double dfX,double dfY)5123 static GUInt16 OGRGF_GetHiddenValue( double dfX, double dfY )
5124 {
5125     GByte abyData[8] = {};
5126     memcpy(abyData, &dfX, sizeof(double));
5127     GUInt16 nValue = abyData[DOUBLE_LSB_OFFSET];
5128     memcpy(abyData, &dfY, sizeof(double));
5129     nValue |= (abyData[DOUBLE_LSB_OFFSET] << 8);
5130 
5131     return nValue;
5132 }
5133 
5134 /************************************************************************/
5135 /*                     OGRGF_NeedSwithArcOrder()                        */
5136 /************************************************************************/
5137 
5138 // We need to define a full ordering between starting point and ending point
5139 // whatever it is.
OGRGF_NeedSwithArcOrder(double x0,double y0,double x2,double y2)5140 static bool OGRGF_NeedSwithArcOrder( double x0, double y0,
5141                                      double x2, double y2 )
5142 {
5143     return x0 < x2 || (x0 == x2 && y0 < y2);
5144 }
5145 
5146 /************************************************************************/
5147 /*                         curveToLineString()                          */
5148 /************************************************************************/
5149 
5150 /**
5151  * \brief Converts an arc circle into an approximate line string
5152  *
5153  * The arc circle is defined by a first point, an intermediate point and a
5154  * final point.
5155  *
5156  * The provided dfMaxAngleStepSizeDegrees is a hint. The discretization
5157  * algorithm may pick a slightly different value.
5158  *
5159  * So as to avoid gaps when rendering curve polygons that share common arcs,
5160  * this method is guaranteed to return a line with reversed vertex if called
5161  * with inverted first and final point, and identical intermediate point.
5162  *
5163  * @param x0 x of first point
5164  * @param y0 y of first point
5165  * @param z0 z of first point
5166  * @param x1 x of intermediate point
5167  * @param y1 y of intermediate point
5168  * @param z1 z of intermediate point
5169  * @param x2 x of final point
5170  * @param y2 y of final point
5171  * @param z2 z of final point
5172  * @param bHasZ TRUE if z must be taken into account
5173  * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the
5174  * arc, zero to use the default setting.
5175  * @param papszOptions options as a null-terminated list of strings or NULL.
5176  * Recognized options:
5177  * <ul>
5178  * <li>ADD_INTERMEDIATE_POINT=STEALTH/YES/NO (Default to STEALTH).
5179  *         Determine if and how the intermediate point must be output in the
5180  *         linestring.  If set to STEALTH, no explicit intermediate point is
5181  *         added but its properties are encoded in low significant bits of
5182  *         intermediate points and OGRGeometryFactory::curveFromLineString() can
5183  *         decode them.  This is the best compromise for round-tripping in OGR
5184  *         and better results with PostGIS
5185  *         <a href="http://postgis.org/docs/ST_LineToCurve.html">ST_LineToCurve()</a>
5186  *         If set to YES, the intermediate point is explicitly added to the
5187  *         linestring.
5188  *         If set to NO, the intermediate point is not explicitly added.
5189  * </li>
5190  * </ul>
5191  *
5192  * @return the converted geometry (ownership to caller).
5193  *
5194  * @since GDAL 2.0
5195  */
5196 
curveToLineString(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,int bHasZ,double dfMaxAngleStepSizeDegrees,const char * const * papszOptions)5197 OGRLineString* OGRGeometryFactory::curveToLineString(
5198     double x0, double y0, double z0,
5199     double x1, double y1, double z1,
5200     double x2, double y2, double z2,
5201     int bHasZ,
5202     double dfMaxAngleStepSizeDegrees,
5203     const char*const* papszOptions )
5204 {
5205     // So as to make sure the same curve followed in both direction results
5206     // in perfectly(=binary identical) symmetrical points.
5207     if( OGRGF_NeedSwithArcOrder(x0, y0, x2, y2) )
5208     {
5209         OGRLineString* poLS =
5210             curveToLineString(x2, y2, z2, x1, y1, z1, x0, y0, z0,
5211                               bHasZ, dfMaxAngleStepSizeDegrees,
5212                               papszOptions);
5213         poLS->reversePoints();
5214         return poLS;
5215     }
5216 
5217     double R = 0.0;
5218     double cx = 0.0;
5219     double cy = 0.0;
5220     double alpha0 = 0.0;
5221     double alpha1 = 0.0;
5222     double alpha2 = 0.0;
5223 
5224     OGRLineString* poLine = new OGRLineString();
5225     bool bIsArc = true;
5226     if( !GetCurveParameters(x0, y0, x1, y1, x2, y2,
5227                            R, cx, cy, alpha0, alpha1, alpha2))
5228     {
5229         bIsArc = false;
5230         cx = 0.0;
5231         cy = 0.0;
5232         R = 0.0;
5233         alpha0 = 0.0;
5234         alpha1 = 0.0;
5235         alpha2 = 0.0;
5236     }
5237 
5238     const int nSign = alpha1 >= alpha0 ? 1 : -1;
5239 
5240     // support default arc step setting.
5241     if( dfMaxAngleStepSizeDegrees < 1e-6 )
5242     {
5243         dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize();
5244     }
5245 
5246     double dfStep = dfMaxAngleStepSizeDegrees / 180 * M_PI;
5247     if( dfStep <= 0.01 / 180 * M_PI )
5248     {
5249         CPLDebug("OGR", "Too small arc step size: limiting to 0.01 degree.");
5250         dfStep = 0.01 / 180 * M_PI;
5251     }
5252 
5253     dfStep *= nSign;
5254 
5255     if( bHasZ )
5256         poLine->addPoint(x0, y0, z0);
5257     else
5258         poLine->addPoint(x0, y0);
5259 
5260     bool bAddIntermediatePoint = false;
5261     bool bStealth = true;
5262     for( const char* const* papszIter = papszOptions;
5263          papszIter && *papszIter;
5264          papszIter++ )
5265     {
5266         char* pszKey = nullptr;
5267         const char* pszValue = CPLParseNameValue(*papszIter, &pszKey);
5268         if( pszKey != nullptr && EQUAL(pszKey, "ADD_INTERMEDIATE_POINT") )
5269         {
5270             if( EQUAL(pszValue, "YES") || EQUAL(pszValue, "TRUE") ||
5271                 EQUAL(pszValue, "ON") )
5272             {
5273                 bAddIntermediatePoint = true;
5274                 bStealth = false;
5275             }
5276             else if( EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") ||
5277                      EQUAL(pszValue, "OFF") )
5278             {
5279                 bAddIntermediatePoint = false;
5280                 bStealth = false;
5281             }
5282             else if( EQUAL(pszValue, "STEALTH") )
5283             {
5284                 // default.
5285             }
5286         }
5287         else
5288         {
5289             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported option: %s",
5290                      *papszIter);
5291         }
5292         CPLFree(pszKey);
5293     }
5294 
5295     if( !bIsArc || bAddIntermediatePoint )
5296     {
5297         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R,
5298                                     z0, z1, bHasZ,
5299                                     alpha0, alpha1, dfStep,
5300                                     FALSE);
5301 
5302         if( bHasZ )
5303             poLine->addPoint(x1, y1, z1);
5304         else
5305             poLine->addPoint(x1, y1);
5306 
5307         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R,
5308                                     z1, z2, bHasZ,
5309                                     alpha1, alpha2, dfStep,
5310                                     FALSE);
5311     }
5312     else
5313     {
5314         OGRGeometryFactoryStrokeArc(poLine, cx, cy, R,
5315                                     z0, z2, bHasZ,
5316                                     alpha0, alpha2, dfStep,
5317                                     bStealth);
5318 
5319         if( bStealth && poLine->getNumPoints() > 6 )
5320         {
5321             // 'Hide' the angle of the intermediate point in the 8
5322             // low-significant bits of the x, y of the first 2 computed points
5323             // (so 32 bits), then put 0xFF, and on the last couple points put
5324             // again the angle but in reverse order, so that overall the
5325             // low-significant bits of all the points are symmetrical w.r.t the
5326             // mid-point.
5327             const double dfRatio = (alpha1 - alpha0) / (alpha2 - alpha0);
5328             double dfAlphaRatio = 0.5 + HIDDEN_ALPHA_SCALE * dfRatio;
5329             if( dfAlphaRatio < 0.0 )
5330             {
5331                 CPLError(CE_Warning, CPLE_AppDefined,
5332                          "AlphaRation < 0: %lf", dfAlphaRatio);
5333                 dfAlphaRatio *= -1;
5334             }
5335             else if( dfAlphaRatio >= std::numeric_limits<GUInt32>::max() ||
5336                      CPLIsNan(dfAlphaRatio) )
5337             {
5338                 CPLError(CE_Warning, CPLE_AppDefined,
5339                          "AlphaRatio too large: %lf", dfAlphaRatio);
5340                 dfAlphaRatio = std::numeric_limits<GUInt32>::max();
5341             }
5342             const GUInt32 nAlphaRatio = static_cast<GUInt32>(dfAlphaRatio);
5343             const GUInt16 nAlphaRatioLow = nAlphaRatio & HIDDEN_ALPHA_HALF_MASK;
5344             const GUInt16 nAlphaRatioHigh =
5345                 nAlphaRatio >> HIDDEN_ALPHA_HALF_WIDTH;
5346             // printf("alpha0=%f, alpha1=%f, alpha2=%f, dfRatio=%f, "/*ok*/
5347             //        "nAlphaRatio = %u\n",
5348             //        alpha0, alpha1, alpha2, dfRatio, nAlphaRatio);
5349 
5350             CPLAssert( ((poLine->getNumPoints()-1 - 6) % 2) == 0 );
5351 
5352             for( int i = 1; i + 1 < poLine->getNumPoints(); i += 2 )
5353             {
5354                 GUInt16 nVal = 0xFFFF;
5355 
5356                 double dfX = poLine->getX(i);
5357                 double dfY = poLine->getY(i);
5358                 if( i == 1 )
5359                     nVal = nAlphaRatioLow;
5360                 else if( i == poLine->getNumPoints() - 2 )
5361                     nVal = nAlphaRatioHigh;
5362                 OGRGF_SetHiddenValue(nVal, dfX, dfY);
5363                 poLine->setPoint(i, dfX, dfY);
5364 
5365                 dfX = poLine->getX(i+1);
5366                 dfY = poLine->getY(i+1);
5367                 if( i == 1 )
5368                     nVal = nAlphaRatioHigh;
5369                 else if( i == poLine->getNumPoints() - 2 )
5370                     nVal = nAlphaRatioLow;
5371                 OGRGF_SetHiddenValue(nVal, dfX, dfY);
5372                 poLine->setPoint(i+1, dfX, dfY);
5373             }
5374         }
5375     }
5376 
5377     if( bHasZ )
5378         poLine->addPoint(x2, y2, z2);
5379     else
5380         poLine->addPoint(x2, y2);
5381 
5382     return poLine;
5383 }
5384 
5385 /************************************************************************/
5386 /*                         OGRGF_FixAngle()                             */
5387 /************************************************************************/
5388 
5389 // Fix dfAngle by offsets of 2 PI so that it lies between dfAngleStart and
5390 // dfAngleStop, whatever their respective order.
OGRGF_FixAngle(double dfAngleStart,double dfAngleStop,double dfAngle)5391 static double OGRGF_FixAngle( double dfAngleStart, double dfAngleStop,
5392                               double dfAngle )
5393 {
5394     if( dfAngleStart < dfAngleStop )
5395     {
5396         while( dfAngle <= dfAngleStart + 1e-8 )
5397             dfAngle += 2 * M_PI;
5398     }
5399     else
5400     {
5401         while( dfAngle >= dfAngleStart - 1e-8 )
5402             dfAngle -= 2 * M_PI;
5403     }
5404     return dfAngle;
5405 }
5406 
5407 /************************************************************************/
5408 /*                         OGRGF_DetectArc()                            */
5409 /************************************************************************/
5410 
5411 //#define VERBOSE_DEBUG_CURVEFROMLINESTRING
5412 
IS_ALMOST_INTEGER(double x)5413 static inline bool IS_ALMOST_INTEGER(double x)
5414 {
5415     const double val = fabs(x - floor(x + 0.5));
5416     return val < 1.0e-8;
5417 }
5418 
OGRGF_DetectArc(const OGRLineString * poLS,int i,OGRCompoundCurve * & poCC,OGRCircularString * & poCS,OGRLineString * & poLSNew)5419 static int OGRGF_DetectArc( const OGRLineString* poLS, int i,
5420                             OGRCompoundCurve*& poCC,
5421                             OGRCircularString*& poCS,
5422                             OGRLineString*& poLSNew )
5423 {
5424     if( i + 3 >= poLS->getNumPoints() )
5425         return -1;
5426 
5427     OGRPoint p0;
5428     OGRPoint p1;
5429     OGRPoint p2;
5430     poLS->getPoint(i, &p0);
5431     poLS->getPoint(i+1, &p1);
5432     poLS->getPoint(i+2, &p2);
5433     double R_1 = 0.0;
5434     double cx_1 = 0.0;
5435     double cy_1 = 0.0;
5436     double alpha0_1 = 0.0;
5437     double alpha1_1 = 0.0;
5438     double alpha2_1 = 0.0;
5439     if( !(OGRGeometryFactory::GetCurveParameters(p0.getX(), p0.getY(),
5440                             p1.getX(), p1.getY(),
5441                             p2.getX(), p2.getY(),
5442                             R_1, cx_1, cy_1,
5443                             alpha0_1, alpha1_1, alpha2_1) &&
5444           fabs(alpha2_1 - alpha0_1) < 2.0 * 20.0 / 180.0 * M_PI) )
5445     {
5446         return -1;
5447     }
5448 
5449     const double dfDeltaAlpha10 = alpha1_1 - alpha0_1;
5450     const double dfDeltaAlpha21 = alpha2_1 - alpha1_1;
5451     const double dfMaxDeltaAlpha = std::max(fabs(dfDeltaAlpha10),
5452                                             fabs(dfDeltaAlpha21));
5453     GUInt32 nAlphaRatioRef =
5454         OGRGF_GetHiddenValue(p1.getX(), p1.getY()) |
5455         (OGRGF_GetHiddenValue(p2.getX(), p2.getY()) << HIDDEN_ALPHA_HALF_WIDTH);
5456     bool bFoundFFFFFFFFPattern = false;
5457     bool bFoundReversedAlphaRatioRef = false;
5458     bool bValidAlphaRatio = nAlphaRatioRef > 0 && nAlphaRatioRef < 0xFFFFFFFF;
5459     int nCountValidAlphaRatio = 1;
5460 
5461     double dfScale = std::max(1.0, R_1);
5462     dfScale = std::max(dfScale, fabs(cx_1));
5463     dfScale = std::max(dfScale, fabs(cy_1));
5464     dfScale = pow(10.0, ceil(log10(dfScale)));
5465     const double dfInvScale = 1.0 / dfScale;
5466 
5467     const int bInitialConstantStep =
5468         (fabs(dfDeltaAlpha10 - dfDeltaAlpha21) / dfMaxDeltaAlpha) < 1.0e-4;
5469     const double dfDeltaEpsilon = bInitialConstantStep ?
5470         dfMaxDeltaAlpha * 1e-4 : dfMaxDeltaAlpha/10;
5471 
5472 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5473     printf("----------------------------\n");/*ok*/
5474     printf("Curve beginning at offset i = %d\n", i);/*ok*/
5475     printf("Initial alpha ratio = %u\n", nAlphaRatioRef);/*ok*/
5476     printf("Initial R = %.16g, cx = %.16g, cy = %.16g\n", R_1, cx_1, cy_1);/*ok*/
5477     printf("dfScale = %f\n", dfScale);/*ok*/
5478     printf("bInitialConstantStep = %d, "/*ok*/
5479             "fabs(dfDeltaAlpha10 - dfDeltaAlpha21)=%.8g, "
5480             "dfMaxDeltaAlpha = %.8f, "
5481             "dfDeltaEpsilon = %.8f (%.8f)\n",
5482             bInitialConstantStep,
5483             fabs(dfDeltaAlpha10 - dfDeltaAlpha21),
5484             dfMaxDeltaAlpha,
5485             dfDeltaEpsilon, 1.0 / 180.0 * M_PI);
5486 #endif
5487     int iMidPoint = -1;
5488     double dfLastValidAlpha = alpha2_1;
5489 
5490     double dfLastLogRelDiff = 0;
5491 
5492     OGRPoint p3;
5493     int j = i + 1;  // Used after for.
5494     for( ; j + 2 < poLS->getNumPoints(); j++ )
5495     {
5496         poLS->getPoint(j, &p1);
5497         poLS->getPoint(j+1, &p2);
5498         poLS->getPoint(j+2, &p3);
5499         double R_2 = 0.0;
5500         double cx_2 = 0.0;
5501         double cy_2 = 0.0;
5502         double alpha0_2 = 0.0;
5503         double alpha1_2 = 0.0;
5504         double alpha2_2 = 0.0;
5505         // Check that the new candidate arc shares the same
5506         // radius, center and winding order.
5507         if( !(OGRGeometryFactory::GetCurveParameters(p1.getX(), p1.getY(),
5508                                 p2.getX(), p2.getY(),
5509                                 p3.getX(), p3.getY(),
5510                                 R_2, cx_2, cy_2,
5511                                 alpha0_2, alpha1_2, alpha2_2)) )
5512         {
5513 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5514             printf("End of curve at j=%d\n : straight line", j);/*ok*/
5515 #endif
5516             break;
5517         }
5518 
5519         const double dfRelDiffR = fabs(R_1 - R_2) * dfInvScale;
5520         const double dfRelDiffCx = fabs(cx_1 - cx_2) * dfInvScale;
5521         const double dfRelDiffCy = fabs(cy_1 - cy_2) * dfInvScale;
5522 
5523 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5524         printf("j=%d: R = %.16g, cx = %.16g, cy = %.16g, "/*ok*/
5525                "rel_diff_R=%.8g rel_diff_cx=%.8g rel_diff_cy=%.8g\n",
5526                j, R_2, cx_2, cy_2, dfRelDiffR, dfRelDiffCx, dfRelDiffCy);
5527 #endif
5528 
5529         if( (dfRelDiffR > 1.0e-6 &&
5530              dfRelDiffCx > 1.0e-6 &&
5531              dfRelDiffCy > 1.0e-6) ||
5532             dfDeltaAlpha10 * (alpha1_2 - alpha0_2) < 0.0 )
5533         {
5534 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5535             printf("End of curve at j=%d\n", j);/*ok*/
5536 #endif
5537             break;
5538         }
5539 
5540         if( dfRelDiffR > 0.0 && dfRelDiffCx > 0.0 && dfRelDiffCy > 0.0 )
5541         {
5542             const double dfLogRelDiff =
5543                 std::min(std::min(fabs(log10(dfRelDiffR)),
5544                                   fabs(log10(dfRelDiffCx))),
5545                          fabs(log10(dfRelDiffCy)));
5546             // printf("dfLogRelDiff = %f, dfLastLogRelDiff=%f, "/*ok*/
5547             //        "dfLogRelDiff - dfLastLogRelDiff=%f\n",
5548             //         dfLogRelDiff, dfLastLogRelDiff,
5549             //         dfLogRelDiff - dfLastLogRelDiff);
5550             if( dfLogRelDiff > 0.0 &&
5551                 dfLastLogRelDiff >= 8.0 && dfLogRelDiff <= 8.0 &&
5552                 dfLogRelDiff < dfLastLogRelDiff - 2.0 )
5553             {
5554 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5555                 printf("End of curve at j=%d. Significant different in "/*ok*/
5556                        "relative error w.r.t previous points\n", j);
5557 #endif
5558                 break;
5559             }
5560             dfLastLogRelDiff = dfLogRelDiff;
5561         }
5562 
5563         const double dfStep10 = fabs(alpha1_2 - alpha0_2);
5564         const double dfStep21 = fabs(alpha2_2 - alpha1_2);
5565         // Check that the angle step is consistent with the original step.
5566         if( !(dfStep10 < 2.0 * dfMaxDeltaAlpha &&
5567               dfStep21 < 2.0 * dfMaxDeltaAlpha) )
5568         {
5569 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5570             printf("End of curve at j=%d: dfStep10=%f, dfStep21=%f, "/*ok*/
5571                    "2*dfMaxDeltaAlpha=%f\n",
5572                    j, dfStep10, dfStep21, 2 * dfMaxDeltaAlpha);
5573 #endif
5574             break;
5575         }
5576 
5577         if( bValidAlphaRatio && j > i + 1 && (i % 2) != (j % 2 ) )
5578         {
5579             const GUInt32 nAlphaRatioReversed =
5580                 (OGRGF_GetHiddenValue(p1.getX(),
5581                                       p1.getY()) << HIDDEN_ALPHA_HALF_WIDTH) |
5582                 (OGRGF_GetHiddenValue(p2.getX(), p2.getY()));
5583 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5584             printf("j=%d, nAlphaRatioReversed = %u\n",/*ok*/
5585                    j, nAlphaRatioReversed);
5586 #endif
5587             if( !bFoundFFFFFFFFPattern && nAlphaRatioReversed == 0xFFFFFFFF )
5588             {
5589                 bFoundFFFFFFFFPattern = true;
5590                 nCountValidAlphaRatio++;
5591             }
5592             else if( bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef &&
5593                         nAlphaRatioReversed == 0xFFFFFFFF )
5594             {
5595                 nCountValidAlphaRatio++;
5596             }
5597             else if( bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef &&
5598                         nAlphaRatioReversed == nAlphaRatioRef )
5599             {
5600                 bFoundReversedAlphaRatioRef = true;
5601                 nCountValidAlphaRatio++;
5602             }
5603             else
5604             {
5605                 if( bInitialConstantStep &&
5606                     fabs(dfLastValidAlpha - alpha0_1) >= M_PI &&
5607                     nCountValidAlphaRatio > 10 )
5608                 {
5609 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5610                     printf("End of curve at j=%d: "/*ok*/
5611                            "fabs(dfLastValidAlpha - alpha0_1)=%f, "
5612                            "nCountValidAlphaRatio=%d\n",
5613                            j,
5614                            fabs(dfLastValidAlpha - alpha0_1),
5615                            nCountValidAlphaRatio);
5616 #endif
5617                     if( dfLastValidAlpha - alpha0_1 > 0 )
5618                     {
5619                         while( dfLastValidAlpha - alpha0_1 - dfMaxDeltaAlpha -
5620                                M_PI > -dfMaxDeltaAlpha/10 )
5621                         {
5622                             dfLastValidAlpha -= dfMaxDeltaAlpha;
5623                             j--;
5624 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5625                             printf("--> corrected as fabs(dfLastValidAlpha - "/*ok*/
5626                                    "alpha0_1)=%f, j=%d\n",
5627                                    fabs(dfLastValidAlpha - alpha0_1), j);
5628 #endif
5629                         }
5630                     }
5631                     else
5632                     {
5633                         while( dfLastValidAlpha - alpha0_1 + dfMaxDeltaAlpha +
5634                                M_PI < dfMaxDeltaAlpha/10 )
5635                         {
5636                             dfLastValidAlpha += dfMaxDeltaAlpha;
5637                             j--;
5638 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5639                             printf( "--> corrected as fabs(dfLastValidAlpha - "/*ok*/
5640                                     "alpha0_1)=%f, j=%d\n",
5641                                     fabs(dfLastValidAlpha - alpha0_1), j);
5642 #endif
5643                         }
5644                     }
5645                     poLS->getPoint(j+1, &p2);
5646                     break;
5647                 }
5648 
5649 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5650                 printf( "j=%d, nAlphaRatioReversed = %u --> inconsistent "/*ok*/
5651                         "values across arc. Don't use it\n",
5652                         j, nAlphaRatioReversed);
5653 #endif
5654                 bValidAlphaRatio = false;
5655             }
5656         }
5657 
5658         // Correct current end angle, consistently with start angle.
5659         dfLastValidAlpha = OGRGF_FixAngle(alpha0_1, alpha1_1, alpha2_2);
5660 
5661         // Try to detect the precise intermediate point of the
5662         // arc circle by detecting irregular angle step
5663         // This is OK if we don't detect the right point or fail
5664         // to detect it.
5665 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5666         printf("j=%d A(0,1)-maxDelta=%.8f A(1,2)-maxDelta=%.8f "/*ok*/
5667                "x1=%.8f y1=%.8f x2=%.8f y2=%.8f x3=%.8f y3=%.8f\n",
5668                j, fabs(dfStep10 - dfMaxDeltaAlpha),
5669                fabs(dfStep21 - dfMaxDeltaAlpha),
5670                p1.getX(), p1.getY(), p2.getX(), p2.getY(),
5671                p3.getX(), p3.getY());
5672 #endif
5673         if( j > i + 1 && iMidPoint < 0 && dfDeltaEpsilon < 1.0 / 180.0 * M_PI )
5674         {
5675             if( fabs(dfStep10 - dfMaxDeltaAlpha) > dfDeltaEpsilon )
5676                 iMidPoint = j + ((bInitialConstantStep) ? 0 : 1);
5677             else if( fabs(dfStep21 - dfMaxDeltaAlpha) > dfDeltaEpsilon )
5678                 iMidPoint = j + ((bInitialConstantStep) ? 1 : 2);
5679 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5680             if( iMidPoint >= 0 )
5681             {
5682                 OGRPoint pMid;
5683                 poLS->getPoint(iMidPoint, &pMid);
5684                 printf("Midpoint detected at j = %d, iMidPoint = %d, "/*ok*/
5685                        "x=%.8f y=%.8f\n",
5686                        j, iMidPoint, pMid.getX(), pMid.getY());
5687             }
5688 #endif
5689         }
5690     }
5691 
5692     // Take a minimum threshold of consecutive points
5693     // on the arc to avoid false positives.
5694     if( j < i + 3 )
5695         return -1;
5696 
5697     bValidAlphaRatio &= bFoundFFFFFFFFPattern && bFoundReversedAlphaRatioRef;
5698 
5699 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5700     printf("bValidAlphaRatio=%d bFoundFFFFFFFFPattern=%d, "/*ok*/
5701            "bFoundReversedAlphaRatioRef=%d\n",
5702            static_cast<int>(bValidAlphaRatio),
5703            static_cast<int>(bFoundFFFFFFFFPattern),
5704            static_cast<int>(bFoundReversedAlphaRatioRef));
5705     printf("alpha0_1=%f dfLastValidAlpha=%f\n",/*ok*/
5706             alpha0_1, dfLastValidAlpha);
5707 #endif
5708 
5709     if( poLSNew != nullptr )
5710     {
5711         double dfScale2 = std::max(1.0, fabs(p0.getX()));
5712         dfScale2 = std::max(dfScale2, fabs(p0.getY()));
5713         // Not strictly necessary, but helps having 'clean' lines without
5714         // duplicated points.
5715         if( fabs(poLSNew->getX(poLSNew->getNumPoints()-1) -
5716                  p0.getX()) / dfScale2 > 1.0e-8 ||
5717             fabs(poLSNew->getY(poLSNew->getNumPoints()-1) -
5718                  p0.getY()) / dfScale2 > 1.0e-8 )
5719             poLSNew->addPoint(&p0);
5720         if( poLSNew->getNumPoints() >= 2 )
5721         {
5722             if( poCC == nullptr )
5723                 poCC = new OGRCompoundCurve();
5724             poCC->addCurveDirectly(poLSNew);
5725         }
5726         else
5727             delete poLSNew;
5728         poLSNew = nullptr;
5729     }
5730 
5731     if( poCS == nullptr )
5732     {
5733         poCS = new OGRCircularString();
5734         poCS->addPoint(&p0);
5735     }
5736 
5737     OGRPoint* poFinalPoint =
5738             ( j + 2 >= poLS->getNumPoints() ) ? &p3 : &p2;
5739 
5740     double dfXMid = 0.0;
5741     double dfYMid = 0.0;
5742     double dfZMid = 0.0;
5743     if( bValidAlphaRatio )
5744     {
5745 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5746         printf("Using alpha ratio...\n");/*ok*/
5747 #endif
5748         double dfAlphaMid = 0.0;
5749         if( OGRGF_NeedSwithArcOrder(p0.getX(), p0.getY(),
5750                                     poFinalPoint->getX(),
5751                                     poFinalPoint->getY()) )
5752         {
5753 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5754             printf("Switching angles\n");/*ok*/
5755 #endif
5756             dfAlphaMid = dfLastValidAlpha + nAlphaRatioRef *
5757                     (alpha0_1 - dfLastValidAlpha) / HIDDEN_ALPHA_SCALE;
5758             dfAlphaMid = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlphaMid);
5759         }
5760         else
5761         {
5762             dfAlphaMid = alpha0_1 + nAlphaRatioRef *
5763                     (dfLastValidAlpha - alpha0_1) / HIDDEN_ALPHA_SCALE;
5764         }
5765 
5766         dfXMid = cx_1 + R_1 * cos(dfAlphaMid);
5767         dfYMid = cy_1 + R_1 * sin(dfAlphaMid);
5768 
5769         if( poLS->getCoordinateDimension() == 3 )
5770         {
5771             double dfLastAlpha = 0.0;
5772             double dfLastZ = 0.0;
5773             int k = i;  // Used after for.
5774             for( ; k < j+2; k++ )
5775             {
5776                 OGRPoint p;
5777                 poLS->getPoint(k, &p);
5778                 double dfAlpha = atan2(p.getY() - cy_1, p.getX() - cx_1);
5779                 dfAlpha = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlpha);
5780                 if( k > i &&
5781                     ((dfAlpha < dfLastValidAlpha && dfAlphaMid < dfAlpha) ||
5782                      (dfAlpha > dfLastValidAlpha && dfAlphaMid > dfAlpha)) )
5783                 {
5784                     const double dfRatio =
5785                         (dfAlphaMid - dfLastAlpha) / (dfAlpha - dfLastAlpha);
5786                     dfZMid = (1 - dfRatio) * dfLastZ + dfRatio * p.getZ();
5787                     break;
5788                 }
5789                 dfLastAlpha = dfAlpha;
5790                 dfLastZ = p.getZ();
5791             }
5792             if( k == j + 2 )
5793                 dfZMid = dfLastZ;
5794             if( IS_ALMOST_INTEGER(dfZMid) )
5795                 dfZMid = static_cast<int>(floor(dfZMid + 0.5));
5796         }
5797 
5798         // A few rounding strategies in case the mid point was at "exact"
5799         // coordinates.
5800         if( R_1 > 1e-5 )
5801         {
5802             const bool bStartEndInteger =
5803                 IS_ALMOST_INTEGER(p0.getX()) &&
5804                 IS_ALMOST_INTEGER(p0.getY()) &&
5805                 IS_ALMOST_INTEGER(poFinalPoint->getX()) &&
5806                 IS_ALMOST_INTEGER(poFinalPoint->getY());
5807             if( bStartEndInteger &&
5808                 fabs(dfXMid - floor(dfXMid + 0.5)) / dfScale < 1e-4 &&
5809                 fabs(dfYMid - floor(dfYMid + 0.5)) / dfScale < 1e-4 )
5810             {
5811                 dfXMid = static_cast<int>(floor(dfXMid + 0.5));
5812                 dfYMid = static_cast<int>(floor(dfYMid + 0.5));
5813                 // Sometimes rounding to closest is not best approach
5814                 // Try neighbouring integers to look for the one that
5815                 // minimize the error w.r.t to the arc center
5816                 // But only do that if the radius is greater than
5817                 // the magnitude of the delta that we will try!
5818                 double dfBestRError =
5819                     fabs(R_1 - DISTANCE(dfXMid, dfYMid, cx_1, cy_1));
5820 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5821                 printf("initial_error=%f\n", dfBestRError);/*ok*/
5822 #endif
5823                 int iBestX = 0;
5824                 int iBestY = 0;
5825                 if( dfBestRError > 0.001 && R_1 > 2 )
5826                 {
5827                     int nSearchRadius = 1;
5828                     // Extend the search radius if the arc circle radius
5829                     // is much higher than the coordinate values.
5830                     double dfMaxCoords =
5831                         std::max(fabs(p0.getX()), fabs(p0.getY()));
5832                     dfMaxCoords = std::max(dfMaxCoords, poFinalPoint->getX());
5833                     dfMaxCoords = std::max(dfMaxCoords, poFinalPoint->getY());
5834                     dfMaxCoords = std::max(dfMaxCoords, dfXMid);
5835                     dfMaxCoords = std::max(dfMaxCoords, dfYMid);
5836                     if( R_1 > dfMaxCoords * 1000 )
5837                         nSearchRadius = 100;
5838                     else if( R_1 > dfMaxCoords * 10 )
5839                         nSearchRadius = 10;
5840                     for( int iY = -nSearchRadius; iY <= nSearchRadius; iY++ )
5841                     {
5842                         for( int iX = -nSearchRadius;
5843                              iX <= nSearchRadius;
5844                              iX++ )
5845                         {
5846                             const double dfCandidateX = dfXMid+iX;
5847                             const double dfCandidateY = dfYMid+iY;
5848                             if( fabs(dfCandidateX - p0.getX()) < 1e-8 &&
5849                                 fabs(dfCandidateY - p0.getY()) < 1e-8 )
5850                                 continue;
5851                             if( fabs(dfCandidateX -
5852                                      poFinalPoint->getX()) < 1e-8 &&
5853                                 fabs(dfCandidateY -
5854                                      poFinalPoint->getY()) < 1e-8 )
5855                                 continue;
5856                             const double dfRError =
5857                                 fabs(R_1 - DISTANCE(dfCandidateX, dfCandidateY,
5858                                                     cx_1, cy_1));
5859 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5860                             printf("x=%d y=%d error=%f besterror=%f\n",/*ok*/
5861                                    static_cast<int>(dfXMid + iX),
5862                                    static_cast<int>(dfYMid + iY),
5863                                    dfRError, dfBestRError);
5864 #endif
5865                             if( dfRError < dfBestRError )
5866                             {
5867                                 iBestX = iX;
5868                                 iBestY = iY;
5869                                 dfBestRError = dfRError;
5870                             }
5871                         }
5872                     }
5873                 }
5874                 dfXMid += iBestX;
5875                 dfYMid += iBestY;
5876             }
5877             else
5878             {
5879                 // Limit the number of significant figures in decimal
5880                 // representation.
5881                 if( fabs(dfXMid) < 100000000.0 )
5882                 {
5883                     dfXMid =
5884                         static_cast<GIntBig>(floor(dfXMid * 100000000+0.5)) / 100000000.0;
5885                 }
5886                 if( fabs(dfYMid) < 100000000.0 )
5887                 {
5888                     dfYMid =
5889                         static_cast<GIntBig>(floor(dfYMid * 100000000+0.5)) / 100000000.0;
5890                 }
5891             }
5892         }
5893 
5894 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5895         printf("dfAlphaMid=%f, x_mid = %f, y_mid = %f\n",/*ok*/
5896                dfLastValidAlpha, dfXMid, dfYMid);
5897 #endif
5898     }
5899 
5900     // If this is a full circle of a non-polygonal zone, we must
5901     // use a 5-point representation to keep the winding order.
5902     if( p0.Equals(poFinalPoint) &&
5903         !EQUAL(poLS->getGeometryName(), "LINEARRING") )
5904     {
5905 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5906         printf("Full circle of a non-polygonal zone\n");/*ok*/
5907 #endif
5908         poLS->getPoint((i + j + 2) / 4, &p1);
5909         poCS->addPoint(&p1);
5910         if( bValidAlphaRatio )
5911         {
5912             p1.setX( dfXMid );
5913             p1.setY( dfYMid );
5914             if( poLS->getCoordinateDimension() == 3 )
5915                 p1.setZ( dfZMid );
5916         }
5917         else
5918         {
5919             poLS->getPoint((i + j + 1) / 2, &p1);
5920         }
5921         poCS->addPoint(&p1);
5922         poLS->getPoint(3 * (i + j + 2) / 4, &p1);
5923         poCS->addPoint(&p1);
5924     }
5925 
5926     else if( bValidAlphaRatio )
5927     {
5928         p1.setX( dfXMid );
5929         p1.setY( dfYMid );
5930         if( poLS->getCoordinateDimension() == 3 )
5931             p1.setZ( dfZMid );
5932         poCS->addPoint(&p1);
5933     }
5934 
5935     // If we have found a candidate for a precise intermediate
5936     // point, use it.
5937     else if( iMidPoint >= 1 && iMidPoint < j )
5938     {
5939         poLS->getPoint(iMidPoint, &p1);
5940         poCS->addPoint(&p1);
5941 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5942         printf("Using detected midpoint...\n");/*ok*/
5943         printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY());/*ok*/
5944 #endif
5945         }
5946         // Otherwise pick up the mid point between both extremities.
5947         else
5948         {
5949             poLS->getPoint((i + j + 1) / 2, &p1);
5950             poCS->addPoint(&p1);
5951 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5952             printf("Pickup 'random' midpoint at index=%d...\n",/*ok*/
5953                    (i + j + 1) / 2);
5954             printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY());/*ok*/
5955 #endif
5956         }
5957         poCS->addPoint(poFinalPoint);
5958 
5959 #ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING
5960     printf("----------------------------\n");/*ok*/
5961 #endif
5962 
5963     if( j + 2 >= poLS->getNumPoints() )
5964         return -2;
5965     return j + 1;
5966 }
5967 
5968 /************************************************************************/
5969 /*                        curveFromLineString()                         */
5970 /************************************************************************/
5971 
5972 /**
5973  * \brief Try to convert a linestring approximating curves into a curve.
5974  *
5975  * This method can return a COMPOUNDCURVE, a CIRCULARSTRING or a LINESTRING.
5976  *
5977  * This method is the reverse of curveFromLineString().
5978  *
5979  * @param poLS handle to the geometry to convert.
5980  * @param papszOptions options as a null-terminated list of strings.
5981  *                     Unused for now. Must be set to NULL.
5982  *
5983  * @return the converted geometry (ownership to caller).
5984  *
5985  * @since GDAL 2.0
5986  */
5987 
curveFromLineString(const OGRLineString * poLS,CPL_UNUSED const char * const * papszOptions)5988 OGRCurve* OGRGeometryFactory::curveFromLineString(
5989     const OGRLineString* poLS,
5990     CPL_UNUSED const char * const * papszOptions)
5991 {
5992     OGRCompoundCurve* poCC = nullptr;
5993     OGRCircularString* poCS = nullptr;
5994     OGRLineString* poLSNew = nullptr;
5995     const int nLSNumPoints = poLS->getNumPoints();
5996     const bool bIsClosed = nLSNumPoints >= 4 && poLS->get_IsClosed();
5997     for( int i = 0; i < nLSNumPoints; /* nothing */ )
5998     {
5999         const int iNewI = OGRGF_DetectArc(poLS, i, poCC, poCS, poLSNew);
6000         if( iNewI == -2 )
6001             break;
6002         if( iNewI >= 0 )
6003         {
6004             i = iNewI;
6005             continue;
6006         }
6007 
6008         if( poCS != nullptr )
6009         {
6010             if( poCC == nullptr )
6011                 poCC = new OGRCompoundCurve();
6012             poCC->addCurveDirectly(poCS);
6013             poCS = nullptr;
6014         }
6015 
6016         OGRPoint p;
6017         poLS->getPoint(i, &p);
6018         if( poLSNew == nullptr )
6019         {
6020             poLSNew = new OGRLineString();
6021             poLSNew->addPoint(&p);
6022         }
6023         // Not strictly necessary, but helps having 'clean' lines without
6024         // duplicated points.
6025         else
6026         {
6027             double dfScale = std::max(1.0, fabs(p.getX()));
6028             dfScale = std::max(dfScale, fabs(p.getY()));
6029             if (bIsClosed && i == nLSNumPoints - 1)
6030                 dfScale = 0;
6031             if( fabs(poLSNew->getX(poLSNew->getNumPoints()-1) - p.getX()) >
6032                     1e-8 * dfScale ||
6033                 fabs(poLSNew->getY(poLSNew->getNumPoints()-1) - p.getY()) >
6034                     1e-8 * dfScale )
6035             {
6036                 poLSNew->addPoint(&p);
6037             }
6038         }
6039 
6040         i++;
6041     }
6042 
6043     OGRCurve* poRet = nullptr;
6044 
6045     if( poLSNew != nullptr && poLSNew->getNumPoints() < 2 )
6046     {
6047         delete poLSNew;
6048         poLSNew = nullptr;
6049         if( poCC != nullptr )
6050         {
6051             if( poCC->getNumCurves() == 1 )
6052             {
6053                 poRet = poCC->stealCurve(0);
6054                 delete poCC;
6055                 poCC = nullptr;
6056             }
6057             else
6058                 poRet = poCC;
6059         }
6060         else
6061             poRet = poLS->clone();
6062     }
6063     else if( poCC != nullptr )
6064     {
6065         if( poLSNew )
6066             poCC->addCurveDirectly(poLSNew);
6067         else
6068             poCC->addCurveDirectly(poCS);
6069         poRet = poCC;
6070     }
6071     else if( poLSNew != nullptr )
6072         poRet = poLSNew;
6073     else if( poCS != nullptr )
6074         poRet = poCS;
6075     else
6076         poRet = poLS->clone();
6077 
6078     poRet->assignSpatialReference( poLS->getSpatialReference() );
6079 
6080     return poRet;
6081 }
6082 
6083 /************************************************************************/
6084 /*                   createFromGeoJson( const char* )                   */
6085 /************************************************************************/
6086 
6087 /**
6088  * @brief Create geometry from GeoJson fragment.
6089  * @param pszJsonString The GeoJSON fragment for the geometry.
6090  * @return a geometry on success, or NULL on error.
6091  * @since GDAL 2.3
6092  */
createFromGeoJson(const char * pszJsonString)6093 OGRGeometry* OGRGeometryFactory::createFromGeoJson( const char *pszJsonString )
6094 {
6095     CPLJSONDocument oDocument;
6096     if( !oDocument.LoadMemory( reinterpret_cast<const GByte*>(pszJsonString)) )
6097     {
6098         return nullptr;
6099     }
6100 
6101     return createFromGeoJson( oDocument.GetRoot() );
6102 }
6103 
6104 /************************************************************************/
6105 /*              createFromGeoJson( const CPLJSONObject& )               */
6106 /************************************************************************/
6107 
6108 /**
6109  * @brief Create geometry from GeoJson fragment.
6110  * @param oJsonObject The JSONObject class describes the GeoJSON geometry.
6111  * @return a geometry on success, or NULL on error.
6112  * @since GDAL 2.3
6113  */
createFromGeoJson(const CPLJSONObject & oJsonObject)6114 OGRGeometry* OGRGeometryFactory::createFromGeoJson( const CPLJSONObject &oJsonObject )
6115 {
6116     if( !oJsonObject.IsValid() )
6117     {
6118         return nullptr;
6119     }
6120 
6121     // TODO: Move from GeoJSON driver functions create geometry here, and replace
6122     // json-c specific json_object to CPLJSONObject
6123     return OGRGeoJSONReadGeometry(static_cast<json_object*>(
6124                                       oJsonObject.GetInternalHandle()));
6125 }
6126