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